diff --git a/perf/dom/compile_perf.dart b/perf/dom/compile_perf.dart index 3fcf72831..914c454d8 100644 --- a/perf/dom/compile_perf.dart +++ b/perf/dom/compile_perf.dart @@ -1,23 +1,25 @@ import '_perf.dart'; -main() => describe('compiler', () { - describe('block instantiation', () { - it('time ', inject((TestBed tb) { - tb.compile(UL_REPEATER); - var items = []; - for(var i = 0; i < 100; i++) { - items.add({"text":'text_$i', "done": i & 1 == 1}); - } - var empty = []; - tb.rootScope.context['classFor'] = (item) => 'ng-${item["done"]}'; +void main() { + describe('compiler', () { + describe('block instantiation', () { + it('time ', inject((TestBed tb) { + tb.compile(UL_REPEATER); + var items = []; + for(var i = 0; i < 100; i++) { + items.add({"text":'text_$i', "done": i & 1 == 1}); + } + var empty = []; + tb.rootScope.context['classFor'] = (item) => 'ng-${item["done"]}'; - time('create 100 blocks', - () => tb.rootScope.apply(() => tb.rootScope.context['items'] = items), - cleanUp: () => tb.rootScope.apply(() => tb.rootScope.context['items'] = empty), - verify: () => expect(tb.rootElement.querySelectorAll('li').length).toEqual(100)); - })); + time('create 100 blocks', + () => tb.rootScope.apply(() => tb.rootScope.context['items'] = items), + cleanUp: () => tb.rootScope.apply(() => tb.rootScope.context['items'] = empty), + verify: () => expect(tb.rootElement.querySelectorAll('li').length).toEqual(100)); + })); + }); }); -}); +} var UL_REPEATER = """ diff --git a/test/bootstrap_spec.dart b/test/bootstrap_spec.dart index df76e0a3b..b74e7b263 100644 --- a/test/bootstrap_spec.dart +++ b/test/bootstrap_spec.dart @@ -2,28 +2,30 @@ library bootstrap_spec; import '_specs.dart'; -main() => describe('bootstrap', () { - BodyElement body = window.document.querySelector('body'); +void main() { + describe('bootstrap', () { + BodyElement body = window.document.querySelector('body'); - it('should default to whole page', () { - body.innerHtml = '
{{"works"}}
'; - ngBootstrap(); - expect(body.innerHtml).toEqual('
works
'); - }); + it('should default to whole page', () { + body.innerHtml = '
{{"works"}}
'; + ngBootstrap(); + expect(body.innerHtml).toEqual('
works
'); + }); - it('should compile starting at ng-app node', () { - body.setInnerHtml( - '
{{ignor me}}
', - treeSanitizer: new NullTreeSanitizer()); - ngBootstrap(); - expect(body.text).toEqual('{{ignor me}}works'); - }); + it('should compile starting at ng-app node', () { + body.setInnerHtml( + '
{{ignor me}}
', + treeSanitizer: new NullTreeSanitizer()); + ngBootstrap(); + expect(body.text).toEqual('{{ignor me}}works'); + }); - it('should compile starting at ng-app node', () { - body.setInnerHtml( - '
{{ignor me}}
', - treeSanitizer: new NullTreeSanitizer()); - ngBootstrap(element:body.querySelector('div[ng-bind]')); - expect(body.text).toEqual('{{ignor me}}works'); + it('should compile starting at ng-app node', () { + body.setInnerHtml( + '
{{ignor me}}
', + treeSanitizer: new NullTreeSanitizer()); + ngBootstrap(element:body.querySelector('div[ng-bind]')); + expect(body.text).toEqual('{{ignor me}}works'); + }); }); -}); +} diff --git a/test/change_detection/dirty_checking_change_detector_spec.dart b/test/change_detection/dirty_checking_change_detector_spec.dart index 0d732f2dc..765776ab1 100644 --- a/test/change_detection/dirty_checking_change_detector_spec.dart +++ b/test/change_detection/dirty_checking_change_detector_spec.dart @@ -5,460 +5,462 @@ import 'package:angular/change_detection/change_detection.dart'; import 'package:angular/change_detection/dirty_checking_change_detector.dart'; import 'dart:collection'; -main() => describe('DirtyCheckingChangeDetector', () { - DirtyCheckingChangeDetector detector; - - beforeEach(() { - GetterCache getterCache = new GetterCache({ - "first": (o) => o.first, - "age": (o) => o.age - }); - detector = new DirtyCheckingChangeDetector(getterCache); - }); - - describe('object field', () { - it('should detect nothing', () { - var changes = detector.collectChanges(); - expect(changes).toEqual(null); +void main() { + describe('DirtyCheckingChangeDetector', () { + DirtyCheckingChangeDetector detector; + + beforeEach(() { + GetterCache getterCache = new GetterCache({ + "first": (o) => o.first, + "age": (o) => o.age + }); + detector = new DirtyCheckingChangeDetector(getterCache); }); - it('should detect field changes', () { - var user = new _User('', ''); - var change; - - detector - ..watch(user, 'first', null) - ..watch(user, 'last', null) - ..collectChanges(); // throw away first set - - change = detector.collectChanges(); - expect(change).toEqual(null); - user..first = 'misko' - ..last = 'hevery'; - - change = detector.collectChanges(); - expect(change.currentValue).toEqual('misko'); - expect(change.previousValue).toEqual(''); - expect(change.nextChange.currentValue).toEqual('hevery'); - expect(change.nextChange.previousValue).toEqual(''); - expect(change.nextChange.nextChange).toEqual(null); - - // force different instance - user.first = 'mis'; - user.first += 'ko'; - - change = detector.collectChanges(); - expect(change).toEqual(null); - - user.last = 'Hevery'; - change = detector.collectChanges(); - expect(change.currentValue).toEqual('Hevery'); - expect(change.previousValue).toEqual('hevery'); - expect(change.nextChange).toEqual(null); + describe('object field', () { + it('should detect nothing', () { + var changes = detector.collectChanges(); + expect(changes).toEqual(null); + }); + + it('should detect field changes', () { + var user = new _User('', ''); + var change; + + detector + ..watch(user, 'first', null) + ..watch(user, 'last', null) + ..collectChanges(); // throw away first set + + change = detector.collectChanges(); + expect(change).toEqual(null); + user..first = 'misko' + ..last = 'hevery'; + + change = detector.collectChanges(); + expect(change.currentValue).toEqual('misko'); + expect(change.previousValue).toEqual(''); + expect(change.nextChange.currentValue).toEqual('hevery'); + expect(change.nextChange.previousValue).toEqual(''); + expect(change.nextChange.nextChange).toEqual(null); + + // force different instance + user.first = 'mis'; + user.first += 'ko'; + + change = detector.collectChanges(); + expect(change).toEqual(null); + + user.last = 'Hevery'; + change = detector.collectChanges(); + expect(change.currentValue).toEqual('Hevery'); + expect(change.previousValue).toEqual('hevery'); + expect(change.nextChange).toEqual(null); + }); + + it('should ignore NaN != NaN', () { + var user = new _User(); + user.age = double.NAN; + detector..watch(user, 'age', null)..collectChanges(); // throw away first set + + var changes = detector.collectChanges(); + expect(changes).toEqual(null); + + user.age = 123; + changes = detector.collectChanges(); + expect(changes.currentValue).toEqual(123); + expect(changes.previousValue.isNaN).toEqual(true); + expect(changes.nextChange).toEqual(null); + }); + + it('should treat map field dereference as []', () { + var obj = {'name':'misko'}; + detector.watch(obj, 'name', null); + detector.collectChanges(); // throw away first set + + obj['name'] = 'Misko'; + var changes = detector.collectChanges(); + expect(changes.currentValue).toEqual('Misko'); + expect(changes.previousValue).toEqual('misko'); + }); }); - it('should ignore NaN != NaN', () { - var user = new _User(); - user.age = double.NAN; - detector..watch(user, 'age', null)..collectChanges(); // throw away first set - - var changes = detector.collectChanges(); - expect(changes).toEqual(null); - - user.age = 123; - changes = detector.collectChanges(); - expect(changes.currentValue).toEqual(123); - expect(changes.previousValue.isNaN).toEqual(true); - expect(changes.nextChange).toEqual(null); + describe('insertions / removals', () { + it('should insert at the end of list', () { + var obj = {}; + var a = detector.watch(obj, 'a', 'a'); + var b = detector.watch(obj, 'b', 'b'); + + obj['a'] = obj['b'] = 1; + var changes = detector.collectChanges(); + expect(changes.handler).toEqual('a'); + expect(changes.nextChange.handler).toEqual('b'); + expect(changes.nextChange.nextChange).toEqual(null); + + obj['a'] = obj['b'] = 2; + a.remove(); + changes = detector.collectChanges(); + expect(changes.handler).toEqual('b'); + expect(changes.nextChange).toEqual(null); + + obj['a'] = obj['b'] = 3; + b.remove(); + changes = detector.collectChanges(); + expect(changes).toEqual(null); + }); + + it('should remove all watches in group and group\'s children', () { + var obj = {}; + detector.watch(obj, 'a', '0a'); + var child1a = detector.newGroup(); + var child1b = detector.newGroup(); + var child2 = child1a.newGroup(); + child1a.watch(obj,'a', '1a'); + child1b.watch(obj,'a', '1b'); + detector.watch(obj, 'a', '0A'); + child1a.watch(obj,'a', '1A'); + child2.watch(obj,'a', '2A'); + + obj['a'] = 1; + expect(detector.collectChanges(), + toEqualChanges(['0a', '0A', '1a', '1A', '2A', '1b'])); + + obj['a'] = 2; + child1a.remove(); // should also remove child2 + expect(detector.collectChanges(), toEqualChanges(['0a', '0A', '1b'])); + }); + + it('should add watches within its own group', () { + var obj = {}; + var ra = detector.watch(obj, 'a', 'a'); + var child = detector.newGroup(); + var cb = child.watch(obj,'b', 'b'); + + obj['a'] = obj['b'] = 1; + expect(detector.collectChanges(), toEqualChanges(['a', 'b'])); + + obj['a'] = obj['b'] = 2; + ra.remove(); + expect(detector.collectChanges(), toEqualChanges(['b'])); + + obj['a'] = obj['b'] = 3; + cb.remove(); + expect(detector.collectChanges(), toEqualChanges([])); + + // TODO: add them back in wrong order, assert events in right order + cb = child.watch(obj,'b', 'b'); + ra = detector.watch(obj, 'a', 'a'); + obj['a'] = obj['b'] = 4; + expect(detector.collectChanges(), toEqualChanges(['a', 'b'])); + }); + + it('should properly add children', () { + var a = detector.newGroup(); + var aChild = a.newGroup(); + var b = detector.newGroup(); + expect(detector.collectChanges).not.toThrow(); + }); }); - it('should treat map field dereference as []', () { - var obj = {'name':'misko'}; - detector.watch(obj, 'name', null); - detector.collectChanges(); // throw away first set - - obj['name'] = 'Misko'; - var changes = detector.collectChanges(); - expect(changes.currentValue).toEqual('Misko'); - expect(changes.previousValue).toEqual('misko'); + describe('list watching', () { + it('should detect changes in list', () { + var list = []; + var record = detector.watch(list, null, 'handler'); + expect(detector.collectChanges()).toEqual(null); + + list.add('a'); + expect(detector.collectChanges().currentValue, toEqualCollectionRecord( + collection: ['a[null -> 0]'], + additions: ['a[null -> 0]'], + moves: [], + removals: [])); + + list.add('b'); + expect(detector.collectChanges().currentValue, toEqualCollectionRecord( + collection: ['a', 'b[null -> 1]'], + additions: ['b[null -> 1]'], + moves: [], + removals: [])); + + list.add('c'); + list.add('d'); + expect(detector.collectChanges().currentValue, toEqualCollectionRecord( + collection: ['a', 'b', 'c[null -> 2]', 'd[null -> 3]'], + additions: ['c[null -> 2]', 'd[null -> 3]'], + moves: [], + removals: [])); + + list.remove('c'); + expect(list).toEqual(['a', 'b', 'd']); + expect(detector.collectChanges().currentValue, toEqualCollectionRecord( + collection: ['a', 'b', 'd[3 -> 2]'], + additions: [], + moves: ['d[3 -> 2]'], + removals: ['c[2 -> null]'])); + + list.clear(); + list.addAll(['d', 'c', 'b', 'a']); + expect(detector.collectChanges().currentValue, toEqualCollectionRecord( + collection: ['d[2 -> 0]', 'c[null -> 1]', 'b[1 -> 2]', 'a[0 -> 3]'], + additions: ['c[null -> 1]'], + moves: ['d[2 -> 0]', 'b[1 -> 2]', 'a[0 -> 3]'], + removals: [])); + }); + + it('should detect changes in list', () { + var list = []; + var record = detector.watch(list.map((i) => i), null, 'handler'); + expect(detector.collectChanges()).toEqual(null); + + list.add('a'); + expect(detector.collectChanges().currentValue, toEqualCollectionRecord( + collection: ['a[null -> 0]'], + additions: ['a[null -> 0]'], + moves: [], + removals: [])); + + list.add('b'); + expect(detector.collectChanges().currentValue, toEqualCollectionRecord( + collection: ['a', 'b[null -> 1]'], + additions: ['b[null -> 1]'], + moves: [], + removals: [])); + + list.add('c'); + list.add('d'); + expect(detector.collectChanges().currentValue, toEqualCollectionRecord( + collection: ['a', 'b', 'c[null -> 2]', 'd[null -> 3]'], + additions: ['c[null -> 2]', 'd[null -> 3]'], + moves: [], + removals: [])); + + list.remove('c'); + expect(list).toEqual(['a', 'b', 'd']); + expect(detector.collectChanges().currentValue, toEqualCollectionRecord( + collection: ['a', 'b', 'd[3 -> 2]'], + additions: [], + moves: ['d[3 -> 2]'], + removals: ['c[2 -> null]'])); + + list.clear(); + list.addAll(['d', 'c', 'b', 'a']); + expect(detector.collectChanges().currentValue, toEqualCollectionRecord( + collection: ['d[2 -> 0]', 'c[null -> 1]', 'b[1 -> 2]', 'a[0 -> 3]'], + additions: ['c[null -> 1]'], + moves: ['d[2 -> 0]', 'b[1 -> 2]', 'a[0 -> 3]'], + removals: [])); + }); + + it('should test string by value rather than by reference', () { + var list = ['a', 'boo']; + detector..watch(list, null, null)..collectChanges(); + + list[1] = 'b' + 'oo'; + + expect(detector.collectChanges()).toEqual(null); + }); + + it('should ignore [NaN] != [NaN]', () { + var list = [double.NAN]; + var record = detector..watch(list, null, null)..collectChanges(); + + expect(detector.collectChanges()).toEqual(null); + }); + + it('should remove and add same item', () { + var list = ['a', 'b', 'c']; + var record = detector.watch(list, null, 'handler'); + detector.collectChanges(); + + list.remove('b'); + expect(detector.collectChanges().currentValue, toEqualCollectionRecord( + collection: ['a', 'c[2 -> 1]'], + additions: [], + moves: ['c[2 -> 1]'], + removals: ['b[1 -> null]'])); + + list.insert(1, 'b'); + expect(list).toEqual(['a', 'b', 'c']); + expect(detector.collectChanges().currentValue, toEqualCollectionRecord( + collection: ['a', 'b[null -> 1]', 'c[1 -> 2]'], + additions: ['b[null -> 1]'], + moves: ['c[1 -> 2]'], + removals: [])); + }); + + it('should support duplicates', () { + var list = ['a', 'a', 'a', 'b', 'b']; + var record = detector.watch(list, null, 'handler'); + detector.collectChanges(); + + list.removeAt(0); + expect(detector.collectChanges().currentValue, toEqualCollectionRecord( + collection: ['a', 'a', 'b[3 -> 2]', 'b[4 -> 3]'], + additions: [], + moves: ['b[3 -> 2]', 'b[4 -> 3]'], + removals: ['a[2 -> null]'])); + }); + + + it('should support insertions/moves', () { + var list = ['a', 'a', 'b', 'b']; + var record = detector.watch(list, null, 'handler'); + detector.collectChanges(); + list.insert(0, 'b'); + expect(list).toEqual(['b', 'a', 'a', 'b', 'b']); + expect(detector.collectChanges().currentValue, toEqualCollectionRecord( + collection: ['b[2 -> 0]', 'a[0 -> 1]', 'a[1 -> 2]', 'b', 'b[null -> 4]'], + additions: ['b[null -> 4]'], + moves: ['b[2 -> 0]', 'a[0 -> 1]', 'a[1 -> 2]'], + removals: [])); + }); + + it('should support UnmodifiableListView', () { + var hiddenList = [1]; + var list = new UnmodifiableListView(hiddenList); + var record = detector.watch(list, null, 'handler'); + expect(detector.collectChanges().currentValue, toEqualCollectionRecord( + collection: ['1[null -> 0]'], + additions: ['1[null -> 0]'], + moves: [], + removals: [])); + + // assert no changes detected + expect(detector.collectChanges()).toEqual(null); + + // change the hiddenList normally this should trigger change detection + // but because we are wrapped in UnmodifiableListView we see nothing. + hiddenList[0] = 2; + expect(detector.collectChanges()).toEqual(null); + }); + + it('should bug', () { + var list = [1, 2, 3, 4]; + var record = detector.watch(list, null, 'handler'); + expect(detector.collectChanges().currentValue, toEqualCollectionRecord( + collection: ['1[null -> 0]', '2[null -> 1]', '3[null -> 2]', '4[null -> 3]'], + additions: ['1[null -> 0]', '2[null -> 1]', '3[null -> 2]', '4[null -> 3]'], + moves: [], + removals: [])); + detector.collectChanges(); + + list.removeRange(0, 1); + expect(detector.collectChanges().currentValue, toEqualCollectionRecord( + collection: ['2[1 -> 0]', '3[2 -> 1]', '4[3 -> 2]'], + additions: [], + moves: ['2[1 -> 0]', '3[2 -> 1]', '4[3 -> 2]'], + removals: ['1[0 -> null]'])); + + list.insert(0, 1); + expect(detector.collectChanges().currentValue, toEqualCollectionRecord( + collection: ['1[null -> 0]', '2[0 -> 1]', '3[1 -> 2]', '4[2 -> 3]'], + additions: ['1[null -> 0]'], + moves: ['2[0 -> 1]', '3[1 -> 2]', '4[2 -> 3]'], + removals: [])); + }); }); - }); - - describe('insertions / removals', () { - it('should insert at the end of list', () { - var obj = {}; - var a = detector.watch(obj, 'a', 'a'); - var b = detector.watch(obj, 'b', 'b'); - - obj['a'] = obj['b'] = 1; - var changes = detector.collectChanges(); - expect(changes.handler).toEqual('a'); - expect(changes.nextChange.handler).toEqual('b'); - expect(changes.nextChange.nextChange).toEqual(null); - - obj['a'] = obj['b'] = 2; - a.remove(); - changes = detector.collectChanges(); - expect(changes.handler).toEqual('b'); - expect(changes.nextChange).toEqual(null); - - obj['a'] = obj['b'] = 3; - b.remove(); - changes = detector.collectChanges(); - expect(changes).toEqual(null); - }); - - it('should remove all watches in group and group\'s children', () { - var obj = {}; - detector.watch(obj, 'a', '0a'); - var child1a = detector.newGroup(); - var child1b = detector.newGroup(); - var child2 = child1a.newGroup(); - child1a.watch(obj,'a', '1a'); - child1b.watch(obj,'a', '1b'); - detector.watch(obj, 'a', '0A'); - child1a.watch(obj,'a', '1A'); - child2.watch(obj,'a', '2A'); - - obj['a'] = 1; - expect(detector.collectChanges(), - toEqualChanges(['0a', '0A', '1a', '1A', '2A', '1b'])); - - obj['a'] = 2; - child1a.remove(); // should also remove child2 - expect(detector.collectChanges(), toEqualChanges(['0a', '0A', '1b'])); - }); - - it('should add watches within its own group', () { - var obj = {}; - var ra = detector.watch(obj, 'a', 'a'); - var child = detector.newGroup(); - var cb = child.watch(obj,'b', 'b'); - - obj['a'] = obj['b'] = 1; - expect(detector.collectChanges(), toEqualChanges(['a', 'b'])); - - obj['a'] = obj['b'] = 2; - ra.remove(); - expect(detector.collectChanges(), toEqualChanges(['b'])); - obj['a'] = obj['b'] = 3; - cb.remove(); - expect(detector.collectChanges(), toEqualChanges([])); - - // TODO: add them back in wrong order, assert events in right order - cb = child.watch(obj,'b', 'b'); - ra = detector.watch(obj, 'a', 'a'); - obj['a'] = obj['b'] = 4; - expect(detector.collectChanges(), toEqualChanges(['a', 'b'])); + describe('map watching', () { + it('should do basic map watching', () { + var map = {}; + var record = detector.watch(map, null, 'handler'); + expect(detector.collectChanges()).toEqual(null); + + map['a'] = 'A'; + expect(detector.collectChanges().currentValue, toEqualMapRecord( + map: ['a[null -> A]'], + additions: ['a[null -> A]'], + changes: [], + removals: [])); + + map['b'] = 'B'; + expect(detector.collectChanges().currentValue, toEqualMapRecord( + map: ['a', 'b[null -> B]'], + additions: ['b[null -> B]'], + changes: [], + removals: [])); + + map['b'] = 'BB'; + map['d'] = 'D'; + expect(detector.collectChanges().currentValue, toEqualMapRecord( + map: ['a', 'b[B -> BB]', 'd[null -> D]'], + additions: ['d[null -> D]'], + changes: ['b[B -> BB]'], + removals: [])); + + map.remove('b'); + expect(map).toEqual({'a': 'A', 'd':'D'}); + expect(detector.collectChanges().currentValue, toEqualMapRecord( + map: ['a', 'd'], + additions: [], + changes: [], + removals: ['b[BB -> null]'])); + + map.clear(); + expect(detector.collectChanges().currentValue, toEqualMapRecord( + map: [], + additions: [], + changes: [], + removals: ['a[A -> null]', 'd[D -> null]'])); + }); + + it('should test string keys by value rather than by reference', () { + var map = {'foo': 0}; + detector..watch(map, null, null)..collectChanges(); + + map['f' + 'oo'] = 0; + + expect(detector.collectChanges()).toEqual(null); + }); + + it('should test string values by value rather than by reference', () { + var map = {'foo': 'bar'}; + detector..watch(map, null, null)..collectChanges(); + + map['foo'] = 'b' + 'ar'; + + expect(detector.collectChanges()).toEqual(null); + }); + + it('should not see a NaN value as a change', () { + var map = {'foo': double.NAN}; + var record = detector..watch(map, null, null)..collectChanges(); + + expect(detector.collectChanges()).toEqual(null); + }); }); - it('should properly add children', () { - var a = detector.newGroup(); - var aChild = a.newGroup(); - var b = detector.newGroup(); - expect(detector.collectChanges).not.toThrow(); + describe('DuplicateMap', () { + DuplicateMap map; + beforeEach(() => map = new DuplicateMap()); + + it('should do basic operations', () { + var k1 = 'a'; + var r1 = new ItemRecord(k1)..currentIndex = 1; + map.put(r1); + expect(map.get(k1, 2)).toEqual(null); + expect(map.get(k1, 1)).toEqual(null); + expect(map.get(k1, 0)).toEqual(r1); + expect(map.remove(r1)).toEqual(r1); + expect(map.get(k1, -1)).toEqual(null); + }); + + it('should do basic operations on duplicate keys', () { + var k1 = 'a'; + var r1 = new ItemRecord(k1)..currentIndex = 1; + var r2 = new ItemRecord(k1)..currentIndex = 2; + map..put(r1)..put(r2); + expect(map.get(k1, 0)).toEqual(r1); + expect(map.get(k1, 1)).toEqual(r2); + expect(map.get(k1, 2)).toEqual(null); + expect(map.remove(r2)).toEqual(r2); + expect(map.get(k1, 0)).toEqual(r1); + expect(map.remove(r1)).toEqual(r1); + expect(map.get(k1, 0)).toEqual(null); + }); }); }); - - describe('list watching', () { - it('should detect changes in list', () { - var list = []; - var record = detector.watch(list, null, 'handler'); - expect(detector.collectChanges()).toEqual(null); - - list.add('a'); - expect(detector.collectChanges().currentValue, toEqualCollectionRecord( - collection: ['a[null -> 0]'], - additions: ['a[null -> 0]'], - moves: [], - removals: [])); - - list.add('b'); - expect(detector.collectChanges().currentValue, toEqualCollectionRecord( - collection: ['a', 'b[null -> 1]'], - additions: ['b[null -> 1]'], - moves: [], - removals: [])); - - list.add('c'); - list.add('d'); - expect(detector.collectChanges().currentValue, toEqualCollectionRecord( - collection: ['a', 'b', 'c[null -> 2]', 'd[null -> 3]'], - additions: ['c[null -> 2]', 'd[null -> 3]'], - moves: [], - removals: [])); - - list.remove('c'); - expect(list).toEqual(['a', 'b', 'd']); - expect(detector.collectChanges().currentValue, toEqualCollectionRecord( - collection: ['a', 'b', 'd[3 -> 2]'], - additions: [], - moves: ['d[3 -> 2]'], - removals: ['c[2 -> null]'])); - - list.clear(); - list.addAll(['d', 'c', 'b', 'a']); - expect(detector.collectChanges().currentValue, toEqualCollectionRecord( - collection: ['d[2 -> 0]', 'c[null -> 1]', 'b[1 -> 2]', 'a[0 -> 3]'], - additions: ['c[null -> 1]'], - moves: ['d[2 -> 0]', 'b[1 -> 2]', 'a[0 -> 3]'], - removals: [])); - }); - - it('should detect changes in list', () { - var list = []; - var record = detector.watch(list.map((i) => i), null, 'handler'); - expect(detector.collectChanges()).toEqual(null); - - list.add('a'); - expect(detector.collectChanges().currentValue, toEqualCollectionRecord( - collection: ['a[null -> 0]'], - additions: ['a[null -> 0]'], - moves: [], - removals: [])); - - list.add('b'); - expect(detector.collectChanges().currentValue, toEqualCollectionRecord( - collection: ['a', 'b[null -> 1]'], - additions: ['b[null -> 1]'], - moves: [], - removals: [])); - - list.add('c'); - list.add('d'); - expect(detector.collectChanges().currentValue, toEqualCollectionRecord( - collection: ['a', 'b', 'c[null -> 2]', 'd[null -> 3]'], - additions: ['c[null -> 2]', 'd[null -> 3]'], - moves: [], - removals: [])); - - list.remove('c'); - expect(list).toEqual(['a', 'b', 'd']); - expect(detector.collectChanges().currentValue, toEqualCollectionRecord( - collection: ['a', 'b', 'd[3 -> 2]'], - additions: [], - moves: ['d[3 -> 2]'], - removals: ['c[2 -> null]'])); - - list.clear(); - list.addAll(['d', 'c', 'b', 'a']); - expect(detector.collectChanges().currentValue, toEqualCollectionRecord( - collection: ['d[2 -> 0]', 'c[null -> 1]', 'b[1 -> 2]', 'a[0 -> 3]'], - additions: ['c[null -> 1]'], - moves: ['d[2 -> 0]', 'b[1 -> 2]', 'a[0 -> 3]'], - removals: [])); - }); - - it('should test string by value rather than by reference', () { - var list = ['a', 'boo']; - detector..watch(list, null, null)..collectChanges(); - - list[1] = 'b' + 'oo'; - - expect(detector.collectChanges()).toEqual(null); - }); - - it('should ignore [NaN] != [NaN]', () { - var list = [double.NAN]; - var record = detector..watch(list, null, null)..collectChanges(); - - expect(detector.collectChanges()).toEqual(null); - }); - - it('should remove and add same item', () { - var list = ['a', 'b', 'c']; - var record = detector.watch(list, null, 'handler'); - detector.collectChanges(); - - list.remove('b'); - expect(detector.collectChanges().currentValue, toEqualCollectionRecord( - collection: ['a', 'c[2 -> 1]'], - additions: [], - moves: ['c[2 -> 1]'], - removals: ['b[1 -> null]'])); - - list.insert(1, 'b'); - expect(list).toEqual(['a', 'b', 'c']); - expect(detector.collectChanges().currentValue, toEqualCollectionRecord( - collection: ['a', 'b[null -> 1]', 'c[1 -> 2]'], - additions: ['b[null -> 1]'], - moves: ['c[1 -> 2]'], - removals: [])); - }); - - it('should support duplicates', () { - var list = ['a', 'a', 'a', 'b', 'b']; - var record = detector.watch(list, null, 'handler'); - detector.collectChanges(); - - list.removeAt(0); - expect(detector.collectChanges().currentValue, toEqualCollectionRecord( - collection: ['a', 'a', 'b[3 -> 2]', 'b[4 -> 3]'], - additions: [], - moves: ['b[3 -> 2]', 'b[4 -> 3]'], - removals: ['a[2 -> null]'])); - }); - - - it('should support insertions/moves', () { - var list = ['a', 'a', 'b', 'b']; - var record = detector.watch(list, null, 'handler'); - detector.collectChanges(); - list.insert(0, 'b'); - expect(list).toEqual(['b', 'a', 'a', 'b', 'b']); - expect(detector.collectChanges().currentValue, toEqualCollectionRecord( - collection: ['b[2 -> 0]', 'a[0 -> 1]', 'a[1 -> 2]', 'b', 'b[null -> 4]'], - additions: ['b[null -> 4]'], - moves: ['b[2 -> 0]', 'a[0 -> 1]', 'a[1 -> 2]'], - removals: [])); - }); - - it('should support UnmodifiableListView', () { - var hiddenList = [1]; - var list = new UnmodifiableListView(hiddenList); - var record = detector.watch(list, null, 'handler'); - expect(detector.collectChanges().currentValue, toEqualCollectionRecord( - collection: ['1[null -> 0]'], - additions: ['1[null -> 0]'], - moves: [], - removals: [])); - - // assert no changes detected - expect(detector.collectChanges()).toEqual(null); - - // change the hiddenList normally this should trigger change detection - // but because we are wrapped in UnmodifiableListView we see nothing. - hiddenList[0] = 2; - expect(detector.collectChanges()).toEqual(null); - }); - - it('should bug', () { - var list = [1, 2, 3, 4]; - var record = detector.watch(list, null, 'handler'); - expect(detector.collectChanges().currentValue, toEqualCollectionRecord( - collection: ['1[null -> 0]', '2[null -> 1]', '3[null -> 2]', '4[null -> 3]'], - additions: ['1[null -> 0]', '2[null -> 1]', '3[null -> 2]', '4[null -> 3]'], - moves: [], - removals: [])); - detector.collectChanges(); - - list.removeRange(0, 1); - expect(detector.collectChanges().currentValue, toEqualCollectionRecord( - collection: ['2[1 -> 0]', '3[2 -> 1]', '4[3 -> 2]'], - additions: [], - moves: ['2[1 -> 0]', '3[2 -> 1]', '4[3 -> 2]'], - removals: ['1[0 -> null]'])); - - list.insert(0, 1); - expect(detector.collectChanges().currentValue, toEqualCollectionRecord( - collection: ['1[null -> 0]', '2[0 -> 1]', '3[1 -> 2]', '4[2 -> 3]'], - additions: ['1[null -> 0]'], - moves: ['2[0 -> 1]', '3[1 -> 2]', '4[2 -> 3]'], - removals: [])); - }); - }); - - describe('map watching', () { - it('should do basic map watching', () { - var map = {}; - var record = detector.watch(map, null, 'handler'); - expect(detector.collectChanges()).toEqual(null); - - map['a'] = 'A'; - expect(detector.collectChanges().currentValue, toEqualMapRecord( - map: ['a[null -> A]'], - additions: ['a[null -> A]'], - changes: [], - removals: [])); - - map['b'] = 'B'; - expect(detector.collectChanges().currentValue, toEqualMapRecord( - map: ['a', 'b[null -> B]'], - additions: ['b[null -> B]'], - changes: [], - removals: [])); - - map['b'] = 'BB'; - map['d'] = 'D'; - expect(detector.collectChanges().currentValue, toEqualMapRecord( - map: ['a', 'b[B -> BB]', 'd[null -> D]'], - additions: ['d[null -> D]'], - changes: ['b[B -> BB]'], - removals: [])); - - map.remove('b'); - expect(map).toEqual({'a': 'A', 'd':'D'}); - expect(detector.collectChanges().currentValue, toEqualMapRecord( - map: ['a', 'd'], - additions: [], - changes: [], - removals: ['b[BB -> null]'])); - - map.clear(); - expect(detector.collectChanges().currentValue, toEqualMapRecord( - map: [], - additions: [], - changes: [], - removals: ['a[A -> null]', 'd[D -> null]'])); - }); - - it('should test string keys by value rather than by reference', () { - var map = {'foo': 0}; - detector..watch(map, null, null)..collectChanges(); - - map['f' + 'oo'] = 0; - - expect(detector.collectChanges()).toEqual(null); - }); - - it('should test string values by value rather than by reference', () { - var map = {'foo': 'bar'}; - detector..watch(map, null, null)..collectChanges(); - - map['foo'] = 'b' + 'ar'; - - expect(detector.collectChanges()).toEqual(null); - }); - - it('should not see a NaN value as a change', () { - var map = {'foo': double.NAN}; - var record = detector..watch(map, null, null)..collectChanges(); - - expect(detector.collectChanges()).toEqual(null); - }); - }); - - describe('DuplicateMap', () { - DuplicateMap map; - beforeEach(() => map = new DuplicateMap()); - - it('should do basic operations', () { - var k1 = 'a'; - var r1 = new ItemRecord(k1)..currentIndex = 1; - map.put(r1); - expect(map.get(k1, 2)).toEqual(null); - expect(map.get(k1, 1)).toEqual(null); - expect(map.get(k1, 0)).toEqual(r1); - expect(map.remove(r1)).toEqual(r1); - expect(map.get(k1, -1)).toEqual(null); - }); - - it('should do basic operations on duplicate keys', () { - var k1 = 'a'; - var r1 = new ItemRecord(k1)..currentIndex = 1; - var r2 = new ItemRecord(k1)..currentIndex = 2; - map..put(r1)..put(r2); - expect(map.get(k1, 0)).toEqual(r1); - expect(map.get(k1, 1)).toEqual(r2); - expect(map.get(k1, 2)).toEqual(null); - expect(map.remove(r2)).toEqual(r2); - expect(map.get(k1, 0)).toEqual(r1); - expect(map.remove(r1)).toEqual(r1); - expect(map.get(k1, 0)).toEqual(null); - }); - }); -}); +} class _User { String first; diff --git a/test/change_detection/watch_group_spec.dart b/test/change_detection/watch_group_spec.dart index cb13b0ecc..1c5e6bd73 100644 --- a/test/change_detection/watch_group_spec.dart +++ b/test/change_detection/watch_group_spec.dart @@ -5,642 +5,644 @@ import 'package:angular/change_detection/watch_group.dart'; import 'package:angular/change_detection/dirty_checking_change_detector.dart'; import 'dirty_checking_change_detector_spec.dart' hide main; -main() => describe('WatchGroup', () { - var context; - var watchGrp; - DirtyCheckingChangeDetector changeDetector; - Logger logger; - - AST parse(String expression) { - var currentAST = new ContextReferenceAST(); - expression.split('.').forEach((name) { - currentAST = new FieldReadAST(currentAST, name); - }); - return currentAST; - } - - expectOrder(list) { - logger.clear(); - watchGrp.detectChanges(); // Clear the initial queue - logger.clear(); - watchGrp.detectChanges(); - expect(logger).toEqual(list); - } - - 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'); - watch.remove(); +void main() { + describe('WatchGroup', () { + var context; + var watchGrp; + DirtyCheckingChangeDetector changeDetector; + Logger logger; + + AST parse(String expression) { + var currentAST = new ContextReferenceAST(); + expression.split('.').forEach((name) { + currentAST = new FieldReadAST(currentAST, name); }); - 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'; - - // 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']); - - // Should detect value change - context['a'] = 'bye'; - watchGrp.detectChanges(); - expect(logger).toEqual(['hello', 'bye']); + return currentAST; + } - // should cleanup after itself - watch.remove(); - expect(watchGrp.fieldCost).toEqual(0); - context['a'] = 'cant see me'; - watchGrp.detectChanges(); - expect(logger).toEqual(['hello', 'bye']); - }); - - 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'; + expectOrder(list) { + logger.clear(); + watchGrp.detectChanges(); // Clear the initial queue + logger.clear(); watchGrp.detectChanges(); - expect(logger).toEqual(['hello', 'hello2', 'bye']); + expect(logger).toEqual(list); + } + + 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'); + watch.remove(); + }); + watch = watchGrp.watch(parse('a'), (v, p) => logger(v)); + watchGrp.detectChanges(); + expect(logger).toEqual(['removed']); + }); }); - it('should reuse handlers', () { - var user1 = {'first': 'misko', 'last': 'hevery'}; - var user2 = {'first': 'misko', 'last': 'Hevery'}; + describe('property chaining', () { + it('should read property', () { + context['a'] = 'hello'; - context['user'] = user1; + // 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']); - // 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); + // make sore no new changes are logged on extra detectChanges + watchGrp.detectChanges(); + expect(logger).toEqual(['hello']); - watchGrp.detectChanges(); - expect(logger).toEqual([user1, 'misko', 'hevery']); - logger.clear(); - - context['user'] = user2; - watchGrp.detectChanges(); - expect(logger).toEqual([user2, 'Hevery']); + // 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.remove(); - expect(watchGrp.fieldCost).toEqual(3); - - watchFirst.remove(); - expect(watchGrp.fieldCost).toEqual(2); + 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']); + }); - 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]); - - watch.remove(); - expect(watchGrp.fieldCost).toEqual(0); - expect(watchGrp.evalCost).toEqual(0); - - context['a']['val'] = 0; - context['b']['val'] = 0; + context['a'] = {'val': 2}; + watchGrp.detectChanges(); + expect(logger).toEqual([[2]]); + }); - watchGrp.detectChanges(); - expect(logger).toEqual(['+', 3, '+', 7]); - }); + it('should eval pure function', () { + context['a'] = {'val': 1}; + context['b'] = {'val': 2}; - it('should eval chained pure function', () { - context['a'] = {'val': 1}; - context['b'] = {'val': 2}; - context['c'] = {'val': 3}; + var watch = watchGrp.watch( + new PureFunctionAST('add', + (a, b) { logger('+'); return a+b; }, + [parse('a.val'), parse('b.val')] + ), + (v, p) => logger(v) + ); - var a_plus_b = new PureFunctionAST('add1', - (a, b) { logger('$a+$b'); return a + b; }, - [parse('a.val'), parse('b.val')]); + // a; a.val; b; b.val; + expect(watchGrp.fieldCost).toEqual(4); + // add + expect(watchGrp.evalCost).toEqual(1); - 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(['+', 3]); - var watch = watchGrp.watch(a_plus_b_plus_c, (v, p) => logger(v)); + // extra checks should not trigger functions + watchGrp.detectChanges(); + watchGrp.detectChanges(); + expect(logger).toEqual(['+', 3]); - // a; a.val; b; b.val; c; c.val; - expect(watchGrp.fieldCost).toEqual(6); - // add - expect(watchGrp.evalCost).toEqual(2); + // multiple arg changes should only trigger function once. + context['a']['val'] = 3; + context['b']['val'] = 4; - watchGrp.detectChanges(); - expect(logger).toEqual(['1+2', '3+3', 6]); - logger.clear(); + watchGrp.detectChanges(); + expect(logger).toEqual(['+', 3, '+', 7]); - // 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(['+', 3, '+', 7]); + }); - context['c']['val'] = 9; - watchGrp.detectChanges(); - expect(logger).toEqual(['13+9', 22]); - 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 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(['1+2', '3+3', 6]); + logger.clear(); - watchGrp.detectChanges(); - expect(logger).toEqual(['methodA(1) => A', 'A']); - logger.clear(); + // extra checks should not trigger functions + watchGrp.detectChanges(); + watchGrp.detectChanges(); + expect(logger).toEqual([]); + logger.clear(); - watchGrp.detectChanges(); - watchGrp.detectChanges(); - expect(logger).toEqual(['methodA(1) => A', 'methodA(1) => A']); - logger.clear(); + // 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(); - obj['valA'] = 'B'; - context['arg0'] = 2; + context['a']['val'] = 9; + watchGrp.detectChanges(); + expect(logger).toEqual(['9+4', '13+5', 18]); + logger.clear(); - watchGrp.detectChanges(); - expect(logger).toEqual(['methodA(2) => B', 'B']); - logger.clear(); + context['c']['val'] = 9; + watchGrp.detectChanges(); + expect(logger).toEqual(['13+9', 22]); + logger.clear(); - watch.remove(); - expect(watchGrp.fieldCost).toEqual(0); - expect(watchGrp.evalCost).toEqual(0); - obj['valA'] = 'C'; - context['arg0'] = 3; + watch.remove(); + expect(watchGrp.fieldCost).toEqual(0); + expect(watchGrp.evalCost).toEqual(0); - watchGrp.detectChanges(); - expect(logger).toEqual([]); - }); + context['a']['val'] = 0; + context['b']['val'] = 0; + watchGrp.detectChanges(); + expect(logger).toEqual([]); + }); - 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) - ); + 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(); - // 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(); + obj['valA'] = 'C'; + context['arg0'] = 3; - 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 method', () { + var obj = new MyClass(logger); + obj.valA = 'A'; + context['obj'] = obj; + context['arg0'] = 1; - watch.remove(); - expect(watchGrp.fieldCost).toEqual(0); - expect(watchGrp.evalCost).toEqual(0); + var watch = watchGrp.watch( + new MethodAST(parse('obj'), 'methodA', [parse('arg0')]), + (v, p) => logger(v) + ); - obj.valA = 'C'; - context['arg0'] = 3; + // obj, arg0; + expect(watchGrp.fieldCost).toEqual(2); + // methodA() + expect(watchGrp.evalCost).toEqual(1); - watchGrp.detectChanges(); - expect(logger).toEqual([]); - }); + watchGrp.detectChanges(); + expect(logger).toEqual(['methodA(1) => A', 'A']); + logger.clear(); - 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); + watchGrp.detectChanges(); + watchGrp.detectChanges(); + expect(logger).toEqual(['methodA(1) => A', 'methodA(1) => A']); + logger.clear(); - watchGrp.detectChanges(); - expect(logger).toEqual(['methodA(0) => MyClass', 'methodA(1) => A', 'A']); - logger.clear(); + obj.valA = 'B'; + context['arg0'] = 2; - watchGrp.detectChanges(); - watchGrp.detectChanges(); - expect(logger).toEqual(['methodA(0) => MyClass', 'methodA(1) => A', - 'methodA(0) => MyClass', 'methodA(1) => A']); - logger.clear(); + watchGrp.detectChanges(); + expect(logger).toEqual(['methodA(2) => B', 'B']); + logger.clear(); - obj2.valA = 'B'; - context['arg0'] = 10; - context['arg1'] = 11; + watch.remove(); + expect(watchGrp.fieldCost).toEqual(0); + expect(watchGrp.evalCost).toEqual(0); - watchGrp.detectChanges(); - expect(logger).toEqual(['methodA(10) => MyClass', 'methodA(11) => B', 'B']); - logger.clear(); + obj.valA = 'C'; + context['arg0'] = 3; - watch.remove(); - expect(watchGrp.fieldCost).toEqual(0); - expect(watchGrp.evalCost).toEqual(0); + watchGrp.detectChanges(); + expect(logger).toEqual([]); + }); - obj2.valA = 'C'; - context['arg0'] = 20; - context['arg1'] = 21; + 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); + + 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([]); - }); + watch.remove(); + expect(watchGrp.fieldCost).toEqual(0); + expect(watchGrp.evalCost).toEqual(0); - 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]); + obj2.valA = 'C'; + context['arg0'] = 20; + context['arg1'] = 21; - // make sore no new changes are logged on extra detectChanges - watchGrp.detectChanges(); - expect(logger).toEqual([123]); - }); + watchGrp.detectChanges(); + expect(logger).toEqual([]); + }); - it('should wrap iterable in ObservableList', () { - context['list'] = []; - var watch = watchGrp.watch(new CollectionAST(parse('list')), (v, p) => logger(v)); + 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]); + }); - 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]'], + 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('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); + }); - 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 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 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 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')); - context['a'] = 2; - expect(watchGrp.detectChanges()).not.toBe(null); - expect(logger).toEqual([-3]); - }); - }); + expectOrder(['a', 'b']); + expectOrder(['a', '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(); + ra.remove(); + expectOrder(['b']); - context['a'] = 1; - expect(watchGrp.detectChanges()).toEqual(6); - expect(logger).toEqual(['0a', '0A', '1a', '1A', '2A', '1b']); // we go by group order - logger.clear(); + cb.remove(); + expectOrder([]); - 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); - }); + // 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']); + }); - 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 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')); + 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']); + }); - expectOrder(['a', 'b']); - expectOrder(['a', 'b']); - ra.remove(); - expectOrder(['b']); - cb.remove(); - expectOrder([]); + 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)); - // 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']); - }); + watchGrp.detectChanges(); + expect(logger).toEqual(['OK', 'OK']); + logger.clear(); + context['a'] = 'A'; + childContext['b'] = 'B'; - 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(); - } + watchGrp.detectChanges(); + expect(logger).toEqual(['A', 'B']); + logger.clear(); }); - 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)); - - watchGrp.detectChanges(); - expect(logger).toEqual(['OK', 'OK']); - logger.clear(); - - context['a'] = 'A'; - childContext['b'] = 'B'; - - watchGrp.detectChanges(); - expect(logger).toEqual(['A', 'B']); - logger.clear(); - }); }); - -}); +} class MyClass { final Logger logger; diff --git a/test/core/cache_spec.dart b/test/core/cache_spec.dart index 67c21644f..f7b8fbd5e 100644 --- a/test/core/cache_spec.dart +++ b/test/core/cache_spec.dart @@ -2,149 +2,151 @@ library cache_spec; import '../_specs.dart'; -main() => describe('CacheFactory', () { +void main() { + describe('CacheFactory', () { - describe('cache', () { - Cache cache; + describe('cache', () { + Cache cache; - beforeEach(() { - cache = new LruCache(); - }); + beforeEach(() { + cache = new LruCache(); + }); - describe('put, get & remove', () { - it('should add cache entries via add and retrieve them via get', () { - var obj = {'bar':'baz'}; - cache.put('key1', 'bar'); - cache.put('key2', obj); + describe('put, get & remove', () { + it('should add cache entries via add and retrieve them via get', () { + var obj = {'bar':'baz'}; + cache.put('key1', 'bar'); + cache.put('key2', obj); - expect(cache.get('key2')).toBe(obj); - expect(cache.get('key1')).toBe('bar'); - }); + expect(cache.get('key2')).toBe(obj); + expect(cache.get('key1')).toBe('bar'); + }); - it('should remove entries via remove', () { - cache.put('k1', 'foo'); - cache.put('k2', 'bar'); + it('should remove entries via remove', () { + cache.put('k1', 'foo'); + cache.put('k2', 'bar'); - cache.remove('k2'); + cache.remove('k2'); - expect(cache.get('k1')).toBe('foo'); - expect(cache.get('k2')).toBeNull(); + expect(cache.get('k1')).toBe('foo'); + expect(cache.get('k2')).toBeNull(); - cache.remove('k1'); + cache.remove('k1'); - expect(cache.get('k1')).toBeNull(); - expect(cache.get('k2')).toBeNull(); - }); + expect(cache.get('k1')).toBeNull(); + expect(cache.get('k2')).toBeNull(); + }); - it('should return null when entry does not exist', () { - expect(cache.remove('non-existent')).toBeNull(); - }); + it('should return null when entry does not exist', () { + expect(cache.remove('non-existent')).toBeNull(); + }); - // TODO(chirayu): to implement - // it('should stringify keys', () { - // cache.put('123', 'foo'); - // cache.put(123, 'bar'); + // TODO(chirayu): to implement + // it('should stringify keys', () { + // cache.put('123', 'foo'); + // cache.put(123, 'bar'); - // expect(cache.get('123')).toBe('bar'); - // expect(cache.info().size).toBe(1); + // expect(cache.get('123')).toBe('bar'); + // expect(cache.info().size).toBe(1); - // cache.remove(123); - // expect(cache.info().size).toBe(0); - // }); + // cache.remove(123); + // expect(cache.info().size).toBe(0); + // }); - it("should return value from put", () { - var obj = {}; - expect(cache.put('k1', obj)).toBe(obj); + it("should return value from put", () { + var obj = {}; + expect(cache.put('k1', obj)).toBe(obj); + }); }); - }); - describe('put, get & remove', () { + describe('put, get & remove', () { - it('should add cache entries via add and retrieve them via get', inject(() { - var obj = {'bar':'baz'}; - cache.put('key1', 'bar'); - cache.put('key2', obj); + it('should add cache entries via add and retrieve them via get', inject(() { + var obj = {'bar':'baz'}; + cache.put('key1', 'bar'); + cache.put('key2', obj); - expect(cache.get('key2')).toBe(obj); - expect(cache.get('key1')).toBe('bar'); - })); + expect(cache.get('key2')).toBe(obj); + expect(cache.get('key1')).toBe('bar'); + })); - it('should remove entries via remove', inject(() { - cache.put('k1', 'foo'); - cache.put('k2', 'bar'); + it('should remove entries via remove', inject(() { + cache.put('k1', 'foo'); + cache.put('k2', 'bar'); - cache.remove('k2'); + cache.remove('k2'); - expect(cache.get('k1')).toBe('foo'); - expect(cache.get('k2')).toBeNull(); + expect(cache.get('k1')).toBe('foo'); + expect(cache.get('k2')).toBeNull(); - cache.remove('k1'); + cache.remove('k1'); - expect(cache.get('k1')).toBeNull(); - expect(cache.get('k2')).toBeNull(); - })); + expect(cache.get('k1')).toBeNull(); + expect(cache.get('k2')).toBeNull(); + })); - it('should return null when entry does not exist', inject(() { - expect(cache.remove('non-existent')).toBeNull(); - })); + it('should return null when entry does not exist', inject(() { + expect(cache.remove('non-existent')).toBeNull(); + })); - it("should return value from put", inject(() { - var obj = {}; - expect(cache.put('k1', obj)).toBe(obj); - })); - }); + it("should return value from put", inject(() { + var obj = {}; + expect(cache.put('k1', obj)).toBe(obj); + })); + }); - describe('removeAll', () { - it('should blow away all data', inject(() { - cache.put('id1', 1); - cache.put('id2', 2); - cache.put('id3', 3); + describe('removeAll', () { + it('should blow away all data', inject(() { + cache.put('id1', 1); + cache.put('id2', 2); + cache.put('id3', 3); + cache.removeAll(); + + expect(cache.get('id1')).toBeNull(); + expect(cache.get('id2')).toBeNull(); + expect(cache.get('id3')).toBeNull(); + })); + }); + }); + + // TODO(chirayu): Add a lot more tests and tests and don't rely on toString() + describe('LRU cache', () { + it('should have LRU behavior with ordering keys and eviction', inject(() { + var cache = new LruCache(capacity: 4); + cache.put(1, 10); + cache.put(2, 20); + cache.put(3, 30); + cache.put(4, 40); + expect(cache.get(2)).toEqual(20); + cache.put(5, 50); + cache.put(6, 60); + expect(cache.get(5)).toEqual(50); + cache.put(7, 70); + cache.put(8, 80); + // 1 has been evicted. + expect(cache.get(1)).toBeNull(); + // The order of items is LRU to MRU. + expect("$cache").toEqual( + r"[LruCache: capacity=4, size=4, items={6: 60, 5: 50, 7: 70, 8: 80}]"); cache.removeAll(); + expect("$cache").toEqual(r"[LruCache: capacity=4, size=0, items={}]"); - expect(cache.get('id1')).toBeNull(); - expect(cache.get('id2')).toBeNull(); - expect(cache.get('id3')).toBeNull(); + var stats = cache.stats(); + expect(stats.capacity).toEqual(4); + expect(stats.size).toEqual(0); + expect(stats.hits).toEqual(2); + expect(stats.misses).toEqual(1); })); }); }); - - // TODO(chirayu): Add a lot more tests and tests and don't rely on toString() - describe('LRU cache', () { - it('should have LRU behavior with ordering keys and eviction', inject(() { - var cache = new LruCache(capacity: 4); - cache.put(1, 10); - cache.put(2, 20); - cache.put(3, 30); - cache.put(4, 40); - expect(cache.get(2)).toEqual(20); - cache.put(5, 50); - cache.put(6, 60); - expect(cache.get(5)).toEqual(50); - cache.put(7, 70); - cache.put(8, 80); - // 1 has been evicted. - expect(cache.get(1)).toBeNull(); - // The order of items is LRU to MRU. - expect("$cache").toEqual( - r"[LruCache: capacity=4, size=4, items={6: 60, 5: 50, 7: 70, 8: 80}]"); - cache.removeAll(); - expect("$cache").toEqual(r"[LruCache: capacity=4, size=0, items={}]"); - - var stats = cache.stats(); - expect(stats.capacity).toEqual(4); - expect(stats.size).toEqual(0); - expect(stats.hits).toEqual(2); - expect(stats.misses).toEqual(1); - })); - }); -}); +} diff --git a/test/core/core_directive_spec.dart b/test/core/core_directive_spec.dart index 2e1927fc8..30e757246 100644 --- a/test/core/core_directive_spec.dart +++ b/test/core/core_directive_spec.dart @@ -2,70 +2,72 @@ library core_directive_spec; import '../_specs.dart'; -main() => describe('DirectiveMap', () { - - beforeEach(module((Module module) { - module..type(AnnotatedIoComponent); - })); - - it('should extract attr map from annotated component', inject((DirectiveMap directives) { - var annotations = directives.annotationsFor(AnnotatedIoComponent); - expect(annotations.length).toEqual(1); - expect(annotations[0] is NgComponent).toBeTruthy(); - - NgComponent annotation = annotations[0]; - expect(annotation.selector).toEqual('annotated-io'); - expect(annotation.visibility).toEqual(NgDirective.LOCAL_VISIBILITY); - expect(annotation.exportExpressions).toEqual(['exportExpressions']); - expect(annotation.publishTypes).toEqual([String]); - expect(annotation.template).toEqual('template'); - expect(annotation.templateUrl).toEqual('templateUrl'); - expect(annotation.cssUrls).toEqual(['cssUrls']); - expect(annotation.applyAuthorStyles).toEqual(true); - expect(annotation.resetStyleInheritance).toEqual(true); - expect(annotation.publishAs).toEqual('ctrl'); - expect(annotation.map).toEqual({ - 'foo': '=>foo', - 'attr': '@attr', - 'expr': '<=>expr', - 'expr-one-way': '=>exprOneWay', - 'expr-one-way-one-shot': '=>!exprOneWayOneShot', - 'callback': '&callback', - 'expr-one-way2': '=>exprOneWay2', - 'expr-two-way': '<=>exprTwoWay' - }); - })); - - describe('exceptions', () { - it('should throw when annotation is for existing mapping', () { - var module = new Module() - ..type(DirectiveMap) - ..type(Bad1Component) - ..type(MetadataExtractor) - ..type(FieldMetadataExtractor); - - var injector = new DynamicInjector(modules: [module]); - expect(() { - injector.get(DirectiveMap); - }).toThrow('Mapping for attribute foo is already defined (while ' - 'processing annottation for field foo of Bad1Component)'); - }); +void main() { + describe('DirectiveMap', () { + + beforeEach(module((Module module) { + module..type(AnnotatedIoComponent); + })); + + it('should extract attr map from annotated component', inject((DirectiveMap directives) { + var annotations = directives.annotationsFor(AnnotatedIoComponent); + expect(annotations.length).toEqual(1); + expect(annotations[0] is NgComponent).toBeTruthy(); + + NgComponent annotation = annotations[0]; + expect(annotation.selector).toEqual('annotated-io'); + expect(annotation.visibility).toEqual(NgDirective.LOCAL_VISIBILITY); + expect(annotation.exportExpressions).toEqual(['exportExpressions']); + expect(annotation.publishTypes).toEqual([String]); + expect(annotation.template).toEqual('template'); + expect(annotation.templateUrl).toEqual('templateUrl'); + expect(annotation.cssUrls).toEqual(['cssUrls']); + expect(annotation.applyAuthorStyles).toEqual(true); + expect(annotation.resetStyleInheritance).toEqual(true); + expect(annotation.publishAs).toEqual('ctrl'); + expect(annotation.map).toEqual({ + 'foo': '=>foo', + 'attr': '@attr', + 'expr': '<=>expr', + 'expr-one-way': '=>exprOneWay', + 'expr-one-way-one-shot': '=>!exprOneWayOneShot', + 'callback': '&callback', + 'expr-one-way2': '=>exprOneWay2', + 'expr-two-way': '<=>exprTwoWay' + }); + })); + + describe('exceptions', () { + it('should throw when annotation is for existing mapping', () { + var module = new Module() + ..type(DirectiveMap) + ..type(Bad1Component) + ..type(MetadataExtractor) + ..type(FieldMetadataExtractor); - it('should throw when annotated both getter and setter', () { + var injector = new DynamicInjector(modules: [module]); + expect(() { + injector.get(DirectiveMap); + }).toThrow('Mapping for attribute foo is already defined (while ' + 'processing annottation for field foo of Bad1Component)'); + }); + + it('should throw when annotated both getter and setter', () { var module = new Module() ..type(DirectiveMap) ..type(Bad2Component) ..type(MetadataExtractor) ..type(FieldMetadataExtractor); - var injector = new DynamicInjector(modules: [module]); - expect(() { - injector.get(DirectiveMap); - }).toThrow('Attribute annotation for foo is defined more than once ' - 'in Bad2Component'); + var injector = new DynamicInjector(modules: [module]); + expect(() { + injector.get(DirectiveMap); + }).toThrow('Attribute annotation for foo is defined more than once ' + 'in Bad2Component'); + }); }); }); -}); +} @NgComponent( selector: 'annotated-io', diff --git a/test/core/registry_spec.dart b/test/core/registry_spec.dart index 532dbf1ae..1c9c6070b 100644 --- a/test/core/registry_spec.dart +++ b/test/core/registry_spec.dart @@ -2,45 +2,47 @@ library registry_spec; import '../_specs.dart'; -main() => describe('RegistryMap', () { - it('should allow for multiple registry keys to be added', () { - var module = new Module() - ..type(MyMap) - ..type(MetadataExtractor) - ..type(A1) - ..type(A2); +main() { + describe('RegistryMap', () { + it('should allow for multiple registry keys to be added', () { + var module = new Module() + ..type(MyMap) + ..type(MetadataExtractor) + ..type(A1) + ..type(A2); - var injector = new DynamicInjector(modules: [module]); - expect(() { - injector.get(MyMap); - }).not.toThrow(); - }); + var injector = new DynamicInjector(modules: [module]); + expect(() { + injector.get(MyMap); + }).not.toThrow(); + }); - it('should iterate over all types', () { - var module = new Module() - ..type(MyMap) - ..type(MetadataExtractor) - ..type(A1); + it('should iterate over all types', () { + var module = new Module() + ..type(MyMap) + ..type(MetadataExtractor) + ..type(A1); - var injector = new DynamicInjector(modules: [module]); - var keys = []; - var types = []; - var map = injector.get(MyMap); - map.forEach((k, t) { keys.add(k); types.add(t); }); - expect(keys).toEqual([new MyAnnotation('A'), new MyAnnotation('B')]); - expect(types).toEqual([A1, A1]); - }); + var injector = new DynamicInjector(modules: [module]); + var keys = []; + var types = []; + var map = injector.get(MyMap); + map.forEach((k, t) { keys.add(k); types.add(t); }); + expect(keys).toEqual([new MyAnnotation('A'), new MyAnnotation('B')]); + expect(types).toEqual([A1, A1]); + }); - it('should safely ignore typedefs', () { - var module = new Module() - ..type(MyMap) - ..type(MetadataExtractor) - ..value(MyTypedef, (String _) => null); + it('should safely ignore typedefs', () { + var module = new Module() + ..type(MyMap) + ..type(MetadataExtractor) + ..value(MyTypedef, (String _) => null); - var injector = new DynamicInjector(modules: [module]); - expect(() => injector.get(MyMap), isNot(throws)); + var injector = new DynamicInjector(modules: [module]); + expect(() => injector.get(MyMap), isNot(throws)); + }); }); -}); +} typedef void MyTypedef(String arg); diff --git a/test/core/scope_spec.dart b/test/core/scope_spec.dart index 20a70b113..1b9c3b6f9 100644 --- a/test/core/scope_spec.dart +++ b/test/core/scope_spec.dart @@ -6,1321 +6,1327 @@ import 'package:angular/change_detection/dirty_checking_change_detector.dart'; import 'dart:async'; import 'dart:math'; -main() => describe('scope', () { - beforeEach(module((Module module) { - Map context = {}; - module.value(GetterCache, new GetterCache({})); - module.type(ChangeDetector, implementedBy: DirtyCheckingChangeDetector); - module.value(Object, context); - module.value(Map, context); - module.type(RootScope); - module.type(_MultiplyFilter); - module.type(_ListHeadFilter); - module.type(_ListTailFilter); - module.type(_SortFilter); - })); - - describe('AST Bridge', () { - it('should watch field', inject((Logger logger, Map context, RootScope rootScope) { - context['field'] = 'Worked!'; - rootScope.watch('field', (value, previous) => logger([value, previous])); - expect(logger).toEqual([]); - rootScope.digest(); - expect(logger).toEqual([['Worked!', null]]); - rootScope.digest(); - expect(logger).toEqual([['Worked!', null]]); +void main() { + describe('scope', () { + beforeEach(module((Module module) { + Map context = {}; + module + ..value(GetterCache, new GetterCache({})) + ..type(ChangeDetector, implementedBy: DirtyCheckingChangeDetector) + ..value(Object, context) + ..value(Map, context) + ..type(RootScope) + ..type(_MultiplyFilter) + ..type(_ListHeadFilter) + ..type(_ListTailFilter) + ..type(_SortFilter); })); - it('should watch field path', inject((Logger logger, Map context, RootScope rootScope) { - context['a'] = {'b': 'AB'}; - rootScope.watch('a.b', (value, previous) => logger(value)); - rootScope.digest(); - expect(logger).toEqual(['AB']); - context['a']['b'] = '123'; - rootScope.digest(); - expect(logger).toEqual(['AB', '123']); - context['a'] = {'b': 'XYZ'}; - rootScope.digest(); - expect(logger).toEqual(['AB', '123', 'XYZ']); - })); - - it('should watch math operations', inject((Logger logger, Map context, RootScope rootScope) { - context['a'] = 1; - context['b'] = 2; - rootScope.watch('a + b + 1', (value, previous) => logger(value)); - rootScope.digest(); - expect(logger).toEqual([4]); - context['a'] = 3; - rootScope.digest(); - expect(logger).toEqual([4, 6]); - context['b'] = 5; - rootScope.digest(); - expect(logger).toEqual([4, 6, 9]); - })); - - - it('should watch literals', inject((Logger logger, Map context, RootScope rootScope) { - context['a'] = 1; - rootScope.watch('1', (value, previous) => logger(value)); - rootScope.watch('"str"', (value, previous) => logger(value)); - rootScope.watch('[a, 2, 3]', (value, previous) => logger(value)); - rootScope.watch('{a:a, b:2}', (value, previous) => logger(value)); - rootScope.digest(); - expect(logger).toEqual([1, 'str', [1, 2, 3], {'a': 1, 'b': 2}]); - logger.clear(); - context['a'] = 3; - rootScope.digest(); - expect(logger).toEqual([[3, 2, 3], {'a': 3, 'b': 2}]); - })); - - it('should invoke closures', inject((Logger logger, Map context, RootScope rootScope) { - context['fn'] = () { - logger('fn'); - return 1; - }; - context['a'] = {'fn': () { - logger('a.fn'); - return 2; - }}; - rootScope.watch('fn()', (value, previous) => logger('=> $value')); - rootScope.watch('a.fn()', (value, previous) => logger('-> $value')); - rootScope.digest(); - expect(logger).toEqual(['fn', 'a.fn', '=> 1', '-> 2', - /* second loop*/ 'fn', 'a.fn']); - logger.clear(); - rootScope.digest(); - expect(logger).toEqual(['fn', 'a.fn']); - })); - - it('should perform conditionals', inject((Logger logger, Map context, RootScope rootScope) { - context['a'] = 1; - context['b'] = 2; - context['c'] = 3; - rootScope.watch('a?b:c', (value, previous) => logger(value)); - rootScope.digest(); - expect(logger).toEqual([2]); - logger.clear(); - context['a'] = 0; - rootScope.digest(); - expect(logger).toEqual([3]); - })); - - - xit('should call function', inject((Logger logger, Map context, RootScope rootScope) { - context['a'] = () { - return () { return 123; }; - }; - rootScope.watch('a()()', (value, previous) => logger(value)); - rootScope.digest(); - expect(logger).toEqual([123]); - logger.clear(); - rootScope.digest(); - expect(logger).toEqual([]); - })); - - it('should access bracket', inject((Logger logger, Map context, RootScope rootScope) { - context['a'] = {'b': 123}; - rootScope.watch('a["b"]', (value, previous) => logger(value)); - rootScope.digest(); - expect(logger).toEqual([123]); - logger.clear(); - rootScope.digest(); - expect(logger).toEqual([]); - })); - - - it('should prefix', inject((Logger logger, Map context, RootScope rootScope) { - context['a'] = true; - rootScope.watch('!a', (value, previous) => logger(value)); - rootScope.digest(); - expect(logger).toEqual([false]); - logger.clear(); - context['a'] = false; - rootScope.digest(); - expect(logger).toEqual([true]); - })); + describe('AST Bridge', () { + it('should watch field', inject((Logger logger, Map context, RootScope rootScope) { + context['field'] = 'Worked!'; + rootScope.watch('field', (value, previous) => logger([value, previous])); + expect(logger).toEqual([]); + rootScope.digest(); + expect(logger).toEqual([['Worked!', null]]); + rootScope.digest(); + expect(logger).toEqual([['Worked!', null]]); + })); - it('should support filters', inject((Logger logger, Map context, - RootScope rootScope, AstParser parser, - FilterMap filters) { - context['a'] = 123; - context['b'] = 2; - rootScope.watch( - parser('a | multiply:b', filters: filters), - (value, previous) => logger(value)); - rootScope.digest(); - expect(logger).toEqual([246]); - logger.clear(); - rootScope.digest(); - expect(logger).toEqual([]); - logger.clear(); - })); + it('should watch field path', inject((Logger logger, Map context, RootScope rootScope) { + context['a'] = {'b': 'AB'}; + rootScope.watch('a.b', (value, previous) => logger(value)); + rootScope.digest(); + expect(logger).toEqual(['AB']); + context['a']['b'] = '123'; + rootScope.digest(); + expect(logger).toEqual(['AB', '123']); + context['a'] = {'b': 'XYZ'}; + rootScope.digest(); + expect(logger).toEqual(['AB', '123', 'XYZ']); + })); - it('should support arrays in filters', inject((Logger logger, Map context, - RootScope rootScope, - AstParser parser, - FilterMap filters) { - context['a'] = [1]; - rootScope.watch( - parser('a | sort | listHead:"A" | listTail:"B"', filters: filters), - (value, previous) => logger(value)); - rootScope.digest(); - expect(logger).toEqual(['sort', 'listHead', 'listTail', ['A', 1, 'B']]); - logger.clear(); - - rootScope.digest(); - expect(logger).toEqual([]); - logger.clear(); - - context['a'].add(2); - rootScope.digest(); - expect(logger).toEqual(['sort', 'listHead', 'listTail', ['A', 1, 2, 'B']]); - logger.clear(); - - // We change the order, but sort should change it to same one and it should not - // call subsequent filters. - context['a'] = [2, 1]; - rootScope.digest(); - expect(logger).toEqual(['sort']); - logger.clear(); - })); - }); + it('should watch math operations', inject((Logger logger, Map context, RootScope rootScope) { + context['a'] = 1; + context['b'] = 2; + rootScope.watch('a + b + 1', (value, previous) => logger(value)); + rootScope.digest(); + expect(logger).toEqual([4]); + context['a'] = 3; + rootScope.digest(); + expect(logger).toEqual([4, 6]); + context['b'] = 5; + rootScope.digest(); + expect(logger).toEqual([4, 6, 9]); + })); - describe('properties', () { - describe('root', () { - it('should point to itself', inject((RootScope rootScope) { - expect(rootScope.rootScope).toEqual(rootScope); + it('should watch literals', inject((Logger logger, Map context, RootScope rootScope) { + context['a'] = 1; + rootScope + ..watch('1', (value, previous) => logger(value)) + ..watch('"str"', (value, previous) => logger(value)) + ..watch('[a, 2, 3]', (value, previous) => logger(value)) + ..watch('{a:a, b:2}', (value, previous) => logger(value)) + ..digest(); + expect(logger).toEqual([1, 'str', [1, 2, 3], {'a': 1, 'b': 2}]); + logger.clear(); + context['a'] = 3; + rootScope.digest(); + expect(logger).toEqual([[3, 2, 3], {'a': 3, 'b': 2}]); })); - it('children should point to root', inject((RootScope rootScope) { - var child = rootScope.createChild(new PrototypeMap(rootScope.context)); - expect(child.rootScope).toEqual(rootScope); - expect(child.createChild(new PrototypeMap(rootScope.context)).rootScope).toEqual(rootScope); + it('should invoke closures', inject((Logger logger, Map context, RootScope rootScope) { + context['fn'] = () { + logger('fn'); + return 1; + }; + context['a'] = {'fn': () { + logger('a.fn'); + return 2; + }}; + rootScope.watch('fn()', (value, previous) => logger('=> $value')); + rootScope.watch('a.fn()', (value, previous) => logger('-> $value')); + rootScope.digest(); + expect(logger).toEqual(['fn', 'a.fn', '=> 1', '-> 2', + /* second loop*/ 'fn', 'a.fn']); + logger.clear(); + rootScope.digest(); + expect(logger).toEqual(['fn', 'a.fn']); })); - }); - - describe('parent', () { - it('should not have parent', inject((RootScope rootScope) { - expect(rootScope.parentScope).toEqual(null); + it('should perform conditionals', inject((Logger logger, Map context, RootScope rootScope) { + context['a'] = 1; + context['b'] = 2; + context['c'] = 3; + rootScope.watch('a?b:c', (value, previous) => logger(value)); + rootScope.digest(); + expect(logger).toEqual([2]); + logger.clear(); + context['a'] = 0; + rootScope.digest(); + expect(logger).toEqual([3]); })); - it('should point to parent', inject((RootScope rootScope) { - var child = rootScope.createChild(new PrototypeMap(rootScope.context)); - expect(rootScope.parentScope).toEqual(null); - expect(child.parentScope).toEqual(rootScope); - expect(child.createChild(new PrototypeMap(rootScope.context)).parentScope).toEqual(child); + xit('should call function', inject((Logger logger, Map context, RootScope rootScope) { + context['a'] = () { + return () { return 123; }; + }; + rootScope.watch('a()()', (value, previous) => logger(value)); + rootScope.digest(); + expect(logger).toEqual([123]); + logger.clear(); + rootScope.digest(); + expect(logger).toEqual([]); })); - }); - }); + it('should access bracket', inject((Logger logger, Map context, RootScope rootScope) { + context['a'] = {'b': 123}; + rootScope.watch('a["b"]', (value, previous) => logger(value)); + rootScope.digest(); + expect(logger).toEqual([123]); + logger.clear(); + rootScope.digest(); + expect(logger).toEqual([]); + })); - describe(r'events', () { - describe('on', () { - it('should allow emit/broadcast when no listeners', inject((RootScope scope) { - scope.emit('foo'); - scope.broadcast('foo'); + it('should prefix', inject((Logger logger, Map context, RootScope rootScope) { + context['a'] = true; + rootScope.watch('!a', (value, previous) => logger(value)); + rootScope.digest(); + expect(logger).toEqual([false]); + logger.clear(); + context['a'] = false; + rootScope.digest(); + expect(logger).toEqual([true]); })); + it('should support filters', inject((Logger logger, Map context, + RootScope rootScope, AstParser parser, + FilterMap filters) { + context['a'] = 123; + context['b'] = 2; + rootScope.watch( + parser('a | multiply:b', filters: filters), + (value, previous) => logger(value)); + rootScope.digest(); + expect(logger).toEqual([246]); + logger.clear(); + rootScope.digest(); + expect(logger).toEqual([]); + logger.clear(); + })); - it(r'should add listener for both emit and broadcast events', inject((RootScope rootScope) { - var log = '', - child = rootScope.createChild(new PrototypeMap(rootScope.context)); - - eventFn(event) { - expect(event).not.toEqual(null); - log += 'X'; - } + it('should support arrays in filters', inject((Logger logger, Map context, + RootScope rootScope, + AstParser parser, + FilterMap filters) { + context['a'] = [1]; + rootScope.watch( + parser('a | sort | listHead:"A" | listTail:"B"', filters: filters), + (value, previous) => logger(value)); + rootScope.digest(); + expect(logger).toEqual(['sort', 'listHead', 'listTail', ['A', 1, 'B']]); + logger.clear(); - child.on('abc').listen(eventFn); - expect(log).toEqual(''); + rootScope.digest(); + expect(logger).toEqual([]); + logger.clear(); - child.emit('abc'); - expect(log).toEqual('X'); + context['a'].add(2); + rootScope.digest(); + expect(logger).toEqual(['sort', 'listHead', 'listTail', ['A', 1, 2, 'B']]); + logger.clear(); - child.broadcast('abc'); - expect(log).toEqual('XX'); + // We change the order, but sort should change it to same one and it should not + // call subsequent filters. + context['a'] = [2, 1]; + rootScope.digest(); + expect(logger).toEqual(['sort']); + logger.clear(); })); + }); - it(r'should return a function that deregisters the listener', inject((RootScope rootScope) { - var log = ''; - var child = rootScope.createChild(new PrototypeMap(rootScope.context)); - var subscription; + describe('properties', () { + describe('root', () { + it('should point to itself', inject((RootScope rootScope) { + expect(rootScope.rootScope).toEqual(rootScope); + })); - eventFn(e) { - log += 'X'; - } + it('children should point to root', inject((RootScope rootScope) { + var child = rootScope.createChild(new PrototypeMap(rootScope.context)); + expect(child.rootScope).toEqual(rootScope); + expect(child.createChild(new PrototypeMap(rootScope.context)).rootScope).toEqual(rootScope); + })); + }); - subscription = child.on('abc').listen(eventFn); - expect(log).toEqual(''); - expect(subscription).toBeDefined(); - child.emit(r'abc'); - child.broadcast('abc'); - expect(log).toEqual('XX'); + describe('parent', () { + it('should not have parent', inject((RootScope rootScope) { + expect(rootScope.parentScope).toEqual(null); + })); - log = ''; - expect(subscription.cancel()).toBe(null); - child.emit(r'abc'); - child.broadcast('abc'); - expect(log).toEqual(''); - })); - it('should not trigger assertions on scope fork', inject((RootScope root) { - var d1 = root.createChild({}); - var d2 = root.createChild({}); - var d3 = d2.createChild({}); - expect(root.apply).not.toThrow(); - d1.on(ScopeEvent.DESTROY).listen((_) => null); - expect(root.apply).not.toThrow(); - d3.on(ScopeEvent.DESTROY).listen((_) => null); - expect(root.apply).not.toThrow(); - d2.on(ScopeEvent.DESTROY).listen((_) => null); - expect(root.apply).not.toThrow(); - })); + it('should point to parent', inject((RootScope rootScope) { + var child = rootScope.createChild(new PrototypeMap(rootScope.context)); + expect(rootScope.parentScope).toEqual(null); + expect(child.parentScope).toEqual(rootScope); + expect(child.createChild(new PrototypeMap(rootScope.context)).parentScope).toEqual(child); + })); + }); + }); - it('should not too eagerly create own streams', inject((RootScope root) { - var a = root.createChild({}); - var a2 = root.createChild({}); - var b = a.createChild({}); - var c = b.createChild({}); - var d = c.createChild({}); - var e = d.createChild({}); - getStreamState() => [root.hasOwnStreams, a.hasOwnStreams, a2.hasOwnStreams, - b.hasOwnStreams, c.hasOwnStreams, d.hasOwnStreams, - e.hasOwnStreams]; + describe(r'events', () { - expect(getStreamState()).toEqual([false, false, false, false, false, false, false]); - expect(root.apply).not.toThrow(); + describe('on', () { + it('should allow emit/broadcast when no listeners', inject((RootScope scope) { + scope.emit('foo'); + scope.broadcast('foo'); + })); - e.on(ScopeEvent.DESTROY).listen((_) => null); - expect(getStreamState()).toEqual([false, false, false, false, false, false, true]); - expect(root.apply).not.toThrow(); - d.on(ScopeEvent.DESTROY).listen((_) => null); - expect(getStreamState()).toEqual([false, false, false, false, false, true, true]); - expect(root.apply).not.toThrow(); + it(r'should add listener for both emit and broadcast events', inject((RootScope rootScope) { + var log = '', + child = rootScope.createChild(new PrototypeMap(rootScope.context)); - b.on(ScopeEvent.DESTROY).listen((_) => null); - expect(getStreamState()).toEqual([false, false, false, true, false, true, true]); - expect(root.apply).not.toThrow(); + eventFn(event) { + expect(event).not.toEqual(null); + log += 'X'; + } - c.on(ScopeEvent.DESTROY).listen((_) => null); - expect(getStreamState()).toEqual([false, false, false, true, true, true, true]); - expect(root.apply).not.toThrow(); + child.on('abc').listen(eventFn); + expect(log).toEqual(''); - a.on(ScopeEvent.DESTROY).listen((_) => null); - expect(getStreamState()).toEqual([false, true, false, true, true, true, true]); - expect(root.apply).not.toThrow(); + child.emit('abc'); + expect(log).toEqual('X'); - a2.on(ScopeEvent.DESTROY).listen((_) => null); - expect(getStreamState()).toEqual([true, true, true, true, true, true, true]); - expect(root.apply).not.toThrow(); - })); + child.broadcast('abc'); + expect(log).toEqual('XX'); + })); - it('should not properly merge streams', inject((RootScope root) { - var a = root.createChild({}); - var a2 = root.createChild({}); - var b = a.createChild({}); - var c = b.createChild({}); - var d = c.createChild({}); - var e = d.createChild({}); + it(r'should return a function that deregisters the listener', inject((RootScope rootScope) { + var log = ''; + var child = rootScope.createChild(new PrototypeMap(rootScope.context)); + var subscription; - getStreamState() => [root.hasOwnStreams, a.hasOwnStreams, a2.hasOwnStreams, - b.hasOwnStreams, c.hasOwnStreams, d.hasOwnStreams, - e.hasOwnStreams]; + eventFn(e) { + log += 'X'; + } - expect(getStreamState()).toEqual([false, false, false, false, false, false, false]); - expect(root.apply).not.toThrow(); + subscription = child.on('abc').listen(eventFn); + expect(log).toEqual(''); + expect(subscription).toBeDefined(); - a2.on(ScopeEvent.DESTROY).listen((_) => null); - expect(getStreamState()).toEqual([false, false, true, false, false, false, false]); - expect(root.apply).not.toThrow(); + child.emit(r'abc'); + child.broadcast('abc'); + expect(log).toEqual('XX'); - e.on(ScopeEvent.DESTROY).listen((_) => null); - expect(getStreamState()).toEqual([true, false, true, false, false, false, true]); - expect(root.apply).not.toThrow(); - })); + log = ''; + expect(subscription.cancel()).toBe(null); + child.emit(r'abc'); + child.broadcast('abc'); + expect(log).toEqual(''); + })); + it('should not trigger assertions on scope fork', inject((RootScope root) { + var d1 = root.createChild({}); + var d2 = root.createChild({}); + var d3 = d2.createChild({}); + expect(root.apply).not.toThrow(); + d1.on(ScopeEvent.DESTROY).listen((_) => null); + expect(root.apply).not.toThrow(); + d3.on(ScopeEvent.DESTROY).listen((_) => null); + expect(root.apply).not.toThrow(); + d2.on(ScopeEvent.DESTROY).listen((_) => null); + expect(root.apply).not.toThrow(); + })); - it('should clean up on cancel', inject((RootScope root) { - var child = root.createChild(null); - var cl = child.on("E").listen((e) => null); - var rl = root.on("E").listen((e) => null); - rl.cancel(); - expect(root.apply).not.toThrow(); - })); + it('should not too eagerly create own streams', inject((RootScope root) { + var a = root.createChild({}); + var a2 = root.createChild({}); + var b = a.createChild({}); + var c = b.createChild({}); + var d = c.createChild({}); + var e = d.createChild({}); + getStreamState() => [root.hasOwnStreams, a.hasOwnStreams, a2.hasOwnStreams, + b.hasOwnStreams, c.hasOwnStreams, d.hasOwnStreams, + e.hasOwnStreams]; - it('should find random bugs', inject((RootScope root) { - List scopes; - List listeners; - List steps; - var random = new Random(); - for (var i = 0; i < 1000; i++) { - if (i % 10 == 0) { - scopes = [root.createChild(null)]; - listeners = []; - steps = []; - } - switch(random.nextInt(4)) { - case 0: - if (scopes.length > 10) break; - var index = random.nextInt(scopes.length); - Scope scope = scopes[index]; - var child = scope.createChild(null); - scopes.add(child); - steps.add('scopes[$index].createChild(null)'); - break; - case 1: - var index = random.nextInt(scopes.length); - Scope scope = scopes[index]; - listeners.add(scope.on('E').listen((e) => null)); - steps.add('scopes[$index].on("E").listen((e)=>null)'); - break; - case 2: - if (scopes.length < 3) break; - var index = random.nextInt(scopes.length - 1) + 1; - Scope scope = scopes[index]; - scope.destroy(); - scopes = scopes.where((Scope s) => s.isAttached).toList(); - steps.add('scopes[$index].destroy()'); - break; - case 3: - if (listeners.length == 0) break; - var index = random.nextInt(listeners.length); - var l = listeners[index]; - l.cancel(); - listeners.remove(l); - steps.add('listeners[$index].cancel()'); - break; - } - try { - root.apply(); - } catch (e) { - expect('').toEqual(steps.join(';\n')); - } - } - })); - }); + expect(getStreamState()).toEqual([false, false, false, false, false, false, false]); + expect(root.apply).not.toThrow(); + e.on(ScopeEvent.DESTROY).listen((_) => null); + expect(getStreamState()).toEqual([false, false, false, false, false, false, true]); + expect(root.apply).not.toThrow(); - describe('emit', () { - var log, child, grandChild, greatGrandChild; + d.on(ScopeEvent.DESTROY).listen((_) => null); + expect(getStreamState()).toEqual([false, false, false, false, false, true, true]); + expect(root.apply).not.toThrow(); - logger(event) { - log.add(event.currentScope.context['id']); - } + b.on(ScopeEvent.DESTROY).listen((_) => null); + expect(getStreamState()).toEqual([false, false, false, true, false, true, true]); + expect(root.apply).not.toThrow(); - beforeEach(module(() { - return (RootScope rootScope) { - log = []; - child = rootScope.createChild({'id': 1}); - grandChild = child.createChild({'id': 2}); - greatGrandChild = grandChild.createChild({'id': 3}); + c.on(ScopeEvent.DESTROY).listen((_) => null); + expect(getStreamState()).toEqual([false, false, false, true, true, true, true]); + expect(root.apply).not.toThrow(); - rootScope.context['id'] = 0; + a.on(ScopeEvent.DESTROY).listen((_) => null); + expect(getStreamState()).toEqual([false, true, false, true, true, true, true]); + expect(root.apply).not.toThrow(); - rootScope.on('myEvent').listen(logger); - child.on('myEvent').listen(logger); - grandChild.on('myEvent').listen(logger); - greatGrandChild.on('myEvent').listen(logger); - }; - })); - - it(r'should bubble event up to the root scope', inject((RootScope rootScope) { - grandChild.emit(r'myEvent'); - expect(log.join('>')).toEqual('2>1>0'); - })); + a2.on(ScopeEvent.DESTROY).listen((_) => null); + expect(getStreamState()).toEqual([true, true, true, true, true, true, true]); + expect(root.apply).not.toThrow(); + })); - it(r'should dispatch exceptions to the exceptionHandler', () { - module((Module module) { - module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler); - }); - inject((ExceptionHandler e) { - LoggingExceptionHandler exceptionHandler = e; - child.on('myEvent').listen((e) { throw 'bubbleException'; }); - grandChild.emit(r'myEvent'); - expect(log.join('>')).toEqual('2>1>0'); - expect(exceptionHandler.errors[0].error).toEqual('bubbleException'); - }); - }); + it('should not properly merge streams', inject((RootScope root) { + var a = root.createChild({}); + var a2 = root.createChild({}); + var b = a.createChild({}); + var c = b.createChild({}); + var d = c.createChild({}); + var e = d.createChild({}); + getStreamState() => [root.hasOwnStreams, a.hasOwnStreams, a2.hasOwnStreams, + b.hasOwnStreams, c.hasOwnStreams, d.hasOwnStreams, + e.hasOwnStreams]; - it(r'should allow stopping event propagation', inject((RootScope rootScope) { - child.on('myEvent').listen((event) { event.stopPropagation(); }); - grandChild.emit(r'myEvent'); - expect(log.join('>')).toEqual('2>1'); - })); + expect(getStreamState()).toEqual([false, false, false, false, false, false, false]); + expect(root.apply).not.toThrow(); + a2.on(ScopeEvent.DESTROY).listen((_) => null); + expect(getStreamState()).toEqual([false, false, true, false, false, false, false]); + expect(root.apply).not.toThrow(); - it(r'should forward method arguments', inject((RootScope rootScope) { - var eventName; - var eventData; - child.on('abc').listen((event) { - eventName = event.name; - eventData = event.data; - }); - child.emit('abc', ['arg1', 'arg2']); - expect(eventName).toEqual('abc'); - expect(eventData).toEqual(['arg1', 'arg2']); - })); + e.on(ScopeEvent.DESTROY).listen((_) => null); + expect(getStreamState()).toEqual([true, false, true, false, false, false, true]); + expect(root.apply).not.toThrow(); + })); - describe(r'event object', () { - it(r'should have methods/properties', inject((RootScope rootScope) { - var event; - child.on('myEvent').listen((e) { - expect(e.targetScope).toBe(grandChild); - expect(e.currentScope).toBe(child); - expect(e.name).toBe('myEvent'); - event = e; - }); - grandChild.emit(r'myEvent'); - expect(event).toBeDefined(); + it('should clean up on cancel', inject((RootScope root) { + var child = root.createChild(null); + var cl = child.on("E").listen((e) => null); + var rl = root.on("E").listen((e) => null); + rl.cancel(); + expect(root.apply).not.toThrow(); })); - it(r'should have preventDefault method and defaultPrevented property', inject((RootScope rootScope) { - var event = grandChild.emit(r'myEvent'); - expect(event.defaultPrevented).toBe(false); - - child.on('myEvent').listen((event) { - event.preventDefault(); - }); - event = grandChild.emit(r'myEvent'); - expect(event.defaultPrevented).toBe(true); + it('should find random bugs', inject((RootScope root) { + List scopes; + List listeners; + List steps; + var random = new Random(); + for (var i = 0; i < 1000; i++) { + if (i % 10 == 0) { + scopes = [root.createChild(null)]; + listeners = []; + steps = []; + } + switch(random.nextInt(4)) { + case 0: + if (scopes.length > 10) break; + var index = random.nextInt(scopes.length); + Scope scope = scopes[index]; + var child = scope.createChild(null); + scopes.add(child); + steps.add('scopes[$index].createChild(null)'); + break; + case 1: + var index = random.nextInt(scopes.length); + Scope scope = scopes[index]; + listeners.add(scope.on('E').listen((e) => null)); + steps.add('scopes[$index].on("E").listen((e)=>null)'); + break; + case 2: + if (scopes.length < 3) break; + var index = random.nextInt(scopes.length - 1) + 1; + Scope scope = scopes[index]; + scope.destroy(); + scopes = scopes.where((Scope s) => s.isAttached).toList(); + steps.add('scopes[$index].destroy()'); + break; + case 3: + if (listeners.length == 0) break; + var index = random.nextInt(listeners.length); + var l = listeners[index]; + l.cancel(); + listeners.remove(l); + steps.add('listeners[$index].cancel()'); + break; + } + try { + root.apply(); + } catch (e) { + expect('').toEqual(steps.join(';\n')); + } + } })); }); - }); - describe('broadcast', () { - describe(r'event propagation', () { - var log, child1, child2, child3, grandChild11, grandChild21, grandChild22, grandChild23, - greatGrandChild211; + describe('emit', () { + var log, child, grandChild, greatGrandChild; logger(event) { log.add(event.currentScope.context['id']); } - beforeEach(inject((RootScope rootScope) { - log = []; - child1 = rootScope.createChild({}); - child2 = rootScope.createChild({}); - child3 = rootScope.createChild({}); - grandChild11 = child1.createChild({}); - grandChild21 = child2.createChild({}); - grandChild22 = child2.createChild({}); - grandChild23 = child2.createChild({}); - greatGrandChild211 = grandChild21.createChild({}); - - rootScope.context['id'] = 0; - child1.context['id'] = 1; - child2.context['id'] = 2; - child3.context['id'] = 3; - grandChild11.context['id'] = 11; - grandChild21.context['id'] = 21; - grandChild22.context['id'] = 22; - grandChild23.context['id'] = 23; - greatGrandChild211.context['id'] = 211; - - rootScope.on('myEvent').listen(logger); - child1.on('myEvent').listen(logger); - child2.on('myEvent').listen(logger); - child3.on('myEvent').listen(logger); - grandChild11.on('myEvent').listen(logger); - grandChild21.on('myEvent').listen(logger); - grandChild22.on('myEvent').listen(logger); - grandChild23.on('myEvent').listen(logger); - greatGrandChild211.on('myEvent').listen(logger); - - // R - // / | \ - // 1 2 3 - // / / | \ - // 11 21 22 23 - // | - // 211 - })); + beforeEach(module(() { + return (RootScope rootScope) { + log = []; + child = rootScope.createChild({'id': 1}); + grandChild = child.createChild({'id': 2}); + greatGrandChild = grandChild.createChild({'id': 3}); + rootScope.context['id'] = 0; - it(r'should broadcast an event from the root scope', inject((RootScope rootScope) { - rootScope.broadcast('myEvent'); - expect(log.join('>')).toEqual('0>1>11>2>21>211>22>23>3'); + rootScope.on('myEvent').listen(logger); + child.on('myEvent').listen(logger); + grandChild.on('myEvent').listen(logger); + greatGrandChild.on('myEvent').listen(logger); + }; })); - - it(r'should broadcast an event from a child scope', inject((RootScope rootScope) { - child2.broadcast('myEvent'); - expect(log.join('>')).toEqual('2>21>211>22>23'); + it(r'should bubble event up to the root scope', inject((RootScope rootScope) { + grandChild.emit(r'myEvent'); + expect(log.join('>')).toEqual('2>1>0'); })); - it(r'should broadcast an event from a leaf scope with a sibling', inject((RootScope rootScope) { - grandChild22.broadcast('myEvent'); - expect(log.join('>')).toEqual('22'); - })); + it(r'should dispatch exceptions to the exceptionHandler', () { + module((Module module) { + module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler); + }); + inject((ExceptionHandler e) { + LoggingExceptionHandler exceptionHandler = e; + child.on('myEvent').listen((e) { throw 'bubbleException'; }); + grandChild.emit(r'myEvent'); + expect(log.join('>')).toEqual('2>1>0'); + expect(exceptionHandler.errors[0].error).toEqual('bubbleException'); + }); + }); - it(r'should broadcast an event from a leaf scope without a sibling', inject((RootScope rootScope) { - grandChild23.broadcast('myEvent'); - expect(log.join('>')).toEqual('23'); + it(r'should allow stopping event propagation', inject((RootScope rootScope) { + child.on('myEvent').listen((event) { event.stopPropagation(); }); + grandChild.emit(r'myEvent'); + expect(log.join('>')).toEqual('2>1'); })); - it(r'should not not fire any listeners for other events', inject((RootScope rootScope) { - rootScope.broadcast('fooEvent'); - expect(log.join('>')).toEqual(''); + it(r'should forward method arguments', inject((RootScope rootScope) { + var eventName; + var eventData; + child.on('abc').listen((event) { + eventName = event.name; + eventData = event.data; + }); + child.emit('abc', ['arg1', 'arg2']); + expect(eventName).toEqual('abc'); + expect(eventData).toEqual(['arg1', 'arg2']); })); - it(r'should return event object', inject((RootScope rootScope) { - var result = child1.broadcast('some'); + describe(r'event object', () { + it(r'should have methods/properties', inject((RootScope rootScope) { + var event; + child.on('myEvent').listen((e) { + expect(e.targetScope).toBe(grandChild); + expect(e.currentScope).toBe(child); + expect(e.name).toBe('myEvent'); + event = e; + }); + grandChild.emit(r'myEvent'); + expect(event).toBeDefined(); + })); + + + it(r'should have preventDefault method and defaultPrevented property', inject((RootScope rootScope) { + var event = grandChild.emit(r'myEvent'); + expect(event.defaultPrevented).toBe(false); + + child.on('myEvent').listen((event) { + event.preventDefault(); + }); + event = grandChild.emit(r'myEvent'); + expect(event.defaultPrevented).toBe(true); + })); + }); + }); - expect(result).toBeDefined(); - expect(result.name).toBe('some'); - expect(result.targetScope).toBe(child1); - })); + describe('broadcast', () { + describe(r'event propagation', () { + var log, child1, child2, child3, grandChild11, grandChild21, grandChild22, grandChild23, + greatGrandChild211; - it('should skip scopes which dont have given event', - inject((RootScope rootScope, Logger log) { - var child1 = rootScope.createChild('A'); - rootScope.createChild('A1'); - rootScope.createChild('A2'); - rootScope.createChild('A3'); - var child2 = rootScope.createChild('B'); - child2.on('event').listen((e) => log(e.data)); - rootScope.broadcast('event', 'OK'); - expect(log).toEqual(['OK']); - })); - }); + logger(event) { + log.add(event.currentScope.context['id']); + } + beforeEach(inject((RootScope rootScope) { + log = []; + child1 = rootScope.createChild({}); + child2 = rootScope.createChild({}); + child3 = rootScope.createChild({}); + grandChild11 = child1.createChild({}); + grandChild21 = child2.createChild({}); + grandChild22 = child2.createChild({}); + grandChild23 = child2.createChild({}); + greatGrandChild211 = grandChild21.createChild({}); + + rootScope.context['id'] = 0; + child1.context['id'] = 1; + child2.context['id'] = 2; + child3.context['id'] = 3; + grandChild11.context['id'] = 11; + grandChild21.context['id'] = 21; + grandChild22.context['id'] = 22; + grandChild23.context['id'] = 23; + greatGrandChild211.context['id'] = 211; + + rootScope.on('myEvent').listen(logger); + child1.on('myEvent').listen(logger); + child2.on('myEvent').listen(logger); + child3.on('myEvent').listen(logger); + grandChild11.on('myEvent').listen(logger); + grandChild21.on('myEvent').listen(logger); + grandChild22.on('myEvent').listen(logger); + grandChild23.on('myEvent').listen(logger); + greatGrandChild211.on('myEvent').listen(logger); + + // R + // / | \ + // 1 2 3 + // / / | \ + // 11 21 22 23 + // | + // 211 + })); + + + it(r'should broadcast an event from the root scope', inject((RootScope rootScope) { + rootScope.broadcast('myEvent'); + expect(log.join('>')).toEqual('0>1>11>2>21>211>22>23>3'); + })); + + + it(r'should broadcast an event from a child scope', inject((RootScope rootScope) { + child2.broadcast('myEvent'); + expect(log.join('>')).toEqual('2>21>211>22>23'); + })); + + + it(r'should broadcast an event from a leaf scope with a sibling', inject((RootScope rootScope) { + grandChild22.broadcast('myEvent'); + expect(log.join('>')).toEqual('22'); + })); + + + it(r'should broadcast an event from a leaf scope without a sibling', inject((RootScope rootScope) { + grandChild23.broadcast('myEvent'); + expect(log.join('>')).toEqual('23'); + })); + + + it(r'should not not fire any listeners for other events', inject((RootScope rootScope) { + rootScope.broadcast('fooEvent'); + expect(log.join('>')).toEqual(''); + })); + + + it(r'should return event object', inject((RootScope rootScope) { + var result = child1.broadcast('some'); + + expect(result).toBeDefined(); + expect(result.name).toBe('some'); + expect(result.targetScope).toBe(child1); + })); + + + it('should skip scopes which dont have given event', + inject((RootScope rootScope, Logger log) { + var child1 = rootScope.createChild('A'); + rootScope.createChild('A1'); + rootScope.createChild('A2'); + rootScope.createChild('A3'); + var child2 = rootScope.createChild('B'); + child2.on('event').listen((e) => log(e.data)); + rootScope.broadcast('event', 'OK'); + expect(log).toEqual(['OK']); + })); + }); - describe(r'listener', () { - it(r'should receive event object', inject((RootScope rootScope) { - var scope = rootScope, - child = scope.createChild({}), - event; - child.on('fooEvent').listen((e) { - event = e; - }); - scope.broadcast('fooEvent'); + describe(r'listener', () { + it(r'should receive event object', inject((RootScope rootScope) { + var scope = rootScope, + child = scope.createChild({}), + event; - expect(event.name).toBe('fooEvent'); - expect(event.targetScope).toBe(scope); - expect(event.currentScope).toBe(child); - })); + child.on('fooEvent').listen((e) { + event = e; + }); + scope.broadcast('fooEvent'); - it(r'should support passing messages as varargs', inject((RootScope rootScope) { - var scope = rootScope, - child = scope.createChild({}), - args; + expect(event.name).toBe('fooEvent'); + expect(event.targetScope).toBe(scope); + expect(event.currentScope).toBe(child); + })); - child.on('fooEvent').listen((e) { - args = e.data; - }); - scope.broadcast('fooEvent', ['do', 're', 'me', 'fa']); + it(r'should support passing messages as varargs', inject((RootScope rootScope) { + var scope = rootScope, + child = scope.createChild({}), + args; - expect(args.length).toBe(4); - expect(args).toEqual(['do', 're', 'me', 'fa']); - })); + child.on('fooEvent').listen((e) { + args = e.data; + }); + scope.broadcast('fooEvent', ['do', 're', 'me', 'fa']); + + expect(args.length).toBe(4); + expect(args).toEqual(['do', 're', 'me', 'fa']); + })); + }); }); }); - }); - describe(r'destroy', () { - var first = null, middle = null, last = null, log = null; + describe(r'destroy', () { + var first = null, middle = null, last = null, log = null; - beforeEach(inject((RootScope rootScope) { - log = ''; + beforeEach(inject((RootScope rootScope) { + log = ''; - first = rootScope.createChild({"check": (n) { log+= '$n'; return n;}}); - middle = rootScope.createChild({"check": (n) { log+= '$n'; return n;}}); - last = rootScope.createChild({"check": (n) { log+= '$n'; return n;}}); + first = rootScope.createChild({"check": (n) { log+= '$n'; return n;}}); + middle = rootScope.createChild({"check": (n) { log+= '$n'; return n;}}); + last = rootScope.createChild({"check": (n) { log+= '$n'; return n;}}); - first.watch('check(1)', (v, l) {}); - middle.watch('check(2)', (v, l) {}); - last.watch('check(3)', (v, l) {}); + first.watch('check(1)', (v, l) {}); + middle.watch('check(2)', (v, l) {}); + last.watch('check(3)', (v, l) {}); - first.on(ScopeEvent.DESTROY).listen((e) { log += 'destroy:first;'; }); + first.on(ScopeEvent.DESTROY).listen((e) { log += 'destroy:first;'; }); - rootScope.digest(); - log = ''; - })); + rootScope.digest(); + log = ''; + })); - it(r'should ignore remove on root', inject((RootScope rootScope) { - rootScope.destroy(); - rootScope.digest(); - expect(log).toEqual('123'); - })); + it(r'should ignore remove on root', inject((RootScope rootScope) { + rootScope.destroy(); + rootScope.digest(); + expect(log).toEqual('123'); + })); - it(r'should remove first', inject((RootScope rootScope) { - first.destroy(); - rootScope.digest(); - expect(log).toEqual('destroy:first;23'); - })); + it(r'should remove first', inject((RootScope rootScope) { + first.destroy(); + rootScope.digest(); + expect(log).toEqual('destroy:first;23'); + })); - it(r'should remove middle', inject((RootScope rootScope) { - middle.destroy(); - rootScope.digest(); - expect(log).toEqual('13'); - })); + it(r'should remove middle', inject((RootScope rootScope) { + middle.destroy(); + rootScope.digest(); + expect(log).toEqual('13'); + })); - it(r'should remove last', inject((RootScope rootScope) { - last.destroy(); - rootScope.digest(); - expect(log).toEqual('12'); - })); + it(r'should remove last', inject((RootScope rootScope) { + last.destroy(); + rootScope.digest(); + expect(log).toEqual('12'); + })); - it(r'should broadcast the destroy event', inject((RootScope rootScope) { - var log = []; - first.on(ScopeEvent.DESTROY).listen((s) => log.add('first')); - var child = first.createChild({}); - child.on(ScopeEvent.DESTROY).listen((s) => log.add('first-child')); + it(r'should broadcast the destroy event', inject((RootScope rootScope) { + var log = []; + first.on(ScopeEvent.DESTROY).listen((s) => log.add('first')); + var child = first.createChild({}); + child.on(ScopeEvent.DESTROY).listen((s) => log.add('first-child')); - first.destroy(); - expect(log).toEqual(['first', 'first-child']); - })); + first.destroy(); + expect(log).toEqual(['first', 'first-child']); + })); - it('should not call reaction function on destroyed scope', inject((RootScope rootScope, Logger log) { - rootScope.context['name'] = 'misko'; - var child = rootScope.createChild(rootScope.context); - rootScope.watch('name', (v, _) { - log('root $v'); - if (v == 'destroy') { - child.destroy(); - } - }); - rootScope.watch('name', (v, _) => log('root2 $v')); - child.watch('name', (v, _) => log('child $v')); - rootScope.apply(); - expect(log).toEqual(['root misko', 'root2 misko', 'child misko']); - log.clear(); - - rootScope.context['name'] = 'destroy'; - rootScope.apply(); - expect(log).toEqual(['root destroy', 'root2 destroy']); - })); - }); + it('should not call reaction function on destroyed scope', inject((RootScope rootScope, Logger log) { + rootScope.context['name'] = 'misko'; + var child = rootScope.createChild(rootScope.context); + rootScope.watch('name', (v, _) { + log('root $v'); + if (v == 'destroy') { + child.destroy(); + } + }); + rootScope.watch('name', (v, _) => log('root2 $v')); + child.watch('name', (v, _) => log('child $v')); + rootScope.apply(); + expect(log).toEqual(['root misko', 'root2 misko', 'child misko']); + log.clear(); + rootScope.context['name'] = 'destroy'; + rootScope.apply(); + expect(log).toEqual(['root destroy', 'root2 destroy']); + })); + }); - describe('digest lifecycle', () { - it(r'should apply expression with full lifecycle', inject((RootScope rootScope) { - var log = ''; - var child = rootScope.createChild({"parent": rootScope.context}); - rootScope.watch('a', (a, _) { log += '1'; }); - child.apply('parent.a = 0'); - expect(log).toEqual('1'); - })); + describe('digest lifecycle', () { + it(r'should apply expression with full lifecycle', inject((RootScope rootScope) { + var log = ''; + var child = rootScope.createChild({"parent": rootScope.context}); + rootScope.watch('a', (a, _) { log += '1'; }); + child.apply('parent.a = 0'); + expect(log).toEqual('1'); + })); - it(r'should catch exceptions', () { - module((Module module) => module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler)); - inject((RootScope rootScope, ExceptionHandler e) { - LoggingExceptionHandler exceptionHandler = e; - var log = []; - var child = rootScope.createChild({}); - rootScope.watch('a', (a, _) => log.add('1')); - rootScope.context['a'] = 0; - child.apply(() { throw 'MyError'; }); - expect(log.join(',')).toEqual('1'); - expect(exceptionHandler.errors[0].error).toEqual('MyError'); - exceptionHandler.errors.removeAt(0); - exceptionHandler.assertEmpty(); + + it(r'should catch exceptions', () { + module((Module module) => module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler)); + inject((RootScope rootScope, ExceptionHandler e) { + LoggingExceptionHandler exceptionHandler = e; + var log = []; + var child = rootScope.createChild({}); + rootScope.watch('a', (a, _) => log.add('1')); + rootScope.context['a'] = 0; + child.apply(() { throw 'MyError'; }); + expect(log.join(',')).toEqual('1'); + expect(exceptionHandler.errors[0].error).toEqual('MyError'); + exceptionHandler.errors.removeAt(0); + exceptionHandler.assertEmpty(); + }); }); - }); - describe(r'exceptions', () { - var log; - beforeEach(module((Module module) { - return module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler); - })); - beforeEach(inject((RootScope rootScope) { - rootScope.context['log'] = () { log += 'digest;'; return null; }; - log = ''; - rootScope.watch('log()', (v, o) => null); - rootScope.digest(); - log = ''; - })); + describe(r'exceptions', () { + var log; + beforeEach(module((Module module) { + return module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler); + })); + beforeEach(inject((RootScope rootScope) { + rootScope.context['log'] = () { log += 'digest;'; return null; }; + log = ''; + rootScope.watch('log()', (v, o) => null); + rootScope.digest(); + log = ''; + })); - it(r'should execute and return value and update', inject( - (RootScope rootScope, ExceptionHandler e) { - LoggingExceptionHandler exceptionHandler = e; - rootScope.context['name'] = 'abc'; - expect(rootScope.apply((context) => context['name'])).toEqual('abc'); - expect(log).toEqual('digest;digest;'); - exceptionHandler.assertEmpty(); - })); + it(r'should execute and return value and update', inject( + (RootScope rootScope, ExceptionHandler e) { + LoggingExceptionHandler exceptionHandler = e; + rootScope.context['name'] = 'abc'; + expect(rootScope.apply((context) => context['name'])).toEqual('abc'); + expect(log).toEqual('digest;digest;'); + exceptionHandler.assertEmpty(); + })); - it(r'should execute and return value and update', inject((RootScope rootScope) { - rootScope.context['name'] = 'abc'; - expect(rootScope.apply('name', {'name': 123})).toEqual(123); - })); + it(r'should execute and return value and update', inject((RootScope rootScope) { + rootScope.context['name'] = 'abc'; + expect(rootScope.apply('name', {'name': 123})).toEqual(123); + })); + + it(r'should catch exception and update', inject((RootScope rootScope, ExceptionHandler e) { + LoggingExceptionHandler exceptionHandler = e; + var error = 'MyError'; + rootScope.apply(() { throw error; }); + expect(log).toEqual('digest;digest;'); + expect(exceptionHandler.errors[0].error).toEqual(error); + })); + }); - it(r'should catch exception and update', inject((RootScope rootScope, ExceptionHandler e) { - LoggingExceptionHandler exceptionHandler = e; + it(r'should proprely reset phase on exception', inject((RootScope rootScope) { var error = 'MyError'; - rootScope.apply(() { throw error; }); - expect(log).toEqual('digest;digest;'); - expect(exceptionHandler.errors[0].error).toEqual(error); + expect(() => rootScope.apply(() { throw error; })).toThrow(error); + expect(() => rootScope.apply(() { throw error; })).toThrow(error); })); }); - it(r'should proprely reset phase on exception', inject((RootScope rootScope) { - var error = 'MyError'; - expect(() => rootScope.apply(() { throw error; })).toThrow(error); - expect(() => rootScope.apply(() { throw error; })).toThrow(error); - })); - }); - - describe('flush lifecycle', () { - it(r'should apply expression with full lifecycle', inject((RootScope rootScope) { - var log = ''; - var child = rootScope.createChild({"parent": rootScope.context}); - rootScope.watch('a', (a, _) { log += '1'; }, readOnly: true); - child.apply('parent.a = 0'); - expect(log).toEqual('1'); - })); + describe('flush lifecycle', () { + it(r'should apply expression with full lifecycle', inject((RootScope rootScope) { + var log = ''; + var child = rootScope.createChild({"parent": rootScope.context}); + rootScope.watch('a', (a, _) { log += '1'; }, readOnly: true); + child.apply('parent.a = 0'); + expect(log).toEqual('1'); + })); - it(r'should schedule domWrites and domReads', inject((RootScope rootScope) { - var log = ''; - var child = rootScope.createChild({"parent": rootScope.context}); - rootScope.watch('a', (a, _) { log += '1'; }, readOnly: true); - child.apply('parent.a = 0'); - expect(log).toEqual('1'); - })); + it(r'should schedule domWrites and domReads', inject((RootScope rootScope) { + var log = ''; + var child = rootScope.createChild({"parent": rootScope.context}); + rootScope.watch('a', (a, _) { log += '1'; }, readOnly: true); + child.apply('parent.a = 0'); + expect(log).toEqual('1'); + })); - it(r'should catch exceptions', () { - module((Module module) => module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler)); - inject((RootScope rootScope, ExceptionHandler e) { - LoggingExceptionHandler exceptionHandler = e; - var log = []; - var child = rootScope.createChild({}); - rootScope.watch('a', (a, _) => log.add('1'), readOnly: true); - rootScope.context['a'] = 0; - child.apply(() { throw 'MyError'; }); - expect(log.join(',')).toEqual('1'); - expect(exceptionHandler.errors[0].error).toEqual('MyError'); - exceptionHandler.errors.removeAt(0); - exceptionHandler.assertEmpty(); + it(r'should catch exceptions', () { + module((Module module) => module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler)); + inject((RootScope rootScope, ExceptionHandler e) { + LoggingExceptionHandler exceptionHandler = e; + var log = []; + var child = rootScope.createChild({}); + rootScope.watch('a', (a, _) => log.add('1'), readOnly: true); + rootScope.context['a'] = 0; + child.apply(() { throw 'MyError'; }); + expect(log.join(',')).toEqual('1'); + expect(exceptionHandler.errors[0].error).toEqual('MyError'); + exceptionHandler.errors.removeAt(0); + exceptionHandler.assertEmpty(); + }); }); - }); - describe(r'exceptions', () { - var log; - beforeEach(module((Module module) { - return module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler); - })); - beforeEach(inject((RootScope rootScope) { - rootScope.context['log'] = () { log += 'digest;'; return null; }; - log = ''; - rootScope.watch('log()', (v, o) => null, readOnly: true); - rootScope.digest(); - log = ''; - })); + describe(r'exceptions', () { + var log; + beforeEach(module((Module module) { + return module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler); + })); + beforeEach(inject((RootScope rootScope) { + rootScope.context['log'] = () { log += 'digest;'; return null; }; + log = ''; + rootScope.watch('log()', (v, o) => null, readOnly: true); + rootScope.digest(); + log = ''; + })); - it(r'should execute and return value and update', inject( - (RootScope rootScope, ExceptionHandler e) { - LoggingExceptionHandler exceptionHandler = e; - rootScope.context['name'] = 'abc'; - expect(rootScope.apply((context) => context['name'])).toEqual('abc'); - expect(log).toEqual('digest;digest;'); - exceptionHandler.assertEmpty(); - })); + it(r'should execute and return value and update', inject( + (RootScope rootScope, ExceptionHandler e) { + LoggingExceptionHandler exceptionHandler = e; + rootScope.context['name'] = 'abc'; + expect(rootScope.apply((context) => context['name'])).toEqual('abc'); + expect(log).toEqual('digest;digest;'); + exceptionHandler.assertEmpty(); + })); - it(r'should execute and return value and update', inject((RootScope rootScope) { - rootScope.context['name'] = 'abc'; - expect(rootScope.apply('name', {'name': 123})).toEqual(123); - })); + it(r'should execute and return value and update', inject((RootScope rootScope) { + rootScope.context['name'] = 'abc'; + expect(rootScope.apply('name', {'name': 123})).toEqual(123); + })); - it(r'should catch exception and update', inject((RootScope rootScope, ExceptionHandler e) { - LoggingExceptionHandler exceptionHandler = e; - var error = 'MyError'; - rootScope.apply(() { throw error; }); - expect(log).toEqual('digest;digest;'); - expect(exceptionHandler.errors[0].error).toEqual(error); - })); + it(r'should catch exception and update', inject((RootScope rootScope, ExceptionHandler e) { + LoggingExceptionHandler exceptionHandler = e; + var error = 'MyError'; + rootScope.apply(() { throw error; }); + expect(log).toEqual('digest;digest;'); + expect(exceptionHandler.errors[0].error).toEqual(error); + })); - it(r'should throw assertion when model changes in flush', inject((RootScope rootScope, Logger log) { - var retValue = 1; - rootScope.context['logger'] = (name) { log(name); return retValue; }; + it(r'should throw assertion when model changes in flush', inject((RootScope rootScope, Logger log) { + var retValue = 1; + rootScope.context['logger'] = (name) { log(name); return retValue; }; - rootScope.watch('logger("watch")', (n, v) => null); - rootScope.watch('logger("flush")', (n, v) => null, readOnly: true); + rootScope.watch('logger("watch")', (n, v) => null); + rootScope.watch('logger("flush")', (n, v) => null, readOnly: true); - // clear watches - rootScope.digest(); - log.clear(); + // clear watches + rootScope.digest(); + log.clear(); - rootScope.flush(); - expect(log).toEqual(['flush', /*assertion*/ 'watch', 'flush']); + rootScope.flush(); + expect(log).toEqual(['flush', /*assertion*/ 'watch', 'flush']); - retValue = 2; - expect(rootScope.flush). + retValue = 2; + expect(rootScope.flush). toThrow('Observer reaction functions should not change model. \n' - 'These watch changes were detected: logger("watch"): 2 <= 1\n' - 'These observe changes were detected: '); - })); + 'These watch changes were detected: logger("watch"): 2 <= 1\n' + 'These observe changes were detected: '); + })); + }); + }); - }); + describe('ScopeLocals', () { + it('should read from locals', inject((RootScope scope) { + scope.context['a'] = 'XXX'; + scope.context['c'] = 'C'; + var scopeLocal = new ScopeLocals(scope.context, {'a': 'A', 'b': 'B'}); + expect(scopeLocal['a']).toEqual('A'); + expect(scopeLocal['b']).toEqual('B'); + expect(scopeLocal['c']).toEqual('C'); + })); - describe('ScopeLocals', () { - it('should read from locals', inject((RootScope scope) { - scope.context['a'] = 'XXX'; - scope.context['c'] = 'C'; - var scopeLocal = new ScopeLocals(scope.context, {'a': 'A', 'b': 'B'}); - expect(scopeLocal['a']).toEqual('A'); - expect(scopeLocal['b']).toEqual('B'); - expect(scopeLocal['c']).toEqual('C'); - })); + it('should write to Scope', inject((RootScope scope) { + scope.context['a'] = 'XXX'; + scope.context['c'] = 'C'; + var scopeLocal = new ScopeLocals(scope.context, {'a': 'A', 'b': 'B'}); - it('should write to Scope', inject((RootScope scope) { - scope.context['a'] = 'XXX'; - scope.context['c'] = 'C'; - var scopeLocal = new ScopeLocals(scope.context, {'a': 'A', 'b': 'B'}); + scopeLocal['a'] = 'aW'; + scopeLocal['b'] = 'bW'; + scopeLocal['c'] = 'cW'; - scopeLocal['a'] = 'aW'; - scopeLocal['b'] = 'bW'; - scopeLocal['c'] = 'cW'; + expect(scope.context['a']).toEqual('aW'); + expect(scope.context['b']).toEqual('bW'); + expect(scope.context['c']).toEqual('cW'); - expect(scope.context['a']).toEqual('aW'); - expect(scope.context['b']).toEqual('bW'); - expect(scope.context['c']).toEqual('cW'); + expect(scopeLocal['a']).toEqual('A'); + expect(scopeLocal['b']).toEqual('B'); + expect(scopeLocal['c']).toEqual('cW'); + })); + }); - expect(scopeLocal['a']).toEqual('A'); - expect(scopeLocal['b']).toEqual('B'); - expect(scopeLocal['c']).toEqual('cW'); - })); - }); + describe(r'watch/digest', () { + it(r'should watch and fire on simple property change', inject((RootScope rootScope) { + var log; - describe(r'watch/digest', () { - it(r'should watch and fire on simple property change', inject((RootScope rootScope) { - var log; + rootScope.watch('name', (a, b) { + log = [a, b]; + }); + rootScope.digest(); + log = null; - rootScope.watch('name', (a, b) { - log = [a, b]; - }); - rootScope.digest(); - log = null; - - expect(log).toEqual(null); - rootScope.digest(); - expect(log).toEqual(null); - rootScope.context['name'] = 'misko'; - rootScope.digest(); - expect(log).toEqual(['misko', null]); - })); + expect(log).toEqual(null); + rootScope.digest(); + expect(log).toEqual(null); + rootScope.context['name'] = 'misko'; + rootScope.digest(); + expect(log).toEqual(['misko', null]); + })); - it('should watch/observe on objects other then contex', inject((RootScope rootScope) { - var log = ''; - var map = {'a': 'A', 'b': 'B'}; - rootScope.watch('a', (a, b) => log += a, context: map); - rootScope.watch('b', (a, b) => log += a, context: map); - rootScope.apply(); - expect(log).toEqual('AB'); - })); + it('should watch/observe on objects other then contex', inject((RootScope rootScope) { + var log = ''; + var map = {'a': 'A', 'b': 'B'}; + rootScope.watch('a', (a, b) => log += a, context: map); + rootScope.watch('b', (a, b) => log += a, context: map); + rootScope.apply(); + expect(log).toEqual('AB'); + })); - it(r'should watch and fire on expression change', inject((RootScope rootScope) { - var log; + it(r'should watch and fire on expression change', inject((RootScope rootScope) { + var log; - rootScope.watch('name.first', (a, b) => log = [a, b]); - rootScope.digest(); - log = null; + rootScope.watch('name.first', (a, b) => log = [a, b]); + rootScope.digest(); + log = null; - rootScope.context['name'] = {}; - expect(log).toEqual(null); - rootScope.digest(); - expect(log).toEqual(null); - rootScope.context['name']['first'] = 'misko'; - rootScope.digest(); - expect(log).toEqual(['misko', null]); - })); + rootScope.context['name'] = {}; + expect(log).toEqual(null); + rootScope.digest(); + expect(log).toEqual(null); + rootScope.context['name']['first'] = 'misko'; + rootScope.digest(); + expect(log).toEqual(['misko', null]); + })); - it(r'should delegate exceptions', () { - module((Module module) { - module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler); - }); - inject((RootScope rootScope, ExceptionHandler e) { - LoggingExceptionHandler exceptionHandler = e; - rootScope.watch('a', (n, o) {throw 'abc';}); - rootScope.context['a'] = 1; - rootScope.digest(); - expect(exceptionHandler.errors.length).toEqual(1); - expect(exceptionHandler.errors[0].error).toEqual('abc'); + it(r'should delegate exceptions', () { + module((Module module) { + module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler); + }); + inject((RootScope rootScope, ExceptionHandler e) { + LoggingExceptionHandler exceptionHandler = e; + rootScope.watch('a', (n, o) {throw 'abc';}); + rootScope.context['a'] = 1; + rootScope.digest(); + expect(exceptionHandler.errors.length).toEqual(1); + expect(exceptionHandler.errors[0].error).toEqual('abc'); + }); }); - }); - it(r'should fire watches in order of addition', inject((RootScope rootScope) { - // this is not an external guarantee, just our own sanity - var log = ''; - rootScope.watch('a', (a, b) { log += 'a'; }); - rootScope.watch('b', (a, b) { log += 'b'; }); - rootScope.watch('c', (a, b) { log += 'c'; }); - rootScope.context['a'] = rootScope.context['b'] = rootScope.context['c'] = 1; - rootScope.digest(); - expect(log).toEqual('abc'); - })); + it(r'should fire watches in order of addition', inject((RootScope rootScope) { + // this is not an external guarantee, just our own sanity + var log = ''; + rootScope + ..watch('a', (a, b) { log += 'a'; }) + ..watch('b', (a, b) { log += 'b'; }) + ..watch('c', (a, b) { log += 'c'; }) + ..context['a'] = rootScope.context['b'] = rootScope.context['c'] = 1 + ..digest(); + expect(log).toEqual('abc'); + })); - it(r'should call child watchers in addition order', inject((RootScope rootScope) { - // this is not an external guarantee, just our own sanity - var log = ''; - var childA = rootScope.createChild({}); - var childB = rootScope.createChild({}); - var childC = rootScope.createChild({}); - childA.watch('a', (a, b) { log += 'a'; }); - childB.watch('b', (a, b) { log += 'b'; }); - childC.watch('c', (a, b) { log += 'c'; }); - childA.context['a'] = childB.context['b'] = childC.context['c'] = 1; - rootScope.digest(); - expect(log).toEqual('abc'); - })); + it(r'should call child watchers in addition order', inject((RootScope rootScope) { + // this is not an external guarantee, just our own sanity + var log = ''; + var childA = rootScope.createChild({}); + var childB = rootScope.createChild({}); + var childC = rootScope.createChild({}); + childA.watch('a', (a, b) { log += 'a'; }); + childB.watch('b', (a, b) { log += 'b'; }); + childC.watch('c', (a, b) { log += 'c'; }); + childA.context['a'] = childB.context['b'] = childC.context['c'] = 1; + rootScope.digest(); + expect(log).toEqual('abc'); + })); - it(r'should run digest multiple times', inject( - (RootScope rootScope) { - // tests a traversal edge case which we originally missed - var log = []; - var childA = rootScope.createChild({'log': log}); - var childB = rootScope.createChild({'log': log}); + it(r'should run digest multiple times', inject( + (RootScope rootScope) { + // tests a traversal edge case which we originally missed + var log = []; + var childA = rootScope.createChild({'log': log}); + var childB = rootScope.createChild({'log': log}); - rootScope.context['log'] = log; + rootScope.context['log'] = log; - rootScope.watch("log.add('r')", (_, __) => null); - childA.watch("log.add('a')", (_, __) => null); - childB.watch("log.add('b')", (_, __) => null); + rootScope.watch("log.add('r')", (_, __) => null); + childA.watch("log.add('a')", (_, __) => null); + childB.watch("log.add('b')", (_, __) => null); - // init - rootScope.digest(); - expect(log.join('')).toEqual('rabrab'); - })); + // init + rootScope.digest(); + expect(log.join('')).toEqual('rabrab'); + })); - it(r'should repeat watch cycle while model changes are identified', inject((RootScope rootScope) { - var log = ''; - rootScope.watch('c', (v, b) {rootScope.context['d'] = v; log+='c'; }); - rootScope.watch('b', (v, b) {rootScope.context['c'] = v; log+='b'; }); - rootScope.watch('a', (v, b) {rootScope.context['b'] = v; log+='a'; }); - rootScope.digest(); - log = ''; - rootScope.context['a'] = 1; - rootScope.digest(); - expect(rootScope.context['b']).toEqual(1); - expect(rootScope.context['c']).toEqual(1); - expect(rootScope.context['d']).toEqual(1); - expect(log).toEqual('abc'); - })); + it(r'should repeat watch cycle while model changes are identified', inject((RootScope rootScope) { + var log = ''; + rootScope + ..watch('c', (v, b) {rootScope.context['d'] = v; log+='c'; }) + ..watch('b', (v, b) {rootScope.context['c'] = v; log+='b'; }) + ..watch('a', (v, b) {rootScope.context['b'] = v; log+='a'; }) + ..digest(); + log = ''; + rootScope.context['a'] = 1; + rootScope.digest(); + expect(rootScope.context['b']).toEqual(1); + expect(rootScope.context['c']).toEqual(1); + expect(rootScope.context['d']).toEqual(1); + expect(log).toEqual('abc'); + })); - it(r'should repeat watch cycle from the root element', inject((RootScope rootScope) { - var log = []; - rootScope.context['log'] = log; - var child = rootScope.createChild({'log':log}); - rootScope.watch("log.add('a')", (_, __) => null); - child.watch("log.add('b')", (_, __) => null); - rootScope.digest(); - expect(log.join('')).toEqual('abab'); - })); + it(r'should repeat watch cycle from the root element', inject((RootScope rootScope) { + var log = []; + rootScope.context['log'] = log; + var child = rootScope.createChild({'log':log}); + rootScope.watch("log.add('a')", (_, __) => null); + child.watch("log.add('b')", (_, __) => null); + rootScope.digest(); + expect(log.join('')).toEqual('abab'); + })); - it(r'should not fire upon watch registration on initial digest', inject((RootScope rootScope) { - var log = ''; - rootScope.context['a'] = 1; - rootScope.watch('a', (a, b) { log += 'a'; }); - rootScope.watch('b', (a, b) { log += 'b'; }); - rootScope.digest(); - log = ''; - rootScope.digest(); - expect(log).toEqual(''); - })); + it(r'should not fire upon watch registration on initial digest', inject((RootScope rootScope) { + var log = ''; + rootScope.context['a'] = 1; + rootScope.watch('a', (a, b) { log += 'a'; }); + rootScope.watch('b', (a, b) { log += 'b'; }); + rootScope.digest(); + log = ''; + rootScope.digest(); + expect(log).toEqual(''); + })); - it(r'should prevent digest recursion', inject((RootScope rootScope) { - var callCount = 0; - rootScope.watch('name', (a, b) { - expect(() { - rootScope.digest(); - }).toThrow(r'digest already in progress'); - callCount++; - }); - rootScope.context['name'] = 'a'; - rootScope.digest(); - expect(callCount).toEqual(1); - })); + it(r'should prevent digest recursion', inject((RootScope rootScope) { + var callCount = 0; + rootScope.watch('name', (a, b) { + expect(() { + rootScope.digest(); + }).toThrow(r'digest already in progress'); + callCount++; + }); + rootScope.context['name'] = 'a'; + rootScope.digest(); + expect(callCount).toEqual(1); + })); - it(r'should return a function that allows listeners to be unregistered', inject( - (RootScope rootScope) { - var listener = jasmine.createSpy('watch listener'); - var watch; + it(r'should return a function that allows listeners to be unregistered', inject( + (RootScope rootScope) { + var listener = jasmine.createSpy('watch listener'); + var watch; - watch = rootScope.watch('foo', listener); - rootScope.digest(); //init - expect(listener).toHaveBeenCalled(); - expect(watch).toBeDefined(); + watch = rootScope.watch('foo', listener); + rootScope.digest(); //init + expect(listener).toHaveBeenCalled(); + expect(watch).toBeDefined(); - listener.reset(); - rootScope.context['foo'] = 'bar'; - rootScope.digest(); //triger - expect(listener).toHaveBeenCalledOnce(); + listener.reset(); + rootScope.context['foo'] = 'bar'; + rootScope.digest(); //triger + expect(listener).toHaveBeenCalledOnce(); - listener.reset(); - rootScope.context['foo'] = 'baz'; - watch.remove(); - rootScope.digest(); //trigger - expect(listener).not.toHaveBeenCalled(); - })); + listener.reset(); + rootScope.context['foo'] = 'baz'; + watch.remove(); + rootScope.digest(); //trigger + expect(listener).not.toHaveBeenCalled(); + })); - it(r'should not infinitely digest when current value is NaN', inject((RootScope rootScope) { - rootScope.context['nan'] = double.NAN; - rootScope.watch('nan', (_, __) => null); + it(r'should not infinitely digest when current value is NaN', inject((RootScope rootScope) { + rootScope.context['nan'] = double.NAN; + rootScope.watch('nan', (_, __) => null); - expect(() { - rootScope.digest(); - }).not.toThrow(); - })); + expect(() { + rootScope.digest(); + }).not.toThrow(); + })); - it(r'should prevent infinite digest and should log firing expressions', inject((RootScope rootScope) { - rootScope.context['a'] = 0; - rootScope.context['b'] = 0; - rootScope.watch('a', (a, __) => rootScope.context['a'] = a + 1); - rootScope.watch('b', (b, __) => rootScope.context['b'] = b + 1); + it(r'should prevent infinite digest and should log firing expressions', inject((RootScope rootScope) { + rootScope.context['a'] = 0; + rootScope.context['b'] = 0; + rootScope.watch('a', (a, __) => rootScope.context['a'] = a + 1); + rootScope.watch('b', (b, __) => rootScope.context['b'] = b + 1); - expect(() { - rootScope.digest(); - }).toThrow('Model did not stabilize in 5 digests. ' - 'Last 3 iterations:\n' - 'a: 2 <= 1, b: 2 <= 1\n' - 'a: 3 <= 2, b: 3 <= 2\n' - 'a: 4 <= 3, b: 4 <= 3'); - })); + expect(() { + rootScope.digest(); + }).toThrow('Model did not stabilize in 5 digests. ' + 'Last 3 iterations:\n' + 'a: 2 <= 1, b: 2 <= 1\n' + 'a: 3 <= 2, b: 3 <= 2\n' + 'a: 4 <= 3, b: 4 <= 3'); + })); - it(r'should always call the watchr with newVal and oldVal equal on the first run', - inject((RootScope rootScope) { - var log = []; - var logger = (newVal, oldVal) { - var val = (newVal == oldVal || (newVal != oldVal && oldVal != newVal)) ? newVal : 'xxx'; - log.add(val); - }; - - rootScope.context['nanValue'] = double.NAN; - rootScope.context['nullValue'] = null; - rootScope.context['emptyString'] = ''; - rootScope.context['falseValue'] = false; - rootScope.context['numberValue'] = 23; - - rootScope.watch('nanValue', logger); - rootScope.watch('nullValue', logger); - rootScope.watch('emptyString', logger); - rootScope.watch('falseValue', logger); - rootScope.watch('numberValue', logger); - - rootScope.digest(); - expect(log.removeAt(0).isNaN).toEqual(true); //jasmine's toBe and toEqual don't work well with NaNs - expect(log).toEqual([null, '', false, 23]); - log = []; - rootScope.digest(); - expect(log).toEqual([]); - })); + it(r'should always call the watchr with newVal and oldVal equal on the first run', + inject((RootScope rootScope) { + var log = []; + var logger = (newVal, oldVal) { + var val = (newVal == oldVal || (newVal != oldVal && oldVal != newVal)) ? newVal : 'xxx'; + log.add(val); + }; + rootScope + ..context['nanValue'] = double.NAN + ..context['nullValue'] = null + ..context['emptyString'] = '' + ..context['falseValue'] = false + ..context['numberValue'] = 23 + ..watch('nanValue', logger) + ..watch('nullValue', logger) + ..watch('emptyString', logger) + ..watch('falseValue', logger) + ..watch('numberValue', logger) + ..digest(); + + expect(log.removeAt(0).isNaN).toEqual(true); //jasmine's toBe and toEqual don't work well with NaNs + expect(log).toEqual([null, '', false, 23]); + log = []; + rootScope.digest(); + expect(log).toEqual([]); + })); - it('should properly watch canstants', inject((RootScope rootScope, Logger log) { - rootScope.watch('[1, 2]', (v, o) => log([v, o])); - expect(log).toEqual([]); - rootScope.apply(); - expect(log).toEqual([[[1, 2], null]]); - })); + it('should properly watch canstants', inject((RootScope rootScope, Logger log) { + rootScope.watch('[1, 2]', (v, o) => log([v, o])); + expect(log).toEqual([]); + rootScope.apply(); + expect(log).toEqual([[[1, 2], null]]); + })); - it('should properly watch array of fields', inject((RootScope rootScope, Logger log) { - rootScope.context['foo'] = 12; - rootScope.context['bar'] = 34; - rootScope.watch('[foo, bar]', (v, o) => log([v, o])); - expect(log).toEqual([]); - rootScope.apply(); - expect(log).toEqual([[[12, 34], null]]); - log.clear(); - rootScope.context['foo'] = 56; - rootScope.context['bar'] = 78; - rootScope.apply(); - expect(log).toEqual([[[56, 78], [12, 34]]]); - })); + it('should properly watch array of fields', inject((RootScope rootScope, Logger log) { + rootScope.context['foo'] = 12; + rootScope.context['bar'] = 34; + rootScope.watch('[foo, bar]', (v, o) => log([v, o])); + expect(log).toEqual([]); + rootScope.apply(); + expect(log).toEqual([[[12, 34], null]]); + log.clear(); + rootScope.context['foo'] = 56; + rootScope.context['bar'] = 78; + rootScope.apply(); + expect(log).toEqual([[[56, 78], [12, 34]]]); + })); - it('should properly watch array of fields2', inject((RootScope rootScope, Logger log) { - rootScope.watch('[ctrl.foo, ctrl.bar]', (v, o) => log([v, o])); - expect(log).toEqual([]); - rootScope.apply(); - expect(log).toEqual([[[null, null], null]]); - log.clear(); - rootScope.context['ctrl'] = {'foo': 56, 'bar': 78}; - rootScope.apply(); - expect(log).toEqual([[[56, 78], [null, null]]]); - })); - }); + it('should properly watch array of fields2', inject((RootScope rootScope, Logger log) { + rootScope.watch('[ctrl.foo, ctrl.bar]', (v, o) => log([v, o])); + expect(log).toEqual([]); + rootScope.apply(); + expect(log).toEqual([[[null, null], null]]); + log.clear(); + rootScope.context['ctrl'] = {'foo': 56, 'bar': 78}; + rootScope.apply(); + expect(log).toEqual([[[56, 78], [null, null]]]); + })); + }); - describe('special binding modes', () { - it('should bind one time', inject((RootScope rootScope, Logger log) { - rootScope.watch('foo', (v, _) => log('foo:$v')); - rootScope.watch(':foo', (v, _) => log(':foo:$v')); - rootScope.watch('::foo', (v, _) => log('::foo:$v')); - rootScope.apply(); - expect(log).toEqual(['foo:null']); - log.clear(); + describe('special binding modes', () { + it('should bind one time', inject((RootScope rootScope, Logger log) { + rootScope.watch('foo', (v, _) => log('foo:$v')); + rootScope.watch(':foo', (v, _) => log(':foo:$v')); + rootScope.watch('::foo', (v, _) => log('::foo:$v')); - rootScope.context['foo'] = true; - rootScope.apply(); - expect(log).toEqual(['foo:true', ':foo:true', '::foo:true']); - log.clear(); + rootScope.apply(); + expect(log).toEqual(['foo:null']); + log.clear(); - rootScope.context['foo'] = 123; - rootScope.apply(); - expect(log).toEqual(['foo:123', ':foo:123']); - log.clear(); + rootScope.context['foo'] = true; + rootScope.apply(); + expect(log).toEqual(['foo:true', ':foo:true', '::foo:true']); + log.clear(); - rootScope.context['foo'] = null; - rootScope.apply(); - expect(log).toEqual(['foo:null']); - log.clear(); - })); - }); + rootScope.context['foo'] = 123; + rootScope.apply(); + expect(log).toEqual(['foo:123', ':foo:123']); + log.clear(); + rootScope.context['foo'] = null; + rootScope.apply(); + expect(log).toEqual(['foo:null']); + log.clear(); + })); + }); - describe('runAsync', () { - it(r'should run callback before watch', inject((RootScope rootScope) { - var log = ''; - rootScope.runAsync(() { log += 'parent.async;'; }); - rootScope.watch('value', (_, __) { log += 'parent.digest;'; }); - rootScope.digest(); - expect(log).toEqual('parent.async;parent.digest;'); - })); - it(r'should cause a digest rerun', inject((RootScope rootScope) { - rootScope.context['log'] = ''; - rootScope.context['value'] = 0; - // NOTE(deboer): watch listener string functions not yet supported - //rootScope.watch('value', 'log = log + ".";'); - rootScope.watch('value', (_, __) { rootScope.context['log'] += "."; }); - rootScope.watch('init', (_, __) { - rootScope.runAsync(() => rootScope.eval('value = 123; log = log + "=" ')); - expect(rootScope.context['value']).toEqual(0); - }); - rootScope.digest(); - expect(rootScope.context['log']).toEqual('.=.'); - })); + describe('runAsync', () { + it(r'should run callback before watch', inject((RootScope rootScope) { + var log = ''; + rootScope.runAsync(() { log += 'parent.async;'; }); + rootScope.watch('value', (_, __) { log += 'parent.digest;'; }); + rootScope.digest(); + expect(log).toEqual('parent.async;parent.digest;'); + })); - it(r'should run async in the same order as added', inject((RootScope rootScope) { - rootScope.context['log'] = ''; - rootScope.runAsync(() => rootScope.eval("log = log + 1")); - rootScope.runAsync(() => rootScope.eval("log = log + 2")); - rootScope.digest(); - expect(rootScope.context['log']).toEqual('12'); - })); - }); + it(r'should cause a digest rerun', inject((RootScope rootScope) { + rootScope.context['log'] = ''; + rootScope.context['value'] = 0; + // NOTE(deboer): watch listener string functions not yet supported + //rootScope.watch('value', 'log = log + ".";'); + rootScope.watch('value', (_, __) { rootScope.context['log'] += "."; }); + rootScope.watch('init', (_, __) { + rootScope.runAsync(() => rootScope.eval('value = 123; log = log + "=" ')); + expect(rootScope.context['value']).toEqual(0); + }); + rootScope.digest(); + expect(rootScope.context['log']).toEqual('.=.'); + })); + it(r'should run async in the same order as added', inject((RootScope rootScope) { + rootScope.context['log'] = ''; + rootScope.runAsync(() => rootScope.eval("log = log + 1")); + rootScope.runAsync(() => rootScope.eval("log = log + 2")); + rootScope.digest(); + expect(rootScope.context['log']).toEqual('12'); + })); + }); - describe('domRead/domWrite', () { - it(r'should run writes before reads', () { - module((Module module) { - module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler); - }); - inject((RootScope rootScope, Logger logger, ExceptionHandler e) { - LoggingExceptionHandler exceptionHandler = e as LoggingExceptionHandler; - rootScope.domWrite(() { - logger('write1'); - rootScope.domWrite(() => logger('write2')); - throw 'write1'; + + describe('domRead/domWrite', () { + it(r'should run writes before reads', () { + module((Module module) { + module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler); }); - rootScope.domRead(() { - logger('read1'); - rootScope.domRead(() => logger('read2')); - rootScope.domWrite(() => logger('write3')); - throw 'read1'; + inject((RootScope rootScope, Logger logger, ExceptionHandler e) { + LoggingExceptionHandler exceptionHandler = e as LoggingExceptionHandler; + rootScope.domWrite(() { + logger('write1'); + rootScope.domWrite(() => logger('write2')); + throw 'write1'; + }); + rootScope.domRead(() { + logger('read1'); + rootScope.domRead(() => logger('read2')); + rootScope.domWrite(() => logger('write3')); + throw 'read1'; + }); + rootScope.watch('value', (_, __) => logger('observe'), readOnly: true); + rootScope.flush(); + expect(logger).toEqual(['write1', 'write2', 'observe', 'read1', 'read2', 'write3']); + expect(exceptionHandler.errors.length).toEqual(2); + expect(exceptionHandler.errors[0].error).toEqual('write1'); + expect(exceptionHandler.errors[1].error).toEqual('read1'); }); - rootScope.watch('value', (_, __) => logger('observe'), readOnly: true); - rootScope.flush(); - expect(logger).toEqual(['write1', 'write2', 'observe', 'read1', 'read2', 'write3']); - expect(exceptionHandler.errors.length).toEqual(2); - expect(exceptionHandler.errors[0].error).toEqual('write1'); - expect(exceptionHandler.errors[1].error).toEqual('read1'); }); }); - }); - describe('exceptionHander', () { - it('should call ExceptionHandler on zone errors', () { - module((Module module) { - module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler); + describe('exceptionHander', () { + it('should call ExceptionHandler on zone errors', () { + module((Module module) { + module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler); + }); + async((inject((RootScope rootScope, NgZone zone, ExceptionHandler e) { + zone.run(() { + scheduleMicrotask(() => throw 'my error'); + }); + var errors = (e as LoggingExceptionHandler).errors; + expect(errors.length).toEqual(1); + expect(errors.first.error).toEqual('my error'); + }))); }); - async((inject((RootScope rootScope, NgZone zone, ExceptionHandler e) { - zone.run(() { - scheduleMicrotask(() => throw 'my error'); + + it('should call ExceptionHandler on digest errors', () { + module((Module module) { + module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler); }); - var errors = (e as LoggingExceptionHandler).errors; - expect(errors.length).toEqual(1); - expect(errors.first.error).toEqual('my error'); - }))); - }); + async((inject((RootScope rootScope, NgZone zone, ExceptionHandler e) { + rootScope.context['badOne'] = () => new Map(); + rootScope.watch('badOne()', (_, __) => null); - it('should call ExceptionHandler on digest errors', () { - module((Module module) { - module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler); + try { + zone.run(() => null); + } catch(_) {} + + var errors = (e as LoggingExceptionHandler).errors; + expect(errors.length).toEqual(1); + expect(errors.first.error, startsWith('Model did not stabilize')); + }))); }); - async((inject((RootScope rootScope, NgZone zone, ExceptionHandler e) { - rootScope.context['badOne'] = () => new Map(); - rootScope.watch('badOne()', (_, __) => null); - - try { - zone.run(() => null); - } catch(_) {} - - var errors = (e as LoggingExceptionHandler).errors; - expect(errors.length).toEqual(1); - expect(errors.first.error, startsWith('Model did not stabilize')); - }))); }); }); -}); +} @NgFilter(name: 'multiply') class _MultiplyFilter { diff --git a/test/core/templateurl_spec.dart b/test/core/templateurl_spec.dart index e24043a5c..c639b9c89 100644 --- a/test/core/templateurl_spec.dart +++ b/test/core/templateurl_spec.dart @@ -39,196 +39,199 @@ class PrefixedUrlRewriter extends UrlRewriter { call(url) => "PREFIX:$url"; } -main() => describe('template url', () { - afterEach(inject((MockHttpBackend backend) { - backend.verifyNoOutstandingRequest(); - })); - - describe('loading with http rewriting', () { - beforeEach(module((Module module) { - module - ..type(HtmlAndCssComponent) - ..type(UrlRewriter, implementedBy: PrefixedUrlRewriter); +void main() { + describe('template url', () { + afterEach(inject((MockHttpBackend backend) { + backend.verifyNoOutstandingRequest(); })); - it('should use the UrlRewriter for both HTML and CSS URLs', async(inject((Http $http, - Compiler $compile, Scope $rootScope, Logger log, Injector injector, NgZone zone, - MockHttpBackend backend, DirectiveMap directives) { + describe('loading with http rewriting', () { + beforeEach(module((Module module) { + module + ..type(HtmlAndCssComponent) + ..type(UrlRewriter, implementedBy: PrefixedUrlRewriter); + })); + + it('should use the UrlRewriter for both HTML and CSS URLs', async(inject( + (Http $http, Compiler $compile, Scope $rootScope, Logger log, + Injector injector, NgZone zone, MockHttpBackend backend, + DirectiveMap directives) { + + backend + ..whenGET('PREFIX:simple.html').respond('
Simple!
') + ..whenGET('PREFIX:simple.css').respond('.hello{}'); + + var element = $('
ignore
'); + zone.run(() { + $compile(element, directives)(injector, element); + }); + + backend.flush(); + microLeap(); + + expect(renderedText(element)).toEqual('.hello{}Simple!'); + expect(element[0].nodes[0].shadowRoot.innerHtml).toEqual( + '
Simple!
' + ); + }))); + }); + + + describe('async template loading', () { + beforeEach(module((Module module) { + module + ..type(LogAttrDirective) + ..type(SimpleUrlComponent) + ..type(HtmlAndCssComponent) + ..type(OnlyCssComponent) + ..type(InlineWithCssComponent); + })); + + it('should replace element with template from url', async(inject( + (Http $http, Compiler $compile, Scope $rootScope, Logger log, + Injector injector, MockHttpBackend backend, DirectiveMap directives) { + backend.expectGET('simple.html').respond('
Simple!
'); + + var element = $('
ignore
'); + $compile(element, directives)(injector, element); + + backend.flush(); + microLeap(); + + expect(renderedText(element)).toEqual('Simple!'); + $rootScope.apply(); + // Note: There is no ordering. It is who ever comes off the wire first! + expect(log.result()).toEqual('LOG; SIMPLE'); + }))); - backend - ..whenGET('PREFIX:simple.html').respond('
Simple!
') - ..whenGET('PREFIX:simple.css').respond('.hello{}'); + it('should load template from URL once', async(inject( + (Http $http, Compiler $compile, Scope $rootScope, Logger log, + Injector injector, MockHttpBackend backend, DirectiveMap directives) { + backend.whenGET('simple.html').respond('
Simple!
'); - var element = $('
ignore
'); - zone.run(() { + var element = $( + '
' + + 'ignore' + + 'ignore' + + '
'); $compile(element, directives)(injector, element); - }); - backend.flush(); - microLeap(); + backend.flush(); + microLeap(); - expect(renderedText(element)).toEqual('.hello{}Simple!'); - expect(element[0].nodes[0].shadowRoot.innerHtml).toEqual( - '
Simple!
' - ); - }))); - }); + expect(renderedText(element)).toEqual('Simple!Simple!'); + $rootScope.apply(); + // Note: There is no ordering. It is who ever comes off the wire first! + expect(log.result()).toEqual('LOG; LOG; SIMPLE; SIMPLE'); + }))); + it('should load a CSS file into a style', async(inject( + (Http $http, Compiler $compile, Scope $rootScope, Logger log, + Injector injector, MockHttpBackend backend, DirectiveMap directives) { + backend + ..expectGET('simple.css').respond('.hello{}') + ..expectGET('simple.html').respond('
Simple!
'); - describe('async template loading', () { - beforeEach(module((Module module) { - module - ..type(LogAttrDirective) - ..type(SimpleUrlComponent) - ..type(HtmlAndCssComponent) - ..type(OnlyCssComponent) - ..type(InlineWithCssComponent); - })); + var element = $('
ignore
'); + $compile(element, directives)(injector, element); - it('should replace element with template from url', async(inject((Http $http, - Compiler $compile, Scope $rootScope, Logger log, Injector injector, - MockHttpBackend backend, DirectiveMap directives) { - backend.expectGET('simple.html').respond('
Simple!
'); + backend.flush(); + microLeap(); - var element = $('
ignore
'); - $compile(element, directives)(injector, element); + expect(renderedText(element)).toEqual('.hello{}Simple!'); + expect(element[0].nodes[0].shadowRoot.innerHtml).toEqual( + '
Simple!
' + ); + $rootScope.apply(); + // Note: There is no ordering. It is who ever comes off the wire first! + expect(log.result()).toEqual('LOG; SIMPLE'); + }))); - backend.flush(); - microLeap(); + it('should load a CSS file with a \$template', async(inject( + (Http $http, Compiler $compile, Scope $rootScope, Injector injector, + MockHttpBackend backend, DirectiveMap directives) { + var element = $('
ignore
'); + backend.expectGET('simple.css').respond('.hello{}'); + $compile(element, directives)(injector, element); - expect(renderedText(element)).toEqual('Simple!'); - $rootScope.apply(); - // Note: There is no ordering. It is who ever comes off the wire first! - expect(log.result()).toEqual('LOG; SIMPLE'); - }))); + backend.flush(); + microLeap(); + expect(renderedText(element)).toEqual('.hello{}inline!'); + }))); - it('should load template from URL once', async(inject((Http $http, - Compiler $compile, Scope $rootScope, Logger log, Injector injector, - MockHttpBackend backend, DirectiveMap directives) { - backend.whenGET('simple.html').respond('
Simple!
'); + it('should ignore CSS load errors ', async(inject( + (Http $http, Compiler $compile, Scope $rootScope, Injector injector, + MockHttpBackend backend, DirectiveMap directives) { + var element = $('
ignore
'); + backend.expectGET('simple.css').respond(500, 'some error'); + $compile(element, directives)(injector, element); - var element = $( - '
' + - 'ignore' + - 'ignore' + - '
'); - $compile(element, directives)(injector, element); - - backend.flush(); - microLeap(); - - expect(renderedText(element)).toEqual('Simple!Simple!'); - $rootScope.apply(); - // Note: There is no ordering. It is who ever comes off the wire first! - expect(log.result()).toEqual('LOG; LOG; SIMPLE; SIMPLE'); - }))); - - it('should load a CSS file into a style', async(inject((Http $http, - Compiler $compile, Scope $rootScope, Logger log, Injector injector, - MockHttpBackend backend, DirectiveMap directives) { - backend - ..expectGET('simple.css').respond('.hello{}') - ..expectGET('simple.html').respond('
Simple!
'); - - var element = $('
ignore
'); - $compile(element, directives)(injector, element); - - backend.flush(); - microLeap(); - - expect(renderedText(element)).toEqual('.hello{}Simple!'); - expect(element[0].nodes[0].shadowRoot.innerHtml).toEqual( - '
Simple!
' - ); - $rootScope.apply(); - // Note: There is no ordering. It is who ever comes off the wire first! - expect(log.result()).toEqual('LOG; SIMPLE'); - }))); - - it('should load a CSS file with a \$template', async(inject((Http $http, - Compiler $compile, Scope $rootScope, Injector injector, + backend.flush(); + microLeap(); + expect(renderedText(element)).toEqual( + '/*\n' + 'HTTP 500: some error\n' + '*/\n' + 'inline!'); + }))); + + it('should load a CSS with no template', async(inject( + (Http $http, Compiler $compile, Scope $rootScope, Injector injector, MockHttpBackend backend, DirectiveMap directives) { - var element = $('
ignore
'); - backend.expectGET('simple.css').respond('.hello{}'); - $compile(element, directives)(injector, element); + var element = $('
ignore
'); + backend.expectGET('simple.css').respond('.hello{}'); + $compile(element, directives)(injector, element); - backend.flush(); - microLeap(); - expect(renderedText(element)).toEqual('.hello{}inline!'); - }))); + backend.flush(); + microLeap(); + expect(renderedText(element)).toEqual('.hello{}'); + }))); - it('should ignore CSS load errors ', async(inject((Http $http, - Compiler $compile, Scope $rootScope, Injector injector, + it('should load the CSS before the template is loaded', async(inject( + (Http $http, Compiler $compile, Scope $rootScope, Injector injector, MockHttpBackend backend, DirectiveMap directives) { - var element = $('
ignore
'); - backend.expectGET('simple.css').respond(500, 'some error'); - $compile(element, directives)(injector, element); - - backend.flush(); - microLeap(); - expect(renderedText(element)).toEqual( - '/*\n' - 'HTTP 500: some error\n' - '*/\n' - 'inline!'); - }))); - - it('should load a CSS with no template', async(inject((Http $http, - Compiler $compile, Scope $rootScope, Injector injector, - MockHttpBackend backend, DirectiveMap directives) { - var element = $('
ignore
'); - backend.expectGET('simple.css').respond('.hello{}'); - $compile(element, directives)(injector, element); - - backend.flush(); - microLeap(); - expect(renderedText(element)).toEqual('.hello{}'); - }))); - - it('should load the CSS before the template is loaded', async(inject((Http $http, - Compiler $compile, Scope $rootScope, Injector injector, - MockHttpBackend backend, DirectiveMap directives) { - backend - ..expectGET('simple.css').respond('.hello{}') - ..expectGET('simple.html').respond('
Simple!
'); - - var element = $('ignore'); - $compile(element, directives)(injector, element); - - backend.flush(); - microLeap(); - expect(renderedText(element)).toEqual('.hello{}Simple!'); - }))); - }); + backend + ..expectGET('simple.css').respond('.hello{}') + ..expectGET('simple.html').respond('
Simple!
'); - describe('multiple css loading', () { - beforeEach(module((Module module) { - module - ..type(LogAttrDirective) - ..type(HtmlAndMultipleCssComponent); - })); + var element = $('ignore'); + $compile(element, directives)(injector, element); - it('should load multiple CSS files into a style', async(inject((Http $http, - Compiler $compile, Scope $rootScope, Logger log, Injector injector, - MockHttpBackend backend, DirectiveMap directives) { - backend - ..expectGET('simple.css').respond('.hello{}') - ..expectGET('another.css').respond('.world{}') - ..expectGET('simple.html').respond('
Simple!
'); - - var element = $('
ignore
'); - $compile(element, directives)(injector, element); - - backend.flush(); - microLeap(); - - expect(renderedText(element)).toEqual('.hello{}.world{}Simple!'); - expect(element[0].nodes[0].shadowRoot.innerHtml).toEqual( - '
Simple!
' - ); - $rootScope.apply(); - // Note: There is no ordering. It is who ever comes off the wire first! - expect(log.result()).toEqual('LOG; SIMPLE'); - }))); + backend.flush(); + microLeap(); + expect(renderedText(element)).toEqual('.hello{}Simple!'); + }))); + }); + + describe('multiple css loading', () { + beforeEach(module((Module module) { + module + ..type(LogAttrDirective) + ..type(HtmlAndMultipleCssComponent); + })); + + it('should load multiple CSS files into a style', async(inject( + (Http $http, Compiler $compile, Scope $rootScope, Logger log, + Injector injector, MockHttpBackend backend, DirectiveMap directives) { + backend + ..expectGET('simple.css').respond('.hello{}') + ..expectGET('another.css').respond('.world{}') + ..expectGET('simple.html').respond('
Simple!
'); + + var element = $('
ignore
'); + $compile(element, directives)(injector, element); + + backend.flush(); + microLeap(); + + expect(renderedText(element)).toEqual('.hello{}.world{}Simple!'); + expect(element[0].nodes[0].shadowRoot.innerHtml).toEqual( + '
Simple!
' + ); + $rootScope.apply(); + // Note: There is no ordering. It is who ever comes off the wire first! + expect(log.result()).toEqual('LOG; SIMPLE'); + }))); + }); }); -}); +} diff --git a/test/core/zone_spec.dart b/test/core/zone_spec.dart index a50b1c9a0..68281e027 100644 --- a/test/core/zone_spec.dart +++ b/test/core/zone_spec.dart @@ -4,358 +4,360 @@ import '../_specs.dart'; import 'dart:async'; -main() => describe('zone', () { - var zone; - var exceptionHandler; - beforeEach(module((Module module) { - exceptionHandler = new LoggingExceptionHandler(); - module.value(ExceptionHandler, exceptionHandler); - })); - - beforeEach(inject((Logger log, ExceptionHandler eh) { - zone = new NgZone(); - zone.onTurnDone = () { - log('onTurnDone'); - }; - zone.onError = (e, s, ls) => eh(e, s); - })); - - - describe('exceptions', () { - it('should rethrow exceptions from the body and call onError', () { - var error; - zone.onError = (e, s, l) => error = e; - expect(() { - zone.run(() { - throw ['hello']; - }); - }).toThrow('hello'); - expect(error).toEqual(['hello']); - }); +void main() { + describe('zone', () { + var zone; + var exceptionHandler; + beforeEach(module((Module module) { + exceptionHandler = new LoggingExceptionHandler(); + module.value(ExceptionHandler, exceptionHandler); + })); + beforeEach(inject((Logger log, ExceptionHandler eh) { + zone = new NgZone(); + zone.onTurnDone = () { + log('onTurnDone'); + }; + zone.onError = (e, s, ls) => eh(e, s); + })); - it('should call onError for errors from scheduleMicrotask', async(inject(() { - zone.run(() { - scheduleMicrotask(() { - throw ["async exception"]; - }); + + describe('exceptions', () { + it('should rethrow exceptions from the body and call onError', () { + var error; + zone.onError = (e, s, l) => error = e; + expect(() { + zone.run(() { + throw ['hello']; + }); + }).toThrow('hello'); + expect(error).toEqual(['hello']); }); - expect(exceptionHandler.errors.length).toEqual(1); - expect(exceptionHandler.errors[0].error).toEqual(["async exception"]); - }))); + it('should call onError for errors from scheduleMicrotask', async(inject(() { + zone.run(() { + scheduleMicrotask(() { + throw ["async exception"]; + }); + }); + + expect(exceptionHandler.errors.length).toEqual(1); + expect(exceptionHandler.errors[0].error).toEqual(["async exception"]); + }))); - it('should allow executing code outside the zone', inject(() { - var zone = new NgZone(); - var outerZone = Zone.current; - var ngZone; - var outsideZone; - zone.run(() { - ngZone = Zone.current; - zone.runOutsideAngular(() { - outsideZone = Zone.current; + + it('should allow executing code outside the zone', inject(() { + var zone = new NgZone(); + var outerZone = Zone.current; + var ngZone; + var outsideZone; + zone.run(() { + ngZone = Zone.current; + zone.runOutsideAngular(() { + outsideZone = Zone.current; + }); }); - }); - expect(outsideZone).toEqual(outerZone); - expect(ngZone.parent).toEqual((outerZone)); - })); + expect(outsideZone).toEqual(outerZone); + expect(ngZone.parent).toEqual((outerZone)); + })); - it('should rethrow exceptions from the onTurnDone and call onError when the zone is sync', () { - zone.onTurnDone = () { - throw ["fromOnTurnDone"]; - }; + it('should rethrow exceptions from the onTurnDone and call onError when the zone is sync', () { + zone.onTurnDone = () { + throw ["fromOnTurnDone"]; + }; - expect(() { - zone.run(() { }); - }).toThrow('fromOnTurnDone'); + expect(() { + zone.run(() { }); + }).toThrow('fromOnTurnDone'); - expect(exceptionHandler.errors.length).toEqual(1); - expect(exceptionHandler.errors[0].error).toEqual(["fromOnTurnDone"]); - }); + expect(exceptionHandler.errors.length).toEqual(1); + expect(exceptionHandler.errors[0].error).toEqual(["fromOnTurnDone"]); + }); - it('should rethrow exceptions from the onTurnDone and call onError when the zone is async', () { - var asyncRan = false; + it('should rethrow exceptions from the onTurnDone and call onError when the zone is async', () { + var asyncRan = false; - zone.onTurnDone = () { - throw ["fromOnTurnDone"]; - }; + zone.onTurnDone = () { + throw ["fromOnTurnDone"]; + }; - expect(() { - zone.run(() { - scheduleMicrotask(() { - asyncRan = true; + expect(() { + zone.run(() { + scheduleMicrotask(() { + asyncRan = true; + }); }); - }); - }).toThrow('fromOnTurnDone'); + }).toThrow('fromOnTurnDone'); - expect(asyncRan).toBeTruthy(); - expect(exceptionHandler.errors.length).toEqual(1); - expect(exceptionHandler.errors[0].error).toEqual(["fromOnTurnDone"]); + expect(asyncRan).toBeTruthy(); + expect(exceptionHandler.errors.length).toEqual(1); + expect(exceptionHandler.errors[0].error).toEqual(["fromOnTurnDone"]); + }); }); - }); - xdescribe('long stack traces', () { - it('should have nice error when crossing scheduleMicrotask boundries', async(inject(() { - var error; - var stack; - var longStacktrace; + xdescribe('long stack traces', () { + it('should have nice error when crossing scheduleMicrotask boundries', async(inject(() { + var error; + var stack; + var longStacktrace; + + zone.onError = (e, s, f) { + error = e; + stack = s; + longStacktrace = f; + }; + var FRAME = new RegExp(r'.*\(.*\:(\d+):\d+\)'); + + var line = ((){ try {throw [];} catch(e, s) { return int.parse(FRAME.firstMatch('$s')[1]);}})(); + var throwFn = () { throw ['double zonned']; }; + var inner = () => zone.run(throwFn); + var middle = () => scheduleMicrotask(inner); + var outer = () => scheduleMicrotask(middle); + zone.run(outer); + + microLeap(); + expect(error).toEqual(['double zonned']); + + // Not in dart2js.. + if ('$stack'.contains('.dart.js')) { + return; + } + + expect('$stack').toContain('zone_spec.dart:${line+1}'); + expect('$stack').toContain('zone_spec.dart:${line+2}'); + expect('$longStacktrace').toContain('zone_spec.dart:${line+3}'); + expect('$longStacktrace').toContain('zone_spec.dart:${line+4}'); + expect('$longStacktrace').toContain('zone_spec.dart:${line+5}'); + }))); + }); - zone.onError = (e, s, f) { - error = e; - stack = s; - longStacktrace = f; - }; - var FRAME = new RegExp(r'.*\(.*\:(\d+):\d+\)'); + it('should call onTurnDone after a synchronous block', inject((Logger log) { + zone.run(() { + log('run'); + }); + expect(log.result()).toEqual('run; onTurnDone'); + })); - var line = ((){ try {throw [];} catch(e, s) { return int.parse(FRAME.firstMatch('$s')[1]);}})(); - var throwFn = () { throw ['double zonned']; }; - var inner = () => zone.run(throwFn); - var middle = () => scheduleMicrotask(inner); - var outer = () => scheduleMicrotask(middle); - zone.run(outer); - microLeap(); - expect(error).toEqual(['double zonned']); - - // Not in dart2js.. - if ('$stack'.contains('.dart.js')) { - return; - } - - expect('$stack').toContain('zone_spec.dart:${line+1}'); - expect('$stack').toContain('zone_spec.dart:${line+2}'); - expect('$longStacktrace').toContain('zone_spec.dart:${line+3}'); - expect('$longStacktrace').toContain('zone_spec.dart:${line+4}'); - expect('$longStacktrace').toContain('zone_spec.dart:${line+5}'); - }))); - }); - - it('should call onTurnDone after a synchronous block', inject((Logger log) { - zone.run(() { - log('run'); + it('should return the body return value from run', () { + expect(zone.run(() { return 6; })).toEqual(6); }); - expect(log.result()).toEqual('run; onTurnDone'); - })); - it('should return the body return value from run', () { - expect(zone.run(() { return 6; })).toEqual(6); - }); + it('should call onTurnDone for a scheduleMicrotask in onTurnDone', async(inject((Logger log) { + var ran = false; + zone.onTurnDone = () { + if (!ran) { + scheduleMicrotask(() { ran = true; log('onTurnAsync'); }); + } + log('onTurnDone'); + }; + zone.run(() { + log('run'); + }); + microLeap(); + + expect(log.result()).toEqual('run; onTurnDone; onTurnAsync; onTurnDone'); + }))); - it('should call onTurnDone for a scheduleMicrotask in onTurnDone', async(inject((Logger log) { - var ran = false; - zone.onTurnDone = () { - if (!ran) { - scheduleMicrotask(() { ran = true; log('onTurnAsync'); }); - } - log('onTurnDone'); - }; - zone.run(() { - log('run'); - }); - microLeap(); - - expect(log.result()).toEqual('run; onTurnDone; onTurnAsync; onTurnDone'); - }))); - - - it('should call onTurnDone for a scheduleMicrotask in onTurnDone triggered by a scheduleMicrotask in run', async(inject((Logger log) { - var ran = false; - zone.onTurnDone = () { - if (!ran) { - scheduleMicrotask(() { ran = true; log('onTurnAsync'); }); - } - log('onTurnDone'); - }; - zone.run(() { - scheduleMicrotask(() { log('scheduleMicrotask'); }); - log('run'); - }); - microLeap(); + it('should call onTurnDone for a scheduleMicrotask in onTurnDone triggered by a scheduleMicrotask in run', async(inject((Logger log) { + var ran = false; + zone.onTurnDone = () { + if (!ran) { + scheduleMicrotask(() { ran = true; log('onTurnAsync'); }); + } + log('onTurnDone'); + }; + zone.run(() { + scheduleMicrotask(() { log('scheduleMicrotask'); }); + log('run'); + }); + microLeap(); - expect(log.result()).toEqual('run; scheduleMicrotask; onTurnDone; onTurnAsync; onTurnDone'); - }))); + expect(log.result()).toEqual('run; scheduleMicrotask; onTurnDone; onTurnAsync; onTurnDone'); + }))); - it('should call onTurnDone once after a turn', async(inject((Logger log) { - zone.run(() { - log('run start'); - scheduleMicrotask(() { - log('async'); + it('should call onTurnDone once after a turn', async(inject((Logger log) { + zone.run(() { + log('run start'); + scheduleMicrotask(() { + log('async'); + }); + log('run end'); }); - log('run end'); - }); - microLeap(); + microLeap(); - expect(log.result()).toEqual('run start; run end; async; onTurnDone'); - }))); + expect(log.result()).toEqual('run start; run end; async; onTurnDone'); + }))); - it('should work for Future.value as well', async(inject((Logger log) { - var futureRan = false; - zone.onTurnDone = () { - if (!futureRan) { - new Future.value(null).then((_) { log('onTurn future'); }); - futureRan = true; - } - log('onTurnDone'); - }; + it('should work for Future.value as well', async(inject((Logger log) { + var futureRan = false; + zone.onTurnDone = () { + if (!futureRan) { + new Future.value(null).then((_) { log('onTurn future'); }); + futureRan = true; + } + log('onTurnDone'); + }; - zone.run(() { - log('run start'); - new Future.value(null) + zone.run(() { + log('run start'); + new Future.value(null) .then((_) { log('future then'); new Future.value(null) - .then((_) { log('future ?'); }); + .then((_) { log('future ?'); }); return new Future.value(null); }) .then((_) { log('future ?'); }); - log('run end'); - }); - microLeap(); + log('run end'); + }); + microLeap(); - expect(log.result()).toEqual('run start; run end; future then; future ?; future ?; onTurnDone; onTurn future; onTurnDone'); - }))); + expect(log.result()).toEqual('run start; run end; future then; future ?; future ?; onTurnDone; onTurn future; onTurnDone'); + }))); - it('should call onTurnDone after each turn', async(inject((Logger log) { - Completer a, b; - zone.run(() { - a = new Completer(); - b = new Completer(); - a.future.then((_) => log('a then')); - b.future.then((_) => log('b then')); - log('run start'); - }); - microLeap(); - zone.run(() { - a.complete(null); - }); - microLeap(); - zone.run(() { - b.complete(null); - }); - microLeap(); + it('should call onTurnDone after each turn', async(inject((Logger log) { + Completer a, b; + zone.run(() { + a = new Completer(); + b = new Completer(); + a.future.then((_) => log('a then')); + b.future.then((_) => log('b then')); + log('run start'); + }); + microLeap(); + zone.run(() { + a.complete(null); + }); + microLeap(); + zone.run(() { + b.complete(null); + }); + microLeap(); - expect(log.result()).toEqual('run start; onTurnDone; a then; onTurnDone; b then; onTurnDone'); - }))); + expect(log.result()).toEqual('run start; onTurnDone; a then; onTurnDone; b then; onTurnDone'); + }))); - it('should call onTurnDone after each turn in a chain', async(inject((Logger log) { - zone.run(() { - log('run start'); - scheduleMicrotask(() { - log('async1'); + it('should call onTurnDone after each turn in a chain', async(inject((Logger log) { + zone.run(() { + log('run start'); scheduleMicrotask(() { - log('async2'); + log('async1'); + scheduleMicrotask(() { + log('async2'); + }); }); + log('run end'); }); - log('run end'); - }); - microLeap(); + microLeap(); - expect(log.result()).toEqual('run start; run end; async1; async2; onTurnDone'); - }))); + expect(log.result()).toEqual('run start; run end; async1; async2; onTurnDone'); + }))); - it('should call onTurnDone for futures created outside of run body', async(inject((Logger log) { - var future = new Future.value(4).then((x) => new Future.value(x)); - zone.run(() { - future.then((_) => log('future then')); - log('zone run'); - }); - microLeap(); - - expect(log.result()).toEqual('zone run; onTurnDone; future then; onTurnDone'); - }))); - - - it('should call onTurnDone even if there was an exception in body', async(inject((Logger log) { - zone.onError = (e, s, l) => log('onError'); - expect(() => zone.run(() { - log('zone run'); - throw 'zoneError'; - })).toThrow('zoneError'); - expect(() => zone.assertInTurn()).toThrow(); - expect(log.result()).toEqual('zone run; onError; onTurnDone'); - }))); - - - it('should call onTurnDone even if there was an exception in scheduleMicrotask', async(inject((Logger log) { - zone.onError = (e, s, l) => log('onError'); - zone.run(() { - log('zone run'); - scheduleMicrotask(() { - log('scheduleMicrotask'); - throw new Error(); + it('should call onTurnDone for futures created outside of run body', async(inject((Logger log) { + var future = new Future.value(4).then((x) => new Future.value(x)); + zone.run(() { + future.then((_) => log('future then')); + log('zone run'); }); - }); + microLeap(); - microLeap(); - - expect(() => zone.assertInTurn()).toThrow(); - expect(log.result()).toEqual('zone run; scheduleMicrotask; onError; onTurnDone'); - }))); - - it('should support assertInZone', async(() { - var calls = ''; - zone.onTurnDone = () { - zone.assertInZone(); - calls += 'done;'; - }; - zone.run(() { - zone.assertInZone(); - calls += 'sync;'; - scheduleMicrotask(() { - zone.assertInZone(); - calls += 'async;'; - }); - }); + expect(log.result()).toEqual('zone run; onTurnDone; future then; onTurnDone'); + }))); + + + it('should call onTurnDone even if there was an exception in body', async(inject((Logger log) { + zone.onError = (e, s, l) => log('onError'); + expect(() => zone.run(() { + log('zone run'); + throw 'zoneError'; + })).toThrow('zoneError'); + expect(() => zone.assertInTurn()).toThrow(); + expect(log.result()).toEqual('zone run; onError; onTurnDone'); + }))); - microLeap(); - expect(calls).toEqual('sync;async;done;'); - })); - it('should throw outside of the zone', () { - expect(async(() { - zone.assertInZone(); + it('should call onTurnDone even if there was an exception in scheduleMicrotask', async(inject((Logger log) { + zone.onError = (e, s, l) => log('onError'); + zone.run(() { + log('zone run'); + scheduleMicrotask(() { + log('scheduleMicrotask'); + throw new Error(); + }); + }); + microLeap(); - })).toThrow(); - }); + expect(() => zone.assertInTurn()).toThrow(); + expect(log.result()).toEqual('zone run; scheduleMicrotask; onError; onTurnDone'); + }))); - it('should support assertInTurn', async(() { - var calls = ''; - zone.onTurnDone = () { - calls += 'done;'; - zone.assertInTurn(); - }; - zone.run(() { - calls += 'sync;'; - zone.assertInTurn(); - scheduleMicrotask(() { - calls += 'async;'; - zone.assertInTurn(); + it('should support assertInZone', async(() { + var calls = ''; + zone.onTurnDone = () { + zone.assertInZone(); + calls += 'done;'; + }; + zone.run(() { + zone.assertInZone(); + calls += 'sync;'; + scheduleMicrotask(() { + zone.assertInZone(); + calls += 'async;'; + }); }); + + microLeap(); + expect(calls).toEqual('sync;async;done;'); + })); + + it('should throw outside of the zone', () { + expect(async(() { + zone.assertInZone(); + microLeap(); + })).toThrow(); }); - microLeap(); - expect(calls).toEqual('sync;async;done;'); - })); + it('should support assertInTurn', async(() { + var calls = ''; + zone.onTurnDone = () { + calls += 'done;'; + zone.assertInTurn(); + }; + zone.run(() { + calls += 'sync;'; + zone.assertInTurn(); + scheduleMicrotask(() { + calls += 'async;'; + zone.assertInTurn(); + }); + }); - it('should assertInTurn outside of the zone', () { - expect(async(() { - zone.assertInTurn(); microLeap(); - })).toThrow('ssertion'); // Support both dart2js and the VM with half a word. + expect(calls).toEqual('sync;async;done;'); + })); + + + it('should assertInTurn outside of the zone', () { + expect(async(() { + zone.assertInTurn(); + microLeap(); + })).toThrow('ssertion'); // Support both dart2js and the VM with half a word. + }); }); -}); +} diff --git a/test/core_dom/compiler_spec.dart b/test/core_dom/compiler_spec.dart index 3566a7644..700b0406b 100644 --- a/test/core_dom/compiler_spec.dart +++ b/test/core_dom/compiler_spec.dart @@ -3,7 +3,8 @@ library compiler_spec; import '../_specs.dart'; -main() => describe('dte.compiler', () { +void main() { + describe('dte.compiler', () { Compiler $compile; DirectiveMap directives; Injector injector; @@ -11,17 +12,17 @@ main() => describe('dte.compiler', () { beforeEach(module((Module module) { module - ..type(TabComponent) - ..type(PublishTypesAttrDirective) - ..type(PaneComponent) - ..type(SimpleTranscludeInAttachAttrDirective) - ..type(IncludeTranscludeAttrDirective) - ..type(LocalAttrDirective) - ..type(OneOfTwoDirectives) - ..type(TwoOfTwoDirectives) - ..type(MyController) - ..type(MyParentController) - ..type(MyChildController); + ..type(TabComponent) + ..type(PublishTypesAttrDirective) + ..type(PaneComponent) + ..type(SimpleTranscludeInAttachAttrDirective) + ..type(IncludeTranscludeAttrDirective) + ..type(LocalAttrDirective) + ..type(OneOfTwoDirectives) + ..type(TwoOfTwoDirectives) + ..type(MyController) + ..type(MyParentController) + ..type(MyChildController); return (Injector _injector) { injector = _injector; $compile = injector.get(Compiler); @@ -114,9 +115,9 @@ main() => describe('dte.compiler', () { it('should compile nested repeater', inject((Compiler $compile) { var element = $( '
' + - '
    ' + - '
  • {{li}}
  • ' + - '
' + + '
    ' + + '
  • {{li}}
  • ' + + '
' + '
'); var template = $compile(element, directives); @@ -137,8 +138,6 @@ main() => describe('dte.compiler', () { expect(log).toEqual(['OneOfTwo', 'TwoOfTwo']); })); - - describe("interpolation", () { it('should interpolate attribute nodes', inject(() { var element = $('
'); @@ -166,22 +165,23 @@ main() => describe('dte.compiler', () { describe('components', () { beforeEach(module((Module module) { - module.type(SimpleComponent); - module.type(CamelCaseMapComponent); - module.type(IoComponent); - module.type(IoControllerComponent); - module.type(UnpublishedIoControllerComponent); - module.type(IncorrectMappingComponent); - module.type(NonAssignableMappingComponent); - module.type(ParentExpressionComponent); - module.type(PublishMeComponent); - module.type(PublishMeDirective); - module.type(LogComponent); - module.type(AttachDetachComponent); - module.type(SimpleAttachComponent); - module.type(SimpleComponent); - module.type(ExprAttrComponent); - module.type(SayHelloFilter); + module + ..type(SimpleComponent) + ..type(CamelCaseMapComponent) + ..type(IoComponent) + ..type(IoControllerComponent) + ..type(UnpublishedIoControllerComponent) + ..type(IncorrectMappingComponent) + ..type(NonAssignableMappingComponent) + ..type(ParentExpressionComponent) + ..type(PublishMeComponent) + ..type(PublishMeDirective) + ..type(LogComponent) + ..type(AttachDetachComponent) + ..type(SimpleAttachComponent) + ..type(SimpleComponent) + ..type(ExprAttrComponent) + ..type(SayHelloFilter); })); it('should select on element', async(inject((NgZone zone) { @@ -230,7 +230,7 @@ main() => describe('dte.compiler', () { var element = $(''); zone.run(() => - $compile(element, directives)(injector, element)); + $compile(element, directives)(injector, element)); microLeap(); expect(renderedText(element)).toEqual('inside poof'); @@ -239,7 +239,7 @@ main() => describe('dte.compiler', () { it('should behave nicely if a mapped attribute is missing', async(inject((NgZone zone) { var element = $(''); zone.run(() => - $compile(element, directives)(injector, element)); + $compile(element, directives)(injector, element)); microLeap(); expect(renderedText(element)).toEqual('inside '); @@ -249,7 +249,7 @@ main() => describe('dte.compiler', () { rootScope.context['val'] = null; var element = $(''); zone.run(() => - $compile(element, directives)(injector, element)); + $compile(element, directives)(injector, element)); microLeap(); expect(renderedText(element)).toEqual('inside '); @@ -446,7 +446,7 @@ main() => describe('dte.compiler', () { var element = $(r'
'); $compile(element, directives)(injector, element); expect(PublishTypesAttrDirective._injector.get(PublishTypesAttrDirective)). - toBe(PublishTypesAttrDirective._injector.get(PublishTypesDirectiveSuperType)); + toBe(PublishTypesAttrDirective._injector.get(PublishTypesDirectiveSuperType)); })); it('should allow repeaters over controllers', async(inject((Logger logger) { @@ -561,9 +561,9 @@ main() => describe('dte.compiler', () { it('should expose a parent controller to the scope of its children', inject((TestBed _) { - var element = _.compile('
' + - '
{{ my_parent.data() }}
' + - '
'); + var element = _.compile('
' + '
{{ my_parent.data() }}
' + '
'); rootScope.apply(); @@ -583,12 +583,12 @@ main() => describe('dte.compiler', () { }); }); +} @NgController( selector: '[my-parent-controller]', - publishAs: 'my_parent' -) + publishAs: 'my_parent') class MyParentController { data() { return "my data"; @@ -597,8 +597,7 @@ class MyParentController { @NgController( selector: '[my-child-controller]', - publishAs: 'my_child' -) + publishAs: 'my_child') class MyChildController {} @NgComponent( @@ -686,8 +685,7 @@ class PublishTypesAttrDirective implements PublishTypesDirectiveSuperType { @NgComponent( selector: 'simple', - template: r'{{name}}(SHADOW-CONTENT)' -) + template: r'{{name}}(SHADOW-CONTENT)') class SimpleComponent { Scope scope; SimpleComponent(Scope this.scope) { @@ -702,8 +700,7 @@ class SimpleComponent { 'attr': '@scope.context.attr', 'expr': '<=>scope.context.expr', 'ondone': '&scope.context.ondone', - } -) + }) class IoComponent { Scope scope; IoComponent(Scope scope) { @@ -723,8 +720,7 @@ class IoComponent { 'once': '=>!exprOnce', 'ondone': '&onDone', 'on-optional': '&onOptional' - } -) + }) class IoControllerComponent { Scope scope; var attr; @@ -746,8 +742,7 @@ class IoControllerComponent { 'expr': '<=>expr', 'ondone': '&onDone', 'onOptional': '&onOptional' - } -) + }) class UnpublishedIoControllerComponent { Scope scope; var attr; @@ -777,8 +772,7 @@ class NonAssignableMappingComponent { } selector: 'camel-case-map', map: const { 'camel-case': '@scope.context.camelCase', - } -) + }) class CamelCaseMapComponent { Scope scope; CamelCaseMapComponent(Scope this.scope) { @@ -791,8 +785,7 @@ class CamelCaseMapComponent { template: '
inside {{fromParent()}}
', map: const { 'from-parent': '&scope.context.fromParent', - } -) + }) class ParentExpressionComponent { Scope scope; ParentExpressionComponent(Scope this.scope); @@ -801,17 +794,14 @@ class ParentExpressionComponent { @NgComponent( selector: 'publish-me', template: r'{{ctrlName.value}}', - publishAs: 'ctrlName' -) + publishAs: 'ctrlName') class PublishMeComponent { String value = 'WORKED'; } - @NgController ( selector: '[publish-me]', - publishAs: 'ctrlName' -) + publishAs: 'ctrlName') class PublishMeDirective { String value = 'WORKED'; } @@ -820,8 +810,7 @@ class PublishMeDirective { @NgComponent( selector: 'log', template: r'', - publishAs: 'ctrlName' -) + publishAs: 'ctrlName') class LogComponent { LogComponent(Scope scope, Logger logger) { logger(scope); @@ -838,8 +827,7 @@ class LogComponent { 'optional-one': '=>optional', 'optional-two': '<=>optional', 'optional-once': '=>!optional', - } -) + }) class AttachDetachComponent implements NgAttachAware, NgDetachAware, NgShadowRootAware { Logger logger; Scope scope; @@ -863,8 +851,7 @@ class AttachDetachComponent implements NgAttachAware, NgDetachAware, NgShadowRoo @NgController( selector: '[my-controller]', - publishAs: 'myCtrl' -) + publishAs: 'myCtrl') class MyController { MyController(Scope scope) { scope.context['name'] = 'MyController'; @@ -892,8 +879,7 @@ class SayHelloFilter { 'expr': '<=>expr', 'one-way': '=>oneWay', 'once': '=>!exprOnce' - } -) + }) class ExprAttrComponent { var expr; var oneWay; diff --git a/test/core_dom/cookies_spec.dart b/test/core_dom/cookies_spec.dart index c4c2d2aa5..f5e5a4458 100644 --- a/test/core_dom/cookies_spec.dart +++ b/test/core_dom/cookies_spec.dart @@ -3,241 +3,243 @@ library cookies_spec; import '../_specs.dart'; import 'package:angular/core_dom/module.dart'; -main() => describe('cookies', () { - deleteAllCookies() { - var cookies = document.cookie.split(";"); - var path = window.location.pathname; - - for (var i = 0; i < cookies.length; i++) { - var cookie = cookies[i]; - var eqPos = cookie.indexOf("="); - var name = eqPos > -1 ? cookie.substring(0, eqPos) : ''; - var parts = path.split('/'); - while (!parts.isEmpty) { - var joinedParts = parts.join('/'); - document.cookie = name + "=;path=" + (joinedParts.isEmpty ? '/': joinedParts) + +void main() { + describe('cookies', () { + deleteAllCookies() { + var cookies = document.cookie.split(";"); + var path = window.location.pathname; + + for (var i = 0; i < cookies.length; i++) { + var cookie = cookies[i]; + var eqPos = cookie.indexOf("="); + var name = eqPos > -1 ? cookie.substring(0, eqPos) : ''; + var parts = path.split('/'); + while (!parts.isEmpty) { + var joinedParts = parts.join('/'); + document.cookie = name + "=;path=" + (joinedParts.isEmpty ? '/': joinedParts) + ";expires=Thu, 01 Jan 1970 00:00:00 GMT"; - parts.removeLast(); + parts.removeLast(); + } } } - } - afterEach(() { - deleteAllCookies(); - expect(document.cookie).toEqual(''); - }); + afterEach(() { + deleteAllCookies(); + expect(document.cookie).toEqual(''); + }); - describe('browser cookies', () { - var cookies; + describe('browser cookies', () { + var cookies; - beforeEach(module((Module module) { - module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler); - })); + beforeEach(module((Module module) { + module.type(ExceptionHandler, implementedBy: LoggingExceptionHandler); + })); - beforeEach(inject((BrowserCookies iCookies) { - iCookies.cookiePath = '/'; - deleteAllCookies(); - expect(document.cookie).toEqual(''); + beforeEach(inject((BrowserCookies iCookies) { + iCookies.cookiePath = '/'; + deleteAllCookies(); + expect(document.cookie).toEqual(''); - iCookies.cookiePath = '/'; - cookies = iCookies; - })); + iCookies.cookiePath = '/'; + cookies = iCookies; + })); - describe('remove via cookies(cookieName, null)', () { + describe('remove via cookies(cookieName, null)', () { - it('should remove a cookie when it is present', () { - document.cookie = 'foo=bar;path=/'; + it('should remove a cookie when it is present', () { + document.cookie = 'foo=bar;path=/'; - cookies['foo'] = null; + cookies['foo'] = null; - expect(document.cookie).toEqual(''); - expect(cookies.all).toEqual({}); - }); + expect(document.cookie).toEqual(''); + expect(cookies.all).toEqual({}); + }); - it('should do nothing when an nonexisting cookie is being removed', () { - cookies['doesntexist'] = null; - expect(document.cookie).toEqual(''); - expect(cookies.all).toEqual({}); + it('should do nothing when an nonexisting cookie is being removed', () { + cookies['doesntexist'] = null; + expect(document.cookie).toEqual(''); + expect(cookies.all).toEqual({}); + }); }); - }); - describe('put via cookies(cookieName, string)', () { + describe('put via cookies(cookieName, string)', () { - it('should create and store a cookie', () { - cookies['cookieName'] = 'cookie=Value'; - expect(document.cookie).toEqual('cookieName=cookie%3DValue'); - expect(cookies.all).toEqual({'cookieName':'cookie=Value'}); - }); + it('should create and store a cookie', () { + cookies['cookieName'] = 'cookie=Value'; + expect(document.cookie).toEqual('cookieName=cookie%3DValue'); + expect(cookies.all).toEqual({'cookieName':'cookie=Value'}); + }); - it('should overwrite an existing unsynced cookie', () { - document.cookie = "cookie=new;path=/"; + it('should overwrite an existing unsynced cookie', () { + document.cookie = "cookie=new;path=/"; - var oldVal = cookies['cookie'] = 'newer'; + var oldVal = cookies['cookie'] = 'newer'; - expect(document.cookie).toEqual('cookie=newer'); - expect(cookies.all).toEqual({'cookie':'newer'}); - expect(oldVal).not.toBe(null); - }); + expect(document.cookie).toEqual('cookie=newer'); + expect(cookies.all).toEqual({'cookie':'newer'}); + expect(oldVal).not.toBe(null); + }); - it('should escape both name and value', () { - cookies['cookie1='] = 'val;ue'; - cookies['cookie2=bar;baz'] = 'val=ue'; + it('should escape both name and value', () { + cookies['cookie1='] = 'val;ue'; + cookies['cookie2=bar;baz'] = 'val=ue'; - var rawCookies = document.cookie.split("; "); //order is not guaranteed, so we need to parse - expect(rawCookies.length).toEqual(2); - expect(rawCookies).toContain('cookie1%3D=val%3Bue'); - expect(rawCookies).toContain('cookie2%3Dbar%3Bbaz=val%3Due'); - }); + var rawCookies = document.cookie.split("; "); //order is not guaranteed, so we need to parse + expect(rawCookies.length).toEqual(2); + expect(rawCookies).toContain('cookie1%3D=val%3Bue'); + expect(rawCookies).toContain('cookie2%3Dbar%3Bbaz=val%3Due'); + }); - it('should log warnings when 4kb per cookie storage limit is reached', - inject((ExceptionHandler exceptionHandler) { - var i, longVal = '', cookieStr; + it('should log warnings when 4kb per cookie storage limit is reached', + inject((ExceptionHandler exceptionHandler) { + var i, longVal = '', cookieStr; - for (i=0; i<4083; i++) { - longVal += 'r'; // Can't do + due to dartbug.com/14281 - } + for (i=0; i<4083; i++) { + longVal += 'r'; // Can't do + due to dartbug.com/14281 + } - cookieStr = document.cookie; - cookies['x'] = longVal; //total size 4093-4096, so it should go through - expect(document.cookie).not.toEqual(cookieStr); - expect(cookies['x']).toEqual(longVal); - //expect(logs.warn).toEqual([]); - var overflow = 'xxxxxxxxxxxxxxxxxxxx'; - cookies['x'] = longVal + overflow; //total size 4097-4099, a warning should be logged - //expect(logs.warn).toEqual( - // [[ "Cookie 'x' possibly not set or overflowed because it was too large (4097 > 4096 " + - // "bytes)!" ]]); - expect(document.cookie).not.toContain(overflow); - - //force browser to dropped a cookie and make sure that the cache is not out of sync - cookies['x'] = 'shortVal'; - expect(cookies['x']).toEqual('shortVal'); //needed to prime the cache - cookieStr = document.cookie; - cookies['x'] = longVal + longVal + longVal; //should be too long for all browsers - - if (document.cookie != cookieStr) { - throw "browser didn't drop long cookie when it was expected. make the " + - "cookie in this test longer"; - } + cookieStr = document.cookie; + cookies['x'] = longVal; //total size 4093-4096, so it should go through + expect(document.cookie).not.toEqual(cookieStr); + expect(cookies['x']).toEqual(longVal); + //expect(logs.warn).toEqual([]); + var overflow = 'xxxxxxxxxxxxxxxxxxxx'; + cookies['x'] = longVal + overflow; //total size 4097-4099, a warning should be logged + //expect(logs.warn).toEqual( + // [[ "Cookie 'x' possibly not set or overflowed because it was too large (4097 > 4096 " + + // "bytes)!" ]]); + expect(document.cookie).not.toContain(overflow); - expect(cookies['x']).toEqual('shortVal'); - var errors = (exceptionHandler as LoggingExceptionHandler).errors; - expect(errors.length).toEqual(2); - expect(errors[0].error). - toEqual("Cookie 'x' possibly not set or overflowed because it was too large (4113 > 4096 bytes)!"); - expect(errors[1].error). - toEqual("Cookie 'x' possibly not set or overflowed because it was too large (12259 > 4096 bytes)!"); - errors.clear(); - })); - }); + //force browser to dropped a cookie and make sure that the cache is not out of sync + cookies['x'] = 'shortVal'; + expect(cookies['x']).toEqual('shortVal'); //needed to prime the cache + cookieStr = document.cookie; + cookies['x'] = longVal + longVal + longVal; //should be too long for all browsers - xdescribe('put via cookies(cookieName, string), if no ', () { - beforeEach(() { - //fakeDocument.basePath = null; - }); + if (document.cookie != cookieStr) { + throw "browser didn't drop long cookie when it was expected. make the " + + "cookie in this test longer"; + } - it('should default path in cookie to "" (empty string)', () { - cookies['cookie'] = 'bender'; - // This only fails in Safari and IE when cookiePath returns null - // Where it now succeeds since baseHref return '' instead of null - expect(document.cookie).toEqual('cookie=bender'); + expect(cookies['x']).toEqual('shortVal'); + var errors = (exceptionHandler as LoggingExceptionHandler).errors; + expect(errors.length).toEqual(2); + expect(errors[0].error). + toEqual("Cookie 'x' possibly not set or overflowed because it was too large (4113 > 4096 bytes)!"); + expect(errors[1].error). + toEqual("Cookie 'x' possibly not set or overflowed because it was too large (12259 > 4096 bytes)!"); + errors.clear(); + })); }); - }); - describe('get via cookies[cookieName]', () { + xdescribe('put via cookies(cookieName, string), if no ', () { + beforeEach(() { + //fakeDocument.basePath = null; + }); - it('should return null for nonexistent cookie', () { - expect(cookies['nonexistent']).toBe(null); + it('should default path in cookie to "" (empty string)', () { + cookies['cookie'] = 'bender'; + // This only fails in Safari and IE when cookiePath returns null + // Where it now succeeds since baseHref return '' instead of null + expect(document.cookie).toEqual('cookie=bender'); + }); }); + describe('get via cookies[cookieName]', () { - it ('should return a value for an existing cookie', () { - document.cookie = "foo=bar=baz;path=/"; - expect(cookies['foo']).toEqual('bar=baz'); - }); + it('should return null for nonexistent cookie', () { + expect(cookies['nonexistent']).toBe(null); + }); - it('should return the the first value provided for a cookie', () { - // For a cookie that has different values that differ by path, the - // value for the most specific path appears first. cookies() - // should provide that value for the cookie. - document.cookie = 'foo="first"; foo="second"'; - expect(cookies['foo']).toEqual('"first"'); - }); - it ('should unescape cookie values that were escaped by puts', () { - document.cookie = "cookie2%3Dbar%3Bbaz=val%3Due;path=/"; - expect(cookies['cookie2=bar;baz']).toEqual('val=ue'); - }); + it ('should return a value for an existing cookie', () { + document.cookie = "foo=bar=baz;path=/"; + expect(cookies['foo']).toEqual('bar=baz'); + }); + it('should return the the first value provided for a cookie', () { + // For a cookie that has different values that differ by path, the + // value for the most specific path appears first. cookies() + // should provide that value for the cookie. + document.cookie = 'foo="first"; foo="second"'; + expect(cookies['foo']).toEqual('"first"'); + }); - it('should preserve leading & trailing spaces in names and values', () { - cookies[' cookie name '] = ' cookie value '; - expect(cookies[' cookie name ']).toEqual(' cookie value '); - expect(cookies['cookie name']).toBe(null); + it ('should unescape cookie values that were escaped by puts', () { + document.cookie = "cookie2%3Dbar%3Bbaz=val%3Due;path=/"; + expect(cookies['cookie2=bar;baz']).toEqual('val=ue'); + }); + + + it('should preserve leading & trailing spaces in names and values', () { + cookies[' cookie name '] = ' cookie value '; + expect(cookies[' cookie name ']).toEqual(' cookie value '); + expect(cookies['cookie name']).toBe(null); + }); }); - }); - describe('getAll via cookies(', () { + describe('getAll via cookies(', () { - it('should return cookies as hash', () { - document.cookie = "foo1=bar1;path=/"; - document.cookie = "foo2=bar2;path=/"; - expect(cookies.all).toEqual({'foo1':'bar1', 'foo2':'bar2'}); - }); + it('should return cookies as hash', () { + document.cookie = "foo1=bar1;path=/"; + document.cookie = "foo2=bar2;path=/"; + expect(cookies.all).toEqual({'foo1':'bar1', 'foo2':'bar2'}); + }); - it('should return empty hash if no cookies exist', () { - expect(cookies.all).toEqual({}); + it('should return empty hash if no cookies exist', () { + expect(cookies.all).toEqual({}); + }); }); - }); - it('should pick up external changes made to browser cookies', () { - cookies['oatmealCookie'] = 'drool'; - expect(cookies.all).toEqual({'oatmealCookie':'drool'}); + it('should pick up external changes made to browser cookies', () { + cookies['oatmealCookie'] = 'drool'; + expect(cookies.all).toEqual({'oatmealCookie':'drool'}); - document.cookie = 'oatmealCookie=changed;path=/'; - expect(cookies['oatmealCookie']).toEqual('changed'); - }); + document.cookie = 'oatmealCookie=changed;path=/'; + expect(cookies['oatmealCookie']).toEqual('changed'); + }); - it('should initialize cookie cache with existing cookies', () { - document.cookie = "existingCookie=existingValue;path=/"; - expect(cookies.all).toEqual({'existingCookie':'existingValue'}); + it('should initialize cookie cache with existing cookies', () { + document.cookie = "existingCookie=existingValue;path=/"; + expect(cookies.all).toEqual({'existingCookie':'existingValue'}); + }); }); - }); - - describe('cookies service', () { - var cookiesService; - beforeEach(inject((Cookies iCookies) { - cookiesService = iCookies; - document.cookie = 'oatmealCookie=fresh;path=/'; - })); - it('should read cookie', () { - expect(cookiesService["oatmealCookie"]).toEqual("fresh"); - }); + describe('cookies service', () { + var cookiesService; + beforeEach(inject((Cookies iCookies) { + cookiesService = iCookies; + document.cookie = 'oatmealCookie=fresh;path=/'; + })); - describe("set cookie", () { - it('should set new key value pair', () { - cookiesService["oven"] = "hot"; - expect(document.cookie).toContain("oven=hot"); + it('should read cookie', () { + expect(cookiesService["oatmealCookie"]).toEqual("fresh"); }); - it('should override existing value', () { - cookiesService["oatmealCookie"] = "stale"; - expect(document.cookie).toContain("oatmealCookie=stale"); + describe("set cookie", () { + it('should set new key value pair', () { + cookiesService["oven"] = "hot"; + expect(document.cookie).toContain("oven=hot"); + }); + + it('should override existing value', () { + cookiesService["oatmealCookie"] = "stale"; + expect(document.cookie).toContain("oatmealCookie=stale"); + }); }); - }); - it('should remove cookie', () { - cookiesService.remove("oatmealCookie"); - expect(document.cookie).not.toContain("oatmealCookie"); + it('should remove cookie', () { + cookiesService.remove("oatmealCookie"); + expect(document.cookie).not.toContain("oatmealCookie"); + }); }); }); -}); +} diff --git a/test/core_dom/http_spec.dart b/test/core_dom/http_spec.dart index bf26cf7c5..b44d651d7 100644 --- a/test/core_dom/http_spec.dart +++ b/test/core_dom/http_spec.dart @@ -28,148 +28,116 @@ class MockLocationWrapper implements LocationWrapper { get location => new MockLocation(url); } -main() => describe('http', () { - MockHttpBackend backend; - MockLocationWrapper locationWrapper; - - var cache; - - flush() { - microLeap(); - backend.flush(); - microLeap(); - } - - beforeEach(module((Module module) { - backend = new MockHttpBackend(); - locationWrapper = new MockLocationWrapper(); - cache = new FakeCache(); - module - ..value(HttpBackend, backend) - ..value(LocationWrapper, locationWrapper) - ..type(ExceptionHandler, implementedBy: LoggingExceptionHandler); - })); - - afterEach(inject((ExceptionHandler eh, Scope scope) { - scope.apply(); - backend.verifyNoOutstandingRequest(); - (eh as LoggingExceptionHandler).assertEmpty(); - })); - - describe('the instance', () { - Http http; - var callback; - - beforeEach(inject((Http h) { - http = h; - callback = jasmine.createSpy('callback'); - })); - +void main() { + describe('http', () { + MockHttpBackend backend; + MockLocationWrapper locationWrapper; - it('should do basic request', async(() { - backend.expect('GET', '/url').respond(''); - http(url: '/url', method: 'GET'); - })); + var cache; + flush() { + microLeap(); + backend.flush(); + microLeap(); + } - it('should pass data if specified', async(() { - backend.expect('POST', '/url', 'some-data').respond(''); - http(url: '/url', method: 'POST', data: 'some-data'); + beforeEach(module((Module module) { + backend = new MockHttpBackend(); + locationWrapper = new MockLocationWrapper(); + cache = new FakeCache(); + module + ..value(HttpBackend, backend) + ..value(LocationWrapper, locationWrapper) + ..type(ExceptionHandler, implementedBy: LoggingExceptionHandler); })); - - it('should not pass data if not specificed', async(() { - // NOTE(deboer): I don't have a good why to test this since - // a null in backend.expect's data parameter means "undefined; - // we don't care about the data field. - backend.expect('POST', '/url', 'null').respond(''); - - http(url: '/url', method: 'POST'); - expect(() { - flush(); - }).toThrow('with different data'); + afterEach(inject((ExceptionHandler eh, Scope scope) { + scope.apply(); + backend.verifyNoOutstandingRequest(); + (eh as LoggingExceptionHandler).assertEmpty(); })); + describe('the instance', () { + Http http; + var callback; - describe('params', () { - it('should do basic request with params and encode', async(() { - backend.expect('GET', '/url?a%3D=%3F%26&b=2').respond(''); - http(url: '/url', params: {'a=':'?&', 'b':2}, method: 'GET'); + beforeEach(inject((Http h) { + http = h; + callback = jasmine.createSpy('callback'); })); - it('should merge params if url contains some already', async(() { - backend.expect('GET', '/url?c=3&a=1&b=2').respond(''); - http(url: '/url?c=3', params: {'a':1, 'b':2}, method: 'GET'); + it('should do basic request', async(() { + backend.expect('GET', '/url').respond(''); + http(url: '/url', method: 'GET'); })); - it('should jsonify objects in params map', async(() { - backend.expect('GET', '/url?a=1&b=%7B%22c%22:3%7D').respond(''); - http(url: '/url', params: {'a':1, 'b':{'c':3}}, method: 'GET'); + it('should pass data if specified', async(() { + backend.expect('POST', '/url', 'some-data').respond(''); + http(url: '/url', method: 'POST', data: 'some-data'); })); - it('should expand arrays in params map', async(() { - backend.expect('GET', '/url?a=1&a=2&a=3').respond(''); - http(url: '/url', params: {'a': [1,2,3]}, method: 'GET'); + it('should not pass data if not specificed', async(() { + // NOTE(deboer): I don't have a good why to test this since + // a null in backend.expect's data parameter means "undefined; + // we don't care about the data field. + backend.expect('POST', '/url', 'null').respond(''); + + http(url: '/url', method: 'POST'); + expect(() { + flush(); + }).toThrow('with different data'); })); - it('should not encode @ in url params', async(() { - //encodeURIComponent is too agressive and doesn't follow http://www.ietf.org/rfc/rfc3986.txt - //with regards to the character set (pchar) allowed in path segments - //so we need this test to make sure that we don't over-encode the params and break stuff - //like buzz api which uses @self + describe('params', () { + it('should do basic request with params and encode', async(() { + backend.expect('GET', '/url?a%3D=%3F%26&b=2').respond(''); + http(url: '/url', params: {'a=':'?&', 'b':2}, method: 'GET'); + })); - backend.expect('GET', r'/Path?!do%26h=g%3Da+h&:bar=$baz@1').respond(''); - http(url: '/Path', params: {':bar': r'$baz@1', '!do&h': 'g=a h'}, method: 'GET'); - })); - }); + it('should merge params if url contains some already', async(() { + backend.expect('GET', '/url?c=3&a=1&b=2').respond(''); + http(url: '/url?c=3', params: {'a':1, 'b':2}, method: 'GET'); + })); - describe('callbacks', () { - it('should pass in the response object when a request is successful', async(() { - backend.expect('GET', '/url').respond(207, 'my content', {'content-encoding': 'smurf'}); - http(url: '/url', method: 'GET').then((HttpResponse response) { - expect(response.data).toEqual('my content'); - expect(response.status).toEqual(207); - expect(response.headers()).toEqual({'content-encoding': 'smurf'}); - expect(response.config.url).toEqual('/url'); - callback(); - }); + it('should jsonify objects in params map', async(() { + backend.expect('GET', '/url?a=1&b=%7B%22c%22:3%7D').respond(''); + http(url: '/url', params: {'a':1, 'b':{'c':3}}, method: 'GET'); + })); - flush(); - expect(callback).toHaveBeenCalledOnce(); - })); + it('should expand arrays in params map', async(() { + backend.expect('GET', '/url?a=1&a=2&a=3').respond(''); + http(url: '/url', params: {'a': [1,2,3]}, method: 'GET'); + })); - it('should pass in the response object when a request failed', async(() { - backend.expect('GET', '/url').respond(543, 'bad error', {'request-id': '123'}); - http(url: '/url', method: 'GET').then((_) {}, onError: (response) { - expect(response.data).toEqual('bad error'); - expect(response.status).toEqual(543); - expect(response.headers()).toEqual({'request-id': '123'}); - expect(response.config.url).toEqual('/url'); - callback(); - }); + it('should not encode @ in url params', async(() { + //encodeURIComponent is too agressive and doesn't follow http://www.ietf.org/rfc/rfc3986.txt + //with regards to the character set (pchar) allowed in path segments + //so we need this test to make sure that we don't over-encode the params and break stuff + //like buzz api which uses @self - flush(); + backend.expect('GET', r'/Path?!do%26h=g%3Da+h&:bar=$baz@1').respond(''); + http(url: '/Path', params: {':bar': r'$baz@1', '!do&h': 'g=a h'}, method: 'GET'); + })); + }); - expect(callback).toHaveBeenCalledOnce(); - })); + describe('callbacks', () { - describe('success', () { - it('should allow http specific callbacks to be registered via "success"', async(() { + it('should pass in the response object when a request is successful', async(() { backend.expect('GET', '/url').respond(207, 'my content', {'content-encoding': 'smurf'}); - http(url: '/url', method: 'GET').then((r) { - expect(r.data).toEqual('my content'); - expect(r.status).toEqual(207); - expect(r.headers()).toEqual({'content-encoding': 'smurf'}); - expect(r.config.url).toEqual('/url'); + http(url: '/url', method: 'GET').then((HttpResponse response) { + expect(response.data).toEqual('my content'); + expect(response.status).toEqual(207); + expect(response.headers()).toEqual({'content-encoding': 'smurf'}); + expect(response.config.url).toEqual('/url'); callback(); }); @@ -177,18 +145,15 @@ main() => describe('http', () { expect(callback).toHaveBeenCalledOnce(); })); - }); - describe('error', () { - it('should allow http specific callbacks to be registered via "error"', async(() { + it('should pass in the response object when a request failed', async(() { backend.expect('GET', '/url').respond(543, 'bad error', {'request-id': '123'}); - http(url: '/url', method: 'GET').then((_) {}, onError: (r) { - if (r is! HttpResponse) { throw r; } - expect(r.data).toEqual('bad error'); - expect(r.status).toEqual(543); - expect(r.headers()).toEqual({'request-id': '123'}); - expect(r.config.url).toEqual('/url'); + http(url: '/url', method: 'GET').then((_) {}, onError: (response) { + expect(response.data).toEqual('bad error'); + expect(response.status).toEqual(543); + expect(response.headers()).toEqual({'request-id': '123'}); + expect(response.config.url).toEqual('/url'); callback(); }); @@ -196,1129 +161,1165 @@ main() => describe('http', () { expect(callback).toHaveBeenCalledOnce(); })); - }); - }); - describe('response headers', () { + describe('success', () { + it('should allow http specific callbacks to be registered via "success"', async(() { + backend.expect('GET', '/url').respond(207, 'my content', {'content-encoding': 'smurf'}); + http(url: '/url', method: 'GET').then((r) { + expect(r.data).toEqual('my content'); + expect(r.status).toEqual(207); + expect(r.headers()).toEqual({'content-encoding': 'smurf'}); + expect(r.config.url).toEqual('/url'); + callback(); + }); - it('should return single header', async(() { - backend.expect('GET', '/url').respond('', {'date': 'date-val'}); - callback.andCallFake((r) { - expect(r.headers('date')).toEqual('date-val'); - }); + flush(); - http(url: '/url', method: 'GET').then(callback); + expect(callback).toHaveBeenCalledOnce(); + })); + }); - flush(); - expect(callback).toHaveBeenCalledOnce(); - })); + describe('error', () { + it('should allow http specific callbacks to be registered via "error"', async(() { + backend.expect('GET', '/url').respond(543, 'bad error', {'request-id': '123'}); + http(url: '/url', method: 'GET').then((_) {}, onError: (r) { + if (r is! HttpResponse) { throw r; } + expect(r.data).toEqual('bad error'); + expect(r.status).toEqual(543); + expect(r.headers()).toEqual({'request-id': '123'}); + expect(r.config.url).toEqual('/url'); + callback(); + }); + flush(); - it('should return null when single header does not exist', async(() { - backend.expect('GET', '/url').respond('', {'Some-Header': 'Fake'}); - callback.andCallFake((r) { - r.headers(); // we need that to get headers parsed first - expect(r.headers('nothing')).toEqual(null); + expect(callback).toHaveBeenCalledOnce(); + })); }); + }); - http(url: '/url', method: 'GET').then(callback); - flush(); - expect(callback).toHaveBeenCalledOnce(); - })); + describe('response headers', () { + it('should return single header', async(() { + backend.expect('GET', '/url').respond('', {'date': 'date-val'}); + callback.andCallFake((r) { + expect(r.headers('date')).toEqual('date-val'); + }); - it('should return all headers as object', async(() { - backend.expect('GET', '/url').respond('', { - 'content-encoding': 'gzip', - 'server': 'Apache' - }); + http(url: '/url', method: 'GET').then(callback); - callback.andCallFake((r) { - expect(r.headers()).toEqual({'content-encoding': 'gzip', 'server': 'Apache'}); - }); + flush(); - http(url: '/url', method: 'GET').then(callback); - flush(); + expect(callback).toHaveBeenCalledOnce(); + })); - expect(callback).toHaveBeenCalledOnce(); - })); + it('should return null when single header does not exist', async(() { + backend.expect('GET', '/url').respond('', {'Some-Header': 'Fake'}); + callback.andCallFake((r) { + r.headers(); // we need that to get headers parsed first + expect(r.headers('nothing')).toEqual(null); + }); - it('should return empty object for jsonp request', async(() { - callback.andCallFake((r) { - expect(r.headers()).toEqual({}); - }); + http(url: '/url', method: 'GET').then(callback); + flush(); - backend.expect('JSONP', '/some').respond(200); - http(url: '/some', method: 'JSONP').then(callback); - flush(); + expect(callback).toHaveBeenCalledOnce(); + })); - expect(callback).toHaveBeenCalledOnce(); - })); - }); + it('should return all headers as object', async(() { + backend.expect('GET', '/url').respond('', { + 'content-encoding': 'gzip', + 'server': 'Apache' + }); - describe('response headers parser', () { - parseHeaders(x) => Http.parseHeaders(new MockHttpRequest(null, null, x)); - - it('should parse basic', () { - var parsed = parseHeaders( - 'date: Thu, 04 Aug 2011 20:23:08 GMT\n' + - 'content-encoding: gzip\n' + - 'transfer-encoding: chunked\n' + - 'x-cache-info: not cacheable; response has already expired, not cacheable; response has already expired\n' + - 'connection: Keep-Alive\n' + - 'x-backend-server: pm-dekiwiki03\n' + - 'pragma: no-cache\n' + - 'server: Apache\n' + - 'x-frame-options: DENY\n' + - 'content-type: text/html; charset=utf-8\n' + - 'vary: Cookie, Accept-Encoding\n' + - 'keep-alive: timeout=5, max=1000\n' + - 'expires: Thu: , 19 Nov 1981 08:52:00 GMT\n'); - - expect(parsed['date']).toEqual('Thu, 04 Aug 2011 20:23:08 GMT'); - expect(parsed['content-encoding']).toEqual('gzip'); - expect(parsed['transfer-encoding']).toEqual('chunked'); - expect(parsed['keep-alive']).toEqual('timeout=5, max=1000'); - }); + callback.andCallFake((r) { + expect(r.headers()).toEqual({'content-encoding': 'gzip', 'server': 'Apache'}); + }); + http(url: '/url', method: 'GET').then(callback); + flush(); - it('should parse lines without space after colon', () { - expect(parseHeaders('key:value')['key']).toEqual('value'); - }); + expect(callback).toHaveBeenCalledOnce(); + })); - it('should trim the values', () { - expect(parseHeaders('key: value ')['key']).toEqual('value'); - }); + it('should return empty object for jsonp request', async(() { + callback.andCallFake((r) { + expect(r.headers()).toEqual({}); + }); + backend.expect('JSONP', '/some').respond(200); + http(url: '/some', method: 'JSONP').then(callback); + flush(); - it('should allow headers without value', () { - expect(parseHeaders('key:')['key']).toEqual(''); + expect(callback).toHaveBeenCalledOnce(); + })); }); - it('should merge headers with same key', () { - expect(parseHeaders('key: a\nkey:b\n')['key']).toEqual('a, b'); - }); + describe('response headers parser', () { + parseHeaders(x) => Http.parseHeaders(new MockHttpRequest(null, null, x)); + + it('should parse basic', () { + var parsed = parseHeaders( + 'date: Thu, 04 Aug 2011 20:23:08 GMT\n' + + 'content-encoding: gzip\n' + + 'transfer-encoding: chunked\n' + + 'x-cache-info: not cacheable; response has already expired, not cacheable; response has already expired\n' + + 'connection: Keep-Alive\n' + + 'x-backend-server: pm-dekiwiki03\n' + + 'pragma: no-cache\n' + + 'server: Apache\n' + + 'x-frame-options: DENY\n' + + 'content-type: text/html; charset=utf-8\n' + + 'vary: Cookie, Accept-Encoding\n' + + 'keep-alive: timeout=5, max=1000\n' + + 'expires: Thu: , 19 Nov 1981 08:52:00 GMT\n'); + + expect(parsed['date']).toEqual('Thu, 04 Aug 2011 20:23:08 GMT'); + expect(parsed['content-encoding']).toEqual('gzip'); + expect(parsed['transfer-encoding']).toEqual('chunked'); + expect(parsed['keep-alive']).toEqual('timeout=5, max=1000'); + }); - it('should normalize keys to lower case', () { - expect(parseHeaders('KeY: value')['key']).toEqual('value'); - }); + it('should parse lines without space after colon', () { + expect(parseHeaders('key:value')['key']).toEqual('value'); + }); - it('should parse CRLF as delimiter', () { - // IE does use CRLF - expect(parseHeaders('a: b\r\nc: d\r\n')).toEqual({'a': 'b', 'c': 'd'}); - expect(parseHeaders('a: b\r\nc: d\r\n')['a']).toEqual('b'); - }); + it('should trim the values', () { + expect(parseHeaders('key: value ')['key']).toEqual('value'); + }); - it('should parse tab after semi-colon', () { - expect(parseHeaders('a:\tbb')['a']).toEqual('bb'); - expect(parseHeaders('a: \tbb')['a']).toEqual('bb'); - }); - }); + it('should allow headers without value', () { + expect(parseHeaders('key:')['key']).toEqual(''); + }); - describe('request headers', () { + it('should merge headers with same key', () { + expect(parseHeaders('key: a\nkey:b\n')['key']).toEqual('a, b'); + }); - it('should send custom headers', async(() { - backend.expect('GET', '/url', null, (headers) { - return headers['Custom'] == 'header'; - }).respond(''); - http(url: '/url', method: 'GET', headers: { - 'Custom': 'header', + it('should normalize keys to lower case', () { + expect(parseHeaders('KeY: value')['key']).toEqual('value'); }); - flush(); - })); + it('should parse CRLF as delimiter', () { + // IE does use CRLF + expect(parseHeaders('a: b\r\nc: d\r\n')).toEqual({'a': 'b', 'c': 'd'}); + expect(parseHeaders('a: b\r\nc: d\r\n')['a']).toEqual('b'); + }); - it('should set default headers for GET request', async(() { - backend.expect('GET', '/url', null, (headers) { - return headers['Accept'] == 'application/json, text/plain, */*'; - }).respond(''); - http(url: '/url', method: 'GET', headers: {}); - flush(); - })); + it('should parse tab after semi-colon', () { + expect(parseHeaders('a:\tbb')['a']).toEqual('bb'); + expect(parseHeaders('a: \tbb')['a']).toEqual('bb'); + }); + }); - it('should set default headers for POST request', async(() { - backend.expect('POST', '/url', 'messageBody', (headers) { - return headers['Accept'] == 'application/json, text/plain, */*' && - headers['Content-Type'] == 'application/json;charset=utf-8'; - }).respond(''); + describe('request headers', () { - http(url: '/url', method: 'POST', headers: {}, data: 'messageBody'); - flush(); - })); + it('should send custom headers', async(() { + backend.expect('GET', '/url', null, (headers) { + return headers['Custom'] == 'header'; + }).respond(''); + http(url: '/url', method: 'GET', headers: { + 'Custom': 'header', + }); - it('should set default headers for PUT request', async(() { - backend.expect('PUT', '/url', 'messageBody', (headers) { - return headers['Accept'] == 'application/json, text/plain, */*' && - headers['Content-Type'] == 'application/json;charset=utf-8'; - }).respond(''); + flush(); + })); - http(url: '/url', method: 'PUT', headers: {}, data: 'messageBody'); - flush(); - })); - it('should set default headers for PATCH request', async(() { - backend.expect('PATCH', '/url', 'messageBody', (headers) { - return headers['Accept'] == 'application/json, text/plain, */*' && - headers['Content-Type'] == 'application/json;charset=utf-8'; - }).respond(''); + it('should set default headers for GET request', async(() { + backend.expect('GET', '/url', null, (headers) { + return headers['Accept'] == 'application/json, text/plain, */*'; + }).respond(''); - http(url: '/url', method: 'PATCH', headers: {}, data: 'messageBody'); - flush(); - })); + http(url: '/url', method: 'GET', headers: {}); + flush(); + })); - it('should set default headers for custom HTTP method', async(() { - backend.expect('FOO', '/url', null, (headers) { - return headers['Accept'] == 'application/json, text/plain, */*'; - }).respond(''); - http(url: '/url', method: 'FOO', headers: {}); - flush(); - })); + it('should set default headers for POST request', async(() { + backend.expect('POST', '/url', 'messageBody', (headers) { + return headers['Accept'] == 'application/json, text/plain, */*' && + headers['Content-Type'] == 'application/json;charset=utf-8'; + }).respond(''); + http(url: '/url', method: 'POST', headers: {}, data: 'messageBody'); + flush(); + })); - it('should override default headers with custom', async(() { - backend.expect('POST', '/url', 'messageBody', (headers) { - return headers['Accept'] == 'Rewritten' && - headers['Content-Type'] == 'Rewritten'; - }).respond(''); - http(url: '/url', method: 'POST', data: 'messageBody', headers: { - 'Accept': 'Rewritten', - 'Content-Type': 'Rewritten' - }); - flush(); - })); + it('should set default headers for PUT request', async(() { + backend.expect('PUT', '/url', 'messageBody', (headers) { + return headers['Accept'] == 'application/json, text/plain, */*' && + headers['Content-Type'] == 'application/json;charset=utf-8'; + }).respond(''); - it('should override default headers with custom in a case insensitive manner', async(() { - backend.expect('POST', '/url', 'messageBody', (headers) { - return headers['accept'] == 'Rewritten' && - headers['content-type'] == 'Content-Type Rewritten' && - headers['Accept'] == null && - headers['Content-Type'] == null; - }).respond(''); - - http(url: '/url', method: 'POST', data: 'messageBody', headers: { - 'accept': 'Rewritten', - 'content-type': 'Content-Type Rewritten' - }); - flush(); - })); + http(url: '/url', method: 'PUT', headers: {}, data: 'messageBody'); + flush(); + })); - it('should not set XSRF cookie for cross-domain requests', async(inject((BrowserCookies cookies) { - cookies['XSRF-TOKEN'] = 'secret'; - locationWrapper.url = 'http://host.com/base'; - backend.expect('GET', 'http://www.test.com/url', null, (headers) { - return headers['X-XSRF-TOKEN'] == null; - }).respond(''); + it('should set default headers for PATCH request', async(() { + backend.expect('PATCH', '/url', 'messageBody', (headers) { + return headers['Accept'] == 'application/json, text/plain, */*' && + headers['Content-Type'] == 'application/json;charset=utf-8'; + }).respond(''); - http(url: 'http://www.test.com/url', method: 'GET', headers: {}); - flush(); - }))); + http(url: '/url', method: 'PATCH', headers: {}, data: 'messageBody'); + flush(); + })); + it('should set default headers for custom HTTP method', async(() { + backend.expect('FOO', '/url', null, (headers) { + return headers['Accept'] == 'application/json, text/plain, */*'; + }).respond(''); - it('should not send Content-Type header if request data/body is null', async(() { - backend.expect('POST', '/url', null, (headers) { - return !headers.containsKey('Content-Type'); - }).respond(''); + http(url: '/url', method: 'FOO', headers: {}); + flush(); + })); - backend.expect('POST', '/url2', null, (headers) { - return !headers.containsKey('content-type'); - }).respond(''); - http(url: '/url', method: 'POST'); - http(url: '/url2', method: 'POST', headers: {'content-type': 'Rewritten'}); - flush(); - })); + it('should override default headers with custom', async(() { + backend.expect('POST', '/url', 'messageBody', (headers) { + return headers['Accept'] == 'Rewritten' && + headers['Content-Type'] == 'Rewritten'; + }).respond(''); + http(url: '/url', method: 'POST', data: 'messageBody', headers: { + 'Accept': 'Rewritten', + 'Content-Type': 'Rewritten' + }); + flush(); + })); - it('should set the XSRF cookie into a XSRF header', async(inject((BrowserCookies cookies) { - checkXSRF(secret, [header]) { - return (headers) { - return headers[header != null ? header : 'X-XSRF-TOKEN'] == secret; - }; - } + it('should override default headers with custom in a case insensitive manner', async(() { + backend.expect('POST', '/url', 'messageBody', (headers) { + return headers['accept'] == 'Rewritten' && + headers['content-type'] == 'Content-Type Rewritten' && + headers['Accept'] == null && + headers['Content-Type'] == null; + }).respond(''); + + http(url: '/url', method: 'POST', data: 'messageBody', headers: { + 'accept': 'Rewritten', + 'content-type': 'Content-Type Rewritten' + }); + flush(); + })); - cookies['XSRF-TOKEN'] = 'secret'; - cookies['aCookie'] = 'secret2'; - backend.expect('GET', '/url', null, checkXSRF('secret')).respond(''); - backend.expect('POST', '/url', null, checkXSRF('secret')).respond(''); - backend.expect('PUT', '/url', null, checkXSRF('secret')).respond(''); - backend.expect('DELETE', '/url', null, checkXSRF('secret')).respond(''); - backend.expect('GET', '/url', null, checkXSRF('secret', 'aHeader')).respond(''); - backend.expect('GET', '/url', null, checkXSRF('secret2')).respond(''); + it('should not set XSRF cookie for cross-domain requests', async(inject((BrowserCookies cookies) { + cookies['XSRF-TOKEN'] = 'secret'; + locationWrapper.url = 'http://host.com/base'; + backend.expect('GET', 'http://www.test.com/url', null, (headers) { + return headers['X-XSRF-TOKEN'] == null; + }).respond(''); - http(url: '/url', method: 'GET'); - http(url: '/url', method: 'POST', headers: {'S-ome': 'Header'}); - http(url: '/url', method: 'PUT', headers: {'Another': 'Header'}); - http(url: '/url', method: 'DELETE', headers: {}); - http(url: '/url', method: 'GET', xsrfHeaderName: 'aHeader'); - http(url: '/url', method: 'GET', xsrfCookieName: 'aCookie'); + http(url: 'http://www.test.com/url', method: 'GET', headers: {}); + flush(); + }))); - flush(); - }))); - it('should send execute result if header value is function', async(() { - var headerConfig = {'Accept': () { return 'Rewritten'; }}; + it('should not send Content-Type header if request data/body is null', async(() { + backend.expect('POST', '/url', null, (headers) { + return !headers.containsKey('Content-Type'); + }).respond(''); - checkHeaders(headers) { - return headers['Accept'] == 'Rewritten'; - } + backend.expect('POST', '/url2', null, (headers) { + return !headers.containsKey('content-type'); + }).respond(''); - backend.expect('GET', '/url', null, checkHeaders).respond(''); - backend.expect('POST', '/url', null, checkHeaders).respond(''); - backend.expect('PUT', '/url', null, checkHeaders).respond(''); - backend.expect('PATCH', '/url', null, checkHeaders).respond(''); - backend.expect('DELETE', '/url', null, checkHeaders).respond(''); + http(url: '/url', method: 'POST'); + http(url: '/url2', method: 'POST', headers: {'content-type': 'Rewritten'}); + flush(); + })); - http(url: '/url', method: 'GET', headers: headerConfig); - http(url: '/url', method: 'POST', headers: headerConfig); - http(url: '/url', method: 'PUT', headers: headerConfig); - http(url: '/url', method: 'PATCH', headers: headerConfig); - http(url: '/url', method: 'DELETE', headers: headerConfig); - flush(); - })); - }); + it('should set the XSRF cookie into a XSRF header', async(inject((BrowserCookies cookies) { + checkXSRF(secret, [header]) { + return (headers) { + return headers[header != null ? header : 'X-XSRF-TOKEN'] == secret; + }; + } + + cookies['XSRF-TOKEN'] = 'secret'; + cookies['aCookie'] = 'secret2'; + backend.expect('GET', '/url', null, checkXSRF('secret')).respond(''); + backend.expect('POST', '/url', null, checkXSRF('secret')).respond(''); + backend.expect('PUT', '/url', null, checkXSRF('secret')).respond(''); + backend.expect('DELETE', '/url', null, checkXSRF('secret')).respond(''); + backend.expect('GET', '/url', null, checkXSRF('secret', 'aHeader')).respond(''); + backend.expect('GET', '/url', null, checkXSRF('secret2')).respond(''); + + http(url: '/url', method: 'GET'); + http(url: '/url', method: 'POST', headers: {'S-ome': 'Header'}); + http(url: '/url', method: 'PUT', headers: {'Another': 'Header'}); + http(url: '/url', method: 'DELETE', headers: {}); + http(url: '/url', method: 'GET', xsrfHeaderName: 'aHeader'); + http(url: '/url', method: 'GET', xsrfCookieName: 'aCookie'); + flush(); + }))); - describe('short methods', () { + it('should send execute result if header value is function', async(() { + var headerConfig = {'Accept': () { return 'Rewritten'; }}; - checkHeader(name, value) { - return (headers) { - return headers[name] == value; - }; - } + checkHeaders(headers) { + return headers['Accept'] == 'Rewritten'; + } - it('should have get()', async(() { - backend.expect('GET', '/url').respond(''); - http.get('/url'); - })); + backend.expect('GET', '/url', null, checkHeaders).respond(''); + backend.expect('POST', '/url', null, checkHeaders).respond(''); + backend.expect('PUT', '/url', null, checkHeaders).respond(''); + backend.expect('PATCH', '/url', null, checkHeaders).respond(''); + backend.expect('DELETE', '/url', null, checkHeaders).respond(''); + http(url: '/url', method: 'GET', headers: headerConfig); + http(url: '/url', method: 'POST', headers: headerConfig); + http(url: '/url', method: 'PUT', headers: headerConfig); + http(url: '/url', method: 'PATCH', headers: headerConfig); + http(url: '/url', method: 'DELETE', headers: headerConfig); - it('get() should allow config param', async(() { - backend.expect('GET', '/url', null, checkHeader('Custom', 'Header')).respond(''); - http.get('/url', headers: {'Custom': 'Header'}); - })); + flush(); + })); + }); - it('should have delete()', async(() { - backend.expect('DELETE', '/url').respond(''); - http.delete('/url'); - })); + describe('short methods', () { + checkHeader(name, value) { + return (headers) { + return headers[name] == value; + }; + } - it('delete() should allow config param', async(() { - backend.expect('DELETE', '/url', null, checkHeader('Custom', 'Header')).respond(''); - http.delete('/url', headers: {'Custom': 'Header'}); - })); + it('should have get()', async(() { + backend.expect('GET', '/url').respond(''); + http.get('/url'); + })); - it('should have head()', async(() { - backend.expect('HEAD', '/url').respond(''); - http.head('/url'); - })); + it('get() should allow config param', async(() { + backend.expect('GET', '/url', null, checkHeader('Custom', 'Header')).respond(''); + http.get('/url', headers: {'Custom': 'Header'}); + })); - it('head() should allow config param', async(() { - backend.expect('HEAD', '/url', null, checkHeader('Custom', 'Header')).respond(''); - http.head('/url', headers: {'Custom': 'Header'}); - })); + it('should have delete()', async(() { + backend.expect('DELETE', '/url').respond(''); + http.delete('/url'); + })); - it('should have post()', async(() { - backend.expect('POST', '/url', 'some-data').respond(''); - http.post('/url', 'some-data'); - })); + it('delete() should allow config param', async(() { + backend.expect('DELETE', '/url', null, checkHeader('Custom', 'Header')).respond(''); + http.delete('/url', headers: {'Custom': 'Header'}); + })); - it('post() should allow config param', async(() { - backend.expect('POST', '/url', 'some-data', checkHeader('Custom', 'Header')).respond(''); - http.post('/url', 'some-data', headers: {'Custom': 'Header'}); - })); + it('should have head()', async(() { + backend.expect('HEAD', '/url').respond(''); + http.head('/url'); + })); - it('should have put()', async(() { - backend.expect('PUT', '/url', 'some-data').respond(''); - http.put('/url', 'some-data'); - })); + it('head() should allow config param', async(() { + backend.expect('HEAD', '/url', null, checkHeader('Custom', 'Header')).respond(''); + http.head('/url', headers: {'Custom': 'Header'}); + })); - it('put() should allow config param', async(() { - backend.expect('PUT', '/url', 'some-data', checkHeader('Custom', 'Header')).respond(''); - http.put('/url', 'some-data', headers: {'Custom': 'Header'}); - })); + it('should have post()', async(() { + backend.expect('POST', '/url', 'some-data').respond(''); + http.post('/url', 'some-data'); + })); - it('should have jsonp()', async(() { - backend.expect('JSONP', '/url').respond(''); - http.jsonp('/url'); - })); + it('post() should allow config param', async(() { + backend.expect('POST', '/url', 'some-data', checkHeader('Custom', 'Header')).respond(''); + http.post('/url', 'some-data', headers: {'Custom': 'Header'}); + })); - it('jsonp() should allow config param', async(() { - backend.expect('JSONP', '/url', null, checkHeader('Custom', 'Header')).respond(''); - http.jsonp('/url', headers: {'Custom': 'Header'}); - })); - }); + it('should have put()', async(() { + backend.expect('PUT', '/url', 'some-data').respond(''); + http.put('/url', 'some-data'); + })); - describe('cache', () { + it('put() should allow config param', async(() { + backend.expect('PUT', '/url', 'some-data', checkHeader('Custom', 'Header')).respond(''); + http.put('/url', 'some-data', headers: {'Custom': 'Header'}); + })); - Cache cache; - beforeEach((() { - cache = new UnboundedCache(); - })); + it('should have jsonp()', async(() { + backend.expect('JSONP', '/url').respond(''); + http.jsonp('/url'); + })); - doFirstCacheRequest([String method, int respStatus, Map headers]) { - backend.expect(method != null ? method :'GET', '/url') - .respond(respStatus != null ? respStatus : 200, 'content', headers); - http(method: method != null ? method : 'GET', url: '/url', cache: cache) - .then((_){}, onError: (_){}); - flush(); - } + it('jsonp() should allow config param', async(() { + backend.expect('JSONP', '/url', null, checkHeader('Custom', 'Header')).respond(''); + http.jsonp('/url', headers: {'Custom': 'Header'}); + })); + }); - it('should cache GET request when cache is provided', async(() { - doFirstCacheRequest(); + describe('cache', () { - http(method: 'get', url: '/url', cache: cache).then(callback); + Cache cache; - microLeap(); + beforeEach((() { + cache = new UnboundedCache(); + })); - expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0].data).toEqual('content'); - })); + doFirstCacheRequest([String method, int respStatus, Map headers]) { + backend.expect(method != null ? method :'GET', '/url') + .respond(respStatus != null ? respStatus : 200, 'content', headers); + http(method: method != null ? method : 'GET', url: '/url', cache: cache) + .then((_){}, onError: (_){}); + flush(); + } - it('should not cache when cache is not provided', async(() { - doFirstCacheRequest(); - backend.expect('GET', '/url').respond(); - http(method: 'GET', url: '/url'); - microLeap(); - })); + it('should cache GET request when cache is provided', async(() { + doFirstCacheRequest(); + http(method: 'get', url: '/url', cache: cache).then(callback); - it('should perform request when cache cleared', async(() { - doFirstCacheRequest(); + microLeap(); - cache.removeAll(); - backend.expect('GET', '/url').respond(); - http(method: 'GET', url: '/url', cache: cache); - microLeap(); - })); + expect(callback).toHaveBeenCalledOnce(); + expect(callback.mostRecentCall.args[0].data).toEqual('content'); + })); - it('should not cache POST request', async(() { - doFirstCacheRequest('POST'); + it('should not cache when cache is not provided', async(() { + doFirstCacheRequest(); - backend.expect('POST', '/url').respond('content2'); - http(method: 'POST', url: '/url', cache: cache).then(callback); - flush(); + backend.expect('GET', '/url').respond(); + http(method: 'GET', url: '/url'); + microLeap(); + })); - expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0].data).toEqual('content2'); - })); + it('should perform request when cache cleared', async(() { + doFirstCacheRequest(); - it('should not cache PUT request', async(() { - doFirstCacheRequest('PUT'); + cache.removeAll(); + backend.expect('GET', '/url').respond(); + http(method: 'GET', url: '/url', cache: cache); + microLeap(); + })); - backend.expect('PUT', '/url').respond('content2'); - http(method: 'PUT', url: '/url', cache: cache).then(callback); - flush(); - expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0].data).toEqual('content2'); - })); + it('should not cache POST request', async(() { + doFirstCacheRequest('POST'); + backend.expect('POST', '/url').respond('content2'); + http(method: 'POST', url: '/url', cache: cache).then(callback); + flush(); - it('should not cache DELETE request', async(() { - doFirstCacheRequest('DELETE'); + expect(callback).toHaveBeenCalledOnce(); + expect(callback.mostRecentCall.args[0].data).toEqual('content2'); + })); - backend.expect('DELETE', '/url').respond(206); - http(method: 'DELETE', url: '/url', cache: cache).then(callback); - flush(); - expect(callback).toHaveBeenCalledOnce(); - })); + it('should not cache PUT request', async(() { + doFirstCacheRequest('PUT'); + backend.expect('PUT', '/url').respond('content2'); + http(method: 'PUT', url: '/url', cache: cache).then(callback); + flush(); - it('should not cache non 2xx responses', async(() { - doFirstCacheRequest('GET', 404); + expect(callback).toHaveBeenCalledOnce(); + expect(callback.mostRecentCall.args[0].data).toEqual('content2'); + })); - backend.expect('GET', '/url').respond('content2'); - http(method: 'GET', url: '/url', cache: cache).then(callback); - flush(); - expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0].data).toEqual('content2'); - })); + it('should not cache DELETE request', async(() { + doFirstCacheRequest('DELETE'); + backend.expect('DELETE', '/url').respond(206); + http(method: 'DELETE', url: '/url', cache: cache).then(callback); + flush(); - it('should cache the headers as well', async(() { - doFirstCacheRequest('GET', 200, {'content-encoding': 'gzip', 'server': 'Apache'}); - callback.andCallFake((r) { - expect(r.headers()).toEqual({'content-encoding': 'gzip', 'server': 'Apache'}); - expect(r.headers('server')).toEqual('Apache'); - }); + expect(callback).toHaveBeenCalledOnce(); + })); - http(method: 'GET', url: '/url', cache: cache).then(callback); - microLeap(); - expect(callback).toHaveBeenCalledOnce(); - })); + it('should not cache non 2xx responses', async(() { + doFirstCacheRequest('GET', 404); + backend.expect('GET', '/url').respond('content2'); + http(method: 'GET', url: '/url', cache: cache).then(callback); + flush(); - it('should not share the cached headers object instance', async(() { - doFirstCacheRequest('GET', 200, {'content-encoding': 'gzip', 'server': 'Apache'}); - callback.andCallFake((r) { - expect(r.headers()).toEqual(cache.get('/url').headers()); - expect(r.headers()).not.toBe(cache.get('/url').headers()); - }); + expect(callback).toHaveBeenCalledOnce(); + expect(callback.mostRecentCall.args[0].data).toEqual('content2'); + })); - http(method: 'GET', url: '/url', cache: cache).then(callback); - microLeap(); - expect(callback).toHaveBeenCalledOnce(); - })); + it('should cache the headers as well', async(() { + doFirstCacheRequest('GET', 200, {'content-encoding': 'gzip', 'server': 'Apache'}); + callback.andCallFake((r) { + expect(r.headers()).toEqual({'content-encoding': 'gzip', 'server': 'Apache'}); + expect(r.headers('server')).toEqual('Apache'); + }); + http(method: 'GET', url: '/url', cache: cache).then(callback); + microLeap(); - it('should cache status code as well', async(() { - doFirstCacheRequest('GET', 201); - callback.andCallFake((r) { - expect(r.status).toEqual(201); - }); + expect(callback).toHaveBeenCalledOnce(); + })); - http(method: 'get', url: '/url', cache: cache).then(callback); - microLeap(); - expect(callback).toHaveBeenCalledOnce(); - })); + it('should not share the cached headers object instance', async(() { + doFirstCacheRequest('GET', 200, {'content-encoding': 'gzip', 'server': 'Apache'}); + callback.andCallFake((r) { + expect(r.headers()).toEqual(cache.get('/url').headers()); + expect(r.headers()).not.toBe(cache.get('/url').headers()); + }); + http(method: 'GET', url: '/url', cache: cache).then(callback); + microLeap(); - it('should use cache even if second request was made before the first returned', async(() { - backend.expect('GET', '/url').respond(201, 'fake-response'); + expect(callback).toHaveBeenCalledOnce(); + })); - callback.andCallFake((r) { - expect(r.data).toEqual('fake-response'); - expect(r.status).toEqual(201); - }); - http(method: 'GET', url: '/url', cache: cache).then(callback); - http(method: 'GET', url: '/url', cache: cache).then(callback); + it('should cache status code as well', async(() { + doFirstCacheRequest('GET', 201); + callback.andCallFake((r) { + expect(r.status).toEqual(201); + }); - flush(); + http(method: 'get', url: '/url', cache: cache).then(callback); + microLeap(); - expect(callback).toHaveBeenCalled(); - expect(callback.callCount).toEqual(2); - })); + expect(callback).toHaveBeenCalledOnce(); + })); - describe('http.defaults.cache', () { + it('should use cache even if second request was made before the first returned', async(() { + backend.expect('GET', '/url').respond(201, 'fake-response'); - it('should be null by default', () { - expect(http.defaults.cache).toBeNull(); - }); + callback.andCallFake((r) { + expect(r.data).toEqual('fake-response'); + expect(r.status).toEqual(201); + }); - it('should cache requests when no cache given in request config', async(() { - http.defaults.cache = cache; + http(method: 'GET', url: '/url', cache: cache).then(callback); + http(method: 'GET', url: '/url', cache: cache).then(callback); - // First request fills the cache from server response. - backend.expect('GET', '/url').respond(200, 'content'); - http(method: 'GET', url: '/url'); // Notice no cache given in config. flush(); - // Second should be served from cache, without sending request to server. - http(method: 'get', url: '/url').then(callback); - microLeap(); + expect(callback).toHaveBeenCalled(); + expect(callback.callCount).toEqual(2); + })); - expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0].data).toEqual('content'); - // Invalidate cache entry. - http.defaults.cache.remove("/url"); + describe('http.defaults.cache', () { - // After cache entry removed, a request should be sent to server. - backend.expect('GET', '/url').respond(200, 'content'); - http(method: 'GET', url: '/url'); - flush(); - })); + it('should be null by default', () { + expect(http.defaults.cache).toBeNull(); + }); - it('should have less priority than explicitly given cache', async(() { - var localCache = new UnboundedCache(); - http.defaults.cache = cache; + it('should cache requests when no cache given in request config', async(() { + http.defaults.cache = cache; - // Fill local cache. - backend.expect('GET', '/url').respond(200, 'content-local-cache'); - http(method: 'GET', url: '/url', cache: localCache); - flush(); + // First request fills the cache from server response. + backend.expect('GET', '/url').respond(200, 'content'); + http(method: 'GET', url: '/url'); // Notice no cache given in config. + flush(); - // Fill default cache. - backend.expect('GET', '/url').respond(200, 'content-default-cache'); - http(method: 'GET', url: '/url'); - flush(); + // Second should be served from cache, without sending request to server. + http(method: 'get', url: '/url').then(callback); + microLeap(); - // Serve request from default cache when no local given. - http(method: 'get', url: '/url').then(callback); - microLeap(); + expect(callback).toHaveBeenCalledOnce(); + expect(callback.mostRecentCall.args[0].data).toEqual('content'); - expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0].data).toEqual('content-default-cache'); - callback.reset(); + // Invalidate cache entry. + http.defaults.cache.remove("/url"); - // Serve request from local cache when it is given (but default filled too). - http(method: 'get', url: '/url', cache: localCache).then(callback); - microLeap(); + // After cache entry removed, a request should be sent to server. + backend.expect('GET', '/url').respond(200, 'content'); + http(method: 'GET', url: '/url'); + flush(); + })); - expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0].data).toEqual('content-local-cache'); - })); + it('should have less priority than explicitly given cache', async(() { + var localCache = new UnboundedCache(); + http.defaults.cache = cache; - it('should be skipped if {cache: false} is passed in request config', async(() { - http.defaults.cache = cache; + // Fill local cache. + backend.expect('GET', '/url').respond(200, 'content-local-cache'); + http(method: 'GET', url: '/url', cache: localCache); + flush(); - backend.expect('GET', '/url').respond(200, 'content'); - http(method: 'GET', url: '/url'); - flush(); + // Fill default cache. + backend.expect('GET', '/url').respond(200, 'content-default-cache'); + http(method: 'GET', url: '/url'); + flush(); - backend.expect('GET', '/url').respond(); - http(method: 'GET', url: '/url', cache: false); - flush(); - })); + // Serve request from default cache when no local given. + http(method: 'get', url: '/url').then(callback); + microLeap(); + + expect(callback).toHaveBeenCalledOnce(); + expect(callback.mostRecentCall.args[0].data).toEqual('content-default-cache'); + callback.reset(); + + // Serve request from local cache when it is given (but default filled too). + http(method: 'get', url: '/url', cache: localCache).then(callback); + microLeap(); + + expect(callback).toHaveBeenCalledOnce(); + expect(callback.mostRecentCall.args[0].data).toEqual('content-local-cache'); + })); + + it('should be skipped if {cache: false} is passed in request config', async(() { + http.defaults.cache = cache; + + backend.expect('GET', '/url').respond(200, 'content'); + http(method: 'GET', url: '/url'); + flush(); + + backend.expect('GET', '/url').respond(); + http(method: 'GET', url: '/url', cache: false); + flush(); + })); + }); }); - }); - // NOTE: We are punting on timeouts for now until we understand - // Dart futures fully. - xdescribe('timeout', () { + // NOTE: We are punting on timeouts for now until we understand + // Dart futures fully. + xdescribe('timeout', () { - it('should abort requests when timeout promise resolves', inject(($q) { - var canceler = $q.defer(); + it('should abort requests when timeout promise resolves', inject(($q) { + var canceler = $q.defer(); - backend.expect('GET', '/some').respond(200); + backend.expect('GET', '/some').respond(200); - http(method: 'GET', url: '/some', timeout: canceler.promise).error( - (data, status, headers, config) { - expect(data).toBeNull(); - expect(status).toEqual(0); - expect(headers()).toEqual({}); - expect(config.url).toEqual('/some'); - callback(); - }); + http(method: 'GET', url: '/some', timeout: canceler.promise).error( + (data, status, headers, config) { + expect(data).toBeNull(); + expect(status).toEqual(0); + expect(headers()).toEqual({}); + expect(config.url).toEqual('/some'); + callback(); + }); - //$rootScope.apply(() { + //$rootScope.apply(() { canceler.resolve(); - //}); + //}); - expect(callback).toHaveBeenCalled(); - backend.verifyNoOutstandingExpectation(); - backend.verifyNoOutstandingRequest(); - })); - }); + expect(callback).toHaveBeenCalled(); + backend.verifyNoOutstandingExpectation(); + backend.verifyNoOutstandingRequest(); + })); + }); - describe('pendingRequests', () { + describe('pendingRequests', () { - it('should be an array of pending requests', async(() { - backend.when('GET').respond(200); - expect(http.pendingRequests.length).toEqual(0); + it('should be an array of pending requests', async(() { + backend.when('GET').respond(200); + expect(http.pendingRequests.length).toEqual(0); - http(method: 'get', url: '/some'); - microLeap(); - expect(http.pendingRequests.length).toEqual(1); + http(method: 'get', url: '/some'); + microLeap(); + expect(http.pendingRequests.length).toEqual(1); - flush(); - expect(http.pendingRequests.length).toEqual(0); - })); + flush(); + expect(http.pendingRequests.length).toEqual(0); + })); - // TODO(deboer): I think this test is incorrect. - // pending requests should refer to the number of requests - // on-the-wire, not the number of times a URL was requested. - xit('should update pending requests even when served from cache', async(() { - var cache = new UnboundedCache(); - backend.when('GET').respond(200); + // TODO(deboer): I think this test is incorrect. + // pending requests should refer to the number of requests + // on-the-wire, not the number of times a URL was requested. + xit('should update pending requests even when served from cache', async(() { + var cache = new UnboundedCache(); + backend.when('GET').respond(200); - http(method: 'get', url: '/cached', cache: cache); - http(method: 'get', url: '/cached', cache: cache); - expect(http.pendingRequests.length).toEqual(2); + http(method: 'get', url: '/cached', cache: cache); + http(method: 'get', url: '/cached', cache: cache); + expect(http.pendingRequests.length).toEqual(2); - flush(); + flush(); - expect(http.pendingRequests.length).toEqual(0); + expect(http.pendingRequests.length).toEqual(0); - http(method: 'get', url: '/cached', cache: true); - jasmine.spyOn(http.pendingRequests, 'add').andCallThrough(); - //expect(http.pendingRequests.add).toHaveBeenCalledOnce(); + http(method: 'get', url: '/cached', cache: true); + jasmine.spyOn(http.pendingRequests, 'add').andCallThrough(); + //expect(http.pendingRequests.add).toHaveBeenCalledOnce(); - expect(http.pendingRequests.length).toEqual(0); - })); + expect(http.pendingRequests.length).toEqual(0); + })); - it('should remove the request before firing callbacks', async(() { - backend.when('GET').respond(200); - http(method: 'get', url: '/url').then((_) { - expect(http.pendingRequests.length).toEqual(0); - }); - microLeap(); + it('should remove the request before firing callbacks', async(() { + backend.when('GET').respond(200); + http(method: 'get', url: '/url').then((_) { + expect(http.pendingRequests.length).toEqual(0); + }); + microLeap(); - expect(http.pendingRequests.length).toEqual(1); - flush(); - })); - }); + expect(http.pendingRequests.length).toEqual(1); + flush(); + })); + }); - describe('defaults', () { + describe('defaults', () { - it('should expose the defaults object at runtime', async(() { - expect(http.defaults).toBeDefined(); + it('should expose the defaults object at runtime', async(() { + expect(http.defaults).toBeDefined(); - http.defaults.headers['common']['foo'] = 'bar'; - backend.expect('GET', '/url', null, (headers) { - return headers['foo'] == 'bar'; - }).respond(''); + http.defaults.headers['common']['foo'] = 'bar'; + backend.expect('GET', '/url', null, (headers) { + return headers['foo'] == 'bar'; + }).respond(''); - http.get('/url'); - flush(); - })); + http.get('/url'); + flush(); + })); + }); }); - }); - describe('url rewriting', () { - beforeEach(module((Module module) { - module - ..type(UrlRewriter, implementedBy: SubstringRewriter); - })); + describe('url rewriting', () { + beforeEach(module((Module module) { + module.type(UrlRewriter, implementedBy: SubstringRewriter); + })); - it('should rewrite URLs before calling the backend', async(inject((Http http, NgZone zone) { - backend.when('GET', 'a').respond(200, VALUE); + it('should rewrite URLs before calling the backend', async(inject((Http http, NgZone zone) { + backend.when('GET', 'a').respond(200, VALUE); - var called = 0; - zone.run(() { - http.getString('a[not sent to backed]').then((v) { - expect(v).toEqual(VALUE); - called += 1; + var called = 0; + zone.run(() { + http.getString('a[not sent to backed]').then((v) { + expect(v).toEqual(VALUE); + called += 1; + }); }); - }); - expect(called).toEqual(0); + expect(called).toEqual(0); - flush(); + flush(); - expect(called).toEqual(1); - }))); + expect(called).toEqual(1); + }))); - it('should support pending requests for different raw URLs', async(inject((Http http, NgZone zone) { - backend.when('GET', 'a').respond(200, VALUE); + it('should support pending requests for different raw URLs', async(inject((Http http, NgZone zone) { + backend.when('GET', 'a').respond(200, VALUE); - var called = 0; - zone.run(() { - http.getString('a[some string]', cache: cache).then((v) { - expect(v).toEqual(VALUE); - called += 1; - }); - http.getString('a[different string]', cache: cache).then((v) { - expect(v).toEqual(VALUE); - called += 10; + var called = 0; + zone.run(() { + http.getString('a[some string]', cache: cache).then((v) { + expect(v).toEqual(VALUE); + called += 1; + }); + http.getString('a[different string]', cache: cache).then((v) { + expect(v).toEqual(VALUE); + called += 10; + }); }); - }); - expect(called).toEqual(0); - flush(); + expect(called).toEqual(0); + flush(); - expect(called).toEqual(11); - }))); + expect(called).toEqual(11); + }))); - it('should support caching', async(inject((Http http, NgZone zone) { - var called = 0; - zone.run(() { - http.getString('fromCache', cache: cache).then((v) { - expect(v).toEqual(CACHED_VALUE); - called += 1; + it('should support caching', async(inject((Http http, NgZone zone) { + var called = 0; + zone.run(() { + http.getString('fromCache', cache: cache).then((v) { + expect(v).toEqual(CACHED_VALUE); + called += 1; + }); }); - }); - expect(called).toEqual(1); - }))); - }); + expect(called).toEqual(1); + }))); + }); - describe('caching', () { - it('should not cache if no cache is present', async(inject((Http http, NgZone zone) { - backend.when('GET', 'a').respond(200, VALUE, null); + describe('caching', () { + it('should not cache if no cache is present', async(inject((Http http, NgZone zone) { + backend.when('GET', 'a').respond(200, VALUE, null); - var called = 0; - zone.run(() { - http.getString('a').then((v) { - expect(v).toEqual(VALUE); - called += 1; - }); - http.getString('a').then((v) { - expect(v).toEqual(VALUE); - called += 10; + var called = 0; + zone.run(() { + http.getString('a').then((v) { + expect(v).toEqual(VALUE); + called += 1; + }); + http.getString('a').then((v) { + expect(v).toEqual(VALUE); + called += 10; + }); }); - }); - expect(called).toEqual(0); + expect(called).toEqual(0); - flush(); + flush(); - expect(called).toEqual(11); - }))); + expect(called).toEqual(11); + }))); - it('should return a pending request', async(inject((Http http, NgZone zone) { - backend.when('GET', 'a').respond(200, VALUE); + it('should return a pending request', async(inject((Http http, NgZone zone) { + backend.when('GET', 'a').respond(200, VALUE); - var called = 0; - zone.run(() { - http.getString('a', cache: cache).then((v) { - expect(v).toEqual(VALUE); - called += 1; - }); - http.getString('a', cache: cache).then((v) { - expect(v).toEqual(VALUE); - called += 10; + var called = 0; + zone.run(() { + http.getString('a', cache: cache).then((v) { + expect(v).toEqual(VALUE); + called += 1; + }); + http.getString('a', cache: cache).then((v) { + expect(v).toEqual(VALUE); + called += 10; + }); }); - }); - expect(called).toEqual(0); - flush(); + expect(called).toEqual(0); + flush(); - expect(called).toEqual(11); - }))); + expect(called).toEqual(11); + }))); - it('should not return a pending request after the request is complete', async(inject((Http http, NgZone zone) { - backend.when('GET', 'a').respond(200, VALUE, null); + it('should not return a pending request after the request is complete', async(inject((Http http, NgZone zone) { + backend.when('GET', 'a').respond(200, VALUE, null); - var called = 0; - zone.run(() { - http.getString('a', cache: cache).then((v) { - expect(v).toEqual(VALUE); - called += 1; + var called = 0; + zone.run(() { + http.getString('a', cache: cache).then((v) { + expect(v).toEqual(VALUE); + called += 1; + }); }); - }); - expect(called).toEqual(0); - flush(); + expect(called).toEqual(0); + flush(); - zone.run(() { - http.getString('a', cache: cache).then((v) { - expect(v).toEqual(VALUE); - called += 10; + zone.run(() { + http.getString('a', cache: cache).then((v) { + expect(v).toEqual(VALUE); + called += 10; + }); }); - }); - expect(called).toEqual(1); - flush(); + expect(called).toEqual(1); + flush(); - expect(called).toEqual(11); - }))); + expect(called).toEqual(11); + }))); - it('should return a cached value if present', async(inject((Http http, NgZone zone) { - var called = 0; - // The URL string 'f' is primed in the FakeCache - zone.run(() { - http.getString('f', cache: cache).then((v) { - expect(v).toEqual(CACHED_VALUE); - called += 1; + it('should return a cached value if present', async(inject((Http http, NgZone zone) { + var called = 0; + // The URL string 'f' is primed in the FakeCache + zone.run(() { + http.getString('f', cache: cache).then((v) { + expect(v).toEqual(CACHED_VALUE); + called += 1; + }); + expect(called).toEqual(0); }); - expect(called).toEqual(0); - }); - expect(called).toEqual(1); - }))); - }); + expect(called).toEqual(1); + }))); + }); - describe('error handling', () { - it('should reject 404 status codes', async(inject((Http http, NgZone zone) { - backend.when('GET', '404.html').respond(404, VALUE); + describe('error handling', () { + it('should reject 404 status codes', async(inject((Http http, NgZone zone) { + backend.when('GET', '404.html').respond(404, VALUE); - var response = null; - zone.run(() { - http.getString('404.html').then( - (v) => response = 'FAILED', - onError:(v) { assert(v != null); return response = v; }); - }); + var response = null; + zone.run(() { + http.getString('404.html').then( + (v) => response = 'FAILED', + onError:(v) { assert(v != null); return response = v; }); + }); - expect(response).toEqual(null); - flush(); - expect(response.status).toEqual(404); - expect(response.toString()).toEqual('HTTP 404: val'); - }))); - }); + expect(response).toEqual(null); + flush(); + expect(response.status).toEqual(404); + expect(response.toString()).toEqual('HTTP 404: val'); + }))); + }); - describe('interceptors', () { - it('should chain request, requestReject, response and responseReject interceptors', async(() { - inject((HttpInterceptors interceptors) { - var savedConfig, savedResponse; - interceptors.add(new HttpInterceptor( - request: (config) { - config.url += '/1'; - savedConfig = config; - return new Future.error('/2'); - })); - interceptors.add(new HttpInterceptor( - requestError: (error) { - savedConfig.url += error; - return new Future.value(savedConfig); - })); - interceptors.add(new HttpInterceptor( - responseError: (rejection) => - new HttpResponse.copy(savedResponse, - data: savedResponse.data + rejection) - )); - interceptors.add(new HttpInterceptor( - response: (response) { - savedResponse = new HttpResponse.copy( - response, data: response.data + ':1'); - return new Future.error(':2'); - })); - }); - inject((Http http) { - var response; - backend.expect('GET', '/url/1/2').respond('response'); - http(method: 'GET', url: '/url').then((r) { - response = r; + describe('interceptors', () { + it('should chain request, requestReject, response and responseReject interceptors', async(() { + inject((HttpInterceptors interceptors) { + var savedConfig, savedResponse; + interceptors.add(new HttpInterceptor( + request: (config) { + config.url += '/1'; + savedConfig = config; + return new Future.error('/2'); + })); + interceptors.add(new HttpInterceptor( + requestError: (error) { + savedConfig.url += error; + return new Future.value(savedConfig); + })); + interceptors.add(new HttpInterceptor( + responseError: (rejection) => + new HttpResponse.copy(savedResponse, + data: savedResponse.data + rejection) + )); + interceptors.add(new HttpInterceptor( + response: (response) { + savedResponse = new HttpResponse.copy( + response, data: response.data + ':1'); + return new Future.error(':2'); + })); }); - flush(); - expect(response.data).toEqual('response:1:2'); - }); - })); + inject((Http http) { + var response; + backend.expect('GET', '/url/1/2').respond('response'); + http(method: 'GET', url: '/url').then((r) { + response = r; + }); + flush(); + expect(response.data).toEqual('response:1:2'); + }); + })); - it('should verify order of execution', async( + it('should verify order of execution', async( inject((HttpInterceptors interceptors, Http http) { - interceptors.add(new HttpInterceptor( - request: (config) { - config.url += '/outer'; - return config; - }, - response: (response) { - return new HttpResponse.copy( - response, data: '{' + response.data + '} outer'); - })); - interceptors.add(new HttpInterceptor( - request: (config) { - config.url += '/inner'; - return config; - }, - response: (response) { - return new HttpResponse.copy( - response, data: '{' + response.data + '} inner'); - })); + interceptors.add(new HttpInterceptor( + request: (config) { + config.url += '/outer'; + return config; + }, + response: (response) { + return new HttpResponse.copy( + response, data: '{' + response.data + '} outer'); + })); + interceptors.add(new HttpInterceptor( + request: (config) { + config.url += '/inner'; + return config; + }, + response: (response) { + return new HttpResponse.copy( + response, data: '{' + response.data + '} inner'); + })); + + var response; + backend.expect('GET', '/url/outer/inner').respond('response'); + http(method: 'GET', url: '/url').then((r) { + response = r; + }); + flush(); + expect(response.data).toEqual('{{response} inner} outer'); + }))); - var response; - backend.expect('GET', '/url/outer/inner').respond('response'); - http(method: 'GET', url: '/url').then((r) { - response = r; - }); - flush(); - expect(response.data).toEqual('{{response} inner} outer'); - }))); + describe('transformData', () { + Http http; + var callback; - describe('transformData', () { - Http http; - var callback; + beforeEach(inject((Http h) { + http = h; + callback = jasmine.createSpy('callback'); + })); - beforeEach(inject((Http h) { - http = h; - callback = jasmine.createSpy('callback'); - })); + describe('request', () { - describe('request', () { + describe('default', () { - describe('default', () { + it('should transform object into json', async(() { + backend.expect('POST', '/url', '{"one":"two"}').respond(''); + http(method: 'POST', url: '/url', data: {'one': 'two'}); + })); - it('should transform object into json', async(() { - backend.expect('POST', '/url', '{"one":"two"}').respond(''); - http(method: 'POST', url: '/url', data: {'one': 'two'}); - })); + it('should ignore strings', async(() { + backend.expect('POST', '/url', 'string-data').respond(''); + http(method: 'POST', url: '/url', data: 'string-data'); + })); + + + it('should ignore File objects', async(() { + var file = new FakeFile(); + expect(file is File).toBeTruthy(); + + backend.expect('POST', '/some', file).respond(''); + http(method: 'POST', url: '/some', data: file); + })); + }); - it('should ignore strings', async(() { - backend.expect('POST', '/url', 'string-data').respond(''); - http(method: 'POST', url: '/url', data: 'string-data'); + + it('should have access to request headers', async(() { + backend.expect('POST', '/url', 'header1').respond(200); + http.post('/url', 'req', + headers: {'h1': 'header1'}, + interceptors: new HttpInterceptor(request: (config) { + config.data = config.header('h1'); + return config; + }) + ).then(callback); + flush(); + + expect(callback).toHaveBeenCalledOnce(); })); - it('should ignore File objects', async(() { - var file = new FakeFile(); - expect(file is File).toBeTruthy(); + it('should pipeline more functions', async(() { + backend.expect('POST', '/url', 'REQ-FIRST:V1').respond(200); + http.post('/url', 'req', + headers: {'h1': 'v1'}, + interceptors: new HttpInterceptors.of([ + new HttpInterceptor(request: (config) { + config.data = config.data + '-first' + ':' + config.header('h1'); + return config; + }), + new HttpInterceptor(request: (config) { + config.data = config.data.toUpperCase(); + return config; + }) + ]) + ).then(callback); + flush(); - backend.expect('POST', '/some', file).respond(''); - http(method: 'POST', url: '/some', data: file); + expect(callback).toHaveBeenCalledOnce(); })); }); - it('should have access to request headers', async(() { - backend.expect('POST', '/url', 'header1').respond(200); - http.post('/url', 'req', - headers: {'h1': 'header1'}, - interceptors: new HttpInterceptor(request: (config) { - config.data = config.header('h1'); - return config; - }) - ).then(callback); - flush(); + describe('response', () { - expect(callback).toHaveBeenCalledOnce(); - })); + describe('default', () { + it('should deserialize json objects', async(() { + backend.expect('GET', '/url').respond('{"foo":"bar","baz":23}'); + http(method: 'GET', url: '/url').then(callback); + flush(); - it('should pipeline more functions', async(() { - backend.expect('POST', '/url', 'REQ-FIRST:V1').respond(200); - http.post('/url', 'req', - headers: {'h1': 'v1'}, - interceptors: new HttpInterceptors.of([ - new HttpInterceptor(request: (config) { - config.data = config.data + '-first' + ':' + config.header('h1'); - return config; - }), - new HttpInterceptor(request: (config) { - config.data = config.data.toUpperCase(); - return config; - }) - ]) - ).then(callback); - flush(); + expect(callback).toHaveBeenCalledOnce(); + expect(callback.mostRecentCall.args[0].data).toEqual({'foo': 'bar', 'baz': 23}); + })); - expect(callback).toHaveBeenCalledOnce(); - })); - }); + it('should deserialize json arrays', async(() { + backend.expect('GET', '/url').respond('[1, "abc", {"foo":"bar"}]'); + http(method: 'GET', url: '/url').then(callback); + flush(); - describe('response', () { + expect(callback).toHaveBeenCalledOnce(); + expect(callback.mostRecentCall.args[0].data).toEqual([1, 'abc', {'foo': 'bar'}]); + })); - describe('default', () { - it('should deserialize json objects', async(() { - backend.expect('GET', '/url').respond('{"foo":"bar","baz":23}'); - http(method: 'GET', url: '/url').then(callback); - flush(); + it('should deserialize json with security prefix', async(() { + backend.expect('GET', '/url').respond(')]}\',\n[1, "abc", {"foo":"bar"}]'); + http(method: 'GET', url: '/url').then(callback); + flush(); - expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0].data).toEqual({'foo': 'bar', 'baz': 23}); - })); + expect(callback).toHaveBeenCalledOnce(); + expect(callback.mostRecentCall.args[0].data).toEqual([1, 'abc', {'foo':'bar'}]); + })); - it('should deserialize json arrays', async(() { - backend.expect('GET', '/url').respond('[1, "abc", {"foo":"bar"}]'); - http(method: 'GET', url: '/url').then(callback); - flush(); - - expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0].data).toEqual([1, 'abc', {'foo': 'bar'}]); - })); + it('should deserialize json with security prefix ")]}\'"', async(() { + backend.expect('GET', '/url').respond(')]}\'\n\n[1, "abc", {"foo":"bar"}]'); + http(method: 'GET', url: '/url').then(callback); + flush(); + expect(callback).toHaveBeenCalledOnce(); + expect(callback.mostRecentCall.args[0].data).toEqual([1, 'abc', {'foo':'bar'}]); + })); - it('should deserialize json with security prefix', async(() { - backend.expect('GET', '/url').respond(')]}\',\n[1, "abc", {"foo":"bar"}]'); - http(method: 'GET', url: '/url').then(callback); - flush(); - expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0].data).toEqual([1, 'abc', {'foo':'bar'}]); - })); + it('should call onError on a JSON parse error', async(() { + backend.expect('GET', '/url').respond('[x]'); + var callbackCalled = false; + var onErrorCalled = false; + http.get('/url').then((_) { + callbackCalled = true; + }, onError: (e,s) { + // Dartium throws "Unexpected character" + // dart2js throws "Unexpected token" + expect('$e').toContain('Unexpected'); + onErrorCalled = true; + }); + flush(); + expect(callbackCalled).toBeFalsy(); + expect(onErrorCalled).toBeTruthy(); + })); - it('should deserialize json with security prefix ")]}\'"', async(() { - backend.expect('GET', '/url').respond(')]}\'\n\n[1, "abc", {"foo":"bar"}]'); - http(method: 'GET', url: '/url').then(callback); - flush(); + it('should not deserialize tpl beginning with ng expression', async(() { + backend.expect('GET', '/url').respond('{{some}}'); + http.get('/url').then(callback); + flush(); - expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0].data).toEqual([1, 'abc', {'foo':'bar'}]); - })); + expect(callback).toHaveBeenCalledOnce(); + expect(callback.mostRecentCall.args[0].data).toEqual('{{some}}'); + })); + }); - it('should call onError on a JSON parse error', async(() { - backend.expect('GET', '/url').respond('[x]'); - var callbackCalled = false; - var onErrorCalled = false; - http.get('/url').then((_) { - callbackCalled = true; - }, onError: (e,s) { - // Dartium throws "Unexpected character" - // dart2js throws "Unexpected token" - expect('$e').toContain('Unexpected'); - onErrorCalled = true; - }); + it('should have access to response headers', async(() { + backend.expect('GET', '/url').respond(200, 'response', {'h1': 'header1'}); + http.get('/url', + interceptors: new HttpInterceptor(response: (r) { + return new HttpResponse.copy(r, data: r.headers('h1')); + }) + ).then(callback); flush(); - expect(callbackCalled).toBeFalsy(); - expect(onErrorCalled).toBeTruthy(); - })); + expect(callback).toHaveBeenCalledOnce(); + expect(callback.mostRecentCall.args[0].data).toEqual('header1'); + })); - it('should not deserialize tpl beginning with ng expression', async(() { - backend.expect('GET', '/url').respond('{{some}}'); - http.get('/url').then(callback); + it('should pipeline more functions', async(() { + backend.expect('POST', '/url').respond(200, 'resp', {'h1': 'v1'}); + http.post('/url', '', interceptors: new HttpInterceptors.of([ + new HttpInterceptor(response: (r) { + return new HttpResponse.copy(r, data: r.data.toUpperCase()); + }), + new HttpInterceptor(response: (r) { + return new HttpResponse.copy(r, data: r.data + '-first' + ':' + r.headers('h1')); + })])).then(callback); flush(); expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0].data).toEqual('{{some}}'); + expect(callback.mostRecentCall.args[0].data).toEqual('RESP-FIRST:V1'); })); }); - - - it('should have access to response headers', async(() { - backend.expect('GET', '/url').respond(200, 'response', {'h1': 'header1'}); - http.get('/url', - interceptors: new HttpInterceptor(response: (r) { - return new HttpResponse.copy(r, data: r.headers('h1')); - }) - ).then(callback); - flush(); - - expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0].data).toEqual('header1'); - })); - - it('should pipeline more functions', async(() { - backend.expect('POST', '/url').respond(200, 'resp', {'h1': 'v1'}); - http.post('/url', '', interceptors: new HttpInterceptors.of([ - new HttpInterceptor(response: (r) { - return new HttpResponse.copy(r, data: r.data.toUpperCase()); - }), - new HttpInterceptor(response: (r) { - return new HttpResponse.copy(r, data: r.data + '-first' + ':' + r.headers('h1')); - })])).then(callback); - flush(); - - expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0].data).toEqual('RESP-FIRST:V1'); - })); }); }); }); -}); +} class FakeFile implements File { DateTime get lastModifiedDate => null; diff --git a/test/directive/ng_form_spec.dart b/test/directive/ng_form_spec.dart index 8ebff1b64..6f94c9521 100644 --- a/test/directive/ng_form_spec.dart +++ b/test/directive/ng_form_spec.dart @@ -2,428 +2,429 @@ library form_spec; import '../_specs.dart'; -main() => -describe('form', () { - TestBed _; +void main() { + describe('form', () { + TestBed _; - it('should set the name of the form and attach it to the scope', inject((Scope scope, TestBed _) { - var element = $('
'); - - expect(scope.context['myForm']).toBeNull(); - - _.compile(element); - scope.apply(); - - expect(scope.context['myForm']).toBeDefined(); - - var form = scope.context['myForm']; - expect(form.name).toEqual('myForm'); - })); - - describe('pristine / dirty', () { - it('should be set to pristine by default', inject((Scope scope, TestBed _) { - var element = $('
'); - - _.compile(element); - scope.apply(); - - var form = scope.context['myForm']; - expect(form.pristine).toEqual(true); - expect(form.dirty).toEqual(false); - })); - - it('should add and remove the correct CSS classes when set to dirty and to pristine', inject((Scope scope, TestBed _) { + it('should set the name of the form and attach it to the scope', inject((Scope scope, TestBed _) { var element = $('
'); - _.compile(element); - scope.apply(); - - var form = scope.context['myForm']; - - form.dirty = true; - expect(form.pristine).toEqual(false); - expect(form.dirty).toEqual(true); - expect(element.hasClass('ng-pristine')).toBe(false); - expect(element.hasClass('ng-dirty')).toBe(true); - - form.pristine = true; - expect(form.pristine).toEqual(true); - expect(form.dirty).toEqual(false); - expect(element.hasClass('ng-pristine')).toBe(true); - expect(element.hasClass('ng-dirty')).toBe(false); - })); - }); - - describe('valid / invalid', () { - it('should add and remove the correct flags when set to valid and to invalid', inject((Scope scope, TestBed _) { - var element = $('
'); - - _.compile(element); - scope.apply(); - - var form = scope.context['myForm']; - - form.invalid = true; - expect(form.valid).toEqual(false); - expect(form.invalid).toEqual(true); - expect(element.hasClass('ng-valid')).toBe(false); - expect(element.hasClass('ng-invalid')).toBe(true); - - form.valid = true; - expect(form.valid).toEqual(true); - expect(form.invalid).toEqual(false); - expect(element.hasClass('ng-invalid')).toBe(false); - expect(element.hasClass('ng-valid')).toBe(true); - })); - - it('should set the validity with respect to all existing validations when setValidity() is used', inject((Scope scope, TestBed _) { - var element = $('
' - ' ' + - ' ' + - ' ' + - '
'); - - _.compile(element); - scope.apply(); - - var form = scope.context['myForm']; - NgModel one = form['one']; - NgModel two = form['two']; - NgModel three = form['three']; - - form.updateControlValidity(one, "some error", false); - expect(form.valid).toBe(false); - expect(form.invalid).toBe(true); - - form.updateControlValidity(two, "some error", false); - expect(form.valid).toBe(false); - expect(form.invalid).toBe(true); - - form.updateControlValidity(one, "some error", true); - expect(form.valid).toBe(false); - expect(form.invalid).toBe(true); - - form.updateControlValidity(two, "some error", true); - expect(form.valid).toBe(true); - expect(form.invalid).toBe(false); - })); - - it('should not handle the control errorType pair more than once', inject((Scope scope, TestBed _) { - var element = $('
' - ' ' + - '
'); - - _.compile(element); - scope.apply(); - - var form = scope.context['myForm']; - NgModel one = form['one']; - - form.updateControlValidity(one, "validation error", false); - expect(form.valid).toBe(false); - expect(form.invalid).toBe(true); - - form.updateControlValidity(one, "validation error", false); - expect(form.valid).toBe(false); - expect(form.invalid).toBe(true); - - form.updateControlValidity(one, "validation error", true); - expect(form.valid).toBe(true); - expect(form.invalid).toBe(false); - })); - - it('should update the validity of the parent form when the inner model changes', inject((Scope scope, TestBed _) { - var element = $('
' - ' ' + - ' ' + - '
'); + expect(scope.context['myForm']).toBeNull(); _.compile(element); scope.apply(); - var form = scope.context['myForm']; - NgModel one = form['one']; - NgModel two = form['two']; - - one.setValidity("required", false); - expect(form.valid).toBe(false); - expect(form.invalid).toBe(true); - - two.setValidity("required", false); - expect(form.valid).toBe(false); - expect(form.invalid).toBe(true); - - one.setValidity("required", true); - expect(form.valid).toBe(false); - expect(form.invalid).toBe(true); - - two.setValidity("required", true); - expect(form.valid).toBe(true); - expect(form.invalid).toBe(false); - })); - - it('should set the validity for the parent form when fieldsets are used', inject((Scope scope, TestBed _) { - var element = $('
' - '
' + - ' ' + - '
' + - '
'); - - _.compile(element); - scope.apply(); + expect(scope.context['myForm']).toBeDefined(); var form = scope.context['myForm']; - var fieldset = _.rootScope.context['f'].directive(NgForm); - var model = _.rootScope.context['m'].directive(NgModel); - - model.setValidity("error", false); - - expect(model.valid).toBe(false); - expect(fieldset.valid).toBe(false); - expect(form.valid).toBe(false); - - model.setValidity("error", true); - - expect(model.valid).toBe(true); - expect(fieldset.valid).toBe(true); - expect(form.valid).toBe(true); - - form.updateControlValidity(fieldset, "error", false); - expect(model.valid).toBe(true); - expect(fieldset.valid).toBe(true); - expect(form.valid).toBe(false); - - fieldset.updateControlValidity(model, "error", false); - expect(model.valid).toBe(true); - expect(fieldset.valid).toBe(false); - expect(form.valid).toBe(false); + expect(form.name).toEqual('myForm'); })); - }); - describe('controls', () { - it('should add each contained ng-model as a control upon compile', inject((Scope scope, TestBed _) { - var element = $('
' - ' ' + - ' ' + - '
'); + describe('pristine / dirty', () { + it('should be set to pristine by default', inject((Scope scope, TestBed _) { + var element = $('
'); - _.compile(element); + _.compile(element); + scope.apply(); - scope.context['mega_model'] = 'mega'; - scope.context['fire_model'] = 'fire'; - scope.apply(); + var form = scope.context['myForm']; + expect(form.pristine).toEqual(true); + expect(form.dirty).toEqual(false); + })); - var form = scope.context['myForm']; - expect(form['mega_name'].modelValue).toBe('mega'); - expect(form['fire_name'].modelValue).toBe('fire'); - })); - - it('should properly remove controls directly from the ngForm instance', inject((Scope scope, TestBed _) { - var element = $('
' - ' ' + - '
'); + it('should add and remove the correct CSS classes when set to dirty and to pristine', inject((Scope scope, TestBed _) { + var element = $('
'); - _.compile(element); - scope.apply(); + _.compile(element); + scope.apply(); - var form = scope.context['myForm']; - var control = form['mega_control']; - form.removeControl(control); - expect(form['mega_control']).toBeNull(); - })); + var form = scope.context['myForm']; - it('should remove all controls when the scope is destroyed', inject((Scope scope, TestBed _) { - Scope childScope = scope.createChild({}); - var element = $('
' + - ' ' + - ' ' + - ' ' + - '
'); + form.dirty = true; + expect(form.pristine).toEqual(false); + expect(form.dirty).toEqual(true); + expect(element.hasClass('ng-pristine')).toBe(false); + expect(element.hasClass('ng-dirty')).toBe(true); - _.compile(element, scope: childScope); - childScope.apply(); + form.pristine = true; + expect(form.pristine).toEqual(true); + expect(form.dirty).toEqual(false); + expect(element.hasClass('ng-pristine')).toBe(true); + expect(element.hasClass('ng-dirty')).toBe(false); + })); + }); - var form = childScope.context['myForm']; - expect(form['one']).toBeDefined(); - expect(form['two']).toBeDefined(); - expect(form['three']).toBeDefined(); + describe('valid / invalid', () { + it('should add and remove the correct flags when set to valid and to invalid', inject((Scope scope, TestBed _) { + var element = $('
'); + + _.compile(element); + scope.apply(); + + var form = scope.context['myForm']; + + form.invalid = true; + expect(form.valid).toEqual(false); + expect(form.invalid).toEqual(true); + expect(element.hasClass('ng-valid')).toBe(false); + expect(element.hasClass('ng-invalid')).toBe(true); + + form.valid = true; + expect(form.valid).toEqual(true); + expect(form.invalid).toEqual(false); + expect(element.hasClass('ng-invalid')).toBe(false); + expect(element.hasClass('ng-valid')).toBe(true); + })); + + it('should set the validity with respect to all existing validations when setValidity() is used', inject((Scope scope, TestBed _) { + var element = $('
' + ' ' + + ' ' + + ' ' + + '
'); + + _.compile(element); + scope.apply(); + + var form = scope.context['myForm']; + NgModel one = form['one']; + NgModel two = form['two']; + NgModel three = form['three']; + + form.updateControlValidity(one, "some error", false); + expect(form.valid).toBe(false); + expect(form.invalid).toBe(true); + + form.updateControlValidity(two, "some error", false); + expect(form.valid).toBe(false); + expect(form.invalid).toBe(true); + + form.updateControlValidity(one, "some error", true); + expect(form.valid).toBe(false); + expect(form.invalid).toBe(true); + + form.updateControlValidity(two, "some error", true); + expect(form.valid).toBe(true); + expect(form.invalid).toBe(false); + })); + + it('should not handle the control errorType pair more than once', inject((Scope scope, TestBed _) { + var element = $('
' + ' ' + + '
'); + + _.compile(element); + scope.apply(); + + var form = scope.context['myForm']; + NgModel one = form['one']; + + form.updateControlValidity(one, "validation error", false); + expect(form.valid).toBe(false); + expect(form.invalid).toBe(true); + + form.updateControlValidity(one, "validation error", false); + expect(form.valid).toBe(false); + expect(form.invalid).toBe(true); + + form.updateControlValidity(one, "validation error", true); + expect(form.valid).toBe(true); + expect(form.invalid).toBe(false); + })); + + it('should update the validity of the parent form when the inner model changes', inject((Scope scope, TestBed _) { + var element = $('
' + ' ' + + ' ' + + '
'); + + _.compile(element); + scope.apply(); + + var form = scope.context['myForm']; + NgModel one = form['one']; + NgModel two = form['two']; + + one.setValidity("required", false); + expect(form.valid).toBe(false); + expect(form.invalid).toBe(true); + + two.setValidity("required", false); + expect(form.valid).toBe(false); + expect(form.invalid).toBe(true); + + one.setValidity("required", true); + expect(form.valid).toBe(false); + expect(form.invalid).toBe(true); + + two.setValidity("required", true); + expect(form.valid).toBe(true); + expect(form.invalid).toBe(false); + })); + + it('should set the validity for the parent form when fieldsets are used', inject((Scope scope, TestBed _) { + var element = $('
' + '
' + + ' ' + + '
' + + '
'); + + _.compile(element); + scope.apply(); + + var form = scope.context['myForm']; + var fieldset = _.rootScope.context['f'].directive(NgForm); + var model = _.rootScope.context['m'].directive(NgModel); + + model.setValidity("error", false); + + expect(model.valid).toBe(false); + expect(fieldset.valid).toBe(false); + expect(form.valid).toBe(false); + + model.setValidity("error", true); + + expect(model.valid).toBe(true); + expect(fieldset.valid).toBe(true); + expect(form.valid).toBe(true); + + form.updateControlValidity(fieldset, "error", false); + expect(model.valid).toBe(true); + expect(fieldset.valid).toBe(true); + expect(form.valid).toBe(false); + + fieldset.updateControlValidity(model, "error", false); + expect(model.valid).toBe(true); + expect(fieldset.valid).toBe(false); + expect(form.valid).toBe(false); + })); + }); - childScope.destroy(); + describe('controls', () { + it('should add each contained ng-model as a control upon compile', inject((Scope scope, TestBed _) { + var element = $('
' + ' ' + + ' ' + + '
'); + + _.compile(element); + + scope.context['mega_model'] = 'mega'; + scope.context['fire_model'] = 'fire'; + scope.apply(); + + var form = scope.context['myForm']; + expect(form['mega_name'].modelValue).toBe('mega'); + expect(form['fire_name'].modelValue).toBe('fire'); + })); + + it('should properly remove controls directly from the ngForm instance', inject((Scope scope, TestBed _) { + var element = $('
' + ' ' + + '
'); + + _.compile(element); + scope.apply(); + + var form = scope.context['myForm']; + var control = form['mega_control']; + form.removeControl(control); + expect(form['mega_control']).toBeNull(); + })); + + it('should remove all controls when the scope is destroyed', inject((Scope scope, TestBed _) { + Scope childScope = scope.createChild({}); + var element = $('
' + + ' ' + + ' ' + + ' ' + + '
'); + + _.compile(element, scope: childScope); + childScope.apply(); + + var form = childScope.context['myForm']; + expect(form['one']).toBeDefined(); + expect(form['two']).toBeDefined(); + expect(form['three']).toBeDefined(); + + childScope.destroy(); + + expect(form['one']).toBeNull(); + expect(form['two']).toBeNull(); + expect(form['three']).toBeNull(); + })); + }); - expect(form['one']).toBeNull(); - expect(form['two']).toBeNull(); - expect(form['three']).toBeNull(); - })); - }); + describe('onSubmit', () { + it('should suppress the submission event if no action is provided within the form', inject((Scope scope, TestBed _) { + var element = $('
'); - describe('onSubmit', () { - it('should suppress the submission event if no action is provided within the form', inject((Scope scope, TestBed _) { - var element = $('
'); + _.compile(element); + scope.apply(); - _.compile(element); - scope.apply(); + Event submissionEvent = new Event.eventType('CustomEvent', 'submit'); - Event submissionEvent = new Event.eventType('CustomEvent', 'submit'); + expect(submissionEvent.defaultPrevented).toBe(false); + element[0].dispatchEvent(submissionEvent); + expect(submissionEvent.defaultPrevented).toBe(true); - expect(submissionEvent.defaultPrevented).toBe(false); - element[0].dispatchEvent(submissionEvent); - expect(submissionEvent.defaultPrevented).toBe(true); + Event fakeEvent = new Event.eventType('CustomEvent', 'running'); - Event fakeEvent = new Event.eventType('CustomEvent', 'running'); + expect(fakeEvent.defaultPrevented).toBe(false); + element[0].dispatchEvent(submissionEvent); + expect(fakeEvent.defaultPrevented).toBe(false); + })); - expect(fakeEvent.defaultPrevented).toBe(false); - element[0].dispatchEvent(submissionEvent); - expect(fakeEvent.defaultPrevented).toBe(false); - })); + it('should not prevent the submission event if an action is defined', inject((Scope scope, TestBed _) { + var element = $('
'); - it('should not prevent the submission event if an action is defined', inject((Scope scope, TestBed _) { - var element = $('
'); + _.compile(element); + scope.apply(); - _.compile(element); - scope.apply(); + Event submissionEvent = new Event.eventType('CustomEvent', 'submit'); - Event submissionEvent = new Event.eventType('CustomEvent', 'submit'); + expect(submissionEvent.defaultPrevented).toBe(false); + element[0].dispatchEvent(submissionEvent); + expect(submissionEvent.defaultPrevented).toBe(false); + })); - expect(submissionEvent.defaultPrevented).toBe(false); - element[0].dispatchEvent(submissionEvent); - expect(submissionEvent.defaultPrevented).toBe(false); - })); + it('should execute the ng-submit expression if provided upon form submission', inject((Scope scope, TestBed _) { + var element = $('
'); - it('should execute the ng-submit expression if provided upon form submission', inject((Scope scope, TestBed _) { - var element = $('
'); + _.compile(element); + scope.apply(); - _.compile(element); - scope.apply(); - - _.rootScope.context['submitted'] = false; - - Event submissionEvent = new Event.eventType('CustomEvent', 'submit'); - element[0].dispatchEvent(submissionEvent); + _.rootScope.context['submitted'] = false; - expect(_.rootScope.context['submitted']).toBe(true); - })); + Event submissionEvent = new Event.eventType('CustomEvent', 'submit'); + element[0].dispatchEvent(submissionEvent); - it('should apply the valid and invalid prefixed submit CSS classes to the element', inject((TestBed _) { - _.compile('
' + - ' ' + - '
'); + expect(_.rootScope.context['submitted']).toBe(true); + })); - NgForm form = _.rootScope.context['superForm']; - Probe probe = _.rootScope.context['i']; - var model = probe.directive(NgModel); + it('should apply the valid and invalid prefixed submit CSS classes to the element', inject((TestBed _) { + _.compile('
' + + ' ' + + '
'); - expect(form.submitted).toBe(false); - expect(form.valid_submit).toBe(false); - expect(form.invalid_submit).toBe(false); - expect(form.element.classes.contains('ng-submit-invalid')).toBe(false); - expect(form.element.classes.contains('ng-submit-valid')).toBe(false); + NgForm form = _.rootScope.context['superForm']; + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); - Event submissionEvent = new Event.eventType('CustomEvent', 'submit'); + expect(form.submitted).toBe(false); + expect(form.valid_submit).toBe(false); + expect(form.invalid_submit).toBe(false); + expect(form.element.classes.contains('ng-submit-invalid')).toBe(false); + expect(form.element.classes.contains('ng-submit-valid')).toBe(false); - form.element.dispatchEvent(submissionEvent); - _.rootScope.apply(); + Event submissionEvent = new Event.eventType('CustomEvent', 'submit'); - expect(form.submitted).toBe(true); - expect(form.valid_submit).toBe(false); - expect(form.invalid_submit).toBe(true); - expect(form.element.classes.contains('ng-submit-invalid')).toBe(true); - expect(form.element.classes.contains('ng-submit-valid')).toBe(false); + form.element.dispatchEvent(submissionEvent); + _.rootScope.apply(); - _.rootScope.apply('myModel = "man"'); - form.element.dispatchEvent(submissionEvent); + expect(form.submitted).toBe(true); + expect(form.valid_submit).toBe(false); + expect(form.invalid_submit).toBe(true); + expect(form.element.classes.contains('ng-submit-invalid')).toBe(true); + expect(form.element.classes.contains('ng-submit-valid')).toBe(false); - expect(form.submitted).toBe(true); - expect(form.valid_submit).toBe(true); - expect(form.invalid_submit).toBe(false); - expect(form.element.classes.contains('ng-submit-invalid')).toBe(false); - expect(form.element.classes.contains('ng-submit-valid')).toBe(true); - })); - }); + _.rootScope.apply('myModel = "man"'); + form.element.dispatchEvent(submissionEvent); - describe('reset()', () { - it('should reset the model value to its original state', inject((TestBed _) { - _.compile('
' + - ' ' + - '
'); - _.rootScope.apply('myModel = "animal"'); + expect(form.submitted).toBe(true); + expect(form.valid_submit).toBe(true); + expect(form.invalid_submit).toBe(false); + expect(form.element.classes.contains('ng-submit-invalid')).toBe(false); + expect(form.element.classes.contains('ng-submit-valid')).toBe(true); + })); + }); - NgForm form = _.rootScope.context['superForm']; + describe('reset()', () { + it('should reset the model value to its original state', inject((TestBed _) { + _.compile('
' + + ' ' + + '
'); + _.rootScope.apply('myModel = "animal"'); - Probe probe = _.rootScope.context['i']; - var model = probe.directive(NgModel); + NgForm form = _.rootScope.context['superForm']; - expect(_.rootScope.context['myModel']).toEqual('animal'); - expect(model.modelValue).toEqual('animal'); - expect(model.viewValue).toEqual('animal'); + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); - _.rootScope.apply('myModel = "man"'); + expect(_.rootScope.context['myModel']).toEqual('animal'); + expect(model.modelValue).toEqual('animal'); + expect(model.viewValue).toEqual('animal'); - expect(_.rootScope.context['myModel']).toEqual('man'); - expect(model.modelValue).toEqual('man'); - expect(model.viewValue).toEqual('man'); + _.rootScope.apply('myModel = "man"'); - form.reset(); - _.rootScope.apply(); + expect(_.rootScope.context['myModel']).toEqual('man'); + expect(model.modelValue).toEqual('man'); + expect(model.viewValue).toEqual('man'); - expect(_.rootScope.context['myModel']).toEqual('animal'); - expect(model.modelValue).toEqual('animal'); - expect(model.viewValue).toEqual('animal'); - })); + form.reset(); + _.rootScope.apply(); - it('should set the form control to be untouched when the model is reset or submitted', inject((TestBed _) { - var form = _.compile('
' + - ' ' + - '
'); - var model = _.rootScope.context['i'].directive(NgModel); - var input = model.element; + expect(_.rootScope.context['myModel']).toEqual('animal'); + expect(model.modelValue).toEqual('animal'); + expect(model.viewValue).toEqual('animal'); + })); - NgForm formModel = _.rootScope.context['duperForm']; + it('should set the form control to be untouched when the model is reset or submitted', inject((TestBed _) { + var form = _.compile('
' + + ' ' + + '
'); + var model = _.rootScope.context['i'].directive(NgModel); + var input = model.element; - expect(formModel.touched).toBe(false); - expect(formModel.untouched).toBe(true); - expect(form.classes.contains('ng-touched')).toBe(false); - expect(form.classes.contains('ng-untouched')).toBe(true); + NgForm formModel = _.rootScope.context['duperForm']; - _.triggerEvent(input, 'blur'); + expect(formModel.touched).toBe(false); + expect(formModel.untouched).toBe(true); + expect(form.classes.contains('ng-touched')).toBe(false); + expect(form.classes.contains('ng-untouched')).toBe(true); - expect(formModel.touched).toBe(true); - expect(formModel.untouched).toBe(false); - expect(form.classes.contains('ng-touched')).toBe(true); - expect(form.classes.contains('ng-untouched')).toBe(false); + _.triggerEvent(input, 'blur'); - formModel.reset(); + expect(formModel.touched).toBe(true); + expect(formModel.untouched).toBe(false); + expect(form.classes.contains('ng-touched')).toBe(true); + expect(form.classes.contains('ng-untouched')).toBe(false); - expect(formModel.touched).toBe(false); - expect(formModel.untouched).toBe(true); - expect(form.classes.contains('ng-touched')).toBe(false); - expect(form.classes.contains('ng-untouched')).toBe(true); + formModel.reset(); - _.triggerEvent(input, 'blur'); + expect(formModel.touched).toBe(false); + expect(formModel.untouched).toBe(true); + expect(form.classes.contains('ng-touched')).toBe(false); + expect(form.classes.contains('ng-untouched')).toBe(true); - expect(formModel.touched).toBe(true); + _.triggerEvent(input, 'blur'); - _.triggerEvent(form, 'submit'); + expect(formModel.touched).toBe(true); - expect(formModel.touched).toBe(false); - expect(formModel.untouched).toBe(true); - expect(form.classes.contains('ng-touched')).toBe(false); - expect(form.classes.contains('ng-untouched')).toBe(true); - })); - }); + _.triggerEvent(form, 'submit'); - describe('regression tests: form', () { - it('should be resolvable by injector if configured by user.', () { - module((Module module) { - module.type(NgForm); - }); + expect(formModel.touched).toBe(false); + expect(formModel.untouched).toBe(true); + expect(form.classes.contains('ng-touched')).toBe(false); + expect(form.classes.contains('ng-untouched')).toBe(true); + })); + }); - inject((Injector injector, Compiler compiler, DirectiveMap directives) { - var element = $('
'); - compiler(element, directives)(injector, element); - // The only expectation is that this doesn't throw + describe('regression tests: form', () { + it('should be resolvable by injector if configured by user.', () { + module((Module module) { + module.type(NgForm); + }); + + inject((Injector injector, Compiler compiler, DirectiveMap directives) { + var element = $('
'); + compiler(element, directives)(injector, element); + // The only expectation is that this doesn't throw + }); }); }); }); -}); +} diff --git a/test/directive/ng_model_validators_spec.dart b/test/directive/ng_model_validators_spec.dart index d42e5fb5c..a549d0d6b 100644 --- a/test/directive/ng_model_validators_spec.dart +++ b/test/directive/ng_model_validators_spec.dart @@ -2,351 +2,352 @@ library ng_model_validators; import '../_specs.dart'; -main() => -describe('ngModel validators', () { - TestBed _; +void main() { + describe('ngModel validators', () { + TestBed _; - beforeEach(inject((TestBed tb) => _ = tb)); + beforeEach(inject((TestBed tb) => _ = tb)); - describe('required', () { - it('should validate the input field if the required attribute is set', inject((RootScope scope) { - _.compile(''); - Probe probe = _.rootScope.context['i']; - var model = probe.directive(NgModel); + describe('required', () { + it('should validate the input field if the required attribute is set', inject((RootScope scope) { + _.compile(''); + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); - model.validate(); - expect(model.valid).toEqual(false); - expect(model.invalid).toEqual(true); + model.validate(); + expect(model.valid).toEqual(false); + expect(model.invalid).toEqual(true); - _.rootScope.context['val'] = 'value'; - model.validate(); + _.rootScope.context['val'] = 'value'; + model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - })); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + })); - it('should validate a number input field if the required attribute is set', inject((RootScope scope) { - _.compile(''); - Probe probe = _.rootScope.context['i']; - var model = probe.directive(NgModel); - - model.validate(); - expect(model.valid).toEqual(false); - expect(model.invalid).toEqual(true); - - _.rootScope.context['val'] = 5; - model.validate(); - - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - })); - - - it('should validate the input field depending on if ng-required is true', inject((RootScope scope) { - _.compile(''); - Probe probe = _.rootScope.context['i']; - var model = probe.directive(NgModel); - - _.rootScope.apply(); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - - _.rootScope.apply(() { - _.rootScope.context['requireMe'] = true; - }); - - model.validate(); - expect(model.valid).toEqual(false); - expect(model.invalid).toEqual(true); - - _.rootScope.apply(() { - _.rootScope.context['requireMe'] = false; - }); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - })); - }); - - describe('[type="url"]', () { - it('should validate the input field given a valid or invalid URL', inject((RootScope scope) { - _.compile(''); - Probe probe = _.rootScope.context['i']; - var model = probe.directive(NgModel); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - - _.rootScope.apply(() { - _.rootScope.context['val'] = 'googledotcom'; - }); - - model.validate(); - expect(model.valid).toEqual(false); - expect(model.invalid).toEqual(true); - - _.rootScope.apply(() { - _.rootScope.context['val'] = 'http://www.google.com'; - }); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - })); - }); - - describe('[type="email"]', () { - it('should validate the input field given a valid or invalid email address', inject((RootScope scope) { - _.compile(''); - Probe probe = _.rootScope.context['i']; - var model = probe.directive(NgModel); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - - _.rootScope.apply(() { - _.rootScope.context['val'] = 'matiasatemail.com'; - }); - - model.validate(); - expect(model.valid).toEqual(false); - expect(model.invalid).toEqual(true); - - _.rootScope.apply(() { - _.rootScope.context['val'] = 'matias@gmail.com'; - }); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - })); - }); - - describe('[type="number"]', () { - it('should validate the input field given a valid or invalid number', inject((RootScope scope) { - _.compile(''); - Probe probe = _.rootScope.context['i']; - var model = probe.directive(NgModel); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - - _.rootScope.apply(() { - _.rootScope.context['val'] = '11'; - }); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - - - _.rootScope.apply(() { - _.rootScope.context['val'] = 10; - }); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - - _.rootScope.apply(() { - _.rootScope.context['val'] = 'twelve'; - }); - - model.validate(); - expect(model.valid).toEqual(false); - expect(model.invalid).toEqual(true); - })); - }); - - describe('pattern', () { - it('should validate the input field if a ng-pattern attribute is provided', inject((RootScope scope) { - _.compile(''); - Probe probe = _.rootScope.context['i']; - var model = probe.directive(NgModel); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - - _.rootScope.apply(() { - _.rootScope.context['val'] = "abc"; - _.rootScope.context['myPattern'] = "[a-z]+"; - }); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - - _.rootScope.apply(() { - _.rootScope.context['val'] = "abc"; - _.rootScope.context['myPattern'] = "[0-9]+"; - }); - - model.validate(); - expect(model.valid).toEqual(false); - expect(model.invalid).toEqual(true); - - _.rootScope.apply(() { - _.rootScope.context['val'] = "123"; - _.rootScope.context['myPattern'] = "123"; - }); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - })); - - it('should validate the input field if a pattern attribute is provided', inject((RootScope scope) { - _.compile(''); - Probe probe = _.rootScope.context['i']; - var model = probe.directive(NgModel); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - - _.rootScope.apply(() { - _.rootScope.context['val'] = "abc"; - }); - - model.validate(); - expect(model.valid).toEqual(false); - expect(model.invalid).toEqual(true); - - _.rootScope.apply(() { - _.rootScope.context['val'] = "012345"; - }); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - - _.rootScope.apply(() { - _.rootScope.context['val'] = "6789"; - }); - - model.validate(); - expect(model.valid).toEqual(false); - expect(model.invalid).toEqual(true); - })); - }); - - describe('minlength', () { - it('should validate the input field if a minlength attribute is provided', inject((RootScope scope) { - _.compile(''); - Probe probe = _.rootScope.context['i']; - var model = probe.directive(NgModel); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - - _.rootScope.apply(() { - _.rootScope.context['val'] = "abc"; - }); - - model.validate(); - expect(model.valid).toEqual(false); - expect(model.invalid).toEqual(true); - - _.rootScope.apply(() { - _.rootScope.context['val'] = "abcdef"; - }); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - })); - - it('should validate the input field if a ng-minlength attribute is provided', inject((RootScope scope) { - _.compile(''); - Probe probe = _.rootScope.context['i']; - var model = probe.directive(NgModel); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - - _.rootScope.apply(() { - _.rootScope.context['val'] = "abcdef"; - _.rootScope.context['len'] = 3; - }); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - - _.rootScope.apply(() { - _.rootScope.context['val'] = "abc"; - _.rootScope.context['len'] = 5; - }); - - model.validate(); - expect(model.valid).toEqual(false); - expect(model.invalid).toEqual(true); - })); - }); - - describe('maxlength', () { - it('should validate the input field if a maxlength attribute is provided', inject((RootScope scope) { - _.compile(''); - Probe probe = _.rootScope.context['i']; - var model = probe.directive(NgModel); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - - _.rootScope.apply(() { - _.rootScope.context['val'] = "abcdef"; - }); - - model.validate(); - expect(model.valid).toEqual(false); - expect(model.invalid).toEqual(true); - - _.rootScope.apply(() { - _.rootScope.context['val'] = "abc"; - }); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - })); - - it('should validate the input field if a ng-maxlength attribute is provided', inject((RootScope scope) { - _.compile(''); - Probe probe = _.rootScope.context['i']; - var model = probe.directive(NgModel); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - - _.rootScope.apply(() { - _.rootScope.context['val'] = "abcdef"; - _.rootScope.context['len'] = 6; - }); - - model.validate(); - expect(model.valid).toEqual(true); - expect(model.invalid).toEqual(false); - - _.rootScope.apply(() { - _.rootScope.context['val'] = "abc"; - _.rootScope.context['len'] = 1; - }); - - model.validate(); - expect(model.valid).toEqual(false); - expect(model.invalid).toEqual(true); - })); + it('should validate a number input field if the required attribute is set', inject((RootScope scope) { + _.compile(''); + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); + + model.validate(); + expect(model.valid).toEqual(false); + expect(model.invalid).toEqual(true); + + _.rootScope.context['val'] = 5; + model.validate(); + + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + })); + + + it('should validate the input field depending on if ng-required is true', inject((RootScope scope) { + _.compile(''); + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); + + _.rootScope.apply(); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['requireMe'] = true; + }); + + model.validate(); + expect(model.valid).toEqual(false); + expect(model.invalid).toEqual(true); + + _.rootScope.apply(() { + _.rootScope.context['requireMe'] = false; + }); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + })); + }); + + describe('[type="url"]', () { + it('should validate the input field given a valid or invalid URL', inject((RootScope scope) { + _.compile(''); + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = 'googledotcom'; + }); + + model.validate(); + expect(model.valid).toEqual(false); + expect(model.invalid).toEqual(true); + + _.rootScope.apply(() { + _.rootScope.context['val'] = 'http://www.google.com'; + }); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + })); + }); + + describe('[type="email"]', () { + it('should validate the input field given a valid or invalid email address', inject((RootScope scope) { + _.compile(''); + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = 'matiasatemail.com'; + }); + + model.validate(); + expect(model.valid).toEqual(false); + expect(model.invalid).toEqual(true); + + _.rootScope.apply(() { + _.rootScope.context['val'] = 'matias@gmail.com'; + }); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + })); + }); + + describe('[type="number"]', () { + it('should validate the input field given a valid or invalid number', inject((RootScope scope) { + _.compile(''); + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = '11'; + }); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + + _.rootScope.apply(() { + _.rootScope.context['val'] = 10; + }); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = 'twelve'; + }); + + model.validate(); + expect(model.valid).toEqual(false); + expect(model.invalid).toEqual(true); + })); + }); + + describe('pattern', () { + it('should validate the input field if a ng-pattern attribute is provided', inject((RootScope scope) { + _.compile(''); + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "abc"; + _.rootScope.context['myPattern'] = "[a-z]+"; + }); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "abc"; + _.rootScope.context['myPattern'] = "[0-9]+"; + }); + + model.validate(); + expect(model.valid).toEqual(false); + expect(model.invalid).toEqual(true); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "123"; + _.rootScope.context['myPattern'] = "123"; + }); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + })); + + it('should validate the input field if a pattern attribute is provided', inject((RootScope scope) { + _.compile(''); + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "abc"; + }); + + model.validate(); + expect(model.valid).toEqual(false); + expect(model.invalid).toEqual(true); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "012345"; + }); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "6789"; + }); + + model.validate(); + expect(model.valid).toEqual(false); + expect(model.invalid).toEqual(true); + })); + }); + + describe('minlength', () { + it('should validate the input field if a minlength attribute is provided', inject((RootScope scope) { + _.compile(''); + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "abc"; + }); + + model.validate(); + expect(model.valid).toEqual(false); + expect(model.invalid).toEqual(true); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "abcdef"; + }); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + })); + + it('should validate the input field if a ng-minlength attribute is provided', inject((RootScope scope) { + _.compile(''); + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "abcdef"; + _.rootScope.context['len'] = 3; + }); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "abc"; + _.rootScope.context['len'] = 5; + }); + + model.validate(); + expect(model.valid).toEqual(false); + expect(model.invalid).toEqual(true); + })); + }); + + describe('maxlength', () { + it('should validate the input field if a maxlength attribute is provided', inject((RootScope scope) { + _.compile(''); + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "abcdef"; + }); + + model.validate(); + expect(model.valid).toEqual(false); + expect(model.invalid).toEqual(true); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "abc"; + }); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + })); + + it('should validate the input field if a ng-maxlength attribute is provided', inject((RootScope scope) { + _.compile(''); + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "abcdef"; + _.rootScope.context['len'] = 6; + }); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "abc"; + _.rootScope.context['len'] = 1; + }); + + model.validate(); + expect(model.valid).toEqual(false); + expect(model.invalid).toEqual(true); + })); + }); }); -}); +} diff --git a/test/directive/ng_style_spec.dart b/test/directive/ng_style_spec.dart index 8406a14cc..f20be0fca 100644 --- a/test/directive/ng_style_spec.dart +++ b/test/directive/ng_style_spec.dart @@ -3,84 +3,86 @@ library ng_style_spec; import '../_specs.dart'; import 'dart:html' as dom; -main() => describe('NgStyle', () { - TestBed _; +void main() { + describe('NgStyle', () { + TestBed _; - beforeEach(inject((TestBed tb) => _ = tb)); + beforeEach(inject((TestBed tb) => _ = tb)); - it('should set', () { - dom.Element element = _.compile('
'); - _.rootScope.apply(); - expect(element.style.height).toEqual('40px'); - }); - - - it('should silently ignore undefined style', () { - dom.Element element = _.compile('
'); - _.rootScope.apply(); - expect(element.classes.contains('ng-exception')).toBeFalsy(); - }); - - - describe('preserving styles set before and after compilation', () { - var scope, preCompStyle, preCompVal, postCompStyle, postCompVal, element; - - beforeEach(inject(() { - preCompStyle = 'width'; - preCompVal = '300px'; - postCompStyle = 'height'; - postCompVal = '100px'; - element = $('
'); - element.css(preCompStyle, preCompVal); - document.body.append(element[0]); - _.compile(element); - scope = _.rootScope; - scope.context['styleObj'] = {'margin-top': '44px'}; - scope.apply(); - element.css(postCompStyle, postCompVal); - })); - - afterEach(() { - element.remove(null); - }); - - - it('should not mess up stuff after compilation', () { - element.css('margin', '44px'); - expect(element.css(preCompStyle)).toEqual(preCompVal); - expect(element.css('margin-top')).toEqual('44px'); - expect(element.css(postCompStyle)).toEqual(postCompVal); - }); - - it(r'should not mess up stuff after $apply with no model changes', () { - element.css('padding-top', '33px'); - scope.apply(); - expect(element.css(preCompStyle)).toEqual(preCompVal); - expect(element.css('margin-top')).toEqual('44px'); - expect(element.css(postCompStyle)).toEqual(postCompVal); - expect(element.css('padding-top')).toEqual('33px'); + it('should set', () { + dom.Element element = _.compile('
'); + _.rootScope.apply(); + expect(element.style.height).toEqual('40px'); }); - it(r'should not mess up stuff after $apply with non-colliding model changes', () { - scope.context['styleObj'] = {'padding-top': '99px'}; - scope.apply(); - expect(element.css(preCompStyle)).toEqual(preCompVal); - expect(element.css('margin-top')).not.toEqual('44px'); - expect(element.css('padding-top')).toEqual('99px'); - expect(element.css(postCompStyle)).toEqual(postCompVal); + it('should silently ignore undefined style', () { + dom.Element element = _.compile('
'); + _.rootScope.apply(); + expect(element.classes.contains('ng-exception')).toBeFalsy(); }); - it(r'should overwrite original styles after a colliding model change', () { - scope.context['styleObj'] = {'height': '99px', 'width': '88px'}; - scope.apply(); - expect(element.css(preCompStyle)).toEqual('88px'); - expect(element.css(postCompStyle)).toEqual('99px'); - scope.context['styleObj'] = {}; - scope.apply(); - expect(element.css(preCompStyle)).not.toEqual('88px'); - expect(element.css(postCompStyle)).not.toEqual('99px'); + describe('preserving styles set before and after compilation', () { + var scope, preCompStyle, preCompVal, postCompStyle, postCompVal, element; + + beforeEach(inject(() { + preCompStyle = 'width'; + preCompVal = '300px'; + postCompStyle = 'height'; + postCompVal = '100px'; + element = $('
'); + element.css(preCompStyle, preCompVal); + document.body.append(element[0]); + _.compile(element); + scope = _.rootScope; + scope.context['styleObj'] = {'margin-top': '44px'}; + scope.apply(); + element.css(postCompStyle, postCompVal); + })); + + afterEach(() { + element.remove(null); + }); + + + it('should not mess up stuff after compilation', () { + element.css('margin', '44px'); + expect(element.css(preCompStyle)).toEqual(preCompVal); + expect(element.css('margin-top')).toEqual('44px'); + expect(element.css(postCompStyle)).toEqual(postCompVal); + }); + + it(r'should not mess up stuff after $apply with no model changes', () { + element.css('padding-top', '33px'); + scope.apply(); + expect(element.css(preCompStyle)).toEqual(preCompVal); + expect(element.css('margin-top')).toEqual('44px'); + expect(element.css(postCompStyle)).toEqual(postCompVal); + expect(element.css('padding-top')).toEqual('33px'); + }); + + + it(r'should not mess up stuff after $apply with non-colliding model changes', () { + scope.context['styleObj'] = {'padding-top': '99px'}; + scope.apply(); + expect(element.css(preCompStyle)).toEqual(preCompVal); + expect(element.css('margin-top')).not.toEqual('44px'); + expect(element.css('padding-top')).toEqual('99px'); + expect(element.css(postCompStyle)).toEqual(postCompVal); + }); + + + it(r'should overwrite original styles after a colliding model change', () { + scope.context['styleObj'] = {'height': '99px', 'width': '88px'}; + scope.apply(); + expect(element.css(preCompStyle)).toEqual('88px'); + expect(element.css(postCompStyle)).toEqual('99px'); + scope.context['styleObj'] = {}; + scope.apply(); + expect(element.css(preCompStyle)).not.toEqual('88px'); + expect(element.css(postCompStyle)).not.toEqual('99px'); + }); }); }); -}); +} diff --git a/test/directive/ng_switch_spec.dart b/test/directive/ng_switch_spec.dart index 55f0c70ff..6d0f961f2 100644 --- a/test/directive/ng_switch_spec.dart +++ b/test/directive/ng_switch_spec.dart @@ -2,202 +2,204 @@ library ng_switch_spec; import '../_specs.dart'; -main() => describe('ngSwitch', () { - TestBed _; - - beforeEach(inject((TestBed tb) => _ = tb)); - - it('should switch on value change', inject(() { - var element = _.compile( - '
' + - '
first:{{name}}
' + - '
second:{{name}}
' + - '
true:{{name}}
' + - '
'); - expect(element.innerHtml).toEqual( - ''); - _.rootScope.context['select'] = 1; - _.rootScope.apply(); - expect(element.text).toEqual('first:'); - _.rootScope.context['name'] = "shyam"; - _.rootScope.apply(); - expect(element.text).toEqual('first:shyam'); - _.rootScope.context['select'] = 2; - _.rootScope.apply(); - expect(element.text).toEqual('second:shyam'); - _.rootScope.context['name'] = 'misko'; - _.rootScope.apply(); - expect(element.text).toEqual('second:misko'); - _.rootScope.context['select'] = true; - _.rootScope.apply(); - expect(element.text).toEqual('true:misko'); - })); - - - it('should show all switch-whens that match the current value', inject(() { - var element = _.compile( - '
    ' + - '
  • first:{{name}}
  • ' + - '
  • , first too:{{name}}
  • ' + - '
  • second:{{name}}
  • ' + - '
  • true:{{name}}
  • ' + - '
'); - expect(element.innerHtml).toEqual('' - '' - '' - ''); - _.rootScope.context['select'] = 1; - _.rootScope.apply(); - expect(element.text).toEqual('first:, first too:'); - _.rootScope.context['name'] = "shyam"; - _.rootScope.apply(); - expect(element.text).toEqual('first:shyam, first too:shyam'); - _.rootScope.context['select'] = 2; - _.rootScope.apply(); - expect(element.text).toEqual('second:shyam'); - _.rootScope.context['name'] = 'misko'; - _.rootScope.apply(); - expect(element.text).toEqual('second:misko'); - _.rootScope.context['select'] = true; - _.rootScope.apply(); - expect(element.text).toEqual('true:misko'); - })); - - - it('should switch on switch-when-default', inject(() { - var element = _.compile( - '
' + - '
one
' + - '
other
' + - '
'); - _.rootScope.apply(); - expect(element.text).toEqual('other'); - _.rootScope.context['select'] = 1; - _.rootScope.apply(); - expect(element.text).toEqual('one'); - })); - - - it('should show all switch-when-default', inject(() { - var element = _.compile( - '
    ' + - '
  • one
  • ' + - '
  • other
  • ' + - '
  • , other too
  • ' + - '
'); - _.rootScope.apply(); - expect(element.text).toEqual('other, other too'); - _.rootScope.context['select'] = 1; - _.rootScope.apply(); - expect(element.text).toEqual('one'); - })); - - - it('should always display the elements that do not match a switch', - inject(() { - var element = _.compile( - '
    ' + - '
  • always
  • ' + - '
  • one
  • ' + - '
  • two
  • ' + - '
  • other,
  • ' + - '
  • other too
  • ' + - '
'); - _.rootScope.apply(); - expect(element.text).toEqual('always other, other too '); - _.rootScope.context['select'] = 1; - _.rootScope.apply(); - expect(element.text).toEqual('always one '); - })); - - - it('should display the elements that do not have ngSwitchWhen nor ' + - 'ngSwitchDefault at the position specified in the template, when the ' + - 'first and last elements in the ngSwitch body do not have a ngSwitch* ' + - 'directive', inject(() { - var element = _.compile( - '
    ' + - '
  • 1
  • ' + - '
  • 2
  • ' + - '
  • 3
  • ' + - '
  • 4
  • ' + - '
  • 5
  • ' + - '
  • 6
  • ' + - '
  • 7
  • ' + - '
  • 8
  • ' + - '
'); - _.rootScope.apply(); - expect(element.text).toEqual('135678'); - _.rootScope.context['select'] = 1; - _.rootScope.apply(); - expect(element.text).toEqual('12368'); - })); - - - it('should display the elements that do not have ngSwitchWhen nor ' + - 'ngSwitchDefault at the position specified in the template when the ' + - 'first and last elements in the ngSwitch have a ngSwitch* directive', - inject(() { - var element = _.compile( - '
    ' + - '
  • 2
  • ' + - '
  • 3
  • ' + - '
  • 4
  • ' + - '
  • 5
  • ' + - '
  • 6
  • ' + - '
  • 7
  • ' + - '
'); - _.rootScope.apply(); - expect(element.text).toEqual('3567'); - _.rootScope.context['select'] = 1; - _.rootScope.apply(); - expect(element.text).toEqual('236'); - })); - - - it('should call change on switch', inject(() { - var element = _.compile( - '
' + - '
{{name}}
' + - '
'); - _.rootScope.context['url'] = 'a'; - _.rootScope.apply(); - expect(_.rootScope.context['name']).toEqual('works'); - expect(element.text).toEqual('works'); - })); - - - it('should properly create and destroy child scopes', inject(() { - var element = _.compile( - '
' + - '
{{name}}
' + - '
'); - _.rootScope.apply(); - - var getChildScope = () => _.rootScope.context['probe'] == null ? - null : _.rootScope.context['probe'].scope; - - expect(getChildScope()).toBeNull(); - - _.rootScope.context['url'] = 'a'; - _.rootScope.context['name'] = 'works'; - _.rootScope.apply(); - var child1 = getChildScope(); - expect(child1).toBeNotNull(); - expect(element.text).toEqual('works'); - var destroyListener = jasmine.createSpy('watch listener'); - var watcher = child1.on(ScopeEvent.DESTROY).listen(destroyListener); - - _.rootScope.context['url'] = 'x'; - _.rootScope.apply(); - expect(getChildScope()).toBeNull(); - expect(destroyListener).toHaveBeenCalledOnce(); - watcher.cancel(); - - _.rootScope.context['url'] = 'a'; - _.rootScope.apply(); - var child2 = getChildScope(); - expect(child2).toBeDefined(); - expect(child2).not.toBe(child1); - })); -}); +void main() { + describe('ngSwitch', () { + TestBed _; + + beforeEach(inject((TestBed tb) => _ = tb)); + + it('should switch on value change', inject(() { + var element = _.compile( + '
' + + '
first:{{name}}
' + + '
second:{{name}}
' + + '
true:{{name}}
' + + '
'); + expect(element.innerHtml).toEqual( + ''); + _.rootScope.context['select'] = 1; + _.rootScope.apply(); + expect(element.text).toEqual('first:'); + _.rootScope.context['name'] = "shyam"; + _.rootScope.apply(); + expect(element.text).toEqual('first:shyam'); + _.rootScope.context['select'] = 2; + _.rootScope.apply(); + expect(element.text).toEqual('second:shyam'); + _.rootScope.context['name'] = 'misko'; + _.rootScope.apply(); + expect(element.text).toEqual('second:misko'); + _.rootScope.context['select'] = true; + _.rootScope.apply(); + expect(element.text).toEqual('true:misko'); + })); + + + it('should show all switch-whens that match the current value', inject(() { + var element = _.compile( + '
    ' + + '
  • first:{{name}}
  • ' + + '
  • , first too:{{name}}
  • ' + + '
  • second:{{name}}
  • ' + + '
  • true:{{name}}
  • ' + + '
'); + expect(element.innerHtml).toEqual('' + '' + '' + ''); + _.rootScope.context['select'] = 1; + _.rootScope.apply(); + expect(element.text).toEqual('first:, first too:'); + _.rootScope.context['name'] = "shyam"; + _.rootScope.apply(); + expect(element.text).toEqual('first:shyam, first too:shyam'); + _.rootScope.context['select'] = 2; + _.rootScope.apply(); + expect(element.text).toEqual('second:shyam'); + _.rootScope.context['name'] = 'misko'; + _.rootScope.apply(); + expect(element.text).toEqual('second:misko'); + _.rootScope.context['select'] = true; + _.rootScope.apply(); + expect(element.text).toEqual('true:misko'); + })); + + + it('should switch on switch-when-default', inject(() { + var element = _.compile( + '
' + + '
one
' + + '
other
' + + '
'); + _.rootScope.apply(); + expect(element.text).toEqual('other'); + _.rootScope.context['select'] = 1; + _.rootScope.apply(); + expect(element.text).toEqual('one'); + })); + + + it('should show all switch-when-default', inject(() { + var element = _.compile( + '
    ' + + '
  • one
  • ' + + '
  • other
  • ' + + '
  • , other too
  • ' + + '
'); + _.rootScope.apply(); + expect(element.text).toEqual('other, other too'); + _.rootScope.context['select'] = 1; + _.rootScope.apply(); + expect(element.text).toEqual('one'); + })); + + + it('should always display the elements that do not match a switch', + inject(() { + var element = _.compile( + '
    ' + + '
  • always
  • ' + + '
  • one
  • ' + + '
  • two
  • ' + + '
  • other,
  • ' + + '
  • other too
  • ' + + '
'); + _.rootScope.apply(); + expect(element.text).toEqual('always other, other too '); + _.rootScope.context['select'] = 1; + _.rootScope.apply(); + expect(element.text).toEqual('always one '); + })); + + + it('should display the elements that do not have ngSwitchWhen nor ' + + 'ngSwitchDefault at the position specified in the template, when the ' + + 'first and last elements in the ngSwitch body do not have a ngSwitch* ' + + 'directive', inject(() { + var element = _.compile( + '
    ' + + '
  • 1
  • ' + + '
  • 2
  • ' + + '
  • 3
  • ' + + '
  • 4
  • ' + + '
  • 5
  • ' + + '
  • 6
  • ' + + '
  • 7
  • ' + + '
  • 8
  • ' + + '
'); + _.rootScope.apply(); + expect(element.text).toEqual('135678'); + _.rootScope.context['select'] = 1; + _.rootScope.apply(); + expect(element.text).toEqual('12368'); + })); + + + it('should display the elements that do not have ngSwitchWhen nor ' + + 'ngSwitchDefault at the position specified in the template when the ' + + 'first and last elements in the ngSwitch have a ngSwitch* directive', + inject(() { + var element = _.compile( + '
    ' + + '
  • 2
  • ' + + '
  • 3
  • ' + + '
  • 4
  • ' + + '
  • 5
  • ' + + '
  • 6
  • ' + + '
  • 7
  • ' + + '
'); + _.rootScope.apply(); + expect(element.text).toEqual('3567'); + _.rootScope.context['select'] = 1; + _.rootScope.apply(); + expect(element.text).toEqual('236'); + })); + + + it('should call change on switch', inject(() { + var element = _.compile( + '
' + + '
{{name}}
' + + '
'); + _.rootScope.context['url'] = 'a'; + _.rootScope.apply(); + expect(_.rootScope.context['name']).toEqual('works'); + expect(element.text).toEqual('works'); + })); + + + it('should properly create and destroy child scopes', inject(() { + var element = _.compile( + '
' + + '
{{name}}
' + + '
'); + _.rootScope.apply(); + + var getChildScope = () => _.rootScope.context['probe'] == null ? + null : _.rootScope.context['probe'].scope; + + expect(getChildScope()).toBeNull(); + + _.rootScope.context['url'] = 'a'; + _.rootScope.context['name'] = 'works'; + _.rootScope.apply(); + var child1 = getChildScope(); + expect(child1).toBeNotNull(); + expect(element.text).toEqual('works'); + var destroyListener = jasmine.createSpy('watch listener'); + var watcher = child1.on(ScopeEvent.DESTROY).listen(destroyListener); + + _.rootScope.context['url'] = 'x'; + _.rootScope.apply(); + expect(getChildScope()).toBeNull(); + expect(destroyListener).toHaveBeenCalledOnce(); + watcher.cancel(); + + _.rootScope.context['url'] = 'a'; + _.rootScope.apply(); + var child2 = getChildScope(); + expect(child2).toBeDefined(); + expect(child2).not.toBe(child1); + })); + }); +} diff --git a/test/filter/currency_spec.dart b/test/filter/currency_spec.dart index 12219233b..adc534971 100644 --- a/test/filter/currency_spec.dart +++ b/test/filter/currency_spec.dart @@ -2,29 +2,31 @@ library curerncy_spec; import '../_specs.dart'; -main() => describe('number', () { - var currency; +void main() { + describe('number', () { + var currency; - beforeEach(inject((FilterMap map, Injector injector) { - currency = injector.get(map[new NgFilter(name: 'currency')]); - })); + beforeEach(inject((FilterMap map, Injector injector) { + currency = injector.get(map[new NgFilter(name: 'currency')]); + })); - it('should do basic currency filtering', () { - expect(currency(0)).toEqual(r'$0.00'); - expect(currency(-999)).toEqual(r'($999.00)'); - expect(currency(1234.5678, r"USD$")).toEqual(r'USD$1,234.57'); - }); + it('should do basic currency filtering', () { + expect(currency(0)).toEqual(r'$0.00'); + expect(currency(-999)).toEqual(r'($999.00)'); + expect(currency(1234.5678, r"USD$")).toEqual(r'USD$1,234.57'); + }); - it('should return empty string for non-numbers', () { - expect(currency(null)).toEqual(null); - }); + it('should return empty string for non-numbers', () { + expect(currency(null)).toEqual(null); + }); - it('should handle zero and nearly-zero values properly', () { - // This expression is known to yield 4.440892098500626e-16 instead of 0.0. - expect(currency(1.07 + 1 - 2.07)).toEqual(r'$0.00'); - expect(currency(0.008)).toEqual(r'$0.01'); - expect(currency(0.003)).toEqual(r'$0.00'); + it('should handle zero and nearly-zero values properly', () { + // This expression is known to yield 4.440892098500626e-16 instead of 0.0. + expect(currency(1.07 + 1 - 2.07)).toEqual(r'$0.00'); + expect(currency(0.008)).toEqual(r'$0.01'); + expect(currency(0.003)).toEqual(r'$0.00'); + }); }); -}); +} diff --git a/test/filter/date_spec.dart b/test/filter/date_spec.dart index 2cb48e19d..9eacebbc9 100644 --- a/test/filter/date_spec.dart +++ b/test/filter/date_spec.dart @@ -2,65 +2,67 @@ library date_spec; import '../_specs.dart'; -main() => describe('date', () { - var morning = DateTime.parse('2010-09-03T07:05:08.008Z'); //7am - var noon = DateTime.parse('2010-09-03T12:05:08.012Z'); //12pm - var midnight = DateTime.parse('2010-09-03T12:05:08.123Z'); //12am - var earlyDate = DateTime.parse('0001-09-03T05:05:08.000Z'); - - var date; - - beforeEach(inject((FilterMap map, Injector injector) { - date = injector.get(map[new NgFilter(name: 'date')]); - })); - - it('should ignore falsy inputs', () { - expect(date(null)).toBeNull(); - expect(date('')).toEqual(''); - }); - - it('should do basic filter', () { - expect(date(noon)).toEqual(date(noon, 'mediumDate')); - }); - - it('should accept various format strings', () { - expect(date(morning, "yy-MM-dd HH:mm:ss")). +void main() { + describe('date', () { + var morning = DateTime.parse('2010-09-03T07:05:08.008Z'); //7am + var noon = DateTime.parse('2010-09-03T12:05:08.012Z'); //12pm + var midnight = DateTime.parse('2010-09-03T12:05:08.123Z'); //12am + var earlyDate = DateTime.parse('0001-09-03T05:05:08.000Z'); + + var date; + + beforeEach(inject((FilterMap map, Injector injector) { + date = injector.get(map[new NgFilter(name: 'date')]); + })); + + it('should ignore falsy inputs', () { + expect(date(null)).toBeNull(); + expect(date('')).toEqual(''); + }); + + it('should do basic filter', () { + expect(date(noon)).toEqual(date(noon, 'mediumDate')); + }); + + it('should accept various format strings', () { + expect(date(morning, "yy-MM-dd HH:mm:ss")). toEqual('10-09-03 07:05:08'); - expect(date(morning, "yy-MM-dd HH:mm:ss.sss")). + expect(date(morning, "yy-MM-dd HH:mm:ss.sss")). toEqual('10-09-03 07:05:08.008'); - }); + }); - it('should accept default formats', () { + it('should accept default formats', () { - expect(date(noon, "medium")). - toEqual('Sep 3, 2010 12:05:08 PM'); + expect(date(noon, "medium")). + toEqual('Sep 3, 2010 12:05:08 PM'); - expect(date(noon, "short")). - toEqual('9/3/10 12:05 PM'); + expect(date(noon, "short")). + toEqual('9/3/10 12:05 PM'); - expect(date(noon, "fullDate")). - toEqual('Friday, September 3, 2010'); + expect(date(noon, "fullDate")). + toEqual('Friday, September 3, 2010'); - expect(date(noon, "longDate")). - toEqual('September 3, 2010'); + expect(date(noon, "longDate")). + toEqual('September 3, 2010'); - expect(date(noon, "mediumDate")). - toEqual('Sep 3, 2010'); + expect(date(noon, "mediumDate")). + toEqual('Sep 3, 2010'); - expect(date(noon, "shortDate")). - toEqual('9/3/10'); + expect(date(noon, "shortDate")). + toEqual('9/3/10'); - expect(date(noon, "mediumTime")). - toEqual('12:05:08 PM'); + expect(date(noon, "mediumTime")). + toEqual('12:05:08 PM'); - expect(date(noon, "shortTime")). - toEqual('12:05 PM'); - }); + expect(date(noon, "shortTime")). + toEqual('12:05 PM'); + }); - it('should use cache without any error', () { + it('should use cache without any error', () { - date(noon, "shortTime"); - date(noon, "shortTime"); + date(noon, "shortTime"); + date(noon, "shortTime"); + }); }); -}); +} diff --git a/test/filter/json_spec.dart b/test/filter/json_spec.dart index 15ae9e42b..cd07291a0 100644 --- a/test/filter/json_spec.dart +++ b/test/filter/json_spec.dart @@ -2,9 +2,11 @@ library json_spec; import '../_specs.dart'; -main() => describe('json', () { - it('should convert primitives, array, map to json', inject((Scope scope, Parser parser, FilterMap filters) { - scope.context['foo'] = [{"string":'foo', "number": 123, "bool": false}]; - expect(parser('foo | json').eval(scope.context, filters)).toEqual('[{"string":"foo","number":123,"bool":false}]'); - })); -}); +void main() { + describe('json', () { + it('should convert primitives, array, map to json', inject((Scope scope, Parser parser, FilterMap filters) { + scope.context['foo'] = [{"string":'foo', "number": 123, "bool": false}]; + expect(parser('foo | json').eval(scope.context, filters)).toEqual('[{"string":"foo","number":123,"bool":false}]'); + })); + }); +} diff --git a/test/filter/lowercase_spec.dart b/test/filter/lowercase_spec.dart index 575deb7ce..50294cba5 100644 --- a/test/filter/lowercase_spec.dart +++ b/test/filter/lowercase_spec.dart @@ -2,9 +2,11 @@ library lowercase_spec; import '../_specs.dart'; -main() => describe('lowercase', () { - it('should convert string to lowercase', inject((Parser parse, FilterMap filters) { - expect(parse('null | lowercase').eval(null, filters)).toEqual(null); - expect(parse('"FOO" | lowercase').eval(null, filters)).toEqual('foo'); - })); -}); +void main() { + describe('lowercase', () { + it('should convert string to lowercase', inject((Parser parse, FilterMap filters) { + expect(parse('null | lowercase').eval(null, filters)).toEqual(null); + expect(parse('"FOO" | lowercase').eval(null, filters)).toEqual('foo'); + })); + }); +} diff --git a/test/filter/number_spec.dart b/test/filter/number_spec.dart index 8e3d4db92..bbec1e4aa 100644 --- a/test/filter/number_spec.dart +++ b/test/filter/number_spec.dart @@ -2,46 +2,48 @@ library number_spec; import '../_specs.dart'; -main() => describe('number', () { - var number; +void main() { + describe('number', () { + var number; - beforeEach(inject((FilterMap map, Injector injector) { - number = injector.get(map[new NgFilter(name: 'number')]); - })); + beforeEach(inject((FilterMap map, Injector injector) { + number = injector.get(map[new NgFilter(name: 'number')]); + })); - it('should do basic filter', () { - expect(number(0, 0)).toEqual('0'); - expect(number(-999)).toEqual('-999'); - expect(number(123)).toEqual('123'); - expect(number(1234567)).toEqual('1,234,567'); - expect(number(1234)).toEqual('1,234'); - expect(number(1234.5678)).toEqual('1,234.568'); - expect(number(double.NAN)).toEqual(''); - expect(number("1234.5678")).toEqual('1,234.568'); - expect(number(1/0)).toEqual("∞"); - expect(number(1, 2)).toEqual("1.00"); - expect(number(.1, 2)).toEqual("0.10"); - expect(number(.01, 2)).toEqual("0.01"); - expect(number(.001, 3)).toEqual("0.001"); - expect(number(.0001, 3)).toEqual("0.000"); - expect(number(9, 2)).toEqual("9.00"); - expect(number(.9, 2)).toEqual("0.90"); - expect(number(.99, 2)).toEqual("0.99"); - expect(number(.999, 3)).toEqual("0.999"); - expect(number(.9999, 3)).toEqual("1.000"); - expect(number(1234.567, 0)).toEqual("1,235"); - expect(number(1234.567, 1)).toEqual("1,234.6"); - expect(number(1234.567, 2)).toEqual("1,234.57"); - }); + it('should do basic filter', () { + expect(number(0, 0)).toEqual('0'); + expect(number(-999)).toEqual('-999'); + expect(number(123)).toEqual('123'); + expect(number(1234567)).toEqual('1,234,567'); + expect(number(1234)).toEqual('1,234'); + expect(number(1234.5678)).toEqual('1,234.568'); + expect(number(double.NAN)).toEqual(''); + expect(number("1234.5678")).toEqual('1,234.568'); + expect(number(1/0)).toEqual("∞"); + expect(number(1, 2)).toEqual("1.00"); + expect(number(.1, 2)).toEqual("0.10"); + expect(number(.01, 2)).toEqual("0.01"); + expect(number(.001, 3)).toEqual("0.001"); + expect(number(.0001, 3)).toEqual("0.000"); + expect(number(9, 2)).toEqual("9.00"); + expect(number(.9, 2)).toEqual("0.90"); + expect(number(.99, 2)).toEqual("0.99"); + expect(number(.999, 3)).toEqual("0.999"); + expect(number(.9999, 3)).toEqual("1.000"); + expect(number(1234.567, 0)).toEqual("1,235"); + expect(number(1234.567, 1)).toEqual("1,234.6"); + expect(number(1234.567, 2)).toEqual("1,234.57"); + }); - it('should filter exponentially small numbers', () { - expect(number(1e-50, 0)).toEqual('0'); - expect(number(1e-6, 6)).toEqual('0.000001'); - expect(number(1e-7, 6)).toEqual('0.000000'); + it('should filter exponentially small numbers', () { + expect(number(1e-50, 0)).toEqual('0'); + expect(number(1e-6, 6)).toEqual('0.000001'); + expect(number(1e-7, 6)).toEqual('0.000000'); - expect(number(-1e-50, 0)).toEqual('-0'); - expect(number(-1e-6, 6)).toEqual('-0.000001'); - expect(number(-1e-7, 6)).toEqual('-0.000000'); + expect(number(-1e-50, 0)).toEqual('-0'); + expect(number(-1e-6, 6)).toEqual('-0.000001'); + expect(number(-1e-7, 6)).toEqual('-0.000000'); + }); }); -}); +} diff --git a/test/filter/uppercase_spec.dart b/test/filter/uppercase_spec.dart index 803b35605..3c1b77fba 100644 --- a/test/filter/uppercase_spec.dart +++ b/test/filter/uppercase_spec.dart @@ -2,9 +2,11 @@ library uppercase_spec; import '../_specs.dart'; -main() => describe('uppercase', () { - it('should convert string to uppercase', inject((Parser parse, FilterMap filters) { - expect(parse('null | uppercase').eval(null, filters)).toEqual(null); - expect(parse('"foo" | uppercase').eval(null, filters)).toEqual('FOO'); - })); -}); +void main() { + describe('uppercase', () { + it('should convert string to uppercase', inject((Parser parse, FilterMap filters) { + expect(parse('null | uppercase').eval(null, filters)).toEqual(null); + expect(parse('"foo" | uppercase').eval(null, filters)).toEqual('FOO'); + })); + }); +} diff --git a/test/introspection_spec.dart b/test/introspection_spec.dart index df8892b14..a91f8857a 100644 --- a/test/introspection_spec.dart +++ b/test/introspection_spec.dart @@ -2,36 +2,38 @@ library introspection_spec; import '_specs.dart'; -main() => describe('introspection', () { - it('should retrieve ElementProbe', inject((TestBed _) { - _.compile('
'); - ElementProbe probe = ngProbe(_.rootElement); - expect(probe.injector.parent).toBe(_.injector); - expect(ngInjector(_.rootElement).parent).toBe(_.injector); - expect(probe.directives[0] is NgBindDirective).toBe(true); - expect(ngDirectives(_.rootElement)[0] is NgBindDirective).toBe(true); - expect(probe.scope).toBe(_.rootScope); - expect(ngScope(_.rootElement)).toBe(_.rootScope); - })); +void main() { + describe('introspection', () { + it('should retrieve ElementProbe', inject((TestBed _) { + _.compile('
'); + ElementProbe probe = ngProbe(_.rootElement); + expect(probe.injector.parent).toBe(_.injector); + expect(ngInjector(_.rootElement).parent).toBe(_.injector); + expect(probe.directives[0] is NgBindDirective).toBe(true); + expect(ngDirectives(_.rootElement)[0] is NgBindDirective).toBe(true); + expect(probe.scope).toBe(_.rootScope); + expect(ngScope(_.rootElement)).toBe(_.rootScope); + })); - toHtml(List list) => list.map((e) => e.outerHtml).join(''); + toHtml(List list) => list.map((e) => e.outerHtml).join(''); - it('should select elements using CSS selector', () { - var div = new Element.html('

'); - var span = div.querySelector('span'); - var shadowRoot = span.createShadowRoot(); - shadowRoot.innerHtml = '
  • stash
  • secret
    • '; + it('should select elements using CSS selector', () { + var div = new Element.html('

      '); + var span = div.querySelector('span'); + var shadowRoot = span.createShadowRoot(); + shadowRoot.innerHtml = '
      • stash
      • secret
        • '; - expect(toHtml(ngQuery(div, 'li'))).toEqual('
        • stash
        • secret
        • '); - expect(toHtml(ngQuery(div, 'li', 'stash'))).toEqual('
        • stash
        • '); - expect(toHtml(ngQuery(div, 'li', 'secret'))).toEqual('
        • secret
        • '); - expect(toHtml(ngQuery(div, 'li', 'xxx'))).toEqual(''); - }); + expect(toHtml(ngQuery(div, 'li'))).toEqual('
        • stash
        • secret
        • '); + expect(toHtml(ngQuery(div, 'li', 'stash'))).toEqual('
        • stash
        • '); + expect(toHtml(ngQuery(div, 'li', 'secret'))).toEqual('
        • secret
        • '); + expect(toHtml(ngQuery(div, 'li', 'xxx'))).toEqual(''); + }); - it('should select elements in the root shadow root', () { - var div = new Element.html('
          '); - var shadowRoot = div.createShadowRoot(); - shadowRoot.innerHtml = '
          • stash
          • secret
            • '; - expect(toHtml(ngQuery(div, 'li'))).toEqual('
            • stash
            • secret
            • '); + it('should select elements in the root shadow root', () { + var div = new Element.html('
              '); + var shadowRoot = div.createShadowRoot(); + shadowRoot.innerHtml = '
              • stash
              • secret
                • '; + expect(toHtml(ngQuery(div, 'li'))).toEqual('
                • stash
                • secret
                • '); + }); }); -}); +} \ No newline at end of file diff --git a/test/io/expression_extractor_spec.dart b/test/io/expression_extractor_spec.dart index 5ecc402d6..16b13f5e3 100644 --- a/test/io/expression_extractor_spec.dart +++ b/test/io/expression_extractor_spec.dart @@ -11,37 +11,39 @@ import 'package:angular/tools/source_metadata_extractor.dart'; import '../jasmine_syntax.dart'; import 'package:unittest/unittest.dart'; -main() => describe('expression_extractor', () { - it('should extract all expressions from source and templates', () { - Module module = new Module(); +void main() { + describe('expression_extractor', () { + it('should extract all expressions from source and templates', () { + Module module = new Module(); - Injector injector = new DynamicInjector(modules: [module], - allowImplicitInjection: true); + Injector injector = new DynamicInjector(modules: [module], + allowImplicitInjection: true); - IoService ioService = new IoServiceImpl(); - var sourceCrawler = new SourceCrawlerImpl(['packages/']); - var sourceMetadataExtractor = new SourceMetadataExtractor(); - List directives = - sourceMetadataExtractor - .gatherDirectiveInfo('test/io/test_files/main.dart', sourceCrawler); - var htmlExtractor = new HtmlExpressionExtractor(directives); - htmlExtractor.crawl('test/io/test_files/', ioService); + IoService ioService = new IoServiceImpl(); + var sourceCrawler = new SourceCrawlerImpl(['packages/']); + var sourceMetadataExtractor = new SourceMetadataExtractor(); + List directives = + sourceMetadataExtractor + .gatherDirectiveInfo('test/io/test_files/main.dart', sourceCrawler); + var htmlExtractor = new HtmlExpressionExtractor(directives); + htmlExtractor.crawl('test/io/test_files/', ioService); - var expressions = htmlExtractor.expressions; - expect(expressions, unorderedEquals([ - 'ctrl.expr', - 'ctrl.anotherExpression', - 'ctrl.callback', - 'ctrl.twoWayStuff', - 'attr', - 'expr', - 'anotherExpression', - 'callback', - 'twoWayStuff', - 'exported + expression', - 'ctrl.inline.template.expression', - 'ngIfCondition', - 'ctrl.if' - ])); + var expressions = htmlExtractor.expressions; + expect(expressions, unorderedEquals([ + 'ctrl.expr', + 'ctrl.anotherExpression', + 'ctrl.callback', + 'ctrl.twoWayStuff', + 'attr', + 'expr', + 'anotherExpression', + 'callback', + 'twoWayStuff', + 'exported + expression', + 'ctrl.inline.template.expression', + 'ngIfCondition', + 'ctrl.if' + ])); + }); }); -}); +} diff --git a/test/io/source_metadata_extractor_spec.dart b/test/io/source_metadata_extractor_spec.dart index af9f2655e..1bc511bea 100644 --- a/test/io/source_metadata_extractor_spec.dart +++ b/test/io/source_metadata_extractor_spec.dart @@ -6,21 +6,23 @@ import 'package:angular/tools/source_metadata_extractor.dart'; import '../jasmine_syntax.dart'; import 'package:unittest/unittest.dart'; -main() => describe('source_metadata_extractor', () { - it('should extract all attribute mappings including annotations', () { - var sourceCrawler = new SourceCrawlerImpl(['packages/']); - var sourceMetadataExtractor = new SourceMetadataExtractor(); - List directives = - sourceMetadataExtractor - .gatherDirectiveInfo('test/io/test_files/main.dart', sourceCrawler); +void main() { + describe('source_metadata_extractor', () { + it('should extract all attribute mappings including annotations', () { + var sourceCrawler = new SourceCrawlerImpl(['packages/']); + var sourceMetadataExtractor = new SourceMetadataExtractor(); + List directives = + sourceMetadataExtractor + .gatherDirectiveInfo('test/io/test_files/main.dart', sourceCrawler); - expect(directives, hasLength(2)); + expect(directives, hasLength(2)); - DirectiveInfo info = directives.elementAt(1); - expect(info.expressionAttrs, unorderedEquals(['expr', 'another-expression', - 'callback', 'two-way-stuff', 'exported-attr'])); - expect(info.expressions, unorderedEquals(['attr', 'expr', - 'anotherExpression', 'callback', 'twoWayStuff', - 'exported + expression'])); + DirectiveInfo info = directives.elementAt(1); + expect(info.expressionAttrs, unorderedEquals(['expr', 'another-expression', + 'callback', 'two-way-stuff', 'exported-attr'])); + expect(info.expressions, unorderedEquals(['attr', 'expr', + 'anotherExpression', 'callback', 'twoWayStuff', + 'exported + expression'])); + }); }); -}); +} diff --git a/test/io/template_cache_generator_spec.dart b/test/io/template_cache_generator_spec.dart index 590f5aca9..4440b0736 100644 --- a/test/io/template_cache_generator_spec.dart +++ b/test/io/template_cache_generator_spec.dart @@ -7,65 +7,67 @@ import 'package:angular/tools/template_cache_generator.dart' as generator; import '../jasmine_syntax.dart'; import 'package:unittest/unittest.dart'; -main() => describe('template_cache_generator', () { +void main() { + describe('template_cache_generator', () { - it('should correctly generate the templates cache file (template)', () { - var tmpDir = Directory.systemTemp.createTempSync(); - Future flush; - try { - flush = generator.main(['test/io/test_files/templates/main.dart', - Platform.environment['DART_SDK'], - '${tmpDir.path}/generated.dart', 'generated', - '%SYSTEM_PACKAGE_ROOT%', - '/test/io/test_files,rewritten', 'MyComponent3']); - } catch(_) { - tmpDir.deleteSync(recursive: true); - rethrow; - } - return flush.then((_) { - expect(new File('${tmpDir.path}/generated.dart').readAsStringSync(), - '// GENERATED, DO NOT EDIT!\n' - 'library generated;\n' - '\n' - 'import \'package:angular/angular.dart\';\n' - '\n' - 'primeTemplateCache(TemplateCache tc) {\n' - 'tc.put("rewritten/templates/main.html", new HttpResponse(200, r"""Hello World!"""));\n' - '}'); - }).whenComplete(() { - tmpDir.deleteSync(recursive: true); + it('should correctly generate the templates cache file (template)', () { + var tmpDir = Directory.systemTemp.createTempSync(); + Future flush; + try { + flush = generator.main(['test/io/test_files/templates/main.dart', + Platform.environment['DART_SDK'], + '${tmpDir.path}/generated.dart', 'generated', + '%SYSTEM_PACKAGE_ROOT%', + '/test/io/test_files,rewritten', 'MyComponent3']); + } catch(_) { + tmpDir.deleteSync(recursive: true); + rethrow; + } + return flush.then((_) { + expect(new File('${tmpDir.path}/generated.dart').readAsStringSync(), + '// GENERATED, DO NOT EDIT!\n' + 'library generated;\n' + '\n' + 'import \'package:angular/angular.dart\';\n' + '\n' + 'primeTemplateCache(TemplateCache tc) {\n' + 'tc.put("rewritten/templates/main.html", new HttpResponse(200, r"""Hello World!"""));\n' + '}'); + }).whenComplete(() { + tmpDir.deleteSync(recursive: true); + }); }); - }); - it('should correctly generate the templates cache file (css)', () { - var tmpDir = Directory.systemTemp.createTempSync(); - Future flush; - try { - flush = generator.main(['test/io/test_files/cssUrls/main.dart', - Platform.environment['DART_SDK'], - '${tmpDir.path}/generated.dart', 'generated', - '%SYSTEM_PACKAGE_ROOT%', - '/test/io/test_files,rewritten', 'MyComponent3']); - } catch(_) { - tmpDir.deleteSync(recursive: true); - rethrow; - } - return flush.then((_) { - expect(new File('${tmpDir.path}/generated.dart').readAsStringSync(), - '// GENERATED, DO NOT EDIT!\n' - 'library generated;\n' - '\n' - 'import \'package:angular/angular.dart\';\n' - '\n' - 'primeTemplateCache(TemplateCache tc) {\n' - 'tc.put("rewritten/cssUrls/one.css", new HttpResponse(200, r"""body {}"""));\n' - 'tc.put("rewritten/cssUrls/three.css", new HttpResponse(200, r"""body {}"""));\n' - 'tc.put("rewritten/cssUrls/two.css", new HttpResponse(200, r"""body {}"""));\n' - '}'); - }).whenComplete(() { - //tmpDir.deleteSync(recursive: true); + it('should correctly generate the templates cache file (css)', () { + var tmpDir = Directory.systemTemp.createTempSync(); + Future flush; + try { + flush = generator.main(['test/io/test_files/cssUrls/main.dart', + Platform.environment['DART_SDK'], + '${tmpDir.path}/generated.dart', 'generated', + '%SYSTEM_PACKAGE_ROOT%', + '/test/io/test_files,rewritten', 'MyComponent3']); + } catch(_) { + tmpDir.deleteSync(recursive: true); + rethrow; + } + return flush.then((_) { + expect(new File('${tmpDir.path}/generated.dart').readAsStringSync(), + '// GENERATED, DO NOT EDIT!\n' + 'library generated;\n' + '\n' + 'import \'package:angular/angular.dart\';\n' + '\n' + 'primeTemplateCache(TemplateCache tc) {\n' + 'tc.put("rewritten/cssUrls/one.css", new HttpResponse(200, r"""body {}"""));\n' + 'tc.put("rewritten/cssUrls/three.css", new HttpResponse(200, r"""body {}"""));\n' + 'tc.put("rewritten/cssUrls/two.css", new HttpResponse(200, r"""body {}"""));\n' + '}'); + }).whenComplete(() { + tmpDir.deleteSync(recursive: true); + }); }); - }); -}); + }); +} diff --git a/test/mock/http_backend_spec.dart b/test/mock/http_backend_spec.dart index 65ff89b19..6d37baf1c 100644 --- a/test/mock/http_backend_spec.dart +++ b/test/mock/http_backend_spec.dart @@ -11,468 +11,469 @@ class _Chain { then(x) => _thenFn(x); } -main() => describe('MockHttpBackend', () { - TestBed _; - beforeEach(inject((TestBed tb) => _ = tb)); +void main() { + describe('MockHttpBackend', () { + TestBed _; + beforeEach(inject((TestBed tb) => _ = tb)); - var hb, callback, realBackendSpy; + var hb, callback, realBackendSpy; - var noop = (_, __) {}; - var undefined = null; + var noop = (_, __) {}; + var undefined = null; - beforeEach(inject((HttpBackend httpBackend) { - callback = jasmine.createSpy('callback'); - hb = httpBackend; - })); + beforeEach(inject((HttpBackend httpBackend) { + callback = jasmine.createSpy('callback'); + hb = httpBackend; + })); - it('should respond with first matched definition', () { - hb.when('GET', '/url1').respond(200, 'content', {}); - hb.when('GET', '/url1').respond(201, 'another', {}); - - callback.andCallFake((status, response) { - expect(status).toBe(200); - expect(response).toBe('content'); - }); - - hb('GET', '/url1', null, callback); - expect(callback).not.toHaveBeenCalled(); - hb.flush(); - expect(callback).toHaveBeenCalledOnce(); - }); - - - it('should respond with JSON', inject((Logger logger) { - hb.when('GET', '/url1').respond(200, ['abc'], {}); - hb.when('GET', '/url2').respond(200, {'key': 'value'}, {}); + it('should respond with first matched definition', () { + hb.when('GET', '/url1').respond(200, 'content', {}); + hb.when('GET', '/url1').respond(201, 'another', {}); + callback.andCallFake((status, response) { + expect(status).toBe(200); + expect(response).toBe('content'); + }); - callback.andCallFake((status, response) { - expect(status).toBe(200); - logger(response); + hb('GET', '/url1', null, callback); + expect(callback).not.toHaveBeenCalled(); + hb.flush(); + expect(callback).toHaveBeenCalledOnce(); }); - hb('GET', '/url1', null, callback); - hb('GET', '/url2', null, callback); - hb.flush(); - expect(logger).toEqual(['["abc"]', '{"key":"value"}']); - })); - - - it('should throw error when unexpected request', () { - hb.when('GET', '/url1').respond(200, 'content'); - expect(() { - hb('GET', '/xxx'); - }).toThrow('Unexpected request: GET /xxx\nNo more requests expected'); - }); + it('should respond with JSON', inject((Logger logger) { + hb.when('GET', '/url1').respond(200, ['abc'], {}); + hb.when('GET', '/url2').respond(200, {'key': 'value'}, {}); - it('should throw an error on an exception in then', async(() { - hb.expectGET('/url').respond(200, 'content'); - expect(() { - hb.request('/url', method: 'GET').then((x) { - throw ["exceptiona"]; + callback.andCallFake((status, response) { + expect(status).toBe(200); + logger(response); }); - hb.flush(); - microLeap(); - }).toThrow('exceptiona'); - })); + hb('GET', '/url1', null, callback); + hb('GET', '/url2', null, callback); + hb.flush(); + expect(logger).toEqual(['["abc"]', '{"key":"value"}']); + })); - it('should match headers if specified', () { - try { - hb.when('GET', '/url', null, {'X': 'val1'}).respond(201, 'content1'); - hb.when('GET', '/url', null, {'X': 'val2'}).respond(202, 'content2'); - hb.when('GET', '/url').respond(203, 'content3'); - hb('GET', '/url', null, (status, response) { - expect(status).toBe(203); - expect(response).toBe('content3'); + it('should throw error when unexpected request', () { + hb.when('GET', '/url1').respond(200, 'content'); + expect(() { + hb('GET', '/xxx'); + }).toThrow('Unexpected request: GET /xxx\nNo more requests expected'); }); - hb('GET', '/url', null, (status, response) { - expect(status).toBe(201); - expect(response).toBe('content1'); - }, {'X': 'val1'}); - hb('GET', '/url', null, (status, response) { - expect(status).toBe(202); - expect(response).toBe('content2'); - }, {'X': 'val2'}); + it('should throw an error on an exception in then', async(() { + hb.expectGET('/url').respond(200, 'content'); - hb.flush(); - } catch (e,s) { print("$e $s"); } - }); + expect(() { + hb.request('/url', method: 'GET').then((x) { + throw ["exceptiona"]; + }); + hb.flush(); + microLeap(); + }).toThrow('exceptiona'); + })); - it('should match data if specified', () { - hb.when('GET', '/a/b', '{a: true}').respond(201, 'content1'); - hb.when('GET', '/a/b').respond(202, 'content2'); + it('should match headers if specified', () { + try { + hb.when('GET', '/url', null, {'X': 'val1'}).respond(201, 'content1'); + hb.when('GET', '/url', null, {'X': 'val2'}).respond(202, 'content2'); + hb.when('GET', '/url').respond(203, 'content3'); - hb('GET', '/a/b', '{a: true}', (status, response) { - expect(status).toBe(201); - expect(response).toBe('content1'); - }); + hb('GET', '/url', null, (status, response) { + expect(status).toBe(203); + expect(response).toBe('content3'); + }); - hb('GET', '/a/b', '{}', (status, response) { - expect(status).toBe(202); - expect(response).toBe('content2'); - }); + hb('GET', '/url', null, (status, response) { + expect(status).toBe(201); + expect(response).toBe('content1'); + }, {'X': 'val1'}); - hb.flush(); - }); + hb('GET', '/url', null, (status, response) { + expect(status).toBe(202); + expect(response).toBe('content2'); + }, {'X': 'val2'}); - - it('should match only method', () { - hb.when('GET').respond(202, 'c'); - callback.andCallFake((status, response) { - expect(status).toBe(202); - expect(response).toBe('c'); + hb.flush(); + } catch (e,s) { print("$e $s"); } }); - hb('GET', '/some', null, callback, {}); - hb('GET', '/another', null, callback, {'X-Fake': 'Header'}); - hb('GET', '/third', 'some-data', callback, {}); - hb.flush(); - - expect(callback).toHaveBeenCalled(); - }); - - - it('should preserve the order of requests', () { - hb.when('GET', '/url1').respond(200, 'first'); - hb.when('GET', '/url2').respond(201, 'second'); - - hb('GET', '/url2', null, callback); - hb('GET', '/url1', null, callback); - - hb.flush(); - - expect(callback.callCount).toBe(2); - expect(callback.argsForCall[0]).toEqual([201, 'second', '']); - expect(callback.argsForCall[1]).toEqual([200, 'first', '']); - }); + it('should match data if specified', () { + hb.when('GET', '/a/b', '{a: true}').respond(201, 'content1'); + hb.when('GET', '/a/b').respond(202, 'content2'); - describe('respond()', () { - it('should take values', () { - hb.expect('GET', '/url1').respond(200, 'first', {'header': 'val'}); - hb('GET', '/url1', null, callback); - hb.flush(); - - expect(callback).toHaveBeenCalledOnceWith(200, 'first', "header: val"); - }); + hb('GET', '/a/b', '{a: true}', (status, response) { + expect(status).toBe(201); + expect(response).toBe('content1'); + }); - it('should take function', () { - hb.expect('GET', '/some').respond((m, u, d, h) { - return [301, m + u + ';' + d + ';a=' + h['a'], {'Connection': 'keep-alive'}]; + hb('GET', '/a/b', '{}', (status, response) { + expect(status).toBe(202); + expect(response).toBe('content2'); }); - hb('GET', '/some', 'data', callback, {'a': 'b'}); hb.flush(); - - expect(callback).toHaveBeenCalledOnceWith(301, 'GET/some;data;a=b', 'Connection: keep-alive'); }); - it('should default status code to 200', () { + + it('should match only method', () { + hb.when('GET').respond(202, 'c'); callback.andCallFake((status, response) { - expect(status).toBe(200); - expect(response).toBe('some-data'); + expect(status).toBe(202); + expect(response).toBe('c'); }); - hb.expect('GET', '/url1').respond('some-data'); - hb.expect('GET', '/url2').respond('some-data', {'X-Header': 'true'}); - hb('GET', '/url1', null, callback); - hb('GET', '/url2', null, callback); + hb('GET', '/some', null, callback, {}); + hb('GET', '/another', null, callback, {'X-Fake': 'Header'}); + hb('GET', '/third', 'some-data', callback, {}); hb.flush(); + expect(callback).toHaveBeenCalled(); - expect(callback.callCount).toBe(2); }); - it('should default response headers to ""', () { - hb.expect('GET', '/url1').respond(200, 'first'); - hb.expect('GET', '/url2').respond('second'); + it('should preserve the order of requests', () { + hb.when('GET', '/url1').respond(200, 'first'); + hb.when('GET', '/url2').respond(201, 'second'); - hb('GET', '/url1', null, callback); hb('GET', '/url2', null, callback); + hb('GET', '/url1', null, callback); hb.flush(); expect(callback.callCount).toBe(2); - expect(callback.argsForCall[0]).toEqual([200, 'first', '']); - expect(callback.argsForCall[1]).toEqual([200, 'second', '']); + expect(callback.argsForCall[0]).toEqual([201, 'second', '']); + expect(callback.argsForCall[1]).toEqual([200, 'first', '']); }); - }); - describe('expect()', () { - it('should require specified order', () { - hb.expect('GET', '/url1').respond(200, ''); - hb.expect('GET', '/url2').respond(200, ''); + describe('respond()', () { + it('should take values', () { + hb.expect('GET', '/url1').respond(200, 'first', {'header': 'val'}); + hb('GET', '/url1', null, callback); + hb.flush(); - expect(() { - hb('GET', '/url2', null, noop, {}); - }).toThrow('Unexpected request: GET /url2\nExpected GET /url1'); - }); + expect(callback).toHaveBeenCalledOnceWith(200, 'first', "header: val"); + }); + it('should take function', () { + hb.expect('GET', '/some').respond((m, u, d, h) { + return [301, m + u + ';' + d + ';a=' + h['a'], {'Connection': 'keep-alive'}]; + }); - it('should have precedence over when()', () { - callback.andCallFake((status, response) { - expect(status).toBe(299); - expect(response).toBe('expect'); + hb('GET', '/some', 'data', callback, {'a': 'b'}); + hb.flush(); + + expect(callback).toHaveBeenCalledOnceWith(301, 'GET/some;data;a=b', 'Connection: keep-alive'); }); - hb.when('GET', '/url').respond(200, 'when'); - hb.expect('GET', '/url').respond(299, 'expect'); + it('should default status code to 200', () { + callback.andCallFake((status, response) { + expect(status).toBe(200); + expect(response).toBe('some-data'); + }); - hb('GET', '/url', null, callback, null); - hb.flush(); - expect(callback).toHaveBeenCalledOnce(); - }); + hb.expect('GET', '/url1').respond('some-data'); + hb.expect('GET', '/url2').respond('some-data', {'X-Header': 'true'}); + hb('GET', '/url1', null, callback); + hb('GET', '/url2', null, callback); + hb.flush(); + expect(callback).toHaveBeenCalled(); + expect(callback.callCount).toBe(2); + }); - it('should throw exception when only headers differs from expectation', () { - hb.when('GET', '/match').respond(200, '', {}); - hb.expect('GET', '/match', null, {'Content-Type': 'application/json'}).respond(200, '', {}); + it('should default response headers to ""', () { + hb.expect('GET', '/url1').respond(200, 'first'); + hb.expect('GET', '/url2').respond('second'); - expect(() { - hb('GET', '/match', null, noop, {}); - }).toThrow('Expected GET /match with different headers\n' + - 'EXPECTED: {"Content-Type":"application/json"}\nGOT: {}'); + hb('GET', '/url1', null, callback); + hb('GET', '/url2', null, callback); + + hb.flush(); + + expect(callback.callCount).toBe(2); + expect(callback.argsForCall[0]).toEqual([200, 'first', '']); + expect(callback.argsForCall[1]).toEqual([200, 'second', '']); + }); }); - it('should throw exception when only data differs from expectation', () { - hb.when('GET', '/match').respond(200, '', {}); - hb.expect('GET', '/match', 'some-data').respond(200, '', {}); + describe('expect()', () { + it('should require specified order', () { + hb.expect('GET', '/url1').respond(200, ''); + hb.expect('GET', '/url2').respond(200, ''); - expect(() { - hb('GET', '/match', 'different', noop, null); - }).toThrow('Expected GET /match with different data\n' + - 'EXPECTED: some-data\nGOT: different'); - }); + expect(() { + hb('GET', '/url2', null, noop, {}); + }).toThrow('Unexpected request: GET /url2\nExpected GET /url1'); + }); - it("should use when's respond() when no expect() respond is defined", () { - callback.andCallFake((status, response) { - expect(status).toBe(201); - expect(response).toBe('data'); + it('should have precedence over when()', () { + callback.andCallFake((status, response) { + expect(status).toBe(299); + expect(response).toBe('expect'); + }); + + hb.when('GET', '/url').respond(200, 'when'); + hb.expect('GET', '/url').respond(299, 'expect'); + + hb('GET', '/url', null, callback, null); + hb.flush(); + expect(callback).toHaveBeenCalledOnce(); }); - hb.when('GET', '/some').respond(201, 'data'); - hb.expect('GET', '/some'); - hb('GET', '/some', null, callback); - hb.flush(); - expect(callback).toHaveBeenCalled(); - expect(() { hb.verifyNoOutstandingExpectation(); }).not.toThrow(); - }); - }); + it('should throw exception when only headers differs from expectation', () { + hb.when('GET', '/match').respond(200, '', {}); + hb.expect('GET', '/match', null, {'Content-Type': 'application/json'}).respond(200, '', {}); + + expect(() { + hb('GET', '/match', null, noop, {}); + }).toThrow('Expected GET /match with different headers\n' + + 'EXPECTED: {"Content-Type":"application/json"}\nGOT: {}'); + }); - describe('flush()', () { - it('flush() should flush requests fired during callbacks', () { - hb.when('GET', '/some').respond(200, ''); - hb.when('GET', '/other').respond(200, ''); - hb('GET', '/some', null, (_, __) { - hb('GET', '/other', null, callback); + it('should throw exception when only data differs from expectation', () { + hb.when('GET', '/match').respond(200, '', {}); + hb.expect('GET', '/match', 'some-data').respond(200, '', {}); + + expect(() { + hb('GET', '/match', 'different', noop, null); + }).toThrow('Expected GET /match with different data\n' + + 'EXPECTED: some-data\nGOT: different'); }); - hb.flush(); - expect(callback).toHaveBeenCalled(); - }); + it("should use when's respond() when no expect() respond is defined", () { + callback.andCallFake((status, response) { + expect(status).toBe(201); + expect(response).toBe('data'); + }); - it('should flush given number of pending requests', () { - hb.when('GET').respond(200, ''); - hb('GET', '/some', null, callback); - hb('GET', '/some', null, callback); - hb('GET', '/some', null, callback); + hb.when('GET', '/some').respond(201, 'data'); + hb.expect('GET', '/some'); + hb('GET', '/some', null, callback); + hb.flush(); - hb.flush(2); - expect(callback).toHaveBeenCalled(); - expect(callback.callCount).toBe(2); + expect(callback).toHaveBeenCalled(); + expect(() { hb.verifyNoOutstandingExpectation(); }).not.toThrow(); + }); }); - it('should throw exception when flushing more requests than pending', () { - hb.when('GET').respond(200, ''); - hb('GET', '/url', null, callback); + describe('flush()', () { + it('flush() should flush requests fired during callbacks', () { + hb.when('GET', '/some').respond(200, ''); + hb.when('GET', '/other').respond(200, ''); + hb('GET', '/some', null, (_, __) { + hb('GET', '/other', null, callback); + }); - expect(() {hb.flush(2);}).toThrow('No more pending request to flush !'); - expect(callback).toHaveBeenCalledOnce(); - }); + hb.flush(); + expect(callback).toHaveBeenCalled(); + }); - it('should throw exception when no request to flush', () { - expect(() {hb.flush();}).toThrow('No pending request to flush !'); + it('should flush given number of pending requests', () { + hb.when('GET').respond(200, ''); + hb('GET', '/some', null, callback); + hb('GET', '/some', null, callback); + hb('GET', '/some', null, callback); - hb.when('GET').respond(200, ''); - hb('GET', '/some', null, callback); - hb.flush(); + hb.flush(2); + expect(callback).toHaveBeenCalled(); + expect(callback.callCount).toBe(2); + }); - expect(() {hb.flush();}).toThrow('No pending request to flush !'); - }); + it('should throw exception when flushing more requests than pending', () { + hb.when('GET').respond(200, ''); + hb('GET', '/url', null, callback); - it('should throw exception if not all expectations satisfied', () { - hb.expect('GET', '/url1').respond(); - hb.expect('GET', '/url2').respond(); + expect(() {hb.flush(2);}).toThrow('No more pending request to flush !'); + expect(callback).toHaveBeenCalledOnce(); + }); - hb('GET', '/url1', null, noop); - expect(() {hb.flush();}).toThrow('Unsatisfied requests: GET /url2'); - }); - }); + + it('should throw exception when no request to flush', () { + expect(() {hb.flush();}).toThrow('No pending request to flush !'); + + hb.when('GET').respond(200, ''); + hb('GET', '/some', null, callback); + hb.flush(); + + expect(() {hb.flush();}).toThrow('No pending request to flush !'); + }); - it('should abort requests when timeout promise resolves', () { - hb.expect('GET', '/url1').respond(200); + it('should throw exception if not all expectations satisfied', () { + hb.expect('GET', '/url1').respond(); + hb.expect('GET', '/url2').respond(); - var canceler, then = jasmine.createSpy('then').andCallFake((fn) { - canceler = fn; + hb('GET', '/url1', null, noop); + expect(() {hb.flush();}).toThrow('Unsatisfied requests: GET /url2'); + }); }); - hb('GET', '/url1', null, callback, null, new _Chain(then: then)); - expect(canceler is Function).toBe(true); - canceler(); // simulate promise resolution + it('should abort requests when timeout promise resolves', () { + hb.expect('GET', '/url1').respond(200); - expect(callback).toHaveBeenCalledWith(-1, null, ''); - hb.verifyNoOutstandingExpectation(); - hb.verifyNoOutstandingRequest(); - }); + var canceler, then = jasmine.createSpy('then').andCallFake((fn) { + canceler = fn; + }); + hb('GET', '/url1', null, callback, null, new _Chain(then: then)); + expect(canceler is Function).toBe(true); - it('should throw an exception if no response defined', () { - hb.when('GET', '/test'); - expect(() { - hb('GET', '/test', null, callback); - }).toThrow('No response defined !'); - }); + canceler(); // simulate promise resolution + + expect(callback).toHaveBeenCalledWith(-1, null, ''); + hb.verifyNoOutstandingExpectation(); + hb.verifyNoOutstandingRequest(); + }); - it('should throw an exception if no response for exception and no definition', () { - hb.expect('GET', '/url'); - expect(() { - hb('GET', '/url', null, callback); - }).toThrow('No response defined !'); - }); + it('should throw an exception if no response defined', () { + hb.when('GET', '/test'); + expect(() { + hb('GET', '/test', null, callback); + }).toThrow('No response defined !'); + }); - it('should respond undefined when JSONP method', () { - hb.when('JSONP', '/url1').respond(200); - hb.expect('JSONP', '/url2').respond(200); + it('should throw an exception if no response for exception and no definition', () { + hb.expect('GET', '/url'); + expect(() { + hb('GET', '/url', null, callback); + }).toThrow('No response defined !'); + }); - expect(hb('JSONP', '/url1')).toBeNull(); - expect(hb('JSONP', '/url2')).toBeNull(); - }); + it('should respond undefined when JSONP method', () { + hb.when('JSONP', '/url1').respond(200); + hb.expect('JSONP', '/url2').respond(200); - describe('verifyExpectations', () { + expect(hb('JSONP', '/url1')).toBeNull(); + expect(hb('JSONP', '/url2')).toBeNull(); + }); + + + describe('verifyExpectations', () { - it('should throw exception if not all expectations were satisfied', () { - hb.expect('POST', '/u1', 'ddd').respond(201, '', {}); - hb.expect('GET', '/u2').respond(200, '', {}); - hb.expect('POST', '/u3').respond(201, '', {}); + it('should throw exception if not all expectations were satisfied', () { + hb.expect('POST', '/u1', 'ddd').respond(201, '', {}); + hb.expect('GET', '/u2').respond(200, '', {}); + hb.expect('POST', '/u3').respond(201, '', {}); - hb('POST', '/u1', 'ddd', noop, {}); + hb('POST', '/u1', 'ddd', noop, {}); - expect(() {hb.verifyNoOutstandingExpectation();}). + expect(() {hb.verifyNoOutstandingExpectation();}). toThrow('Unsatisfied requests: GET /u2, POST /u3'); - }); + }); - it('should do nothing when no expectation', () { - hb.when('DELETE', '/some').respond(200, ''); + it('should do nothing when no expectation', () { + hb.when('DELETE', '/some').respond(200, ''); - expect(() {hb.verifyNoOutstandingExpectation();}).not.toThrow(); - }); + expect(() {hb.verifyNoOutstandingExpectation();}).not.toThrow(); + }); - it('should do nothing when all expectations satisfied', () { - hb.expect('GET', '/u2').respond(200, '', {}); - hb.expect('POST', '/u3').respond(201, '', {}); - hb.when('DELETE', '/some').respond(200, ''); + it('should do nothing when all expectations satisfied', () { + hb.expect('GET', '/u2').respond(200, '', {}); + hb.expect('POST', '/u3').respond(201, '', {}); + hb.when('DELETE', '/some').respond(200, ''); - hb('GET', '/u2'); - hb('POST', '/u3'); + hb('GET', '/u2'); + hb('POST', '/u3'); - expect(() {hb.verifyNoOutstandingExpectation();}).not.toThrow(); + expect(() {hb.verifyNoOutstandingExpectation();}).not.toThrow(); + }); }); - }); - describe('verifyRequests', () { + describe('verifyRequests', () { - it('should throw exception if not all requests were flushed', () { - hb.when('GET').respond(200); - hb('GET', '/some', null, noop, {}); + it('should throw exception if not all requests were flushed', () { + hb.when('GET').respond(200); + hb('GET', '/some', null, noop, {}); - expect(() { - hb.verifyNoOutstandingRequest(); - }).toThrow('Unflushed requests: 1'); + expect(() { + hb.verifyNoOutstandingRequest(); + }).toThrow('Unflushed requests: 1'); + }); }); - }); - describe('resetExpectations', () { + describe('resetExpectations', () { - it('should remove all expectations', () { - hb.expect('GET', '/u2').respond(200, '', {}); - hb.expect('POST', '/u3').respond(201, '', {}); - hb.resetExpectations(); + it('should remove all expectations', () { + hb.expect('GET', '/u2').respond(200, '', {}); + hb.expect('POST', '/u3').respond(201, '', {}); + hb.resetExpectations(); - expect(() {hb.verifyNoOutstandingExpectation();}).not.toThrow(); - }); + expect(() {hb.verifyNoOutstandingExpectation();}).not.toThrow(); + }); - it('should remove all pending responses', () { - var cancelledClb = jasmine.createSpy('cancelled'); + it('should remove all pending responses', () { + var cancelledClb = jasmine.createSpy('cancelled'); - hb.expect('GET', '/url').respond(200, ''); - hb('GET', '/url', null, cancelledClb); - hb.resetExpectations(); + hb.expect('GET', '/url').respond(200, ''); + hb('GET', '/url', null, cancelledClb); + hb.resetExpectations(); - hb.expect('GET', '/url').respond(300, ''); - hb('GET', '/url', null, callback, {}); - hb.flush(); + hb.expect('GET', '/url').respond(300, ''); + hb('GET', '/url', null, callback, {}); + hb.flush(); - expect(callback).toHaveBeenCalledOnce(); - expect(cancelledClb).not.toHaveBeenCalled(); - }); + expect(callback).toHaveBeenCalledOnce(); + expect(cancelledClb).not.toHaveBeenCalled(); + }); - it('should not remove definitions', () { - var cancelledClb = jasmine.createSpy('cancelled'); + it('should not remove definitions', () { + var cancelledClb = jasmine.createSpy('cancelled'); - hb.when('GET', '/url').respond(200, 'success'); - hb('GET', '/url', null, cancelledClb); - hb.resetExpectations(); + hb.when('GET', '/url').respond(200, 'success'); + hb('GET', '/url', null, cancelledClb); + hb.resetExpectations(); - hb('GET', '/url', null, callback, {}); - hb.flush(); + hb('GET', '/url', null, callback, {}); + hb.flush(); - expect(callback).toHaveBeenCalledOnce(); - expect(cancelledClb).not.toHaveBeenCalled(); + expect(callback).toHaveBeenCalledOnce(); + expect(cancelledClb).not.toHaveBeenCalled(); + }); }); - }); - describe('expect/when shortcuts', () { - [[(x) => hb.expectGET(x), 'GET'], - [(x) => hb.expectPOST(x), 'POST'], - [(x) => hb.expectPUT(x), 'PUT'], - [(x) => hb.expectPATCH(x), 'PATCH'], - [(x) => hb.expectDELETE(x), 'DELETE'], - [(x) => hb.expectJSONP(x), 'JSONP'], - [(x) => hb.whenGET(x), 'GET'], - [(x) => hb.whenPOST(x), 'POST'], - [(x) => hb.whenPUT(x), 'PUT'], - [(x) => hb.whenPATCH(x), 'PATCH'], - [(x) => hb.whenDELETE(x), 'DELETE'], - [(x) => hb.whenJSONP(x), 'JSONP'] - ].forEach((step) { + describe('expect/when shortcuts', () { + [[(x) => hb.expectGET(x), 'GET'], + [(x) => hb.expectPOST(x), 'POST'], + [(x) => hb.expectPUT(x), 'PUT'], + [(x) => hb.expectPATCH(x), 'PATCH'], + [(x) => hb.expectDELETE(x), 'DELETE'], + [(x) => hb.expectJSONP(x), 'JSONP'], + [(x) => hb.whenGET(x), 'GET'], + [(x) => hb.whenPOST(x), 'POST'], + [(x) => hb.whenPUT(x), 'PUT'], + [(x) => hb.whenPATCH(x), 'PATCH'], + [(x) => hb.whenDELETE(x), 'DELETE'], + [(x) => hb.whenJSONP(x), 'JSONP'] + ].forEach((step) { var shortcut = step[0], method = step[1]; it('should provide $shortcut shortcut method', () { shortcut('/foo').respond('bar'); @@ -481,37 +482,38 @@ main() => describe('MockHttpBackend', () { expect(callback).toHaveBeenCalledOnceWith(200, 'bar', ''); }); }); - }); + }); - describe('MockHttpExpectation', () { + describe('MockHttpExpectation', () { - it('should accept url as regexp', () { - var exp = new MockHttpExpectation('GET', new RegExp('^\/x')); + it('should accept url as regexp', () { + var exp = new MockHttpExpectation('GET', new RegExp('^\/x')); - expect(exp.match('GET', '/x')).toBe(true); - expect(exp.match('GET', '/xxx/x')).toBe(true); - expect(exp.match('GET', 'x')).toBe(false); - expect(exp.match('GET', 'a/x')).toBe(false); - }); + expect(exp.match('GET', '/x')).toBe(true); + expect(exp.match('GET', '/xxx/x')).toBe(true); + expect(exp.match('GET', 'x')).toBe(false); + expect(exp.match('GET', 'a/x')).toBe(false); + }); - it('should accept data as regexp', () { - var exp = new MockHttpExpectation('POST', '/url', new RegExp('\{.*?\}')); + it('should accept data as regexp', () { + var exp = new MockHttpExpectation('POST', '/url', new RegExp('\{.*?\}')); - expect(exp.match('POST', '/url', '{"a": "aa"}')).toBe(true); - expect(exp.match('POST', '/url', '{"one": "two"}')).toBe(true); - expect(exp.match('POST', '/url', '{"one"')).toBe(false); - }); + expect(exp.match('POST', '/url', '{"a": "aa"}')).toBe(true); + expect(exp.match('POST', '/url', '{"one": "two"}')).toBe(true); + expect(exp.match('POST', '/url', '{"one"')).toBe(false); + }); - it('should accept headers as function', () { - var exp = new MockHttpExpectation('GET', '/url', undefined, (h) { - return h['Content-Type'] == 'application/json'; - }); + it('should accept headers as function', () { + var exp = new MockHttpExpectation('GET', '/url', undefined, (h) { + return h['Content-Type'] == 'application/json'; + }); - expect(exp.matchHeaders({})).toBe(false); - expect(exp.matchHeaders({'Content-Type': 'application/json', 'X-Another': 'true'})).toBe(true); + expect(exp.matchHeaders({})).toBe(false); + expect(exp.matchHeaders({'Content-Type': 'application/json', 'X-Another': 'true'})).toBe(true); + }); }); }); -}); +} diff --git a/test/mock/test_bed_spec.dart b/test/mock/test_bed_spec.dart index 19380a111..c2cbaf772 100644 --- a/test/mock/test_bed_spec.dart +++ b/test/mock/test_bed_spec.dart @@ -2,36 +2,37 @@ library angular.mock.test_bed_spec; import '../_specs.dart'; -main() => -describe('test bed', () { - TestBed _; - Compiler $compile; - Injector injector; - Scope $rootScope; +void main() { + describe('test bed', () { + TestBed _; + Compiler $compile; + Injector injector; + Scope $rootScope; - beforeEach(module((Module module) { - module..type(MyTestBedDirective); - return (TestBed tb) => _ = tb; - })); + beforeEach(module((Module module) { + module..type(MyTestBedDirective); + return (TestBed tb) => _ = tb; + })); - it('should allow for a scope-based compile', () { + it('should allow for a scope-based compile', () { - inject((Scope scope) { - Scope childScope = scope.createChild({}); + inject((Scope scope) { + Scope childScope = scope.createChild({}); - var element = $('
                  '); - _.compile(element, scope: childScope); + var element = $('
                  '); + _.compile(element, scope: childScope); - Probe probe = _.rootScope.context['i']; - var directiveInst = probe.directive(MyTestBedDirective); + Probe probe = _.rootScope.context['i']; + var directiveInst = probe.directive(MyTestBedDirective); - childScope.destroy(); + childScope.destroy(); - expect(directiveInst.destroyed).toBe(true); + expect(directiveInst.destroyed).toBe(true); + }); }); - }); -}); + }); +} @NgDirective(selector: '[my-directive]') class MyTestBedDirective { diff --git a/test/mock/zone_spec.dart b/test/mock/zone_spec.dart index 08ece8b4d..e5415b7c1 100644 --- a/test/mock/zone_spec.dart +++ b/test/mock/zone_spec.dart @@ -3,332 +3,334 @@ library angular.mock.zone_spec; import '../_specs.dart'; import 'dart:async'; -main() => describe('mock zones', () { - describe('sync', () { - it('should throw an error on scheduleMicrotask', () { - expect(sync(() { - scheduleMicrotask(() => dump("i never run")); - })).toThrow('scheduleMicrotask called from sync function'); - }); - +void main() { + describe('mock zones', () { + describe('sync', () { + it('should throw an error on scheduleMicrotask', () { + expect(sync(() { + scheduleMicrotask(() => dump("i never run")); + })).toThrow('scheduleMicrotask called from sync function'); + }); - it('should throw an error on timer', () { - expect(sync(() { - Timer.run(() => dump("i never run")); - })).toThrow('Timer created from sync function'); - }); + it('should throw an error on timer', () { + expect(sync(() { + Timer.run(() => dump("i never run")); + })).toThrow('Timer created from sync function'); + }); - it('should throw an error on periodic timer', () { - expect(sync(() { - new Timer.periodic(new Duration(milliseconds: 10), - (_) => dump("i never run")); - })).toThrow('periodic Timer created from sync function'); - }); - }); - describe('async', () { - it('should run synchronous code', () { - var ran = false; - async(() { ran = true; })(); - expect(ran).toBe(true); + it('should throw an error on periodic timer', () { + expect(sync(() { + new Timer.periodic(new Duration(milliseconds: 10), + (_) => dump("i never run")); + })).toThrow('periodic Timer created from sync function'); + }); }); + describe('async', () { + it('should run synchronous code', () { + var ran = false; + async(() { ran = true; })(); + expect(ran).toBe(true); + }); - it('should run async code', () { - var ran = false; - var thenRan = false; - async(() { - new Future.value('s').then((_) { thenRan = true; }); - expect(thenRan).toBe(false); - microLeap(); - expect(thenRan).toBe(true); - ran = true; - })(); - expect(ran).toBe(true); - }); + it('should run async code', () { + var ran = false; + var thenRan = false; + async(() { + new Future.value('s').then((_) { thenRan = true; }); + expect(thenRan).toBe(false); + microLeap(); + expect(thenRan).toBe(true); + ran = true; + })(); + expect(ran).toBe(true); + }); - it('should run async code with scheduleMicrotask', () { - var ran = false; - var thenRan = false; - async(() { - scheduleMicrotask(() { thenRan = true; }); - expect(thenRan).toBe(false); - microLeap(); - expect(thenRan).toBe(true); - ran = true; - })(); - expect(ran).toBe(true); - }); + it('should run async code with scheduleMicrotask', () { + var ran = false; + var thenRan = false; + async(() { + scheduleMicrotask(() { thenRan = true; }); + expect(thenRan).toBe(false); + microLeap(); + expect(thenRan).toBe(true); + ran = true; + })(); + expect(ran).toBe(true); + }); - it('should run chained thens', () { - var log = []; - async(() { - new Future.value('s') - .then((_) { log.add('firstThen'); }) - .then((_) { log.add('2ndThen'); }); - expect(log.join(' ')).toEqual(''); - microLeap(); - expect(log.join(' ')).toEqual('firstThen 2ndThen'); - })(); - }); + it('should run chained thens', () { + var log = []; + async(() { + new Future.value('s') + .then((_) { log.add('firstThen'); }) + .then((_) { log.add('2ndThen'); }); + expect(log.join(' ')).toEqual(''); + microLeap(); + expect(log.join(' ')).toEqual('firstThen 2ndThen'); + })(); + }); - it('shold run futures created in futures', () { - var log = []; - async(() { - new Future.value('s') - .then((_) { - log.add('firstThen'); - new Future.value('t').then((_) { - log.add('2ndThen'); - }); - }); - expect(log.join(' ')).toEqual(''); - microLeap(); - expect(log.join(' ')).toEqual('firstThen 2ndThen'); - })(); - }); - it('should run all the async calls if asked', () { - var log = []; - async(() { - new Future.value('s') - .then((_) { - log.add('firstThen'); - new Future.value('t').then((_) { - log.add('2ndThen'); + it('shold run futures created in futures', () { + var log = []; + async(() { + new Future.value('s') + .then((_) { + log.add('firstThen'); + new Future.value('t').then((_) { + log.add('2ndThen'); + }); }); - }); - expect(log.join(' ')).toEqual(''); - microLeap(); - expect(log.join(' ')).toEqual('firstThen 2ndThen'); - })(); - }); - - - it('should not complain if you dangle callbacks', () { - async(() { - new Future.value("s").then((_) {}); - })(); - }); - + expect(log.join(' ')).toEqual(''); + microLeap(); + expect(log.join(' ')).toEqual('firstThen 2ndThen'); + })(); + }); - it('should complain if you dangle exceptions', () { - expect(() { + it('should run all the async calls if asked', () { + var log = []; async(() { - new Future.value("s").then((_) { - throw ["dangling"]; + new Future.value('s') + .then((_) { + log.add('firstThen'); + new Future.value('t').then((_) { + log.add('2ndThen'); + }); }); + expect(log.join(' ')).toEqual(''); + microLeap(); + expect(log.join(' ')).toEqual('firstThen 2ndThen'); })(); - }).toThrow("dangling"); - }); + }); - it('should complain if the test throws an exception', () { - expect(() { + it('should not complain if you dangle callbacks', () { async(() { - throw "blah"; + new Future.value("s").then((_) {}); })(); - }).toThrow("blah"); - }); - - - it('should complain if the test throws an exception during async calls', () { - expect(async(() { - new Future.value('s').then((_) { throw "blah then"; }); - microLeap(); - })).toThrow("blah then"); - }); + }); - it('should throw errors from the microLeap call', async(() { - new Future.value('s').then((_) { throw "blah then 2"; }); - expect(() { - microLeap(); - }).toThrow("blah then 2"); - })); - describe('timers', () { - it('should not run queued timer on insufficient clock tick', async(() { - bool timerRan = false; - var timer = new Timer(new Duration(milliseconds: 10), - () => timerRan = true); + it('should complain if you dangle exceptions', () { + expect(() { + async(() { + new Future.value("s").then((_) { + throw ["dangling"]; + }); + })(); + }).toThrow("dangling"); + }); - clockTick(milliseconds: 9); - expect(timerRan).toBeFalsy(); - timer.cancel(); - })); + it('should complain if the test throws an exception', () { + expect(() { + async(() { + throw "blah"; + })(); + }).toThrow("blah"); + }); - it('should run queued zero duration timer on zero tick', async(() { - bool timerRan = false; - Timer.run(() => timerRan = true); + it('should complain if the test throws an exception during async calls', () { + expect(async(() { + new Future.value('s').then((_) { throw "blah then"; }); + microLeap(); + })).toThrow("blah then"); + }); - clockTick(); - expect(timerRan).toBeTruthy(); + it('should throw errors from the microLeap call', async(() { + new Future.value('s').then((_) { throw "blah then 2"; }); + expect(() { + microLeap(); + }).toThrow("blah then 2"); })); + describe('timers', () { + it('should not run queued timer on insufficient clock tick', async(() { + bool timerRan = false; + var timer = new Timer(new Duration(milliseconds: 10), + () => timerRan = true); - it('should run queued timer after sufficient clock ticks', async(() { - bool timerRan = false; - new Timer(new Duration(milliseconds: 10), () => timerRan = true); + clockTick(milliseconds: 9); + expect(timerRan).toBeFalsy(); - clockTick(milliseconds: 9); - expect(timerRan).toBeFalsy(); - clockTick(milliseconds: 1); - expect(timerRan).toBeTruthy(); - })); + timer.cancel(); + })); - it('should run queued timer only once', async(() { - int timerRan = 0; - new Timer(new Duration(milliseconds: 10), () => timerRan++); + it('should run queued zero duration timer on zero tick', async(() { + bool timerRan = false; + Timer.run(() => timerRan = true); - clockTick(milliseconds: 10); - expect(timerRan).toBe(1); - clockTick(milliseconds: 10); - expect(timerRan).toBe(1); - clockTick(minutes: 10); - expect(timerRan).toBe(1); - })); + clockTick(); + expect(timerRan).toBeTruthy(); + })); - it('should run periodic timer', async(() { - int timerRan = 0; - var timer = new Timer.periodic(new Duration(milliseconds: 10), - (_) => timerRan++); + it('should run queued timer after sufficient clock ticks', async(() { + bool timerRan = false; + new Timer(new Duration(milliseconds: 10), () => timerRan = true); - clockTick(milliseconds: 9); - expect(timerRan).toBe(0); - clockTick(milliseconds: 1); - expect(timerRan).toBe(1); - clockTick(milliseconds: 30); - expect(timerRan).toBe(4); + clockTick(milliseconds: 9); + expect(timerRan).toBeFalsy(); + clockTick(milliseconds: 1); + expect(timerRan).toBeTruthy(); + })); - timer.cancel(); - })); + it('should run queued timer only once', async(() { + int timerRan = 0; + new Timer(new Duration(milliseconds: 10), () => timerRan++); - it('should not run cancelled timer', async(() { - bool timerRan = false; - var timer = new Timer(new Duration(milliseconds: 10), - () => timerRan = true); + clockTick(milliseconds: 10); + expect(timerRan).toBe(1); + clockTick(milliseconds: 10); + expect(timerRan).toBe(1); + clockTick(minutes: 10); + expect(timerRan).toBe(1); + })); - timer.cancel(); - clockTick(milliseconds: 10); - expect(timerRan).toBeFalsy(); - })); + it('should run periodic timer', async(() { + int timerRan = 0; + var timer = new Timer.periodic(new Duration(milliseconds: 10), + (_) => timerRan++); + clockTick(milliseconds: 9); + expect(timerRan).toBe(0); + clockTick(milliseconds: 1); + expect(timerRan).toBe(1); + clockTick(milliseconds: 30); + expect(timerRan).toBe(4); - it('should not run cancelled periodic timer', async(() { - bool timerRan = false; - var timer = new Timer.periodic(new Duration(milliseconds: 10), - (_) => timerRan = true); + timer.cancel(); + })); - timer.cancel(); - clockTick(milliseconds: 10); - expect(timerRan).toBeFalsy(); - })); + it('should not run cancelled timer', async(() { + bool timerRan = false; + var timer = new Timer(new Duration(milliseconds: 10), + () => timerRan = true); + timer.cancel(); - it('should be able to cancel periodic timer from callback', async(() { - int timerRan = 0; - var timer = new Timer.periodic(new Duration(milliseconds: 10), - (t) { - timerRan++; - t.cancel(); - }); + clockTick(milliseconds: 10); + expect(timerRan).toBeFalsy(); + })); - clockTick(milliseconds: 10); - expect(timerRan).toBe(1); - clockTick(milliseconds: 10); - expect(timerRan).toBe(1); + it('should not run cancelled periodic timer', async(() { + bool timerRan = false; + var timer = new Timer.periodic(new Duration(milliseconds: 10), + (_) => timerRan = true); - timer.cancel(); - })); + timer.cancel(); + clockTick(milliseconds: 10); + expect(timerRan).toBeFalsy(); + })); - it('should process micro-tasks before timers', async(() { - var log = []; - scheduleMicrotask(() => log.add('scheduleMicrotask')); - new Timer(new Duration(milliseconds: 10), - () => log.add('timer')); - var timer = new Timer.periodic(new Duration(milliseconds: 10), - (_) => log.add('periodic_timer')); + it('should be able to cancel periodic timer from callback', async(() { + int timerRan = 0; + var timer = new Timer.periodic(new Duration(milliseconds: 10), + (t) { + timerRan++; + t.cancel(); + }); - expect(log.join(' ')).toEqual(''); + clockTick(milliseconds: 10); + expect(timerRan).toBe(1); - clockTick(milliseconds: 10); + clockTick(milliseconds: 10); + expect(timerRan).toBe(1); - expect(log.join(' ')).toEqual('scheduleMicrotask timer periodic_timer'); + timer.cancel(); + })); - timer.cancel(); - })); + it('should process micro-tasks before timers', async(() { + var log = []; - it('should process micro-tasks created in timers before next timers', async(() { - var log = []; + scheduleMicrotask(() => log.add('scheduleMicrotask')); + new Timer(new Duration(milliseconds: 10), + () => log.add('timer')); + var timer = new Timer.periodic(new Duration(milliseconds: 10), + (_) => log.add('periodic_timer')); - scheduleMicrotask(() => log.add('scheduleMicrotask')); - new Timer(new Duration(milliseconds: 10), - () { - log.add('timer'); - scheduleMicrotask(() => log.add('timer_scheduleMicrotask')); - }); - var timer = new Timer.periodic(new Duration(milliseconds: 10), - (_) { - log.add('periodic_timer'); - scheduleMicrotask(() => log.add('periodic_timer_scheduleMicrotask')); - }); + expect(log.join(' ')).toEqual(''); - expect(log.join(' ')).toEqual(''); + clockTick(milliseconds: 10); - clockTick(milliseconds: 10); - expect(log.join(' ')).toEqual('scheduleMicrotask timer timer_scheduleMicrotask periodic_timer'); + expect(log.join(' ')).toEqual('scheduleMicrotask timer periodic_timer'); - clockTick(); - expect(log.join(' ')).toEqual('scheduleMicrotask timer timer_scheduleMicrotask periodic_timer'); + timer.cancel(); + })); - clockTick(milliseconds: 10); - expect(log.join(' ')).toEqual('scheduleMicrotask timer timer_scheduleMicrotask periodic_timer periodic_timer_scheduleMicrotask periodic_timer'); - timer.cancel(); - })); + it('should process micro-tasks created in timers before next timers', async(() { + var log = []; + scheduleMicrotask(() => log.add('scheduleMicrotask')); + new Timer(new Duration(milliseconds: 10), + () { + log.add('timer'); + scheduleMicrotask(() => log.add('timer_scheduleMicrotask')); + }); + var timer = new Timer.periodic(new Duration(milliseconds: 10), + (_) { + log.add('periodic_timer'); + scheduleMicrotask(() => log.add('periodic_timer_scheduleMicrotask')); + }); - it('should not leak timers between asyncs', () { - var log = []; + expect(log.join(' ')).toEqual(''); - async(() { - var timer = new Timer.periodic(new Duration(milliseconds: 10), - (_) => log.add('periodic_timer')); - new Timer(new Duration(milliseconds: 10), - () => log.add('timer')); clockTick(milliseconds: 10); - timer.cancel(); - })(); - expect(log.join(' ')).toEqual('periodic_timer timer'); + expect(log.join(' ')).toEqual('scheduleMicrotask timer timer_scheduleMicrotask periodic_timer'); + + clockTick(); + expect(log.join(' ')).toEqual('scheduleMicrotask timer timer_scheduleMicrotask periodic_timer'); - async(() { clockTick(milliseconds: 10); - })(); - expect(log.join(' ')).toEqual('periodic_timer timer'); - }); + expect(log.join(' ')).toEqual('scheduleMicrotask timer timer_scheduleMicrotask periodic_timer periodic_timer_scheduleMicrotask periodic_timer'); + timer.cancel(); + })); + + + it('should not leak timers between asyncs', () { + var log = []; + + async(() { + var timer = new Timer.periodic(new Duration(milliseconds: 10), + (_) => log.add('periodic_timer')); + new Timer(new Duration(milliseconds: 10), + () => log.add('timer')); + clockTick(milliseconds: 10); + timer.cancel(); + })(); + expect(log.join(' ')).toEqual('periodic_timer timer'); + + async(() { + clockTick(milliseconds: 10); + })(); + expect(log.join(' ')).toEqual('periodic_timer timer'); + }); - it('should throw an error on dangling timers', () { - expect(async(() { - new Timer.periodic(new Duration(milliseconds: 10), - (_) => dump("i never run")); - })).toThrow('1 active timer(s) are still in the queue.'); + + it('should throw an error on dangling timers', () { + expect(async(() { + new Timer.periodic(new Duration(milliseconds: 10), + (_) => dump("i never run")); + })).toThrow('1 active timer(s) are still in the queue.'); + }); }); }); }); -}); +} diff --git a/test/playback/playback_http_spec.dart b/test/playback/playback_http_spec.dart index cc2a0230a..9c2fe4b1b 100644 --- a/test/playback/playback_http_spec.dart +++ b/test/playback/playback_http_spec.dart @@ -3,73 +3,75 @@ library playback_http_spec; import '../_specs.dart'; import 'package:angular/playback/playback_http.dart'; -main() => describe('Playback HTTP', () { - MockHttpBackend backend; - beforeEach(module((Module m) { - backend = new MockHttpBackend(); - var wrapper = new HttpBackendWrapper(backend); - m - ..value(HttpBackendWrapper, wrapper) - ..type(PlaybackHttpBackendConfig); - })); - - afterEach(() { - backend.verifyNoOutstandingRequest(); - backend.verifyNoOutstandingExpectation(); - }); - - describe('RecordingHttpBackend', () { +void main() { + describe('Playback HTTP', () { + MockHttpBackend backend; beforeEach(module((Module m) { - m.type(HttpBackend, implementedBy: RecordingHttpBackend); + backend = new MockHttpBackend(); + var wrapper = new HttpBackendWrapper(backend); + m..value(HttpBackendWrapper, wrapper)..type(PlaybackHttpBackendConfig); })); + afterEach(() { + backend.verifyNoOutstandingRequest(); + backend.verifyNoOutstandingExpectation(); + }); + + describe('RecordingHttpBackend', () { + beforeEach(module((Module m) { + m.type(HttpBackend, implementedBy: RecordingHttpBackend); + })); + - it('should record a request', async(inject((Http http) { - backend.expectGET('request').respond(200, 'response'); + it('should record a request', async(inject((Http http) { + backend.expectGET('request').respond(200, 'response'); - var responseData; + var responseData; - http(method: 'GET', url: 'request').then((HttpResponse r) { - responseData = r.data; - }); + http(method: 'GET', url: 'request').then((HttpResponse r) { + responseData = r.data; + }); - microLeap(); - backend.flush(); - backend + microLeap(); + backend.flush(); + backend .expectPOST('/record', - r'{"key":"{\"url\":\"request\",\"method\":\"GET\",\"requestHeaders\":{\"Accept\":\"application/json, text/plain, */*\",\"X-XSRF-TOKEN\":\"secret\"},\"data\":null}",' + + r'{"key":"{\"url\":\"request\",\"method\":\"GET\",\"requestHeaders\":' + r'{\"Accept\":\"application/json, text/plain, */*\",\"X-XSRF-TOKEN\":\"secret\"},\"data\":null}",' r'"data":"{\"status\":200,\"headers\":\"\",\"data\":\"response\"}"}') .respond(200); - microLeap(); - backend.flush(); - microLeap(); + microLeap(); + backend.flush(); + microLeap(); - expect(responseData).toEqual('response'); - }))); - }); + expect(responseData).toEqual('response'); + }))); + }); - describe('PlaybackHttpBackend', () { - beforeEach(module((Module m) { - m.type(HttpBackend, implementedBy: PlaybackHttpBackend); - })); + describe('PlaybackHttpBackend', () { + beforeEach(module((Module m) { + m.type(HttpBackend, implementedBy: PlaybackHttpBackend); + })); - it('should replay a request', async(inject((Http http, HttpBackend hb) { - (hb as PlaybackHttpBackend).data = { - r'{"url":"request","method":"GET","requestHeaders":{"Accept":"application/json, text/plain, */*","X-XSRF-TOKEN":"secret"},"data":null}': {'status': 200, 'headers': '', 'data': 'playback data'} - }; + it('should replay a request', async(inject((Http http, HttpBackend hb) { + (hb as PlaybackHttpBackend).data = { + r'{"url":"request","method":"GET","requestHeaders":{"Accept":"application/json, text/plain, */*","X-XSRF-TOKEN":"secret"},"data":null}': + {'status': 200, 'headers': '', 'data': 'playback data'} + }; - var responseData; + var responseData; - http(method: 'GET', url: 'request').then((HttpResponse r) { - responseData = r.data; - }); + http(method: 'GET', url: 'request').then((HttpResponse r) { + responseData = r.data; + }); - microLeap(); + microLeap(); - expect(responseData).toEqual('playback data'); - }))); + expect(responseData).toEqual('playback data'); + }))); + }); }); -}); +} diff --git a/test/tools/html_extractor_spec.dart b/test/tools/html_extractor_spec.dart index 399a64b28..976e19442 100644 --- a/test/tools/html_extractor_spec.dart +++ b/test/tools/html_extractor_spec.dart @@ -7,86 +7,88 @@ import 'package:unittest/unittest.dart'; import '../jasmine_syntax.dart'; import 'mock_io_service.dart'; -main() => describe('html_extractor', () { +void main() { + describe('html_extractor', () { - it('should extract text mustache expressions', () { - var ioService = new MockIoService({ - 'foo.html': r''' + it('should extract text mustache expressions', () { + var ioService = new MockIoService({ + 'foo.html': r'''
                  foo {{ctrl.bar}} baz {{aux}}
                  ''' - }); + }); - var extractor = new HtmlExpressionExtractor([]); - extractor.crawl('/', ioService); - expect(extractor.expressions.toList()..sort(), - equals(['aux', 'ctrl.bar'])); - }); + var extractor = new HtmlExpressionExtractor([]); + extractor.crawl('/', ioService); + expect(extractor.expressions.toList()..sort(), + equals(['aux', 'ctrl.bar'])); + }); - it('should extract attribute mustache expressions', () { - var ioService = new MockIoService({ - 'foo.html': r''' + it('should extract attribute mustache expressions', () { + var ioService = new MockIoService({ + 'foo.html': r'''
                  ''' - }); + }); - var extractor = new HtmlExpressionExtractor([]); - extractor.crawl('/', ioService); - expect(extractor.expressions.toList()..sort(), - equals(['aux', 'ctrl.bar'])); - }); + var extractor = new HtmlExpressionExtractor([]); + extractor.crawl('/', ioService); + expect(extractor.expressions.toList()..sort(), + equals(['aux', 'ctrl.bar'])); + }); - it('should extract ng-repeat expressions', () { - var ioService = new MockIoService({ - 'foo.html': r''' + it('should extract ng-repeat expressions', () { + var ioService = new MockIoService({ + 'foo.html': r'''
                  ''' - }); + }); - var extractor = new HtmlExpressionExtractor([]); - extractor.crawl('/', ioService); - expect(extractor.expressions.toList()..sort(), - equals(['ctrl.bar'])); - }); + var extractor = new HtmlExpressionExtractor([]); + extractor.crawl('/', ioService); + expect(extractor.expressions.toList()..sort(), + equals(['ctrl.bar'])); + }); - it('should extract expressions provided in the directive info', () { - var ioService = new MockIoService({}); + it('should extract expressions provided in the directive info', () { + var ioService = new MockIoService({}); - var extractor = new HtmlExpressionExtractor([ - new DirectiveInfo('', [], ['foo', 'bar']) - ]); - extractor.crawl('/', ioService); - expect(extractor.expressions.toList()..sort(), - equals(['bar', 'foo'])); - }); + var extractor = new HtmlExpressionExtractor([ + new DirectiveInfo('', [], ['foo', 'bar']) + ]); + extractor.crawl('/', ioService); + expect(extractor.expressions.toList()..sort(), + equals(['bar', 'foo'])); + }); - it('should extract expressions from expression attributes', () { - var ioService = new MockIoService({ - 'foo.html': r''' + it('should extract expressions from expression attributes', () { + var ioService = new MockIoService({ + 'foo.html': r''' ''' - }); + }); - var extractor = new HtmlExpressionExtractor([ - new DirectiveInfo('foo', ['bar']) - ]); - extractor.crawl('/', ioService); - expect(extractor.expressions.toList()..sort(), - equals(['ctrl.baz'])); - }); + var extractor = new HtmlExpressionExtractor([ + new DirectiveInfo('foo', ['bar']) + ]); + extractor.crawl('/', ioService); + expect(extractor.expressions.toList()..sort(), + equals(['ctrl.baz'])); + }); - it('should ignore ng-repeat while extracting attribute expressions', () { - var ioService = new MockIoService({ - 'foo.html': r''' + it('should ignore ng-repeat while extracting attribute expressions', () { + var ioService = new MockIoService({ + 'foo.html': r'''
                  ''' - }); + }); - var extractor = new HtmlExpressionExtractor([ - new DirectiveInfo('[ng-repeat]', ['ng-repeat']) - ]); - extractor.crawl('/', ioService); - // Basically we don't want to extract "foo in ctrl.bar". - expect(extractor.expressions.toList()..sort(), - equals(['ctrl.bar'])); + var extractor = new HtmlExpressionExtractor([ + new DirectiveInfo('[ng-repeat]', ['ng-repeat']) + ]); + extractor.crawl('/', ioService); + // Basically we don't want to extract "foo in ctrl.bar". + expect(extractor.expressions.toList()..sort(), + equals(['ctrl.bar'])); + }); }); -}); +} diff --git a/test/tools/selector_spec.dart b/test/tools/selector_spec.dart index 3c9a13938..38d69a0bb 100644 --- a/test/tools/selector_spec.dart +++ b/test/tools/selector_spec.dart @@ -6,77 +6,79 @@ import 'package:unittest/unittest.dart'; import '../jasmine_syntax.dart'; -main() => describe('selector', () { +void main() { + describe('selector', () { + + it('should match directive on element', () { + var node = new Element.html(''); + expect(matchesNode(node, 'b'), isTrue); + expect(matchesNode(node, 'em'), isFalse); + }); + + it('should match directive on class', () { + var node = new Element.html('
                  '); + expect(matchesNode(node, '.b'), isTrue); + expect(matchesNode(node, '.c'), isFalse); + }); + + + it('should match directive on [attribute]', () { + var node = new Element.html('
                  '); + expect(matchesNode(node, '[directive]'), isTrue); + expect(matchesNode(node, '[directiff]'), isFalse); + + node = new Element.html('
                  '); + expect(matchesNode(node, '[directive=abc]'), isTrue); + expect(matchesNode(node, '[directive=bcd]'), isFalse); + }); + + + it('should match directive on element[attribute]', () { + var node = new Element.html(''); + expect(matchesNode(node, 'b[directive]'), isTrue); + expect(matchesNode(node, 'c[directive]'), isFalse); + }); + + + it('should match directive on [attribute=value]', () { + var node = new Element.html('
                  '); + expect(matchesNode(node, '[directive=value]'), isTrue); + }); + + + it('should match directive on element[attribute=value]', () { + var node = new Element.html(''); + expect(matchesNode(node, 'b[directive=value]'), isTrue); + expect(matchesNode(node, 'b[directive=wrongvalue]'), isFalse); + }); + + it('should match attributes', () { + var node = new Element.html('
                  '); + expect(matchesNode(node, '[*=/xyz/]'), isTrue); + expect(matchesNode(node, '[*=/xyzz/]'), isFalse); + }); + + it('should match whildcard attributes', () { + var node = new Element.html('
                  '); + expect(matchesNode(node, '[attr-*]'), isTrue); + expect(matchesNode(node, '[attr-*=blah]'), isTrue); + expect(matchesNode(node, '[attr-*=halb]'), isFalse); + expect(matchesNode(node, '[foo-*]'), isFalse); + expect(matchesNode(node, '[foo-*=blah]'), isFalse); + expect(matchesNode(node, '[foo-*=halb]'), isFalse); + }); + + it('should match text', () { + var node = new Text('before-abc-after'); + expect(matchesNode(node, ':contains(/abc/)'), isTrue); + expect(matchesNode(node, ':contains(/cde/)'), isFalse); + }); + + it('should match on multiple directives', () { + var node = new Element.html('
                  '); + expect(matchesNode(node, '[directive=d][foo=f]'), isTrue); + expect(matchesNode(node, '[directive=d][bar=baz]'), isFalse); + }); - it('should match directive on element', () { - var node = new Element.html(''); - expect(matchesNode(node, 'b'), isTrue); - expect(matchesNode(node, 'em'), isFalse); }); - - it('should match directive on class', () { - var node = new Element.html('
                  '); - expect(matchesNode(node, '.b'), isTrue); - expect(matchesNode(node, '.c'), isFalse); - }); - - - it('should match directive on [attribute]', () { - var node = new Element.html('
                  '); - expect(matchesNode(node, '[directive]'), isTrue); - expect(matchesNode(node, '[directiff]'), isFalse); - - node = new Element.html('
                  '); - expect(matchesNode(node, '[directive=abc]'), isTrue); - expect(matchesNode(node, '[directive=bcd]'), isFalse); - }); - - - it('should match directive on element[attribute]', () { - var node = new Element.html(''); - expect(matchesNode(node, 'b[directive]'), isTrue); - expect(matchesNode(node, 'c[directive]'), isFalse); - }); - - - it('should match directive on [attribute=value]', () { - var node = new Element.html('
                  '); - expect(matchesNode(node, '[directive=value]'), isTrue); - }); - - - it('should match directive on element[attribute=value]', () { - var node = new Element.html(''); - expect(matchesNode(node, 'b[directive=value]'), isTrue); - expect(matchesNode(node, 'b[directive=wrongvalue]'), isFalse); - }); - - it('should match attributes', () { - var node = new Element.html('
                  '); - expect(matchesNode(node, '[*=/xyz/]'), isTrue); - expect(matchesNode(node, '[*=/xyzz/]'), isFalse); - }); - - it('should match whildcard attributes', () { - var node = new Element.html('
                  '); - expect(matchesNode(node, '[attr-*]'), isTrue); - expect(matchesNode(node, '[attr-*=blah]'), isTrue); - expect(matchesNode(node, '[attr-*=halb]'), isFalse); - expect(matchesNode(node, '[foo-*]'), isFalse); - expect(matchesNode(node, '[foo-*=blah]'), isFalse); - expect(matchesNode(node, '[foo-*=halb]'), isFalse); - }); - - it('should match text', () { - var node = new Text('before-abc-after'); - expect(matchesNode(node, ':contains(/abc/)'), isTrue); - expect(matchesNode(node, ':contains(/cde/)'), isFalse); - }); - - it('should match on multiple directives', () { - var node = new Element.html('
                  '); - expect(matchesNode(node, '[directive=d][foo=f]'), isTrue); - expect(matchesNode(node, '[directive=d][bar=baz]'), isFalse); - }); - -}); +} diff --git a/test/tools/source_metadata_extractor_spec.dart b/test/tools/source_metadata_extractor_spec.dart index 238c6a747..49f1beb80 100644 --- a/test/tools/source_metadata_extractor_spec.dart +++ b/test/tools/source_metadata_extractor_spec.dart @@ -8,113 +8,115 @@ import 'package:unittest/unittest.dart'; import '../jasmine_syntax.dart'; -main() => describe('SourceMetadataExtractor', () { - - it('should extract expressions and attribute names with expressions', () { - var info = extractDirectiveInfo([ - new DirectiveMetadata('FooComponent', COMPONENT, 'foo-component', { - 'barVal': '@bar', - 'baz-expr1': '<=>baz1', - 'baz-expr2': '=>baz2', - 'baz-expr3': '=>!baz3', - 'baz-callback': '&aux', - }) - ]); - - expect(flattenList(info, (DirectiveInfo i) => i.expressionAttrs), - equals(['baz-expr1', - 'baz-expr2', - 'baz-expr3', - 'baz-callback'])); - expect(flattenList(info, (DirectiveInfo i) => i.expressions), - equals(['bar', - 'baz1', - 'baz2', - 'baz3', - 'aux'])); - }); - - it('should build a component selector if one is not explicitly specified', () { - var info = extractDirectiveInfo([ - new DirectiveMetadata('MyFooComponent', COMPONENT, 'my-foo', { - 'foo-expr': '=>fooExpr' - }) - ]); - - expect(info, hasLength(1)); - expect(info[0].selector, equals('my-foo')); - }); - - it('should build an element directive selector if one is not explicitly specified', () { - var info = extractDirectiveInfo([ - new DirectiveMetadata('MyFooDirective', DIRECTIVE, 'my-foo', { - 'foo-expr': '=>fooExpr' - }) - ]); - - expect(info, hasLength(1)); - expect(info[0].selector, equals('my-foo')); - }); - - it('should build an attr directive selector if one is not explicitly specified', () { - var info = extractDirectiveInfo([ - new DirectiveMetadata('MyFooAttrDirective', '[my-foo]', '[my-foo]', { - 'foo-expr': '=>fooExpr' - }) - ]); - - expect(info, hasLength(1)); - expect(info[0].selector, equals('[my-foo]')); - }); - - it('should figure out attribute name if dot(.) is used', () { - var info = extractDirectiveInfo([ - new DirectiveMetadata('MyFooAttrDirective', DIRECTIVE, '[my-foo]', { - '.': '=>fooExpr' - }) - ]); +void main() { + describe('SourceMetadataExtractor', () { + + it('should extract expressions and attribute names with expressions', () { + var info = extractDirectiveInfo([ + new DirectiveMetadata('FooComponent', COMPONENT, 'foo-component', { + 'barVal': '@bar', + 'baz-expr1': '<=>baz1', + 'baz-expr2': '=>baz2', + 'baz-expr3': '=>!baz3', + 'baz-callback': '&aux', + }) + ]); + + expect(flattenList(info, (DirectiveInfo i) => i.expressionAttrs), + equals(['baz-expr1', + 'baz-expr2', + 'baz-expr3', + 'baz-callback'])); + expect(flattenList(info, (DirectiveInfo i) => i.expressions), + equals(['bar', + 'baz1', + 'baz2', + 'baz3', + 'aux'])); + }); + + it('should build a component selector if one is not explicitly specified', () { + var info = extractDirectiveInfo([ + new DirectiveMetadata('MyFooComponent', COMPONENT, 'my-foo', { + 'foo-expr': '=>fooExpr' + }) + ]); + + expect(info, hasLength(1)); + expect(info[0].selector, equals('my-foo')); + }); + + it('should build an element directive selector if one is not explicitly specified', () { + var info = extractDirectiveInfo([ + new DirectiveMetadata('MyFooDirective', DIRECTIVE, 'my-foo', { + 'foo-expr': '=>fooExpr' + }) + ]); + + expect(info, hasLength(1)); + expect(info[0].selector, equals('my-foo')); + }); + + it('should build an attr directive selector if one is not explicitly specified', () { + var info = extractDirectiveInfo([ + new DirectiveMetadata('MyFooAttrDirective', '[my-foo]', '[my-foo]', { + 'foo-expr': '=>fooExpr' + }) + ]); + + expect(info, hasLength(1)); + expect(info[0].selector, equals('[my-foo]')); + }); + + it('should figure out attribute name if dot(.) is used', () { + var info = extractDirectiveInfo([ + new DirectiveMetadata('MyFooAttrDirective', DIRECTIVE, '[my-foo]', { + '.': '=>fooExpr' + }) + ]); + + expect(flattenList(info, (DirectiveInfo i) => i.expressionAttrs), + equals(['my-foo'])); + }); + + it('should figure out attribute name from selector if dot(.) is used', () { + var info = extractDirectiveInfo([ + new DirectiveMetadata('MyFooAttrDirective', DIRECTIVE, '[blah][foo]', { + '.': '=>fooExpr' + }) + ]); + + expect(flattenList(info, (DirectiveInfo i) => i.expressionAttrs), + equals(['foo'])); + }); + + it('should include exported expression attributes', () { + var info = extractDirectiveInfo([ + new DirectiveMetadata('MyFooAttrDirective', DIRECTIVE, '[blah][foo]', { + '.': '=>fooExpr' + }, ['baz']) + ]); + + expect(flattenList(info, (DirectiveInfo i) => i.expressionAttrs), + equals(['foo', 'baz'])); + }); + + it('should include exported expressions', () { + var info = extractDirectiveInfo([ + new DirectiveMetadata('MyFooAttrDirective', DIRECTIVE, '[blah][foo]', { + '.': '=>fooExpr' + }, null, ['ctrl.baz']) + ]); + + expect(flattenList(info, (DirectiveInfo i) => i.expressions), + equals(['fooExpr', 'ctrl.baz'])); + }); - expect(flattenList(info, (DirectiveInfo i) => i.expressionAttrs), - equals(['my-foo'])); }); - - it('should figure out attribute name from selector if dot(.) is used', () { - var info = extractDirectiveInfo([ - new DirectiveMetadata('MyFooAttrDirective', DIRECTIVE, '[blah][foo]', { - '.': '=>fooExpr' - }) - ]); - - expect(flattenList(info, (DirectiveInfo i) => i.expressionAttrs), - equals(['foo'])); - }); - - it('should include exported expression attributes', () { - var info = extractDirectiveInfo([ - new DirectiveMetadata('MyFooAttrDirective', DIRECTIVE, '[blah][foo]', { - '.': '=>fooExpr' - }, ['baz']) - ]); - - expect(flattenList(info, (DirectiveInfo i) => i.expressionAttrs), - equals(['foo', 'baz'])); - }); - - it('should include exported expressions', () { - var info = extractDirectiveInfo([ - new DirectiveMetadata('MyFooAttrDirective', DIRECTIVE, '[blah][foo]', { - '.': '=>fooExpr' - }, null, ['ctrl.baz']) - ]); - - expect(flattenList(info, (DirectiveInfo i) => i.expressions), - equals(['fooExpr', 'ctrl.baz'])); - }); - -}); +} flattenList(list, map) => list.map(map).fold([], (prev, exprs) => - new List.from(prev)..addAll(exprs)); +new List.from(prev)..addAll(exprs)); List extractDirectiveInfo(List metadata) { var sourceCrawler = new MockSourceCrawler();