diff --git a/modules/angular2/test/change_detection/change_detector_spec.ts b/modules/angular2/test/change_detection/change_detector_spec.ts index a7d01eb66a..12ee689b1a 100644 --- a/modules/angular2/test/change_detection/change_detector_spec.ts +++ b/modules/angular2/test/change_detection/change_detector_spec.ts @@ -74,11 +74,11 @@ export function main() { function _bindSimpleValue(expression: string, context = null) { var dispatcher = new TestDispatcher(); - var protoCd = _getProtoChangeDetector(getDefinition(expression)); + var testDef = getDefinition(expression); + var protoCd = _getProtoChangeDetector(testDef.cdDef); var cd = protoCd.instantiate(dispatcher); - var locals = null; - cd.hydrate(context, locals, null); + cd.hydrate(context, testDef.locals, null); cd.detectChanges(); return dispatcher.log; } @@ -231,6 +231,34 @@ export function main() { var td = new TestData(person); expect(_bindSimpleValue('a.sayHi("Jim")', td)).toEqual(['propName=Hi, Jim']); }); + + describe("Locals", () => { + it('should read a value from locals', + () => { expect(_bindSimpleValue('valueFromLocals')).toEqual(['propName=value']); }); + + it('should invoke a function from local', + () => { expect(_bindSimpleValue('functionFromLocals')).toEqual(['propName=value']); }); + + it('should handle nested locals', + () => { expect(_bindSimpleValue('nestedLocals')).toEqual(['propName=value']); }); + + it("should fall back to a regular field read when the locals map" + + "does not have the requested field", + () => { + expect(_bindSimpleValue('fallbackLocals', new Person("Jim"))) + .toEqual(['propName=Jim']); + }); + + it('should correctly handle nested properties', () => { + var address = new Address('Grenoble'); + var person = new Person('Victor', address); + + expect(_bindSimpleValue('contextNestedPropertyWithLocals', person)) + .toEqual(['propName=Grenoble']); + expect(_bindSimpleValue('localPropertyWithSimilarContext', person)) + .toEqual(['propName=MTV']); + }); + }); }); }); @@ -269,21 +297,12 @@ export function main() { function dirs(directives: List) { return new FakeDirectives(directives, []); } - function convertLocalsToVariableBindings(locals) { - var variableBindings = []; - var loc = locals; - while (isPresent(loc)) { - MapWrapper.forEach(loc.current, (v, k) => ListWrapper.push(variableBindings, k)); - loc = loc.parent; - } - return variableBindings; - } - function createChangeDetector(propName: string, exp: string, context = null, - locals = null, registry = null) { + registry = null) { var dispatcher = new TestDispatcher(); - var variableBindings = convertLocalsToVariableBindings(locals); + var locals = null; + var variableBindings = []; var records = [BindingRecord.createForElement(ast(exp), 0, propName)]; var pcd = createProtoChangeDetector(records, variableBindings, [], registry); @@ -293,12 +312,6 @@ export function main() { return {"changeDetector": cd, "dispatcher": dispatcher}; } - function executeWatch(memo: string, exp: string, context = null, locals = null) { - var res = createChangeDetector(memo, exp, context, locals); - res["changeDetector"].detectChanges(); - return res["dispatcher"].log; - } - describe(`${name} change detection`, () => { var dispatcher; @@ -375,7 +388,7 @@ export function main() { var registry = new FakePipeRegistry('pipe', () => new CountingPipe()); var person = new Person('bob'); - var c = createChangeDetector('name', 'name | pipe', person, null, registry); + var c = createChangeDetector('name', 'name | pipe', person, registry); var cd = c["changeDetector"]; var dispatcher = c["dispatcher"]; @@ -688,46 +701,6 @@ export function main() { }); }); - describe("Locals", () => { - it('should read a value from locals', () => { - var locals = new Locals(null, MapWrapper.createFromPairs([["key", "value"]])); - - expect(executeWatch('key', 'key', null, locals)).toEqual(['key=value']); - }); - - it('should invoke a function from local', () => { - var locals = new Locals(null, MapWrapper.createFromPairs([["key", () => "value"]])); - - expect(executeWatch('key', 'key()', null, locals)).toEqual(['key=value']); - }); - - it('should handle nested locals', () => { - var nested = new Locals(null, MapWrapper.createFromPairs([["key", "value"]])); - var locals = new Locals(nested, MapWrapper.create()); - - expect(executeWatch('key', 'key', null, locals)).toEqual(['key=value']); - }); - - it("should fall back to a regular field read when the locals map" + - "does not have the requested field", - () => { - var locals = new Locals(null, MapWrapper.createFromPairs([["key", "value"]])); - - expect(executeWatch('name', 'name', new Person("Jim"), locals)) - .toEqual(['name=Jim']); - }); - - it('should correctly handle nested properties', () => { - var address = new Address('Grenoble'); - var person = new Person('Victor', address); - var locals = new Locals(null, MapWrapper.createFromPairs([['city', 'MTV']])); - expect(executeWatch('address.city', 'address.city', person, locals)) - .toEqual(['address.city=Grenoble']); - expect(executeWatch('city', 'city', person, locals)).toEqual(['city=MTV']); - }); - - }); - describe("handle children", () => { var parent, child; @@ -916,8 +889,7 @@ export function main() { it("should destroy all active pipes during dehyration", () => { var pipe = new OncePipe(); var registry = new FakePipeRegistry('pipe', () => pipe); - var c = - createChangeDetector("memo", "name | pipe", new Person('bob'), null, registry); + var c = createChangeDetector("memo", "name | pipe", new Person('bob'), registry); var cd = c["changeDetector"]; cd.detectChanges(); @@ -933,7 +905,7 @@ export function main() { var registry = new FakePipeRegistry('pipe', () => new CountingPipe()); var ctx = new Person("Megatron"); - var c = createChangeDetector("memo", "name | pipe", ctx, null, registry); + var c = createChangeDetector("memo", "name | pipe", ctx, registry); var cd = c["changeDetector"]; var dispatcher = c["dispatcher"]; @@ -951,7 +923,7 @@ export function main() { var registry = new FakePipeRegistry('pipe', () => new OncePipe()); var ctx = new Person("Megatron"); - var c = createChangeDetector("memo", "name | pipe", ctx, null, registry); + var c = createChangeDetector("memo", "name | pipe", ctx, registry); var cd = c["changeDetector"]; cd.detectChanges(); @@ -969,7 +941,7 @@ export function main() { var registry = new FakePipeRegistry('pipe', () => pipe); var ctx = new Person("Megatron"); - var c = createChangeDetector("memo", "name | pipe", ctx, null, registry); + var c = createChangeDetector("memo", "name | pipe", ctx, registry); var cd = c["changeDetector"]; cd.detectChanges(); @@ -983,8 +955,7 @@ export function main() { () => { var registry = new FakePipeRegistry('pipe', () => new IdentityPipe()); - var c = - createChangeDetector("memo", "name | pipe", new Person('bob'), null, registry); + var c = createChangeDetector("memo", "name | pipe", new Person('bob'), registry); var cd = c["changeDetector"]; cd.detectChanges(); @@ -997,7 +968,7 @@ export function main() { var registry = new FakePipeRegistry('pipe', () => new IdentityPipe()); var ctx = new Person("Megatron"); - var c = createChangeDetector("memo", "name | pipe", ctx, null, registry); + var c = createChangeDetector("memo", "name | pipe", ctx, registry); var cd = c["changeDetector"]; var dispatcher = c["dispatcher"]; @@ -1015,7 +986,7 @@ export function main() { var registry = new FakePipeRegistry('pipe', () => new WrappedPipe()); var ctx = new Person("Megatron"); - var c = createChangeDetector("memo", "name | pipe", ctx, null, registry); + var c = createChangeDetector("memo", "name | pipe", ctx, registry); var cd = c["changeDetector"]; var dispatcher = c["dispatcher"]; diff --git a/modules/angular2/test/change_detection/generator/gen_change_detectors.dart b/modules/angular2/test/change_detection/generator/gen_change_detectors.dart index 9bf7496e08..b7de748cea 100644 --- a/modules/angular2/test/change_detection/generator/gen_change_detectors.dart +++ b/modules/angular2/test/change_detection/generator/gen_change_detectors.dart @@ -13,11 +13,11 @@ void main(List args) { var allDefs = getAllDefinitions(); for (var i = 0; i < allDefs.length; ++i) { var className = 'ChangeDetector${i}'; - codegen.generate('dynamic', className, allDefs[i]); + codegen.generate('dynamic', className, allDefs[i].cdDef); if (i > 0) { buf.write(','); } - buf.write(" '''${allDefs[i].id}''': " + buf.write(" '''${allDefs[i].cdDef.id}''': " "$className.$PROTO_CHANGE_DETECTOR_FACTORY_METHOD"); } buf.write('};'); diff --git a/modules/angular2/test/change_detection/simple_watch_config.ts b/modules/angular2/test/change_detection/simple_watch_config.ts index 8000117f76..fd59b2096c 100644 --- a/modules/angular2/test/change_detection/simple_watch_config.ts +++ b/modules/angular2/test/change_detection/simple_watch_config.ts @@ -1,20 +1,31 @@ -import {ListWrapper} from 'angular2/src/facade/collection'; -import {BindingRecord, ChangeDetectorDefinition, Lexer, Parser} from 'angular2/change_detection'; +import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection'; +import {isBlank, isPresent} from 'angular2/src/facade/lang'; +import { + BindingRecord, + ChangeDetectorDefinition, + Lexer, + Locals, + Parser +} from 'angular2/change_detection'; import {reflector} from 'angular2/src/reflection/reflection'; import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabilities'; var _parser = new Parser(new Lexer()); -function _createChangeDetectorDefinition(id: string, expression: string): ChangeDetectorDefinition { +function _createBindingRecords(expression: string): List { reflector.reflectionCapabilities = new ReflectionCapabilities(); var ast = _parser.parseBinding(expression, 'location'); - var bindingRecords = [BindingRecord.createForElement(ast, 0, PROP_NAME)]; + return [BindingRecord.createForElement(ast, 0, PROP_NAME)]; +} - var strategy = null; +function _convertLocalsToVariableBindings(locals: Locals): List { var variableBindings = []; - var directiveRecords = []; - return new ChangeDetectorDefinition(id, strategy, variableBindings, bindingRecords, - directiveRecords); + var loc = locals; + while (isPresent(loc) && isPresent(loc.current)) { + MapWrapper.forEach(loc.current, (v, k) => ListWrapper.push(variableBindings, k)); + loc = loc.parent; + } + return variableBindings; } export var PROP_NAME = 'propName'; @@ -22,21 +33,46 @@ export var PROP_NAME = 'propName'; /** * In this case, we expect `id` and `expression` to be the same string. */ -export function getDefinition(id: string): ChangeDetectorDefinition { - if (ListWrapper.indexOf(_availableDefinitions, id) < 0) { +export function getDefinition(id: string): TestDefinition { + var expression = null; + var locals = null; + if (MapWrapper.contains(_availableDefinitionsWithLocals, id)) { + var val = MapWrapper.get(_availableDefinitionsWithLocals, id); + expression = val.expression; + locals = val.locals; + } else if (ListWrapper.indexOf(_availableDefinitions, id) >= 0) { + expression = id; + } + if (isBlank(expression)) { throw `No ChangeDetectorDefinition for ${id} available. Please modify this file if necessary.`; } - return _createChangeDetectorDefinition(id, id); + + var strategy = null; + var variableBindings = isPresent(locals) ? _convertLocalsToVariableBindings(locals) : []; + var bindingRecords = _createBindingRecords(expression); + var directiveRecords = []; + var cdDef = new ChangeDetectorDefinition(id, strategy, variableBindings, bindingRecords, + directiveRecords); + return new TestDefinition(id, cdDef, locals); +} + +export class TestDefinition { + constructor(public id: string, public cdDef: ChangeDetectorDefinition, public locals: Locals) {} } /** * Get all available ChangeDetectorDefinition objects. Used to pre-generate Dart * `ChangeDetector` classes. */ -export function getAllDefinitions(): List { +export function getAllDefinitions(): List { return ListWrapper.map(_availableDefinitions, (id) => getDefinition(id)); } +/** + * The list of all test definitions this config supplies. + * Items in this list that do not appear in other structures define tests with expressions + * equivalent to their ids. + */ var _availableDefinitions = [ '10', '"str"', @@ -79,5 +115,53 @@ var _availableDefinitions = [ 'address?.toString()', 'sayHi("Jim")', 'a()(99)', - 'a.sayHi("Jim")' + 'a.sayHi("Jim")', + 'valueFromLocals', + 'functionFromLocals', + 'nestedLocals', + 'fallbackLocals', + 'contextNestedPropertyWithLocals', + 'localPropertyWithSimilarContext' ]; + +class _ExpressionWithLocals { + constructor(public expression: string, public locals: Locals) {} +} + +/** + * Map from test id to _ExpressionWithLocals. + * Tests in this map define an expression and local values which those expressions refer to. + */ +var _availableDefinitionsWithLocals = MapWrapper.createFromPairs([ + [ + 'valueFromLocals', + new _ExpressionWithLocals('key', + new Locals(null, MapWrapper.createFromPairs([['key', 'value']]))) + ], + [ + 'functionFromLocals', + new _ExpressionWithLocals( + 'key()', new Locals(null, MapWrapper.createFromPairs([['key', () => 'value']]))) + ], + [ + 'nestedLocals', + new _ExpressionWithLocals( + 'key', new Locals(new Locals(null, MapWrapper.createFromPairs([['key', 'value']])), + MapWrapper.create())) + ], + [ + 'fallbackLocals', + new _ExpressionWithLocals('name', + new Locals(null, MapWrapper.createFromPairs([['key', 'value']]))) + ], + [ + 'contextNestedPropertyWithLocals', + new _ExpressionWithLocals('address.city', + new Locals(null, MapWrapper.createFromPairs([['city', 'MTV']]))) + ], + [ + 'localPropertyWithSimilarContext', + new _ExpressionWithLocals('city', + new Locals(null, MapWrapper.createFromPairs([['city', 'MTV']]))) + ] +]);