feat(compiler): add TemplateCompiler

TemplateCompiler is the entry point to the new compiler

Related to #3605
Closes #4220
This commit is contained in:
Tobias Bosch
2015-09-14 15:59:09 -07:00
parent eaa20f661a
commit 457b689bf0
47 changed files with 2064 additions and 725 deletions

View File

@ -9,12 +9,15 @@ import {
inject,
it,
xit,
TestComponentBuilder
TestComponentBuilder,
beforeEachBindings
} from 'angular2/test_lib';
import {MapWrapper} from 'angular2/src/core/facade/collection';
import {HtmlParser} from 'angular2/src/compiler/html_parser';
import {DirectiveMetadata, TypeMetadata, ChangeDetectionMetadata} from 'angular2/src/compiler/api';
import {MockSchemaRegistry} from './template_parser_spec';
import {
NormalizedDirectiveMetadata,
TypeMetadata,
ChangeDetectionMetadata
} from 'angular2/src/compiler/directive_metadata';
import {TemplateParser} from 'angular2/src/compiler/template_parser';
import {
Parser,
@ -33,9 +36,12 @@ import {Pipes} from 'angular2/src/core/change_detection/pipes';
import {createChangeDetectorDefinitions} from 'angular2/src/compiler/change_definition_factory';
import {TestContext, TestDirective, TestDispatcher, TestPipes} from './change_detector_mocks';
import {TEST_BINDINGS} from './test_bindings';
export function main() {
describe('ChangeDefinitionFactory', () => {
var domParser: HtmlParser;
beforeEachBindings(() => TEST_BINDINGS);
var parser: TemplateParser;
var dispatcher: TestDispatcher;
var context: TestContext;
@ -44,26 +50,23 @@ export function main() {
var pipes: Pipes;
var eventLocals: Locals;
beforeEach(() => {
domParser = new HtmlParser();
parser = new TemplateParser(
new Parser(new Lexer()),
new MockSchemaRegistry({'invalidProp': false}, {'mappedAttr': 'mappedProp'}));
beforeEach(inject([TemplateParser], (_templateParser) => {
parser = _templateParser;
context = new TestContext();
directive = new TestDirective();
dispatcher = new TestDispatcher([directive], []);
locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
eventLocals = new Locals(null, MapWrapper.createFromStringMap({'$event': null}));
pipes = new TestPipes();
});
}));
function createChangeDetector(template: string, directives: DirectiveMetadata[],
function createChangeDetector(template: string, directives: NormalizedDirectiveMetadata[],
protoViewIndex: number = 0): ChangeDetector {
var protoChangeDetectors =
createChangeDetectorDefinitions(
new TypeMetadata({typeName: 'SomeComp'}), ChangeDetectionStrategy.Default,
new ChangeDetectorGenConfig(true, true, false, false),
parser.parse(domParser.parse(template, 'TestComp'), directives))
createChangeDetectorDefinitions(new TypeMetadata({name: 'SomeComp'}),
ChangeDetectionStrategy.Default,
new ChangeDetectorGenConfig(true, true, false, false),
parser.parse(template, directives, 'TestComp'))
.map(definition => new DynamicProtoChangeDetector(definition));
var changeDetector = protoChangeDetectors[protoViewIndex].instantiate(dispatcher);
changeDetector.hydrate(context, locals, dispatcher, pipes);
@ -103,8 +106,8 @@ export function main() {
});
it('should write directive properties', () => {
var dirMeta = new DirectiveMetadata({
type: new TypeMetadata({typeName: 'SomeDir'}),
var dirMeta = new NormalizedDirectiveMetadata({
type: new TypeMetadata({name: 'SomeDir'}),
selector: 'div',
changeDetection: new ChangeDetectionMetadata({properties: ['dirProp']})
});
@ -117,8 +120,8 @@ export function main() {
});
it('should watch directive host properties', () => {
var dirMeta = new DirectiveMetadata({
type: new TypeMetadata({typeName: 'SomeDir'}),
var dirMeta = new NormalizedDirectiveMetadata({
type: new TypeMetadata({name: 'SomeDir'}),
selector: 'div',
changeDetection: new ChangeDetectionMetadata({hostProperties: {'elProp': 'dirProp'}})
});
@ -131,8 +134,8 @@ export function main() {
});
it('should handle directive events', () => {
var dirMeta = new DirectiveMetadata({
type: new TypeMetadata({typeName: 'SomeDir'}),
var dirMeta = new NormalizedDirectiveMetadata({
type: new TypeMetadata({name: 'SomeDir'}),
selector: 'div',
changeDetection:
new ChangeDetectionMetadata({hostListeners: {'click': 'onEvent($event)'}})

View File

@ -9,9 +9,10 @@ import {
beforeEach,
afterEach,
AsyncTestCompleter,
inject
inject,
beforeEachBindings
} from 'angular2/test_lib';
import {IS_DART} from '../platform';
import {bind} from 'angular2/src/core/di';
import {CONST_EXPR} from 'angular2/src/core/facade/lang';
import {MapWrapper} from 'angular2/src/core/facade/collection';
@ -19,21 +20,16 @@ import {Promise} from 'angular2/src/core/facade/async';
import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler';
import {HtmlParser} from 'angular2/src/compiler/html_parser';
import {
DirectiveMetadata,
NormalizedDirectiveMetadata,
TypeMetadata,
ChangeDetectionMetadata,
SourceModule
} from 'angular2/src/compiler/api';
import {MockSchemaRegistry} from './template_parser_spec';
ChangeDetectionMetadata
} from 'angular2/src/compiler/directive_metadata';
import {SourceModule, SourceExpression, moduleRef} from 'angular2/src/compiler/source_module';
import {TemplateParser} from 'angular2/src/compiler/template_parser';
import {
Parser,
Lexer,
ChangeDetectorGenConfig,
ChangeDetectionStrategy,
ChangeDispatcher,
@ -45,61 +41,76 @@ import {
import {evalModule} from './eval_module';
import {TEST_BINDINGS} from './test_bindings';
import {TestContext, TestDispatcher, TestPipes} from './change_detector_mocks';
import {codeGenValueFn, codeGenExportVariable} from 'angular2/src/compiler/util';
// Attention: These module names have to correspond to real modules!
const MODULE_NAME = 'angular2/test/compiler/change_detector_compiler_spec';
const THIS_MODULE = 'angular2/test/compiler/change_detector_compiler_spec';
var THIS_MODULE_REF = moduleRef(THIS_MODULE);
export function main() {
describe('ChangeDetectorCompiler', () => {
var domParser: HtmlParser;
beforeEachBindings(() => TEST_BINDINGS);
var parser: TemplateParser;
var compiler: ChangeDetectionCompiler;
function createCompiler(useJit: boolean): ChangeDetectionCompiler {
return new ChangeDetectionCompiler(new ChangeDetectorGenConfig(true, true, false, useJit));
}
beforeEach(() => {
domParser = new HtmlParser();
parser = new TemplateParser(
new Parser(new Lexer()),
new MockSchemaRegistry({'invalidProp': false}, {'mappedAttr': 'mappedProp'}));
});
beforeEach(inject([TemplateParser, ChangeDetectionCompiler], (_parser, _compiler) => {
parser = _parser;
compiler = _compiler;
}));
describe('compileComponentRuntime', () => {
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
directives: DirectiveMetadata[] = CONST_EXPR([])): string[] {
var type = new TypeMetadata({typeName: 'SomeComp'});
var parsedTemplate = parser.parse(domParser.parse(template, 'TestComp'), directives);
directives: NormalizedDirectiveMetadata[] = CONST_EXPR([])): string[] {
var type = new TypeMetadata({name: 'SomeComp'});
var parsedTemplate = parser.parse(template, directives, 'TestComp');
var factories =
compiler.compileComponentRuntime(type, ChangeDetectionStrategy.Default, parsedTemplate);
return testChangeDetector(factories[0]);
}
it('should watch element properties (no jit)', () => {
expect(detectChanges(createCompiler(false), '<div [el-prop]="someProp">'))
.toEqual(['elementProperty(elProp)=someValue']);
describe('no jit', () => {
beforeEachBindings(() => [
bind(ChangeDetectorGenConfig)
.toValue(new ChangeDetectorGenConfig(true, true, false, false))
]);
it('should watch element properties', () => {
expect(detectChanges(compiler, '<div [el-prop]="someProp">'))
.toEqual(['elementProperty(elProp)=someValue']);
});
});
it('should watch element properties (jit)', () => {
expect(detectChanges(createCompiler(true), '<div [el-prop]="someProp">'))
.toEqual(['elementProperty(elProp)=someValue']);
describe('jit', () => {
beforeEachBindings(() => [
bind(ChangeDetectorGenConfig)
.toValue(new ChangeDetectorGenConfig(true, true, false, true))
]);
it('should watch element properties', () => {
expect(detectChanges(compiler, '<div [el-prop]="someProp">'))
.toEqual(['elementProperty(elProp)=someValue']);
});
});
});
describe('compileComponentCodeGen', () => {
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
directives: DirectiveMetadata[] = CONST_EXPR([])): Promise<string[]> {
var type = new TypeMetadata({typeName: 'SomeComp'});
var parsedTemplate = parser.parse(domParser.parse(template, 'TestComp'), directives);
var sourceModule =
directives: NormalizedDirectiveMetadata[] = CONST_EXPR([])):
Promise<string[]> {
var type = new TypeMetadata({name: 'SomeComp'});
var parsedTemplate = parser.parse(template, directives, 'TestComp');
var sourceExpression =
compiler.compileComponentCodeGen(type, ChangeDetectionStrategy.Default, parsedTemplate);
var testableModule = createTestableModule(sourceModule, 0);
var testableModule = createTestableModule(sourceExpression, 0).getSourceWithImports();
return evalModule(testableModule.source, testableModule.imports, null);
}
it('should watch element properties', inject([AsyncTestCompleter], (async) => {
detectChanges(createCompiler(true), '<div [el-prop]="someProp">')
detectChanges(compiler, '<div [el-prop]="someProp">')
.then((value) => {
expect(value).toEqual(['elementProperty(elProp)=someValue']);
async.done();
@ -111,19 +122,12 @@ export function main() {
});
}
function createTestableModule(sourceModule: SourceModule, changeDetectorIndex: number):
SourceModule {
var testableSource;
var testableImports = [[MODULE_NAME, 'mocks']].concat(sourceModule.imports);
if (IS_DART) {
testableSource = `${sourceModule.source}
run(_) { return mocks.testChangeDetector(CHANGE_DETECTORS[${changeDetectorIndex}]); }`;
} else {
testableSource = `${sourceModule.source}
exports.run = function(_) { return mocks.testChangeDetector(CHANGE_DETECTORS[${changeDetectorIndex}]); }`;
}
return new SourceModule(null, testableSource, testableImports);
function createTestableModule(source: SourceExpression, changeDetectorIndex: number): SourceModule {
var resultExpression =
`${THIS_MODULE_REF}testChangeDetector((${source.expression})[${changeDetectorIndex}])`;
var testableSource = `${source.declarations.join('\n')}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
return new SourceModule(null, testableSource);
}
export function testChangeDetector(changeDetectorFactory: Function): string[] {

View File

@ -9,16 +9,13 @@ import {
beforeEach,
afterEach,
AsyncTestCompleter,
inject
inject,
beforeEachBindings
} from 'angular2/test_lib';
import {IS_DART} from '../platform';
import {CONST_EXPR, stringify, isType, Type, isBlank} from 'angular2/src/core/facade/lang';
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
import {HtmlParser} from 'angular2/src/compiler/html_parser';
import {TemplateParser} from 'angular2/src/compiler/template_parser';
import {MockSchemaRegistry} from './template_parser_spec';
import {Parser, Lexer} from 'angular2/src/core/change_detection/change_detection';
import {
CommandVisitor,
TextCmd,
@ -32,14 +29,19 @@ import {
} from 'angular2/src/core/compiler/template_commands';
import {CommandCompiler} from 'angular2/src/compiler/command_compiler';
import {
DirectiveMetadata,
NormalizedDirectiveMetadata,
TypeMetadata,
TemplateMetadata,
SourceModule
} from 'angular2/src/compiler/api';
NormalizedTemplateMetadata
} from 'angular2/src/compiler/directive_metadata';
import {SourceModule, SourceExpression, moduleRef} from 'angular2/src/compiler/source_module';
import {ViewEncapsulation} from 'angular2/src/core/render/api';
import {evalModule} from './eval_module';
import {escapeSingleQuoteString} from 'angular2/src/compiler/util';
import {
escapeSingleQuoteString,
codeGenValueFn,
codeGenExportVariable
} from 'angular2/src/compiler/util';
import {TEST_BINDINGS} from './test_bindings';
const BEGIN_ELEMENT = 'BEGIN_ELEMENT';
const END_ELEMENT = 'END_ELEMENT';
@ -50,8 +52,9 @@ const NG_CONTENT = 'NG_CONTENT';
const EMBEDDED_TEMPLATE = 'EMBEDDED_TEMPLATE';
// Attention: These module names have to correspond to real modules!
const MODULE_NAME = 'angular2/test/compiler/command_compiler_spec';
const TEMPLATE_COMMANDS_MODULE_NAME = 'angular2/src/core/compiler/template_commands';
const THIS_MODULE_NAME = 'angular2/test/compiler/command_compiler_spec';
var THIS_MODULE_REF = moduleRef(THIS_MODULE_NAME);
var TEMPLATE_COMMANDS_MODULE_REF = moduleRef('angular2/src/core/compiler/template_commands');
// Attention: read by eval!
export class RootComp {}
@ -59,27 +62,26 @@ export class SomeDir {}
export class AComp {}
var RootCompTypeMeta =
new TypeMetadata({typeName: 'RootComp', id: 1, type: RootComp, typeUrl: MODULE_NAME});
new TypeMetadata({id: 1, name: 'RootComp', runtime: RootComp, moduleId: THIS_MODULE_NAME});
var SomeDirTypeMeta =
new TypeMetadata({typeName: 'SomeDir', id: 2, type: SomeDir, typeUrl: MODULE_NAME});
var ACompTypeMeta = new TypeMetadata({typeName: 'AComp', id: 3, type: AComp, typeUrl: MODULE_NAME});
new TypeMetadata({id: 2, name: 'SomeDir', runtime: SomeDir, moduleId: THIS_MODULE_NAME});
var ACompTypeMeta =
new TypeMetadata({id: 3, name: 'AComp', runtime: AComp, moduleId: THIS_MODULE_NAME});
var NESTED_COMPONENT = new CompiledTemplate('someNestedComponentId', []);
var NESTED_COMPONENT = new CompiledTemplate(45, () => []);
export function main() {
describe('CommandCompiler', () => {
var domParser: HtmlParser;
beforeEachBindings(() => TEST_BINDINGS);
var parser: TemplateParser;
var commandCompiler: CommandCompiler;
var componentTemplateFactory: Function;
beforeEach(() => {
domParser = new HtmlParser();
parser = new TemplateParser(
new Parser(new Lexer()),
new MockSchemaRegistry({'invalidProp': false}, {'mappedAttr': 'mappedProp'}));
commandCompiler = new CommandCompiler();
});
beforeEach(inject([TemplateParser, CommandCompiler], (_templateParser, _commandCompiler) => {
parser = _templateParser;
commandCompiler = _commandCompiler;
}));
function createComp({type, selector, template, encapsulation, ngContentSelectors}: {
type?: TypeMetadata,
@ -87,7 +89,7 @@ export function main() {
template?: string,
encapsulation?: ViewEncapsulation,
ngContentSelectors?: string[]
}): DirectiveMetadata {
}): NormalizedDirectiveMetadata {
if (isBlank(encapsulation)) {
encapsulation = ViewEncapsulation.None;
}
@ -100,11 +102,11 @@ export function main() {
if (isBlank(template)) {
template = '';
}
return new DirectiveMetadata({
return new NormalizedDirectiveMetadata({
selector: selector,
isComponent: true,
type: type,
template: new TemplateMetadata({
template: new NormalizedTemplateMetadata({
template: template,
ngContentSelectors: ngContentSelectors,
encapsulation: encapsulation
@ -112,8 +114,8 @@ export function main() {
});
}
function createDirective(type: TypeMetadata, selector: string): DirectiveMetadata {
return new DirectiveMetadata({selector: selector, isComponent: false, type: type});
function createDirective(type: TypeMetadata, selector: string): NormalizedDirectiveMetadata {
return new NormalizedDirectiveMetadata({selector: selector, isComponent: false, type: type});
}
@ -229,7 +231,7 @@ export function main() {
['ACompType'],
false,
null,
'AComp'
3
],
[END_COMPONENT]
]);
@ -258,7 +260,7 @@ export function main() {
['ACompType'],
false,
null,
'AComp'
3
],
[END_COMPONENT]
]);
@ -273,7 +275,7 @@ export function main() {
run(rootComp, [comp])
.then((data) => {
expect(data).toEqual([
[BEGIN_COMPONENT, 'a', [], [], [], ['ACompType'], true, null, 'AComp'],
[BEGIN_COMPONENT, 'a', [], [], [], ['ACompType'], true, null, 3],
[END_COMPONENT]
]);
async.done();
@ -287,7 +289,7 @@ export function main() {
run(rootComp, [comp])
.then((data) => {
expect(data).toEqual([
[BEGIN_COMPONENT, 'a', [], [], [], ['ACompType'], false, null, 'AComp'],
[BEGIN_COMPONENT, 'a', [], [], [], ['ACompType'], false, null, 3],
[TEXT, 't', false, 0],
[END_COMPONENT]
]);
@ -362,15 +364,15 @@ export function main() {
describe('compileComponentRuntime', () => {
beforeEach(() => {
componentTemplateFactory = (directiveType: TypeMetadata) => {
return new CompiledTemplate(directiveType.typeName, []);
componentTemplateFactory = (directive: NormalizedDirectiveMetadata) => {
return new CompiledTemplate(directive.type.id, () => []);
};
});
function run(component: DirectiveMetadata, directives: DirectiveMetadata[]):
Promise<any[][]> {
var parsedTemplate = parser.parse(
domParser.parse(component.template.template, component.type.typeName), directives);
function run(component: NormalizedDirectiveMetadata,
directives: NormalizedDirectiveMetadata[]): Promise<any[][]> {
var parsedTemplate =
parser.parse(component.template.template, directives, component.type.name);
var commands = commandCompiler.compileComponentRuntime(component, parsedTemplate,
componentTemplateFactory);
return PromiseWrapper.resolve(humanize(commands));
@ -382,19 +384,18 @@ export function main() {
describe('compileComponentCodeGen', () => {
beforeEach(() => {
componentTemplateFactory = (directiveType: TypeMetadata, imports: string[][]) => {
imports.push([TEMPLATE_COMMANDS_MODULE_NAME, 'tcm']);
return `new tcm.CompiledTemplate(${escapeSingleQuoteString(directiveType.typeName)}, [])`;
componentTemplateFactory = (directive: NormalizedDirectiveMetadata) => {
return `new ${TEMPLATE_COMMANDS_MODULE_REF}CompiledTemplate(${directive.type.id}, ${codeGenValueFn([], '{}')})`;
};
});
function run(component: DirectiveMetadata, directives: DirectiveMetadata[]):
Promise<any[][]> {
var parsedTemplate = parser.parse(
domParser.parse(component.template.template, component.type.typeName), directives);
function run(component: NormalizedDirectiveMetadata,
directives: NormalizedDirectiveMetadata[]): Promise<any[][]> {
var parsedTemplate =
parser.parse(component.template.template, directives, component.type.name);
var sourceModule = commandCompiler.compileComponentCodeGen(component, parsedTemplate,
componentTemplateFactory);
var testableModule = createTestableModule(sourceModule);
var testableModule = createTestableModule(sourceModule).getSourceWithImports();
return evalModule(testableModule.source, testableModule.imports, null);
}
@ -453,6 +454,7 @@ class CommandHumanizer implements CommandVisitor {
cmd.directives.map(checkAndStringifyType),
cmd.nativeShadow,
cmd.ngContentIndex,
// TODO humanizeTemplate(cmd.template)
cmd.template.id
]);
return null;
@ -475,15 +477,9 @@ class CommandHumanizer implements CommandVisitor {
}
}
function createTestableModule(sourceModule: SourceModule): SourceModule {
var testableSource;
var testableImports = [[MODULE_NAME, 'mocks']].concat(sourceModule.imports);
if (IS_DART) {
testableSource = `${sourceModule.source}
run(_) { return mocks.humanize(COMMANDS); }`;
} else {
testableSource = `${sourceModule.source}
exports.run = function(_) { return mocks.humanize(COMMANDS); }`;
}
return new SourceModule(null, testableSource, testableImports);
function createTestableModule(source: SourceExpression): SourceModule {
var resultExpression = `${THIS_MODULE_REF}humanize(${source.expression})`;
var testableSource = `${source.declarations.join('\n')}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
return new SourceModule(null, testableSource);
}

View File

@ -13,28 +13,29 @@ import {
} from 'angular2/test_lib';
import {
DirectiveMetadata,
NormalizedDirectiveMetadata,
TypeMetadata,
TemplateMetadata,
NormalizedTemplateMetadata,
ChangeDetectionMetadata
} from 'angular2/src/compiler/api';
} from 'angular2/src/compiler/directive_metadata';
import {ViewEncapsulation} from 'angular2/src/core/render/api';
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection';
export function main() {
describe('Compiler api', () => {
describe('DirectiveMetadata', () => {
var fullTypeMeta: TypeMetadata;
var fullTemplateMeta: TemplateMetadata;
var fullTemplateMeta: NormalizedTemplateMetadata;
var fullChangeDetectionMeta: ChangeDetectionMetadata;
var fullDirectiveMeta: DirectiveMetadata;
var fullDirectiveMeta: NormalizedDirectiveMetadata;
beforeEach(() => {
fullTypeMeta = new TypeMetadata({id: 23, typeName: 'SomeType', typeUrl: 'someUrl'});
fullTemplateMeta = new TemplateMetadata({
fullTypeMeta = new TypeMetadata({id: 23, name: 'SomeType', moduleId: 'someUrl'});
fullTemplateMeta = new NormalizedTemplateMetadata({
encapsulation: ViewEncapsulation.Emulated,
template: '<a></a>',
styles: ['someStyle'],
styleAbsUrls: ['someStyleUrl'],
hostAttributes: {'attr1': 'attrValue2'},
ngContentSelectors: ['*']
});
fullChangeDetectionMeta = new ChangeDetectionMetadata({
@ -51,10 +52,10 @@ export function main() {
callDoCheck: true,
callOnInit: true
});
fullDirectiveMeta = new DirectiveMetadata({
fullDirectiveMeta = new NormalizedDirectiveMetadata({
selector: 'someSelector',
isComponent: true,
hostAttributes: {'attr1': 'attrValue2'},
dynamicLoadable: true,
type: fullTypeMeta, template: fullTemplateMeta,
changeDetection: fullChangeDetectionMeta,
});
@ -63,12 +64,13 @@ export function main() {
describe('DirectiveMetadata', () => {
it('should serialize with full data', () => {
expect(DirectiveMetadata.fromJson(fullDirectiveMeta.toJson())).toEqual(fullDirectiveMeta);
expect(NormalizedDirectiveMetadata.fromJson(fullDirectiveMeta.toJson()))
.toEqual(fullDirectiveMeta);
});
it('should serialize with no data', () => {
var empty = new DirectiveMetadata();
expect(DirectiveMetadata.fromJson(empty.toJson())).toEqual(empty);
var empty = new NormalizedDirectiveMetadata();
expect(NormalizedDirectiveMetadata.fromJson(empty.toJson())).toEqual(empty);
});
});
@ -84,12 +86,13 @@ export function main() {
describe('TemplateMetadata', () => {
it('should serialize with full data', () => {
expect(TemplateMetadata.fromJson(fullTemplateMeta.toJson())).toEqual(fullTemplateMeta);
expect(NormalizedTemplateMetadata.fromJson(fullTemplateMeta.toJson()))
.toEqual(fullTemplateMeta);
});
it('should serialize with no data', () => {
var empty = new TemplateMetadata();
expect(TemplateMetadata.fromJson(empty.toJson())).toEqual(empty);
var empty = new NormalizedTemplateMetadata();
expect(NormalizedTemplateMetadata.fromJson(empty.toJson())).toEqual(empty);
});
});

View File

@ -7,31 +7,27 @@ Uri toDartDataUri(String source) {
}
createIsolateSource(String moduleSource, List<List<String>> moduleImports) {
var moduleSourceParts = [];
var moduleSourceParts = ['import "dart:isolate";'];
moduleImports.forEach((sourceImport) {
String modName = sourceImport[0];
String modAlias = sourceImport[1];
moduleSourceParts.add("import 'package:${modName}.dart' as ${modAlias};");
});
moduleSourceParts.add(moduleSource);
return """
import "dart:isolate";
import "${toDartDataUri(moduleSourceParts.join('\n'))}" as mut;
moduleSourceParts.add(moduleSource);
moduleSourceParts.add("""
main(List args, SendPort replyPort) {
replyPort.send(mut.run(args));
replyPort.send(run(args));
}
""");
return moduleSourceParts.join('\n');
}
""";
}
var timeStamp = new DateTime.now().millisecondsSinceEpoch;
dynamic callModule(dynamic data) { return data.map( (a) => a+1); }
evalModule(String moduleSource, List<List<String>> moduleImports, List args) {
String source = createIsolateSource(moduleSource, moduleImports);
evalModule(String moduleSource, List<List<String>> imports, List args) {
String source = createIsolateSource(moduleSource, imports);
Completer completer = new Completer();
RawReceivePort receivePort;
receivePort = new RawReceivePort( (message) {
@ -43,12 +39,12 @@ evalModule(String moduleSource, List<List<String>> moduleImports, List args) {
// With this, spawning multiple isolates gets faster as Darts does not
// reload the files from the server.
var packageRoot = Uri.parse('/packages_${timeStamp}');
return Isolate.spawnUri(toDartDataUri(source), args, receivePort.sendPort, packageRoot: packageRoot).then( (isolate) {
return Isolate.spawnUri(toDartDataUri(source), args, receivePort.sendPort, packageRoot: packageRoot).then( (isolate) {
RawReceivePort errorPort;
errorPort = new RawReceivePort( (message) {
completer.completeError(message);
});
isolate.addErrorListener(errorPort.sendPort);
return completer.future;
return completer.future;
});
}

View File

@ -1,18 +1,17 @@
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
import {isPresent, global} from 'angular2/src/core/facade/lang';
import {isPresent, global, StringWrapper} from 'angular2/src/core/facade/lang';
var evalCounter = 0;
function nextModuleName() {
function nextModuleId() {
return `evalScript${evalCounter++}`;
}
export function evalModule(moduleSource: string, moduleImports: string[][], args: any[]):
Promise<any> {
var moduleName = nextModuleName();
export function evalModule(moduleSource: string, imports: string[][], args: any[]): Promise<any> {
var moduleId = nextModuleId();
var moduleSourceWithImports = [];
var importModuleNames = [];
moduleImports.forEach(sourceImport => {
imports.forEach(sourceImport => {
var modName = sourceImport[0];
var modAlias = sourceImport[1];
importModuleNames.push(modName);
@ -28,8 +27,8 @@ export function evalModule(moduleSource: string, moduleImports: string[][], args
var moduleBody = new Function('require', 'exports', 'module', moduleSourceWithImports.join('\n'));
var System = global['System'];
if (isPresent(System) && isPresent(System.registerDynamic)) {
System.registerDynamic(moduleName, importModuleNames, false, moduleBody);
return <Promise<any>>System.import(moduleName).then((module) => module.run(args));
System.registerDynamic(moduleId, importModuleNames, false, moduleBody);
return <Promise<any>>System.import(moduleId).then((module) => module.run(args));
} else {
var exports = {};
moduleBody(require, exports, {});

View File

@ -19,14 +19,14 @@ import {evalModule} from './eval_module';
// when evaling the test module!
export var TEST_VALUE = 23;
const THIS_MODULE = 'angular2/test/compiler/eval_module_spec';
export function main() {
describe('evalModule', () => {
it('should call the "run" function and allow to use imports',
inject([AsyncTestCompleter], (async) => {
var moduleSource = IS_DART ? testDartModule : testJsModule;
var imports = [['angular2/test/compiler/eval_module_spec', 'testMod']];
evalModule(moduleSource, imports, [1])
evalModule(moduleSource, [[THIS_MODULE, 'tst']], [1])
.then((value) => {
expect(value).toEqual([1, 23]);
async.done();
@ -36,15 +36,15 @@ export function main() {
}
var testDartModule = `
run(data) {
data.add(testMod.TEST_VALUE);
run(data) {
data.add(tst.TEST_VALUE);
return data;
}
`;
var testJsModule = `
exports.run = function(data) {
data.push(testMod.TEST_VALUE);
data.push(tst.TEST_VALUE);
return data;
}
`;

View File

@ -0,0 +1,131 @@
import {
ddescribe,
describe,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
beforeEachBindings
} from 'angular2/test_lib';
import {stringify} from 'angular2/src/core/facade/lang';
import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
import {
Component,
View,
Directive,
ViewEncapsulation,
ChangeDetectionStrategy,
OnChanges,
OnInit,
DoCheck,
OnDestroy,
AfterContentInit,
AfterContentChecked,
AfterViewInit,
AfterViewChecked
} from 'angular2/core';
import {TEST_BINDINGS} from './test_bindings';
import {IS_DART} from '../platform';
export function main() {
describe('RuntimeMetadataResolver', () => {
beforeEachBindings(() => TEST_BINDINGS);
describe('getMetadata', () => {
it('should read metadata',
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
var meta = resolver.getMetadata(ComponentWithEverything);
expect(meta.selector).toEqual('someSelector');
expect(meta.isComponent).toBe(true);
expect(meta.dynamicLoadable).toBe(true);
expect(meta.type.runtime).toBe(ComponentWithEverything);
expect(meta.type.name).toEqual(stringify(ComponentWithEverything));
expect(meta.type.moduleId).toEqual('someModuleId');
expect(meta.changeDetection.callAfterContentChecked).toBe(true);
expect(meta.changeDetection.callAfterContentInit).toBe(true);
expect(meta.changeDetection.callAfterViewChecked).toBe(true);
expect(meta.changeDetection.callAfterViewInit).toBe(true);
expect(meta.changeDetection.callDoCheck).toBe(true);
expect(meta.changeDetection.callOnChanges).toBe(true);
expect(meta.changeDetection.callOnInit).toBe(true);
expect(meta.changeDetection.changeDetection).toBe(ChangeDetectionStrategy.CheckAlways);
expect(meta.changeDetection.properties).toEqual(['someProp']);
expect(meta.changeDetection.events).toEqual(['someEvent']);
expect(meta.changeDetection.hostListeners)
.toEqual({'someHostListener': 'someHostListenerExpr'});
expect(meta.changeDetection.hostProperties)
.toEqual({'someHostProp': 'someHostPropExpr'});
expect(meta.template.encapsulation).toBe(ViewEncapsulation.Emulated);
expect(meta.template.hostAttributes).toEqual({'someHostAttr': 'someHostAttrValue'});
expect(meta.template.styles).toEqual(['someStyle']);
expect(meta.template.styleUrls).toEqual(['someStyleUrl']);
expect(meta.template.template).toEqual('someTemplate');
expect(meta.template.templateUrl).toEqual('someTemplateUrl');
}));
it('should use the moduleId from the reflector if none is given',
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
var expectedValue = IS_DART ? 'angular2/test/compiler/runtime_metadata_spec' : null;
expect(resolver.getMetadata(DirectiveWithoutModuleId).type.moduleId)
.toEqual(expectedValue);
}));
});
describe('getViewDirectivesMetadata', () => {
it('should return the directive metadatas',
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
expect(resolver.getViewDirectivesMetadata(ComponentWithEverything))
.toEqual([resolver.getMetadata(DirectiveWithoutModuleId)]);
}));
});
});
}
@Directive({selector: 'someSelector'})
class DirectiveWithoutModuleId {
}
@Component({
selector: 'someSelector',
properties: ['someProp'],
events: ['someEvent'],
host: {
'[someHostProp]': 'someHostPropExpr',
'(someHostListener)': 'someHostListenerExpr',
'someHostAttr': 'someHostAttrValue'
},
dynamicLoadable: true,
moduleId: 'someModuleId',
changeDetection: ChangeDetectionStrategy.CheckAlways
})
@View({
template: 'someTemplate',
templateUrl: 'someTemplateUrl',
encapsulation: ViewEncapsulation.Emulated,
styles: ['someStyle'],
styleUrls: ['someStyleUrl'],
directives: [DirectiveWithoutModuleId]
})
class ComponentWithEverything implements OnChanges,
OnInit, DoCheck, OnDestroy, AfterContentInit, AfterContentChecked, AfterViewInit,
AfterViewChecked {
onChanges(changes: StringMap<string, any>): void {}
onInit(): void {}
doCheck(): void {}
onDestroy(): void {}
afterContentInit(): void {}
afterContentChecked(): void {}
afterViewInit(): void {}
afterViewChecked(): void {}
}

View File

@ -0,0 +1,17 @@
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
import {StringMap} from 'angular2/src/core/facade/collection';
import {isPresent} from 'angular2/src/core/facade/lang';
export class MockSchemaRegistry implements ElementSchemaRegistry {
constructor(public existingProperties: StringMap<string, boolean>,
public attrPropMapping: StringMap<string, string>) {}
hasProperty(tagName: string, property: string): boolean {
var result = this.existingProperties[property];
return isPresent(result) ? result : true;
}
getMappedPropName(attrName: string): string {
var result = this.attrPropMapping[attrName];
return isPresent(result) ? result : attrName;
}
}

View File

@ -0,0 +1,43 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
TestComponentBuilder
} from 'angular2/test_lib';
import {SourceModule, moduleRef} from 'angular2/src/compiler/source_module';
export function main() {
describe('SourceModule', () => {
describe('getSourceWithImports', () => {
it('should generate named imports for modules', () => {
var sourceWithImports =
new SourceModule('some/moda', `${moduleRef('some/modb')}A`).getSourceWithImports();
expect(sourceWithImports.source).toEqual('import0.A');
expect(sourceWithImports.imports).toEqual([['some/modb', 'import0']]);
});
it('should dedupe imports', () => {
var sourceWithImports =
new SourceModule('some/moda', `${moduleRef('some/modb')}A + ${moduleRef('some/modb')}B`)
.getSourceWithImports();
expect(sourceWithImports.source).toEqual('import0.A + import0.B');
expect(sourceWithImports.imports).toEqual([['some/modb', 'import0']]);
});
it('should not use an import for the moduleId of the SourceModule', () => {
var sourceWithImports =
new SourceModule('some/moda', `${moduleRef('some/moda')}A`).getSourceWithImports();
expect(sourceWithImports.source).toEqual('A');
expect(sourceWithImports.imports).toEqual([]);
});
});
});
}

View File

@ -9,19 +9,27 @@ import {
beforeEach,
afterEach,
AsyncTestCompleter,
inject
inject,
beforeEachBindings
} from 'angular2/test_lib';
import {IS_DART} from '../platform';
import {bind} from 'angular2/src/core/di';
import {SpyXHR} from '../core/spies';
import {XHR} from 'angular2/src/core/render/xhr';
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
import {CONST_EXPR, isPresent, StringWrapper} from 'angular2/src/core/facade/lang';
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
import {evalModule} from './eval_module';
import {StyleCompiler} from 'angular2/src/compiler/style_compiler';
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
import {DirectiveMetadata, TemplateMetadata, TypeMetadata} from 'angular2/src/compiler/api';
import {
NormalizedDirectiveMetadata,
NormalizedTemplateMetadata,
TypeMetadata
} from 'angular2/src/compiler/directive_metadata';
import {SourceExpression, SourceModule} from 'angular2/src/compiler/source_module';
import {ViewEncapsulation} from 'angular2/src/core/render/api';
import {TEST_BINDINGS} from './test_bindings';
import {codeGenValueFn, codeGenExportVariable} from 'angular2/src/compiler/util';
// Attention: These module names have to correspond to real modules!
const MODULE_NAME = 'angular2/test/compiler/style_compiler_spec';
@ -33,19 +41,21 @@ const IMPORT_ABS_MODULE_NAME_WITH_IMPORT =
export function main() {
describe('StyleCompiler', () => {
var compiler: StyleCompiler;
var xhr;
beforeEach(() => {
var xhr: SpyXHR;
beforeEachBindings(() => {
xhr = <any>new SpyXHR();
compiler = new StyleCompiler(xhr, new UrlResolver());
return [TEST_BINDINGS, bind(XHR).toValue(xhr)];
});
var compiler: StyleCompiler;
beforeEach(inject([StyleCompiler], (_compiler) => { compiler = _compiler; }));
function comp(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation):
DirectiveMetadata {
return new DirectiveMetadata({
type: new TypeMetadata({id: 23, typeUrl: 'someUrl'}),
template: new TemplateMetadata(
NormalizedDirectiveMetadata {
return new NormalizedDirectiveMetadata({
type: new TypeMetadata({id: 23, moduleId: 'someUrl'}),
template: new NormalizedTemplateMetadata(
{styles: styles, styleAbsUrls: styleAbsUrls, encapsulation: encapsulation})
});
}
@ -117,9 +127,10 @@ export function main() {
function runTest(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation,
expectedStyles: string[]) {
return inject([AsyncTestCompleter], (async) => {
var sourceModule =
var sourceExpression =
compiler.compileComponentCodeGen(comp(styles, styleAbsUrls, encapsulation));
evalModule(testableModule(sourceModule.source), sourceModule.imports, null)
var sourceWithImports = testableExpression(sourceExpression).getSourceWithImports();
evalModule(sourceWithImports.source, sourceWithImports.imports, null)
.then((value) => {
compareStyles(value, expectedStyles);
async.done();
@ -163,9 +174,12 @@ export function main() {
function runTest(style: string, expectedStyles: string[], expectedShimStyles: string[]) {
return inject([AsyncTestCompleter], (async) => {
var sourceModules = compiler.compileStylesheetCodeGen(MODULE_NAME, style);
PromiseWrapper.all(sourceModules.map(sourceModule =>
evalModule(testableModule(sourceModule.source),
sourceModule.imports, null)))
PromiseWrapper.all(sourceModules.map(sourceModule => {
var sourceWithImports =
testableModule(sourceModule).getSourceWithImports();
return evalModule(sourceWithImports.source, sourceWithImports.imports,
null);
}))
.then((values) => {
compareStyles(values[0], expectedStyles);
compareStyles(values[1], expectedShimStyles);
@ -188,16 +202,17 @@ export function main() {
});
}
function testableModule(sourceModule: string) {
if (IS_DART) {
return `${sourceModule}
run(_) { return STYLES; }
`;
} else {
return `${sourceModule}
exports.run = function(_) { return STYLES; };
`;
}
function testableExpression(source: SourceExpression): SourceModule {
var testableSource = `${source.declarations.join('\n')}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], source.expression)};`;
return new SourceModule(null, testableSource);
}
function testableModule(sourceModule: SourceModule): SourceModule {
var testableSource = `${sourceModule.source}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], 'STYLES')};`;
return new SourceModule(sourceModule.moduleId, testableSource);
}
// Needed for Android browsers which add an extra space at the end of some lines

View File

@ -0,0 +1,334 @@
import {
ddescribe,
describe,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
beforeEachBindings
} from 'angular2/test_lib';
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
import {Type, isPresent, isBlank, stringify, isString} from 'angular2/src/core/facade/lang';
import {MapWrapper, SetWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
import {
TemplateCompiler,
NormalizedComponentWithViewDirectives
} from 'angular2/src/compiler/template_compiler';
import {
DirectiveMetadata,
NormalizedDirectiveMetadata,
INormalizedDirectiveMetadata
} from 'angular2/src/compiler/directive_metadata';
import {evalModule} from './eval_module';
import {SourceModule, moduleRef} from 'angular2/src/compiler/source_module';
import {XHR} from 'angular2/src/core/render/xhr';
import {MockXHR} from 'angular2/src/core/render/xhr_mock';
import {Locals} from 'angular2/src/core/change_detection/change_detection';
import {
CommandVisitor,
TextCmd,
NgContentCmd,
BeginElementCmd,
BeginComponentCmd,
EmbeddedTemplateCmd,
TemplateCmd,
visitAllCommands,
CompiledTemplate
} from 'angular2/src/core/compiler/template_commands';
import {Component, View, Directive} from 'angular2/core';
import {TEST_BINDINGS} from './test_bindings';
import {TestContext, TestDispatcher, TestPipes} from './change_detector_mocks';
import {codeGenValueFn, codeGenExportVariable} from 'angular2/src/compiler/util';
// Attention: This path has to point to this test file!
const THIS_MODULE = 'angular2/test/compiler/template_compiler_spec';
var THIS_MODULE_REF = moduleRef(THIS_MODULE);
export function main() {
describe('TemplateCompiler', () => {
var compiler: TemplateCompiler;
var runtimeMetadataResolver: RuntimeMetadataResolver;
beforeEachBindings(() => TEST_BINDINGS);
beforeEach(inject([TemplateCompiler, RuntimeMetadataResolver],
(_compiler, _runtimeMetadataResolver) => {
compiler = _compiler;
runtimeMetadataResolver = _runtimeMetadataResolver;
}));
describe('compile templates', () => {
function runTests(compile) {
it('should compile host components', inject([AsyncTestCompleter], (async) => {
compile([CompWithBindingsAndStyles])
.then((humanizedTemplate) => {
expect(humanizedTemplate['styles']).toEqual([]);
expect(humanizedTemplate['commands'][0]).toEqual('<comp-a>');
expect(humanizedTemplate['cd']).toEqual(['elementProperty(title)=someDirValue']);
async.done();
});
}));
it('should compile nested components', inject([AsyncTestCompleter], (async) => {
compile([CompWithBindingsAndStyles])
.then((humanizedTemplate) => {
var nestedTemplate = humanizedTemplate['commands'][1];
expect(nestedTemplate['styles']).toEqual(['div {color: red}']);
expect(nestedTemplate['commands'][0]).toEqual('<a>');
expect(nestedTemplate['cd']).toEqual(['elementProperty(href)=someCtxValue']);
async.done();
});
}));
it('should compile recursive components', inject([AsyncTestCompleter], (async) => {
compile([TreeComp])
.then((humanizedTemplate) => {
expect(humanizedTemplate['commands'][0]).toEqual('<tree>');
expect(humanizedTemplate['commands'][1]['commands'][0]).toEqual('<tree>');
expect(humanizedTemplate['commands'][1]['commands'][1]['commands'][0])
.toEqual('<tree>');
async.done();
});
}));
}
describe('compileHostComponentRuntime', () => {
function compile(components: Type[]): Promise<any[]> {
return compiler.compileHostComponentRuntime(components[0]).then(humanizeTemplate);
}
runTests(compile);
it('should cache components', inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
// we expect only one request!
xhr.expect('angular2/test/compiler/compUrl.html', '');
PromiseWrapper.all([
compiler.compileHostComponentRuntime(CompWithTemplateUrl),
compiler.compileHostComponentRuntime(CompWithTemplateUrl)
])
.then((humanizedTemplates) => {
expect(humanizedTemplates[0]).toEqual(humanizedTemplates[1]);
async.done();
});
xhr.flush();
}));
it('should only allow dynamic loadable components', () => {
expect(() => compiler.compileHostComponentRuntime(PlainDirective))
.toThrowError(
`Could not compile '${stringify(PlainDirective)}' because it is not dynamically loadable.`);
expect(() => compiler.compileHostComponentRuntime(CompWithoutHost))
.toThrowError(
`Could not compile '${stringify(CompWithoutHost)}' because it is not dynamically loadable.`);
});
});
describe('compileTemplatesCodeGen', () => {
function normalizeComponent(component: Type):
Promise<NormalizedComponentWithViewDirectives> {
var compAndViewDirMetas = [runtimeMetadataResolver.getMetadata(component)].concat(
runtimeMetadataResolver.getViewDirectivesMetadata(component));
return PromiseWrapper.all(compAndViewDirMetas.map(meta =>
compiler.normalizeDirective(meta)))
.then((normalizedCompAndViewDirMetas: NormalizedDirectiveMetadata[]) =>
new NormalizedComponentWithViewDirectives(
normalizedCompAndViewDirMetas[0],
normalizedCompAndViewDirMetas.slice(1)));
}
function compile(components: Type[]): Promise<any[]> {
return PromiseWrapper.all(components.map(normalizeComponent))
.then((normalizedCompWithViewDirMetas: NormalizedComponentWithViewDirectives[]) => {
var sourceModule =
compiler.compileTemplatesCodeGen(THIS_MODULE, normalizedCompWithViewDirMetas);
var sourceWithImports =
testableTemplateModule(sourceModule,
normalizedCompWithViewDirMetas[0].component)
.getSourceWithImports();
return evalModule(sourceWithImports.source, sourceWithImports.imports, null);
});
}
runTests(compile);
});
});
describe('serializeTemplateMetadata and deserializeTemplateMetadata', () => {
it('should serialize and deserialize', inject([AsyncTestCompleter], (async) => {
compiler.normalizeDirective(
runtimeMetadataResolver.getMetadata(CompWithBindingsAndStyles))
.then((meta: NormalizedDirectiveMetadata) => {
var json = compiler.serializeTemplateMetadata(meta);
expect(isString(json)).toBe(true);
// Note: serializing will clear our the runtime type!
var clonedMeta =
<NormalizedDirectiveMetadata>compiler.deserializeTemplateMetadata(json);
expect(meta.changeDetection).toEqual(clonedMeta.changeDetection);
expect(meta.template).toEqual(clonedMeta.template);
expect(meta.selector).toEqual(clonedMeta.selector);
expect(meta.type.name).toEqual(clonedMeta.type.name);
async.done();
});
}));
});
describe('normalizeDirective', () => {
it('should normalize the template',
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
xhr.expect('angular2/test/compiler/compUrl.html', 'loadedTemplate');
compiler.normalizeDirective(runtimeMetadataResolver.getMetadata(CompWithTemplateUrl))
.then((meta: NormalizedDirectiveMetadata) => {
expect(meta.template.template).toEqual('loadedTemplate');
async.done();
});
xhr.flush();
}));
it('should copy all the other fields', inject([AsyncTestCompleter], (async) => {
var meta = runtimeMetadataResolver.getMetadata(CompWithBindingsAndStyles);
compiler.normalizeDirective(meta).then((normMeta: NormalizedDirectiveMetadata) => {
expect(normMeta.selector).toEqual(meta.selector);
expect(normMeta.dynamicLoadable).toEqual(meta.dynamicLoadable);
expect(normMeta.isComponent).toEqual(meta.isComponent);
expect(normMeta.type).toEqual(meta.type);
expect(normMeta.changeDetection).toEqual(meta.changeDetection);
async.done();
});
}));
});
describe('compileStylesheetCodeGen', () => {
it('should compile stylesheets into code', inject([AsyncTestCompleter], (async) => {
var cssText = 'div {color: red}';
var sourceModule = compiler.compileStylesheetCodeGen('someModuleId', cssText)[0];
var sourceWithImports = testableStylesModule(sourceModule).getSourceWithImports();
evalModule(sourceWithImports.source, sourceWithImports.imports, null)
.then(loadedCssText => {
expect(loadedCssText).toEqual([cssText]);
async.done();
});
}));
});
});
}
@Component({
selector: 'comp-a',
dynamicLoadable: true,
host: {'[title]': 'someProp'},
moduleId: THIS_MODULE
})
@View({template: '<a [href]="someProp"></a>', styles: ['div {color: red}']})
class CompWithBindingsAndStyles {
}
@Component({selector: 'tree', dynamicLoadable: true, moduleId: THIS_MODULE})
@View({template: '<tree></tree>', directives: [TreeComp]})
class TreeComp {
}
@Component({selector: 'comp-url', dynamicLoadable: true, moduleId: THIS_MODULE})
@View({templateUrl: 'compUrl.html'})
class CompWithTemplateUrl {
}
@Directive({selector: 'plain', moduleId: THIS_MODULE})
class PlainDirective {
}
@Component({selector: 'comp', moduleId: THIS_MODULE})
@View({template: ''})
class CompWithoutHost {
}
function testableTemplateModule(sourceModule: SourceModule, comp: INormalizedDirectiveMetadata):
SourceModule {
var normComp = <NormalizedDirectiveMetadata>comp;
var resultExpression = `${THIS_MODULE_REF}humanizeTemplate(Host${normComp.type.name}Template)`;
var testableSource = `${sourceModule.source}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
return new SourceModule(sourceModule.moduleId, testableSource);
}
function testableStylesModule(sourceModule: SourceModule): SourceModule {
var testableSource = `${sourceModule.source}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], 'STYLES')};`;
return new SourceModule(sourceModule.moduleId, testableSource);
}
// Attention: read by eval!
export function humanizeTemplate(template: CompiledTemplate,
humanizedTemplates: Map<number, StringMap<string, any>> = null):
StringMap<string, any> {
if (isBlank(humanizedTemplates)) {
humanizedTemplates = new Map();
}
var result = humanizedTemplates.get(template.id);
if (isPresent(result)) {
return result;
}
var commands = [];
result = {
'styles': template.styles,
'commands': commands,
'cd': testChangeDetector(template.changeDetectorFactories[0])
};
humanizedTemplates.set(template.id, result);
visitAllCommands(new CommandHumanizer(commands, humanizedTemplates), template.commands);
return result;
}
function testChangeDetector(changeDetectorFactory: Function): string[] {
var ctx = new TestContext();
ctx.someProp = 'someCtxValue';
var dir1 = new TestContext();
dir1.someProp = 'someDirValue';
var dispatcher = new TestDispatcher([dir1], []);
var cd = changeDetectorFactory(dispatcher);
var locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
cd.hydrate(ctx, locals, dispatcher, new TestPipes());
cd.detectChanges();
return dispatcher.log;
}
class CommandHumanizer implements CommandVisitor {
constructor(private result: any[],
private humanizedTemplates: Map<number, StringMap<string, any>>) {}
visitText(cmd: TextCmd, context: any): any { return null; }
visitNgContent(cmd: NgContentCmd, context: any): any { return null; }
visitBeginElement(cmd: BeginElementCmd, context: any): any {
this.result.push(`<${cmd.name}>`);
return null;
}
visitEndElement(context: any): any {
this.result.push('</>');
return null;
}
visitBeginComponent(cmd: BeginComponentCmd, context: any): any {
this.result.push(`<${cmd.name}>`);
this.result.push(humanizeTemplate(cmd.template, this.humanizedTemplates));
return null;
}
visitEndComponent(context: any): any { return this.visitEndElement(context); }
visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any { return null; }
}

View File

@ -1,176 +0,0 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
TestComponentBuilder
} from 'angular2/test_lib';
import {HtmlParser} from 'angular2/src/compiler/html_parser';
import {TypeMetadata, TemplateMetadata} from 'angular2/src/compiler/api';
import {ViewEncapsulation} from 'angular2/src/core/render/api';
import {TemplateLoader} from 'angular2/src/compiler/template_loader';
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
import {XHR} from 'angular2/src/core/render/xhr';
import {MockXHR} from 'angular2/src/core/render/xhr_mock';
export function main() {
describe('TemplateLoader', () => {
var loader: TemplateLoader;
var dirType: TypeMetadata;
var xhr: MockXHR;
var htmlParser: HtmlParser;
beforeEach(inject([XHR], (mockXhr) => {
xhr = mockXhr;
htmlParser = new HtmlParser();
loader = new TemplateLoader(xhr, new UrlResolver(), htmlParser);
dirType = new TypeMetadata({typeUrl: 'http://sometypeurl', typeName: 'SomeComp'});
}));
describe('loadTemplate', () => {
describe('inline template', () => {
it('should store the template', inject([AsyncTestCompleter], (async) => {
loader.loadTemplate(dirType, null, 'a', null, [], ['test.css'])
.then((template: TemplateMetadata) => {
expect(template.template).toEqual('a');
async.done();
});
}));
it('should resolve styles against the typeUrl', inject([AsyncTestCompleter], (async) => {
loader.loadTemplate(dirType, null, 'a', null, [], ['test.css'])
.then((template: TemplateMetadata) => {
expect(template.styleAbsUrls).toEqual(['http://sometypeurl/test.css']);
async.done();
});
}));
});
describe('templateUrl', () => {
it('should load a template from a url that is resolved against typeUrl',
inject([AsyncTestCompleter], (async) => {
xhr.expect('http://sometypeurl/sometplurl', 'a');
loader.loadTemplate(dirType, null, null, 'sometplurl', [], ['test.css'])
.then((template: TemplateMetadata) => {
expect(template.template).toEqual('a');
async.done();
});
xhr.flush();
}));
it('should resolve styles against the templateUrl',
inject([AsyncTestCompleter], (async) => {
xhr.expect('http://sometypeurl/tpl/sometplurl', 'a');
loader.loadTemplate(dirType, null, null, 'tpl/sometplurl', [], ['test.css'])
.then((template: TemplateMetadata) => {
expect(template.styleAbsUrls).toEqual(['http://sometypeurl/tpl/test.css']);
async.done();
});
xhr.flush();
}));
});
});
describe('loadTemplateFromString', () => {
it('should store the viewEncapsulationin the result', () => {
var viewEncapsulation = ViewEncapsulation.Native;
var template = loader.createTemplateFromString(dirType, viewEncapsulation, '',
'http://someurl/', [], []);
expect(template.encapsulation).toBe(viewEncapsulation);
});
it('should keep the template as html', () => {
var template =
loader.createTemplateFromString(dirType, null, 'a', 'http://someurl/', [], []);
expect(template.template).toEqual('a')
});
it('should collect and keep ngContent', () => {
var template = loader.createTemplateFromString(
dirType, null, '<ng-content select="a"></ng-content>', 'http://someurl/', [], []);
expect(template.ngContentSelectors).toEqual(['a']);
expect(template.template).toEqual('<ng-content select="a"></ng-content>');
});
it('should normalize ngContent wildcard selector', () => {
var template = loader.createTemplateFromString(
dirType, null,
'<ng-content></ng-content><ng-content select></ng-content><ng-content select="*"></ng-content>',
'http://someurl/', [], []);
expect(template.ngContentSelectors).toEqual(['*', '*', '*']);
});
it('should collect and remove top level styles in the template', () => {
var template = loader.createTemplateFromString(dirType, null, '<style>a</style>',
'http://someurl/', [], []);
expect(template.styles).toEqual(['a']);
expect(template.template).toEqual('');
});
it('should collect and remove styles inside in elements', () => {
var template = loader.createTemplateFromString(dirType, null, '<div><style>a</style></div>',
'http://someurl/', [], []);
expect(template.styles).toEqual(['a']);
expect(template.template).toEqual('<div></div>');
});
it('should collect and remove styleUrls in the template', () => {
var template = loader.createTemplateFromString(
dirType, null, '<link rel="stylesheet" href="aUrl">', 'http://someurl/', [], []);
expect(template.styleAbsUrls).toEqual(['http://someurl/aUrl']);
expect(template.template).toEqual('');
});
it('should collect and remove styleUrls in elements', () => {
var template = loader.createTemplateFromString(
dirType, null, '<div><link rel="stylesheet" href="aUrl"></div>', 'http://someurl/', [],
[]);
expect(template.styleAbsUrls).toEqual(['http://someurl/aUrl']);
expect(template.template).toEqual('<div></div>');
});
it('should keep link elements with non stylesheet rel attribute', () => {
var template = loader.createTemplateFromString(
dirType, null, '<link href="b" rel="a"></link>', 'http://someurl/', [], []);
expect(template.styleAbsUrls).toEqual([]);
expect(template.template).toEqual('<link href="b" rel="a"></link>');
});
it('should extract @import style urls into styleAbsUrl', () => {
var template = loader.createTemplateFromString(dirType, null, '', 'http://someurl',
['@import "test.css";'], []);
expect(template.styles).toEqual(['']);
expect(template.styleAbsUrls).toEqual(['http://someurl/test.css']);
});
it('should resolve relative urls in inline styles', () => {
var template =
loader.createTemplateFromString(dirType, null, '', 'http://someurl',
['.foo{background-image: url(\'double.jpg\');'], []);
expect(template.styles)
.toEqual(['.foo{background-image: url(\'http://someurl/double.jpg\');']);
});
it('should resolve relative style urls in styleUrls', () => {
var template =
loader.createTemplateFromString(dirType, null, '', 'http://someurl', [], ['test.css']);
expect(template.styles).toEqual([]);
expect(template.styleAbsUrls).toEqual(['http://someurl/test.css']);
});
});
});
}

View File

@ -0,0 +1,268 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
TestComponentBuilder,
beforeEachBindings
} from 'angular2/test_lib';
import {
TypeMetadata,
NormalizedTemplateMetadata,
TemplateMetadata
} from 'angular2/src/compiler/directive_metadata';
import {ViewEncapsulation} from 'angular2/src/core/render/api';
import {TemplateNormalizer} from 'angular2/src/compiler/template_normalizer';
import {XHR} from 'angular2/src/core/render/xhr';
import {MockXHR} from 'angular2/src/core/render/xhr_mock';
import {TEST_BINDINGS} from './test_bindings';
export function main() {
describe('TemplateNormalizer', () => {
var dirType: TypeMetadata;
beforeEachBindings(() => TEST_BINDINGS);
beforeEach(
() => { dirType = new TypeMetadata({moduleId: 'some/module/id', name: 'SomeComp'}); });
describe('loadTemplate', () => {
describe('inline template', () => {
it('should store the template',
inject([AsyncTestCompleter, TemplateNormalizer],
(async, normalizer: TemplateNormalizer) => {
normalizer.normalizeTemplate(dirType, new TemplateMetadata({
encapsulation: null,
template: 'a',
templateUrl: null,
styles: [],
styleUrls: ['test.css']
}))
.then((template: NormalizedTemplateMetadata) => {
expect(template.template).toEqual('a');
async.done();
});
}));
it('should resolve styles on the annotation against the moduleId',
inject([AsyncTestCompleter, TemplateNormalizer],
(async, normalizer: TemplateNormalizer) => {
normalizer.normalizeTemplate(dirType, new TemplateMetadata({
encapsulation: null,
template: '',
templateUrl: null,
styles: [],
styleUrls: ['test.css']
}))
.then((template: NormalizedTemplateMetadata) => {
expect(template.styleAbsUrls).toEqual(['some/module/test.css']);
async.done();
});
}));
it('should resolve styles in the template against the moduleId',
inject([AsyncTestCompleter, TemplateNormalizer],
(async, normalizer: TemplateNormalizer) => {
normalizer.normalizeTemplate(dirType, new TemplateMetadata({
encapsulation: null,
template: '<style>@import test.css</style>',
templateUrl: null,
styles: [],
styleUrls: []
}))
.then((template: NormalizedTemplateMetadata) => {
expect(template.styleAbsUrls).toEqual(['some/module/test.css']);
async.done();
});
}));
});
describe('templateUrl', () => {
it('should load a template from a url that is resolved against moduleId',
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
xhr.expect('some/module/sometplurl', 'a');
normalizer.normalizeTemplate(dirType, new TemplateMetadata({
encapsulation: null,
template: null,
templateUrl: 'sometplurl',
styles: [],
styleUrls: ['test.css']
}))
.then((template: NormalizedTemplateMetadata) => {
expect(template.template).toEqual('a');
async.done();
});
xhr.flush();
}));
it('should resolve styles on the annotation against the moduleId',
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
xhr.expect('some/module/tpl/sometplurl', '');
normalizer.normalizeTemplate(dirType, new TemplateMetadata({
encapsulation: null,
template: null,
templateUrl: 'tpl/sometplurl',
styles: [],
styleUrls: ['test.css']
}))
.then((template: NormalizedTemplateMetadata) => {
expect(template.styleAbsUrls).toEqual(['some/module/test.css']);
async.done();
});
xhr.flush();
}));
it('should resolve styles in the template against the templateUrl',
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
xhr.expect('some/module/tpl/sometplurl', '<style>@import test.css</style>');
normalizer.normalizeTemplate(dirType, new TemplateMetadata({
encapsulation: null,
template: null,
templateUrl: 'tpl/sometplurl',
styles: [],
styleUrls: []
}))
.then((template: NormalizedTemplateMetadata) => {
expect(template.styleAbsUrls).toEqual(['some/module/tpl/test.css']);
async.done();
});
xhr.flush();
}));
});
});
describe('normalizeLoadedTemplate', () => {
it('should store the viewEncapsulationin the result',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var viewEncapsulation = ViewEncapsulation.Native;
var template = normalizer.normalizeLoadedTemplate(
dirType,
new TemplateMetadata({encapsulation: viewEncapsulation, styles: [], styleUrls: []}),
'', 'some/module/');
expect(template.encapsulation).toBe(viewEncapsulation);
}));
it('should keep the template as html',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), 'a',
'some/module/');
expect(template.template).toEqual('a')
}));
it('should collect and keep ngContent',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<ng-content select="a"></ng-content>', 'some/module/');
expect(template.ngContentSelectors).toEqual(['a']);
expect(template.template).toEqual('<ng-content select="a"></ng-content>');
}));
it('should normalize ngContent wildcard selector',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<ng-content></ng-content><ng-content select></ng-content><ng-content select="*"></ng-content>',
'some/module/');
expect(template.ngContentSelectors).toEqual(['*', '*', '*']);
}));
it('should collect and remove top level styles in the template',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<style>a</style>', 'some/module/');
expect(template.styles).toEqual(['a']);
expect(template.template).toEqual('');
}));
it('should collect and remove styles inside in elements',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<div><style>a</style></div>', 'some/module/');
expect(template.styles).toEqual(['a']);
expect(template.template).toEqual('<div></div>');
}));
it('should collect and remove styleUrls in the template',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<link rel="stylesheet" href="aUrl">', 'some/module/');
expect(template.styleAbsUrls).toEqual(['some/module/aUrl']);
expect(template.template).toEqual('');
}));
it('should collect and remove styleUrls in elements',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<div><link rel="stylesheet" href="aUrl"></div>', 'some/module/');
expect(template.styleAbsUrls).toEqual(['some/module/aUrl']);
expect(template.template).toEqual('<div></div>');
}));
it('should keep link elements with non stylesheet rel attribute',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<link href="b" rel="a"></link>', 'some/module/');
expect(template.styleAbsUrls).toEqual([]);
expect(template.template).toEqual('<link href="b" rel="a"></link>');
}));
it('should extract @import style urls into styleAbsUrl',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType, new TemplateMetadata(
{encapsulation: null, styles: ['@import "test.css";'], styleUrls: []}),
'', 'some/module/id');
expect(template.styles).toEqual(['']);
expect(template.styleAbsUrls).toEqual(['some/module/test.css']);
}));
it('should resolve relative urls in inline styles',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType, new TemplateMetadata({
encapsulation: null,
styles: ['.foo{background-image: url(\'double.jpg\');'],
styleUrls: []
}),
'', 'some/module/id');
expect(template.styles)
.toEqual(['.foo{background-image: url(\'some/module/double.jpg\');']);
}));
it('should resolve relative style urls in styleUrls',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new TemplateMetadata({encapsulation: null, styles: [], styleUrls: ['test.css']}), '',
'some/module/id');
expect(template.styles).toEqual([]);
expect(template.styleAbsUrls).toEqual(['some/module/test.css']);
}));
});
});
}

View File

@ -1,15 +1,26 @@
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
import {isPresent} from 'angular2/src/core/facade/lang';
import {Parser, Lexer} from 'angular2/src/core/change_detection/change_detection';
import {TemplateParser, splitClasses} from 'angular2/src/compiler/template_parser';
import {HtmlParser} from 'angular2/src/compiler/html_parser';
import {
DirectiveMetadata,
ddescribe,
describe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
inject,
beforeEachBindings
} from 'angular2/test_lib';
import {bind} from 'angular2/src/core/di';
import {TEST_BINDINGS} from './test_bindings';
import {isPresent} from 'angular2/src/core/facade/lang';
import {TemplateParser, splitClasses} from 'angular2/src/compiler/template_parser';
import {
NormalizedDirectiveMetadata,
TypeMetadata,
ChangeDetectionMetadata,
TemplateMetadata
} from 'angular2/src/compiler/api';
NormalizedTemplateMetadata
} from 'angular2/src/compiler/directive_metadata';
import {
templateVisitAll,
TemplateAstVisitor,
@ -29,6 +40,7 @@ import {
} from 'angular2/src/compiler/template_ast';
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
import {MockSchemaRegistry} from './schema_registry_mock';
import {Unparser} from '../core/change_detection/parser/unparser';
@ -36,24 +48,26 @@ var expressionUnparser = new Unparser();
export function main() {
describe('TemplateParser', () => {
var domParser: HtmlParser;
beforeEachBindings(() => [
TEST_BINDINGS,
bind(ElementSchemaRegistry)
.toValue(new MockSchemaRegistry({'invalidProp': false}, {'mappedAttr': 'mappedProp'}))
]);
var parser: TemplateParser;
var ngIf;
beforeEach(() => {
domParser = new HtmlParser();
parser = new TemplateParser(
new Parser(new Lexer()),
new MockSchemaRegistry({'invalidProp': false}, {'mappedAttr': 'mappedProp'}));
ngIf = new DirectiveMetadata({
beforeEach(inject([TemplateParser], (_parser) => {
parser = _parser;
ngIf = new NormalizedDirectiveMetadata({
selector: '[ng-if]',
type: new TypeMetadata({typeName: 'NgIf'}),
type: new TypeMetadata({name: 'NgIf'}),
changeDetection: new ChangeDetectionMetadata({properties: ['ngIf']})
});
});
}));
function parse(template: string, directives: DirectiveMetadata[]): TemplateAst[] {
return parser.parse(domParser.parse(template, 'TestComp'), directives);
function parse(template: string, directives: NormalizedDirectiveMetadata[]): TemplateAst[] {
return parser.parse(template, directives, 'TestComp');
}
describe('parse', () => {
@ -341,16 +355,16 @@ export function main() {
});
describe('directives', () => {
it('should locate directives ordered by typeName and components first', () => {
var dirA = new DirectiveMetadata(
{selector: '[a=b]', type: new TypeMetadata({typeName: 'DirA'})});
var dirB =
new DirectiveMetadata({selector: '[a]', type: new TypeMetadata({typeName: 'DirB'})});
var comp = new DirectiveMetadata({
it('should locate directives ordered by name and components first', () => {
var dirA = new NormalizedDirectiveMetadata(
{selector: '[a=b]', type: new TypeMetadata({name: 'DirA'})});
var dirB = new NormalizedDirectiveMetadata(
{selector: '[a]', type: new TypeMetadata({name: 'DirB'})});
var comp = new NormalizedDirectiveMetadata({
selector: 'div',
isComponent: true,
type: new TypeMetadata({typeName: 'ZComp'}),
template: new TemplateMetadata({ngContentSelectors: []})
type: new TypeMetadata({name: 'ZComp'}),
template: new NormalizedTemplateMetadata({ngContentSelectors: []})
});
expect(humanizeTemplateAsts(parse('<div a="b">', [dirB, dirA, comp])))
.toEqual([
@ -363,10 +377,10 @@ export function main() {
});
it('should locate directives in property bindings', () => {
var dirA = new DirectiveMetadata(
{selector: '[a=b]', type: new TypeMetadata({typeName: 'DirA'})});
var dirB =
new DirectiveMetadata({selector: '[b]', type: new TypeMetadata({typeName: 'DirB'})});
var dirA = new NormalizedDirectiveMetadata(
{selector: '[a=b]', type: new TypeMetadata({name: 'DirA'})});
var dirB = new NormalizedDirectiveMetadata(
{selector: '[b]', type: new TypeMetadata({name: 'DirB'})});
expect(humanizeTemplateAsts(parse('<div [a]="b">', [dirA, dirB])))
.toEqual([
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
@ -383,10 +397,10 @@ export function main() {
});
it('should locate directives in variable bindings', () => {
var dirA = new DirectiveMetadata(
{selector: '[a=b]', type: new TypeMetadata({typeName: 'DirA'})});
var dirB =
new DirectiveMetadata({selector: '[b]', type: new TypeMetadata({typeName: 'DirB'})});
var dirA = new NormalizedDirectiveMetadata(
{selector: '[a=b]', type: new TypeMetadata({name: 'DirA'})});
var dirB = new NormalizedDirectiveMetadata(
{selector: '[b]', type: new TypeMetadata({name: 'DirB'})});
expect(humanizeTemplateAsts(parse('<div #a="b">', [dirA, dirB])))
.toEqual([
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
@ -396,9 +410,9 @@ export function main() {
});
it('should parse directive host properties', () => {
var dirA = new DirectiveMetadata({
var dirA = new NormalizedDirectiveMetadata({
selector: 'div',
type: new TypeMetadata({typeName: 'DirA'}),
type: new TypeMetadata({name: 'DirA'}),
changeDetection: new ChangeDetectionMetadata({hostProperties: {'a': 'expr'}})
});
expect(humanizeTemplateAsts(parse('<div></div>', [dirA])))
@ -417,9 +431,9 @@ export function main() {
});
it('should parse directive host listeners', () => {
var dirA = new DirectiveMetadata({
var dirA = new NormalizedDirectiveMetadata({
selector: 'div',
type: new TypeMetadata({typeName: 'DirA'}),
type: new TypeMetadata({name: 'DirA'}),
changeDetection: new ChangeDetectionMetadata({hostListeners: {'a': 'expr'}})
});
expect(humanizeTemplateAsts(parse('<div></div>', [dirA])))
@ -431,9 +445,9 @@ export function main() {
});
it('should parse directive properties', () => {
var dirA = new DirectiveMetadata({
var dirA = new NormalizedDirectiveMetadata({
selector: 'div',
type: new TypeMetadata({typeName: 'DirA'}),
type: new TypeMetadata({name: 'DirA'}),
changeDetection: new ChangeDetectionMetadata({properties: ['aProp']})
});
expect(humanizeTemplateAsts(parse('<div [a-prop]="expr"></div>', [dirA])))
@ -450,9 +464,9 @@ export function main() {
});
it('should parse renamed directive properties', () => {
var dirA = new DirectiveMetadata({
var dirA = new NormalizedDirectiveMetadata({
selector: 'div',
type: new TypeMetadata({typeName: 'DirA'}),
type: new TypeMetadata({name: 'DirA'}),
changeDetection: new ChangeDetectionMetadata({properties: ['b:a']})
});
expect(humanizeTemplateAsts(parse('<div [a]="expr"></div>', [dirA])))
@ -464,9 +478,9 @@ export function main() {
});
it('should parse literal directive properties', () => {
var dirA = new DirectiveMetadata({
var dirA = new NormalizedDirectiveMetadata({
selector: 'div',
type: new TypeMetadata({typeName: 'DirA'}),
type: new TypeMetadata({name: 'DirA'}),
changeDetection: new ChangeDetectionMetadata({properties: ['a']})
});
expect(humanizeTemplateAsts(parse('<div a="literal"></div>', [dirA])))
@ -484,9 +498,9 @@ export function main() {
});
it('should support optional directive properties', () => {
var dirA = new DirectiveMetadata({
var dirA = new NormalizedDirectiveMetadata({
selector: 'div',
type: new TypeMetadata({typeName: 'DirA'}),
type: new TypeMetadata({name: 'DirA'}),
changeDetection: new ChangeDetectionMetadata({properties: ['a']})
});
expect(humanizeTemplateAsts(parse('<div></div>', [dirA])))
@ -549,13 +563,13 @@ export function main() {
describe('directives', () => {
it('should locate directives in property bindings', () => {
var dirA = new DirectiveMetadata({
var dirA = new NormalizedDirectiveMetadata({
selector: '[a=b]',
type: new TypeMetadata({typeName: 'DirA'}),
type: new TypeMetadata({name: 'DirA'}),
changeDetection: new ChangeDetectionMetadata({properties: ['a']})
});
var dirB = new DirectiveMetadata(
{selector: '[b]', type: new TypeMetadata({typeName: 'DirB'})});
var dirB = new NormalizedDirectiveMetadata(
{selector: '[b]', type: new TypeMetadata({name: 'DirB'})});
expect(humanizeTemplateAsts(parse('<div template="a b" b>', [dirA, dirB])))
.toEqual([
[EmbeddedTemplateAst, 'TestComp > div:nth-child(0)'],
@ -573,10 +587,10 @@ export function main() {
});
it('should locate directives in variable bindings', () => {
var dirA = new DirectiveMetadata(
{selector: '[a=b]', type: new TypeMetadata({typeName: 'DirA'})});
var dirB = new DirectiveMetadata(
{selector: '[b]', type: new TypeMetadata({typeName: 'DirB'})});
var dirA = new NormalizedDirectiveMetadata(
{selector: '[a=b]', type: new TypeMetadata({name: 'DirA'})});
var dirB = new NormalizedDirectiveMetadata(
{selector: '[b]', type: new TypeMetadata({name: 'DirB'})});
expect(humanizeTemplateAsts(parse('<div template="#a=b" b>', [dirA, dirB])))
.toEqual([
[EmbeddedTemplateAst, 'TestComp > div:nth-child(0)'],
@ -609,12 +623,13 @@ export function main() {
});
describe('content projection', () => {
function createComp(selector: string, ngContentSelectors: string[]): DirectiveMetadata {
return new DirectiveMetadata({
function createComp(selector: string, ngContentSelectors: string[]):
NormalizedDirectiveMetadata {
return new NormalizedDirectiveMetadata({
selector: selector,
isComponent: true,
type: new TypeMetadata({typeName: 'SomeComp'}),
template: new TemplateMetadata({ngContentSelectors: ngContentSelectors})
type: new TypeMetadata({name: 'SomeComp'}),
template: new NormalizedTemplateMetadata({ngContentSelectors: ngContentSelectors})
})
}
@ -710,26 +725,26 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp > div:nth-ch
it('should not throw on invalid property names if the property is used by a directive',
() => {
var dirA = new DirectiveMetadata({
var dirA = new NormalizedDirectiveMetadata({
selector: 'div',
type: new TypeMetadata({typeName: 'DirA'}),
type: new TypeMetadata({name: 'DirA'}),
changeDetection: new ChangeDetectionMetadata({properties: ['invalidProp']})
});
expect(() => parse('<div [invalid-prop]></div>', [dirA])).not.toThrow();
});
it('should not allow more than 1 component per element', () => {
var dirA = new DirectiveMetadata({
var dirA = new NormalizedDirectiveMetadata({
selector: 'div',
isComponent: true,
type: new TypeMetadata({typeName: 'DirA'}),
template: new TemplateMetadata({ngContentSelectors: []})
type: new TypeMetadata({name: 'DirA'}),
template: new NormalizedTemplateMetadata({ngContentSelectors: []})
});
var dirB = new DirectiveMetadata({
var dirB = new NormalizedDirectiveMetadata({
selector: 'div',
isComponent: true,
type: new TypeMetadata({typeName: 'DirB'}),
template: new TemplateMetadata({ngContentSelectors: []})
type: new TypeMetadata({name: 'DirB'}),
template: new NormalizedTemplateMetadata({ngContentSelectors: []})
});
expect(() => parse('<div>', [dirB, dirA])).toThrowError(`Template parse errors:
More than one component: DirA,DirB in TestComp > div:nth-child(0)`);
@ -737,11 +752,11 @@ More than one component: DirA,DirB in TestComp > div:nth-child(0)`);
it('should not allow components or element nor event bindings on explicit embedded templates',
() => {
var dirA = new DirectiveMetadata({
var dirA = new NormalizedDirectiveMetadata({
selector: '[a]',
isComponent: true,
type: new TypeMetadata({typeName: 'DirA'}),
template: new TemplateMetadata({ngContentSelectors: []})
type: new TypeMetadata({name: 'DirA'}),
template: new NormalizedTemplateMetadata({ngContentSelectors: []})
});
expect(() => parse('<template [a]="b" (e)="f"></template>', [dirA]))
.toThrowError(`Template parse errors:
@ -751,11 +766,11 @@ Event binding e on an embedded template in TestComp > template:nth-child(0)[(e)=
});
it('should not allow components or element bindings on inline embedded templates', () => {
var dirA = new DirectiveMetadata({
var dirA = new NormalizedDirectiveMetadata({
selector: '[a]',
isComponent: true,
type: new TypeMetadata({typeName: 'DirA'}),
template: new TemplateMetadata({ngContentSelectors: []})
type: new TypeMetadata({name: 'DirA'}),
template: new NormalizedTemplateMetadata({ngContentSelectors: []})
});
expect(() => parse('<div *a="b">', [dirA])).toThrowError(`Template parse errors:
Components on an embedded template: DirA in TestComp > div:nth-child(0)
@ -887,17 +902,3 @@ class TemplateContentProjectionHumanizer implements TemplateAstVisitor {
visitDirective(ast: DirectiveAst, context: any): any { return null; }
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
}
export class MockSchemaRegistry implements ElementSchemaRegistry {
constructor(public existingProperties: StringMap<string, boolean>,
public attrPropMapping: StringMap<string, string>) {}
hasProperty(tagName: string, property: string): boolean {
var result = this.existingProperties[property];
return isPresent(result) ? result : true;
}
getMappedPropName(attrName: string): string {
var result = this.attrPropMapping[attrName];
return isPresent(result) ? result : attrName;
}
}

View File

@ -0,0 +1,26 @@
import {bind, Binding} from 'angular2/src/core/di';
import {TemplateParser} from 'angular2/src/compiler/template_parser';
import {HtmlParser} from 'angular2/src/compiler/html_parser';
import {TemplateNormalizer} from 'angular2/src/compiler/template_normalizer';
import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler';
import {StyleCompiler} from 'angular2/src/compiler/style_compiler';
import {CommandCompiler} from 'angular2/src/compiler/command_compiler';
import {TemplateCompiler} from 'angular2/src/compiler/template_compiler';
import {ChangeDetectorGenConfig} from 'angular2/src/core/change_detection/change_detection';
import {MockSchemaRegistry} from './schema_registry_mock';
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
// TODO(tbosch): move this into test_injector once the new compiler pipeline is used fully
export var TEST_BINDINGS = [
HtmlParser,
TemplateParser,
TemplateNormalizer,
RuntimeMetadataResolver,
StyleCompiler,
CommandCompiler,
ChangeDetectionCompiler,
bind(ChangeDetectorGenConfig).toValue(new ChangeDetectorGenConfig(true, true, false, false)),
TemplateCompiler,
bind(ElementSchemaRegistry).toValue(new MockSchemaRegistry({}, {}))
];

View File

@ -242,6 +242,20 @@ export function main() {
expect(reflector.method("abc")("anything", ["fake"])).toEqual(['fake']);
});
});
if (IS_DART) {
describe("moduleId", () => {
it("should return the moduleId for a type", () => {
expect(reflector.moduleId(TestObjWith00Args))
.toEqual('angular2/test/core/reflection/reflector_spec');
});
it("should return an empty array otherwise", () => {
var p = reflector.interfaces(ClassWithDecorators);
expect(p).toEqual([]);
});
});
}
});
}