test(change detect): Port Locals tests to pregenerated cds
Move existing unit tests exercising Locals to also test Dart's pre-generated change detectors. See #502
This commit is contained in:
parent
fda8b1d87c
commit
d5195d4097
|
@ -74,11 +74,11 @@ export function main() {
|
||||||
|
|
||||||
function _bindSimpleValue(expression: string, context = null) {
|
function _bindSimpleValue(expression: string, context = null) {
|
||||||
var dispatcher = new TestDispatcher();
|
var dispatcher = new TestDispatcher();
|
||||||
var protoCd = _getProtoChangeDetector(getDefinition(expression));
|
var testDef = getDefinition(expression);
|
||||||
|
var protoCd = _getProtoChangeDetector(testDef.cdDef);
|
||||||
var cd = protoCd.instantiate(dispatcher);
|
var cd = protoCd.instantiate(dispatcher);
|
||||||
|
|
||||||
var locals = null;
|
cd.hydrate(context, testDef.locals, null);
|
||||||
cd.hydrate(context, locals, null);
|
|
||||||
cd.detectChanges();
|
cd.detectChanges();
|
||||||
return dispatcher.log;
|
return dispatcher.log;
|
||||||
}
|
}
|
||||||
|
@ -231,6 +231,34 @@ export function main() {
|
||||||
var td = new TestData(person);
|
var td = new TestData(person);
|
||||||
expect(_bindSimpleValue('a.sayHi("Jim")', td)).toEqual(['propName=Hi, Jim']);
|
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<any>) { return new FakeDirectives(directives, []); }
|
function dirs(directives: List<any>) { 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,
|
function createChangeDetector(propName: string, exp: string, context = null,
|
||||||
locals = null, registry = null) {
|
registry = null) {
|
||||||
var dispatcher = new TestDispatcher();
|
var dispatcher = new TestDispatcher();
|
||||||
|
|
||||||
var variableBindings = convertLocalsToVariableBindings(locals);
|
var locals = null;
|
||||||
|
var variableBindings = [];
|
||||||
|
|
||||||
var records = [BindingRecord.createForElement(ast(exp), 0, propName)];
|
var records = [BindingRecord.createForElement(ast(exp), 0, propName)];
|
||||||
var pcd = createProtoChangeDetector(records, variableBindings, [], registry);
|
var pcd = createProtoChangeDetector(records, variableBindings, [], registry);
|
||||||
|
@ -293,12 +312,6 @@ export function main() {
|
||||||
return {"changeDetector": cd, "dispatcher": dispatcher};
|
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`, () => {
|
describe(`${name} change detection`, () => {
|
||||||
var dispatcher;
|
var dispatcher;
|
||||||
|
|
||||||
|
@ -375,7 +388,7 @@ export function main() {
|
||||||
var registry = new FakePipeRegistry('pipe', () => new CountingPipe());
|
var registry = new FakePipeRegistry('pipe', () => new CountingPipe());
|
||||||
|
|
||||||
var person = new Person('bob');
|
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 cd = c["changeDetector"];
|
||||||
var dispatcher = c["dispatcher"];
|
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", () => {
|
describe("handle children", () => {
|
||||||
var parent, child;
|
var parent, child;
|
||||||
|
|
||||||
|
@ -916,8 +889,7 @@ export function main() {
|
||||||
it("should destroy all active pipes during dehyration", () => {
|
it("should destroy all active pipes during dehyration", () => {
|
||||||
var pipe = new OncePipe();
|
var pipe = new OncePipe();
|
||||||
var registry = new FakePipeRegistry('pipe', () => pipe);
|
var registry = new FakePipeRegistry('pipe', () => pipe);
|
||||||
var c =
|
var c = createChangeDetector("memo", "name | pipe", new Person('bob'), registry);
|
||||||
createChangeDetector("memo", "name | pipe", new Person('bob'), null, registry);
|
|
||||||
var cd = c["changeDetector"];
|
var cd = c["changeDetector"];
|
||||||
|
|
||||||
cd.detectChanges();
|
cd.detectChanges();
|
||||||
|
@ -933,7 +905,7 @@ export function main() {
|
||||||
var registry = new FakePipeRegistry('pipe', () => new CountingPipe());
|
var registry = new FakePipeRegistry('pipe', () => new CountingPipe());
|
||||||
var ctx = new Person("Megatron");
|
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 cd = c["changeDetector"];
|
||||||
var dispatcher = c["dispatcher"];
|
var dispatcher = c["dispatcher"];
|
||||||
|
|
||||||
|
@ -951,7 +923,7 @@ export function main() {
|
||||||
var registry = new FakePipeRegistry('pipe', () => new OncePipe());
|
var registry = new FakePipeRegistry('pipe', () => new OncePipe());
|
||||||
var ctx = new Person("Megatron");
|
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 cd = c["changeDetector"];
|
||||||
|
|
||||||
cd.detectChanges();
|
cd.detectChanges();
|
||||||
|
@ -969,7 +941,7 @@ export function main() {
|
||||||
var registry = new FakePipeRegistry('pipe', () => pipe);
|
var registry = new FakePipeRegistry('pipe', () => pipe);
|
||||||
var ctx = new Person("Megatron");
|
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 cd = c["changeDetector"];
|
||||||
|
|
||||||
cd.detectChanges();
|
cd.detectChanges();
|
||||||
|
@ -983,8 +955,7 @@ export function main() {
|
||||||
() => {
|
() => {
|
||||||
|
|
||||||
var registry = new FakePipeRegistry('pipe', () => new IdentityPipe());
|
var registry = new FakePipeRegistry('pipe', () => new IdentityPipe());
|
||||||
var c =
|
var c = createChangeDetector("memo", "name | pipe", new Person('bob'), registry);
|
||||||
createChangeDetector("memo", "name | pipe", new Person('bob'), null, registry);
|
|
||||||
var cd = c["changeDetector"];
|
var cd = c["changeDetector"];
|
||||||
|
|
||||||
cd.detectChanges();
|
cd.detectChanges();
|
||||||
|
@ -997,7 +968,7 @@ export function main() {
|
||||||
var registry = new FakePipeRegistry('pipe', () => new IdentityPipe());
|
var registry = new FakePipeRegistry('pipe', () => new IdentityPipe());
|
||||||
var ctx = new Person("Megatron");
|
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 cd = c["changeDetector"];
|
||||||
var dispatcher = c["dispatcher"];
|
var dispatcher = c["dispatcher"];
|
||||||
|
|
||||||
|
@ -1015,7 +986,7 @@ export function main() {
|
||||||
var registry = new FakePipeRegistry('pipe', () => new WrappedPipe());
|
var registry = new FakePipeRegistry('pipe', () => new WrappedPipe());
|
||||||
var ctx = new Person("Megatron");
|
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 cd = c["changeDetector"];
|
||||||
var dispatcher = c["dispatcher"];
|
var dispatcher = c["dispatcher"];
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,11 @@ void main(List<String> args) {
|
||||||
var allDefs = getAllDefinitions();
|
var allDefs = getAllDefinitions();
|
||||||
for (var i = 0; i < allDefs.length; ++i) {
|
for (var i = 0; i < allDefs.length; ++i) {
|
||||||
var className = 'ChangeDetector${i}';
|
var className = 'ChangeDetector${i}';
|
||||||
codegen.generate('dynamic', className, allDefs[i]);
|
codegen.generate('dynamic', className, allDefs[i].cdDef);
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
buf.write(',');
|
buf.write(',');
|
||||||
}
|
}
|
||||||
buf.write(" '''${allDefs[i].id}''': "
|
buf.write(" '''${allDefs[i].cdDef.id}''': "
|
||||||
"$className.$PROTO_CHANGE_DETECTOR_FACTORY_METHOD");
|
"$className.$PROTO_CHANGE_DETECTOR_FACTORY_METHOD");
|
||||||
}
|
}
|
||||||
buf.write('};');
|
buf.write('};');
|
||||||
|
|
|
@ -1,20 +1,31 @@
|
||||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {BindingRecord, ChangeDetectorDefinition, Lexer, Parser} from 'angular2/change_detection';
|
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 {reflector} from 'angular2/src/reflection/reflection';
|
||||||
import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabilities';
|
import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabilities';
|
||||||
|
|
||||||
var _parser = new Parser(new Lexer());
|
var _parser = new Parser(new Lexer());
|
||||||
|
|
||||||
function _createChangeDetectorDefinition(id: string, expression: string): ChangeDetectorDefinition {
|
function _createBindingRecords(expression: string): List<BindingRecord> {
|
||||||
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||||
var ast = _parser.parseBinding(expression, 'location');
|
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<any> {
|
||||||
var variableBindings = [];
|
var variableBindings = [];
|
||||||
var directiveRecords = [];
|
var loc = locals;
|
||||||
return new ChangeDetectorDefinition(id, strategy, variableBindings, bindingRecords,
|
while (isPresent(loc) && isPresent(loc.current)) {
|
||||||
directiveRecords);
|
MapWrapper.forEach(loc.current, (v, k) => ListWrapper.push(variableBindings, k));
|
||||||
|
loc = loc.parent;
|
||||||
|
}
|
||||||
|
return variableBindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
export var PROP_NAME = 'propName';
|
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.
|
* In this case, we expect `id` and `expression` to be the same string.
|
||||||
*/
|
*/
|
||||||
export function getDefinition(id: string): ChangeDetectorDefinition {
|
export function getDefinition(id: string): TestDefinition {
|
||||||
if (ListWrapper.indexOf(_availableDefinitions, id) < 0) {
|
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.`;
|
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
|
* Get all available ChangeDetectorDefinition objects. Used to pre-generate Dart
|
||||||
* `ChangeDetector` classes.
|
* `ChangeDetector` classes.
|
||||||
*/
|
*/
|
||||||
export function getAllDefinitions(): List<ChangeDetectorDefinition> {
|
export function getAllDefinitions(): List<TestDefinition> {
|
||||||
return ListWrapper.map(_availableDefinitions, (id) => getDefinition(id));
|
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 = [
|
var _availableDefinitions = [
|
||||||
'10',
|
'10',
|
||||||
'"str"',
|
'"str"',
|
||||||
|
@ -79,5 +115,53 @@ var _availableDefinitions = [
|
||||||
'address?.toString()',
|
'address?.toString()',
|
||||||
'sayHi("Jim")',
|
'sayHi("Jim")',
|
||||||
'a()(99)',
|
'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']])))
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
Loading…
Reference in New Issue