', []))
.toThrowError(
`Template parse errors:\nInvalid property name 'bar.foo' ("
][bar.foo]>"): TestComp@0:3`);
});
it('should parse bound properties via [...] and not report them as attributes', () => {
expect(humanizeTplAst(parse('
', [])))
.toEqual([
[ElementAst, 'div'],
[BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null]
]);
});
it('should parse bound properties via bind- and not report them as attributes', () => {
expect(humanizeTplAst(parse('
', [])))
.toEqual([
[ElementAst, 'div'],
[BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null]
]);
});
it('should parse bound properties via {{...}} and not report them as attributes', () => {
expect(humanizeTplAst(parse('
', [])))
.toEqual([
[ElementAst, 'div'],
[BoundElementPropertyAst, PropertyBindingType.Property, 'prop', '{{ v }}', null]
]);
});
});
describe('events', () => {
it('should parse bound events with a target', () => {
expect(humanizeTplAst(parse('
', [])))
.toEqual([[ElementAst, 'div'], [BoundEventAst, 'event', 'window', 'v']]);
});
it('should parse bound events via (...) and not report them as attributes', () => {
expect(humanizeTplAst(parse('
', [])))
.toEqual([[ElementAst, 'div'], [BoundEventAst, 'someEvent', null, 'v']]);
});
it('should parse bound events via on- and not report them as attributes', () => {
expect(humanizeTplAst(parse('
', [])))
.toEqual([[ElementAst, 'div'], [BoundEventAst, 'event', null, 'v']]);
});
it('should allow events on explicit embedded templates that are emitted by a directive',
() => {
var dirA = CompileDirectiveMetadata.create({
selector: 'template',
outputs: ['e'],
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'})
});
expect(humanizeTplAst(parse('', [dirA])))
.toEqual([
[EmbeddedTemplateAst],
[BoundEventAst, 'e', null, 'f'],
[DirectiveAst, dirA],
]);
});
});
describe('bindon', () => {
it('should parse bound events and properties via [(...)] and not report them as attributes',
() => {
expect(humanizeTplAst(parse('
', [])))
.toEqual([
[ElementAst, 'div'],
[BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null],
[BoundEventAst, 'propChange', null, 'v = $event']
]);
});
it('should parse bound events and properties via bindon- and not report them as attributes',
() => {
expect(humanizeTplAst(parse('
', [])))
.toEqual([
[ElementAst, 'div'],
[BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null],
[BoundEventAst, 'propChange', null, 'v = $event']
]);
});
});
describe('directives', () => {
it('should order directives by the directives array in the View and match them only once',
() => {
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'})
});
var dirB = CompileDirectiveMetadata.create({
selector: '[b]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirB'})
});
var dirC = CompileDirectiveMetadata.create({
selector: '[c]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirC'})
});
expect(humanizeTplAst(parse('
', [dirA, dirB])))
.toEqual([
[ElementAst, 'div'],
[BoundElementPropertyAst, PropertyBindingType.Property, 'a', 'b', null],
[DirectiveAst, dirA]
]);
});
it('should locate directives in event bindings', () => {
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirB'})
});
expect(humanizeTplAst(parse('
', [dirA])))
.toEqual(
[[ElementAst, 'div'], [BoundEventAst, 'a', null, 'b'], [DirectiveAst, dirA]]);
});
it('should parse directive host properties', () => {
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
host: {'[a]': 'expr'}
});
expect(humanizeTplAst(parse('', [dirA])))
.toEqual([
[ElementAst, 'div'],
[DirectiveAst, dirA],
[BoundElementPropertyAst, PropertyBindingType.Property, 'a', 'expr', null]
]);
});
it('should parse directive host listeners', () => {
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
host: {'(a)': 'expr'}
});
expect(humanizeTplAst(parse('', [dirA])))
.toEqual(
[[ElementAst, 'div'], [DirectiveAst, dirA], [BoundEventAst, 'a', null, 'expr']]);
});
it('should parse directive properties', () => {
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
inputs: ['aProp']
});
expect(humanizeTplAst(parse('', [dirA])))
.toEqual([
[ElementAst, 'div'],
[DirectiveAst, dirA],
[BoundDirectivePropertyAst, 'aProp', 'expr']
]);
});
it('should parse renamed directive properties', () => {
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
inputs: ['b:a']
});
expect(humanizeTplAst(parse('', [dirA])))
.toEqual([
[ElementAst, 'div'],
[DirectiveAst, dirA],
[BoundDirectivePropertyAst, 'b', 'expr']
]);
});
it('should parse literal directive properties', () => {
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
inputs: ['a']
});
expect(humanizeTplAst(parse('', [dirA])))
.toEqual([
[ElementAst, 'div'],
[AttrAst, 'a', 'literal'],
[DirectiveAst, dirA],
[BoundDirectivePropertyAst, 'a', '"literal"']
]);
});
it('should favor explicit bound properties over literal properties', () => {
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
inputs: ['a']
});
expect(humanizeTplAst(parse('', [dirA])))
.toEqual([
[ElementAst, 'div'],
[AttrAst, 'a', 'literal'],
[DirectiveAst, dirA],
[BoundDirectivePropertyAst, 'a', '"literal2"']
]);
});
it('should support optional directive properties', () => {
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
inputs: ['a']
});
expect(humanizeTplAst(parse('', [dirA])))
.toEqual([[ElementAst, 'div'], [DirectiveAst, dirA]]);
});
});
describe('providers', () => {
var nextProviderId;
function createToken(value: string): CompileTokenMetadata {
var token;
if (value.startsWith('type:')) {
token = new CompileTokenMetadata({
identifier:
new CompileTypeMetadata({moduleUrl: someModuleUrl, name: value.substring(5)})
});
} else {
token = new CompileTokenMetadata({value: value});
}
return token;
}
function createDep(value: string): CompileDiDependencyMetadata {
var isOptional = false;
if (value.startsWith('optional:')) {
isOptional = true;
value = value.substring(9);
}
var isSelf = false;
if (value.startsWith('self:')) {
isSelf = true;
value = value.substring(5);
}
var isHost = false;
if (value.startsWith('host:')) {
isHost = true;
value = value.substring(5);
}
return new CompileDiDependencyMetadata(
{token: createToken(value), isOptional: isOptional, isSelf: isSelf, isHost: isHost});
}
function createProvider(
token: string, {multi = false, deps = /*@ts2dart_const*/[
]}: {multi?: boolean, deps?: string[]} = {}): CompileProviderMetadata {
return new CompileProviderMetadata({
token: createToken(token),
multi: multi,
useClass: new CompileTypeMetadata({name: `provider${nextProviderId++}`}),
deps: deps.map(createDep)
});
}
function createDir(selector: string,
{providers = null, viewProviders = null, deps = /*@ts2dart_const*/[],
queries = /*@ts2dart_const*/[]}: {
providers?: CompileProviderMetadata[],
viewProviders?: CompileProviderMetadata[],
deps?: string[],
queries?: string[]
} = {}): CompileDirectiveMetadata {
var isComponent = !selector.startsWith('[');
return CompileDirectiveMetadata.create({
selector: selector,
type: new CompileTypeMetadata(
{moduleUrl: someModuleUrl, name: selector, diDeps: deps.map(createDep)}),
isComponent: isComponent,
template: new CompileTemplateMetadata({ngContentSelectors: []}),
providers: providers,
viewProviders: viewProviders,
queries: queries.map((value) =>
new CompileQueryMetadata({selectors: [createToken(value)]}))
});
}
beforeEach(() => { nextProviderId = 0; });
it('should provide a component', () => {
var comp = createDir('my-comp');
var elAst: ElementAst = parse('', [comp])[0];
expect(elAst.providers.length).toBe(1);
expect(elAst.providers[0].providerType).toBe(ProviderAstType.Component);
expect(elAst.providers[0].providers[0].useClass).toBe(comp.type);
});
it('should provide a directive', () => {
var dirA = createDir('[dirA]');
var elAst: ElementAst = parse('
', [dirA])[0];
expect(elAst.providers.length).toBe(1);
expect(elAst.providers[0].providerType).toBe(ProviderAstType.Directive);
expect(elAst.providers[0].providers[0].useClass).toBe(dirA.type);
});
it('should use the public providers of a directive', () => {
var provider = createProvider('service');
var dirA = createDir('[dirA]', {providers: [provider]});
var elAst: ElementAst = parse('
', [dirA])[0];
expect(elAst.providers.length).toBe(2);
expect(elAst.providers[1].providerType).toBe(ProviderAstType.PublicService);
expect(elAst.providers[1].providers).toEqual([provider]);
});
it('should use the private providers of a component', () => {
var provider = createProvider('service');
var comp = createDir('my-comp', {viewProviders: [provider]});
var elAst: ElementAst = parse('', [comp])[0];
expect(elAst.providers.length).toBe(2);
expect(elAst.providers[1].providerType).toBe(ProviderAstType.PrivateService);
expect(elAst.providers[1].providers).toEqual([provider]);
});
it('should support multi providers', () => {
var provider0 = createProvider('service0', {multi: true});
var provider1 = createProvider('service1', {multi: true});
var provider2 = createProvider('service0', {multi: true});
var dirA = createDir('[dirA]', {providers: [provider0, provider1]});
var dirB = createDir('[dirB]', {providers: [provider2]});
var elAst: ElementAst = parse('
', [dirA, dirB])[0];
expect(elAst.providers.length).toBe(4);
expect(elAst.providers[2].providers).toEqual([provider0, provider2]);
expect(elAst.providers[3].providers).toEqual([provider1]);
});
it('should overwrite non multi providers', () => {
var provider1 = createProvider('service0');
var provider2 = createProvider('service1');
var provider3 = createProvider('service0');
var dirA = createDir('[dirA]', {providers: [provider1, provider2]});
var dirB = createDir('[dirB]', {providers: [provider3]});
var elAst: ElementAst = parse('
', [dirA, dirB])[0];
expect(elAst.providers.length).toBe(4);
expect(elAst.providers[2].providers).toEqual([provider3]);
expect(elAst.providers[3].providers).toEqual([provider2]);
});
it('should overwrite component providers by directive providers', () => {
var compProvider = createProvider('service0');
var dirProvider = createProvider('service0');
var comp = createDir('my-comp', {providers: [compProvider]});
var dirA = createDir('[dirA]', {providers: [dirProvider]});
var elAst: ElementAst = parse('', [dirA, comp])[0];
expect(elAst.providers.length).toBe(3);
expect(elAst.providers[2].providers).toEqual([dirProvider]);
});
it('should overwrite view providers by directive providers', () => {
var viewProvider = createProvider('service0');
var dirProvider = createProvider('service0');
var comp = createDir('my-comp', {viewProviders: [viewProvider]});
var dirA = createDir('[dirA]', {providers: [dirProvider]});
var elAst: ElementAst = parse('', [dirA, comp])[0];
expect(elAst.providers.length).toBe(3);
expect(elAst.providers[2].providers).toEqual([dirProvider]);
});
it('should overwrite directives by providers', () => {
var dirProvider = createProvider('type:my-comp');
var comp = createDir('my-comp', {providers: [dirProvider]});
var elAst: ElementAst = parse('', [comp])[0];
expect(elAst.providers.length).toBe(1);
expect(elAst.providers[0].providers).toEqual([dirProvider]);
});
it('should throw if mixing multi and non multi providers', () => {
var provider0 = createProvider('service0');
var provider1 = createProvider('service0', {multi: true});
var dirA = createDir('[dirA]', {providers: [provider0]});
var dirB = createDir('[dirB]', {providers: [provider1]});
expect(() => parse('
', [dirA, dirB]))
.toThrowError(
`Template parse errors:\n` +
`Mixing multi and non multi provider is not possible for token service0 ("[ERROR ->]
"): TestComp@0:0`);
});
it('should sort providers by their DI order', () => {
var provider0 = createProvider('service0', {deps: ['type:[dir2]']});
var provider1 = createProvider('service1');
var dir2 = createDir('[dir2]', {deps: ['service1']});
var comp = createDir('my-comp', {providers: [provider0, provider1]});
var elAst: ElementAst = parse('', [comp, dir2])[0];
expect(elAst.providers.length).toBe(4);
expect(elAst.providers[0].providers[0].useClass).toEqual(comp.type);
expect(elAst.providers[1].providers).toEqual([provider1]);
expect(elAst.providers[2].providers[0].useClass).toEqual(dir2.type);
expect(elAst.providers[3].providers).toEqual([provider0]);
});
it('should sort directives by their DI order', () => {
var dir0 = createDir('[dir0]', {deps: ['type:my-comp']});
var dir1 = createDir('[dir1]', {deps: ['type:[dir0]']});
var dir2 = createDir('[dir2]', {deps: ['type:[dir1]']});
var comp = createDir('my-comp');
var elAst: ElementAst =
parse('', [comp, dir2, dir0, dir1])[0];
expect(elAst.providers.length).toBe(4);
expect(elAst.directives[0].directive).toBe(comp);
expect(elAst.directives[1].directive).toBe(dir0);
expect(elAst.directives[2].directive).toBe(dir1);
expect(elAst.directives[3].directive).toBe(dir2);
});
it('should mark directives and dependencies of directives as eager', () => {
var provider0 = createProvider('service0');
var provider1 = createProvider('service1');
var dirA = createDir('[dirA]', {providers: [provider0, provider1], deps: ['service0']});
var elAst: ElementAst = parse('
', [dirA])[0];
expect(elAst.providers.length).toBe(3);
expect(elAst.providers[0].providers).toEqual([provider0]);
expect(elAst.providers[0].eager).toBe(true);
expect(elAst.providers[1].providers[0].useClass).toEqual(dirA.type);
expect(elAst.providers[1].eager).toBe(true);
expect(elAst.providers[2].providers).toEqual([provider1]);
expect(elAst.providers[2].eager).toBe(false);
});
it('should mark dependencies on parent elements as eager', () => {
var provider0 = createProvider('service0');
var provider1 = createProvider('service1');
var dirA = createDir('[dirA]', {providers: [provider0, provider1]});
var dirB = createDir('[dirB]', {deps: ['service0']});
var elAst: ElementAst =
parse('
', [dirA, dirB])[0];
expect(elAst.providers.length).toBe(3);
expect(elAst.providers[0].providers[0].useClass).toEqual(dirA.type);
expect(elAst.providers[0].eager).toBe(true);
expect(elAst.providers[1].providers).toEqual([provider0]);
expect(elAst.providers[1].eager).toBe(true);
expect(elAst.providers[2].providers).toEqual([provider1]);
expect(elAst.providers[2].eager).toBe(false);
});
it('should mark queried providers as eager', () => {
var provider0 = createProvider('service0');
var provider1 = createProvider('service1');
var dirA =
createDir('[dirA]', {providers: [provider0, provider1], queries: ['service0']});
var elAst: ElementAst = parse('', [dirA])[0];
expect(elAst.providers.length).toBe(3);
expect(elAst.providers[0].providers[0].useClass).toEqual(dirA.type);
expect(elAst.providers[0].eager).toBe(true);
expect(elAst.providers[1].providers).toEqual([provider0]);
expect(elAst.providers[1].eager).toBe(true);
expect(elAst.providers[2].providers).toEqual([provider1]);
expect(elAst.providers[2].eager).toBe(false);
});
it('should not mark dependencies accross embedded views as eager', () => {
var provider0 = createProvider('service0');
var dirA = createDir('[dirA]', {providers: [provider0]});
var dirB = createDir('[dirB]', {deps: ['service0']});
var elAst: ElementAst =
parse('
', [dirA, dirB])[0];
expect(elAst.providers.length).toBe(2);
expect(elAst.providers[0].providers[0].useClass).toEqual(dirA.type);
expect(elAst.providers[0].eager).toBe(true);
expect(elAst.providers[1].providers).toEqual([provider0]);
expect(elAst.providers[1].eager).toBe(false);
});
it('should report missing @Self() deps as errors', () => {
var dirA = createDir('[dirA]', {deps: ['self:provider0']});
expect(() => parse('', [dirA]))
.toThrowErrorWith(
'No provider for provider0 ("[ERROR ->]"): TestComp@0:0');
});
it('should change missing @Self() that are optional to nulls', () => {
var dirA = createDir('[dirA]', {deps: ['optional:self:provider0']});
var elAst: ElementAst = parse('', [dirA])[0];
expect(elAst.providers[0].providers[0].deps[0].isValue).toBe(true);
expect(elAst.providers[0].providers[0].deps[0].value).toBe(null);
});
it('should report missing @Host() deps as errors', () => {
var dirA = createDir('[dirA]', {deps: ['host:provider0']});
expect(() => parse('', [dirA]))
.toThrowErrorWith(
'No provider for provider0 ("[ERROR ->]"): TestComp@0:0');
});
it('should change missing @Host() that are optional to nulls', () => {
var dirA = createDir('[dirA]', {deps: ['optional:host:provider0']});
var elAst: ElementAst = parse('', [dirA])[0];
expect(elAst.providers[0].providers[0].deps[0].isValue).toBe(true);
expect(elAst.providers[0].providers[0].deps[0].value).toBe(null);
});
});
describe('references', () => {
it('should parse references via #... and not report them as attributes', () => {
expect(humanizeTplAst(parse('
', [])))
.toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]);
});
it('should parse references via ref-... and not report them as attributes', () => {
expect(humanizeTplAst(parse('
', [])))
.toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]);
});
it('should parse references via var-... and report them as deprecated', () => {
expect(humanizeTplAst(parse('
', [])))
.toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]);
expect(console.warnings)
.toEqual([
[
'Template parse warnings:',
'"var-" on non elements is deprecated. Use "ref-" instead! ("
', [])))
.toEqual([[ElementAst, 'div'], [ReferenceAst, 'someA', null]]);
});
it('should assign references with empty value to the element', () => {
expect(humanizeTplAst(parse('', [])))
.toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]);
});
it('should assign references to directives via exportAs', () => {
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
exportAs: 'dirA'
});
expect(humanizeTplAst(parse('', [dirA])))
.toEqual([
[ElementAst, 'div'],
[AttrAst, 'a', ''],
[ReferenceAst, 'a', identifierToken(dirA.type)],
[DirectiveAst, dirA],
]);
});
it('should report references with values that dont match a directive as errors', () => {
expect(() => parse('', [])).toThrowError(`Template parse errors:
There is no directive with "exportAs" set to "dirA" ("
]#a="dirA">
"): TestComp@0:5`);
});
it('should report invalid reference names', () => {
expect(() => parse('', [])).toThrowError(`Template parse errors:
"-" is not allowed in reference names ("
]#a-b>
"): TestComp@0:5`);
});
it('should report variables as errors', () => {
expect(() => parse('', [])).toThrowError(`Template parse errors:
"let-" is only supported on template elements. ("
]let-a>
"): TestComp@0:5`);
});
it('should assign references with empty value to components', () => {
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
isComponent: true,
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
exportAs: 'dirA',
template: new CompileTemplateMetadata({ngContentSelectors: []})
});
expect(humanizeTplAst(parse('', [dirA])))
.toEqual([
[ElementAst, 'div'],
[AttrAst, 'a', ''],
[ReferenceAst, 'a', identifierToken(dirA.type)],
[DirectiveAst, dirA],
]);
});
it('should not locate directives in references', () => {
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'})
});
expect(humanizeTplAst(parse('
', [dirA])))
.toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]);
});
});
describe('explicit templates', () => {
it('should create embedded templates for elements', () => {
expect(humanizeTplAst(parse('', [])))
.toEqual([[EmbeddedTemplateAst]]);
expect(humanizeTplAst(parse('', [])))
.toEqual([[EmbeddedTemplateAst]]);
});
it('should create embedded templates for elements regardless the namespace',
() => {
expect(humanizeTplAst(parse('', [])))
.toEqual([
[ElementAst, '@svg:svg'],
[EmbeddedTemplateAst],
]);
});
it('should support references via #...', () => {
expect(humanizeTplAst(parse('', [])))
.toEqual([
[EmbeddedTemplateAst],
[ReferenceAst, 'a', identifierToken(Identifiers.TemplateRef)]
]);
});
it('should support references via ref-...', () => {
expect(humanizeTplAst(parse('', [])))
.toEqual([
[EmbeddedTemplateAst],
[ReferenceAst, 'a', identifierToken(Identifiers.TemplateRef)]
]);
});
it('should parse variables via let-...', () => {
expect(humanizeTplAst(parse('', [])))
.toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b']]);
});
it('should parse variables via var-... and report them as deprecated', () => {
expect(humanizeTplAst(parse('', [])))
.toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b']]);
expect(console.warnings)
.toEqual([
[
'Template parse warnings:',
'"var-" on elements is deprecated. Use "let-" instead! ("]var-a="b">"): TestComp@0:10'
].join('\n')
]);
});
it('should not locate directives in variables', () => {
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'})
});
expect(humanizeTplAst(parse('', [dirA])))
.toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b']]);
});
});
describe('inline templates', () => {
it('should wrap the element into an EmbeddedTemplateAST', () => {
expect(humanizeTplAst(parse('
', [ngIf])))
.toEqual([
[EmbeddedTemplateAst],
[DirectiveAst, ngIf],
[BoundDirectivePropertyAst, 'ngIf', 'test'],
[ElementAst, 'div']
]);
});
it('should parse variables via #... and report them as deprecated', () => {
expect(humanizeTplAst(parse('
', [])))
.toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b'], [ElementAst, 'div']]);
expect(console.warnings)
.toEqual([
[
'Template parse warnings:',
'"#" inside of expressions is deprecated. Use "let" instead! ("
]*ngIf="#a=b">"): TestComp@0:5'
].join('\n')
]);
});
it('should parse variables via var ... and report them as deprecated', () => {
expect(humanizeTplAst(parse('
', [])))
.toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b'], [ElementAst, 'div']]);
expect(console.warnings)
.toEqual([
[
'Template parse warnings:',
'"var" inside of expressions is deprecated. Use "let" instead! ("
]*ngIf="var a=b">"): TestComp@0:5'
].join('\n')
]);
});
it('should parse variables via let ...', () => {
expect(humanizeTplAst(parse('
', [])))
.toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b'], [ElementAst, 'div']]);
});
describe('directives', () => {
it('should locate directives in property bindings', () => {
var dirA = CompileDirectiveMetadata.create({
selector: '[a=b]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
inputs: ['a']
});
var dirB = CompileDirectiveMetadata.create({
selector: '[b]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirB'})
});
expect(humanizeTplAst(parse('
', [dirA, dirB])))
.toEqual([
[EmbeddedTemplateAst],
[DirectiveAst, dirA],
[BoundDirectivePropertyAst, 'a', 'b'],
[ElementAst, 'div'],
[AttrAst, 'b', ''],
[DirectiveAst, dirB]
]);
});
it('should not locate directives in variables', () => {
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'})
});
expect(humanizeTplAst(parse('
', [dirA])))
.toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b'], [ElementAst, 'div']]);
});
it('should not locate directives in references', () => {
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'})
});
expect(humanizeTplAst(parse('
', [dirA])))
.toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]);
});
});
it('should work with *... and use the attribute name as property binding name', () => {
expect(humanizeTplAst(parse('
', [ngIf])))
.toEqual([
[EmbeddedTemplateAst],
[DirectiveAst, ngIf],
[BoundDirectivePropertyAst, 'ngIf', 'test'],
[ElementAst, 'div']
]);
});
it('should work with *... and empty value', () => {
expect(humanizeTplAst(parse('
', [ngIf])))
.toEqual([
[EmbeddedTemplateAst],
[DirectiveAst, ngIf],
[BoundDirectivePropertyAst, 'ngIf', 'null'],
[ElementAst, 'div']
]);
});
});
});
describe('content projection', () => {
var compCounter;
beforeEach(() => { compCounter = 0; });
function createComp(selector: string,
ngContentSelectors: string[]): CompileDirectiveMetadata {
return CompileDirectiveMetadata.create({
selector: selector,
isComponent: true,
type:
new CompileTypeMetadata({moduleUrl: someModuleUrl, name: `SomeComp${compCounter++}`}),
template: new CompileTemplateMetadata({ngContentSelectors: ngContentSelectors})
})
}
function createDir(selector: string): CompileDirectiveMetadata {
return CompileDirectiveMetadata.create({
selector: selector,
type:
new CompileTypeMetadata({moduleUrl: someModuleUrl, name: `SomeDir${compCounter++}`})
})
}
describe('project text nodes', () => {
it('should project text nodes with wildcard selector', () => {
expect(humanizeContentProjection(parse('
',
[createComp('div', ['*'])])))
.toEqual([['div', null], ['#text({{hello}})', 0], ['span', 0]]);
});
it('should match the element when there is an inline template', () => {
expect(humanizeContentProjection(
parse('
',
[createComp('div', ['a', 'b']), ngIf])))
.toEqual([['div', null], ['template', 1], ['a', null]]);
});
});
it('should support other directives before the component', () => {
expect(humanizeContentProjection(
parse('
hello
', [createDir('div'), createComp('div', ['*'])])))
.toEqual([['div', null], ['#text(hello)', 0]]);
});
});
describe('splitClasses', () => {
it('should keep an empty class', () => { expect(splitClasses('a')).toEqual(['a']); });
it('should split 2 classes', () => { expect(splitClasses('a b')).toEqual(['a', 'b']); });
it('should trim classes', () => { expect(splitClasses(' a b ')).toEqual(['a', 'b']); });
});
describe('error cases', () => {
it('should report when ng-content has content', () => {
expect(() => parse('content', []))
.toThrowError(`Template parse errors:
element cannot have content. must be immediately followed by ("[ERROR ->]content"): TestComp@0:0`);
});
it('should report invalid property names', () => {
expect(() => parse('', [])).toThrowError(`Template parse errors:
Can't bind to 'invalidProp' since it isn't a known native property ("
][invalidProp]>
"): TestComp@0:5`);
});
it('should report errors in expressions', () => {
expect(() => parse('', [])).toThrowErrorWith(`Template parse errors:
Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("
][prop]="a b">
"): TestComp@0:5`);
});
it('should not throw on invalid property names if the property is used by a directive',
() => {
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
inputs: ['invalidProp']
});
expect(() => parse('', [dirA])).not.toThrow();
});
it('should not allow more than 1 component per element', () => {
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
isComponent: true,
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
template: new CompileTemplateMetadata({ngContentSelectors: []})
});
var dirB = CompileDirectiveMetadata.create({
selector: 'div',
isComponent: true,
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirB'}),
template: new CompileTemplateMetadata({ngContentSelectors: []})
});
expect(() => parse('
', [dirB, dirA])).toThrowError(`Template parse errors:
More than one component: DirB,DirA ("[ERROR ->]
"): TestComp@0:0`);
});
it('should not allow components or element bindings nor dom events on explicit embedded templates',
() => {
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
isComponent: true,
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
template: new CompileTemplateMetadata({ngContentSelectors: []})
});
expect(() => parse('', [dirA]))
.toThrowError(`Template parse errors:
Event binding e not emitted by any directive on an embedded template ("](e)="f">"): TestComp@0:18
Components on an embedded template: DirA ("[ERROR ->]"): TestComp@0:0
Property binding a not used by any directive on an embedded template ("[ERROR ->]"): TestComp@0:0`);
});
it('should not allow components or element bindings on inline embedded templates', () => {
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
isComponent: true,
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
template: new CompileTemplateMetadata({ngContentSelectors: []})
});
expect(() => parse('', [dirA])).toThrowError(`Template parse errors:
Components on an embedded template: DirA ("[ERROR ->]"): TestComp@0:0
Property binding a not used by any directive on an embedded template ("[ERROR ->]"): TestComp@0:0`);
});
});
describe('ignore elements', () => {
it('should ignore a', []))).toEqual([[TextAst, 'a']]);
});
it('should ignore a', []))).toEqual([[TextAst, 'a']]);
});
describe('', () => {
it('should keep elements if they have an absolute non package: url',
() => {
expect(humanizeTplAst(parse('a', [])))
.toEqual([
[ElementAst, 'link'],
[AttrAst, 'rel', 'stylesheet'],
[AttrAst, 'href', 'http://someurl'],
[TextAst, 'a']
]);
});
it('should keep elements if they have no uri', () => {
expect(humanizeTplAst(parse('a', [])))
.toEqual([[ElementAst, 'link'], [AttrAst, 'rel', 'stylesheet'], [TextAst, 'a']]);
expect(humanizeTplAst(parse('a', [])))
.toEqual([[ElementAst, 'link'], [AttrAst, 'REL', 'stylesheet'], [TextAst, 'a']]);
});
it('should ignore elements if they have a relative uri', () => {
expect(humanizeTplAst(parse('a', [])))
.toEqual([[TextAst, 'a']]);
expect(humanizeTplAst(parse('a', [])))
.toEqual([[TextAst, 'a']]);
});
it('should ignore elements if they have a package: uri', () => {
expect(humanizeTplAst(parse('a', [])))
.toEqual([[TextAst, 'a']]);
});
});
it('should ignore bindings on children of elements with ngNonBindable', () => {
expect(humanizeTplAst(parse('
{{b}}
', [])))
.toEqual([[ElementAst, 'div'], [AttrAst, 'ngNonBindable', ''], [TextAst, '{{b}}']]);
});
it('should keep nested children of elements with ngNonBindable', () => {
expect(humanizeTplAst(parse('
', [])))
.toEqual([[ElementAst, 'div'], [AttrAst, 'ngNonBindable', ''], [TextAst, 'a']]);
});
it('should ignore elements inside of elements with ngNonBindable',
() => {
expect(humanizeTplAst(parse('
a
', [])))
.toEqual([[ElementAst, 'div'], [AttrAst, 'ngNonBindable', ''], [TextAst, 'a']]);
});
it('should convert elements into regular elements inside of elements with ngNonBindable',
() => {
expect(humanizeTplAst(parse('
a
', [])))
.toEqual([
[ElementAst, 'div'],
[AttrAst, 'ngNonBindable', ''],
[ElementAst, 'ng-content'],
[TextAst, 'a']
]);
});
});
describe('source spans', () => {
it('should support ng-content', () => {
var parsed = parse('', []);
expect(humanizeTplAstSourceSpans(parsed))
.toEqual([[NgContentAst, '']]);
});
it('should support embedded template', () => {
expect(humanizeTplAstSourceSpans(parse('', [])))
.toEqual([[EmbeddedTemplateAst, '']]);
});
it('should support element and attributes', () => {
expect(humanizeTplAstSourceSpans(parse('