/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AttributeMarker} from '@angular/compiler/src/core';
import {setup} from '@angular/compiler/test/aot/test_util';
import {compile, expectEmit} from './mock_compile';
/**
* These tests are codified version of the tests in compiler_canonical_spec.ts. Every
* test in compiler_canonical_spec.ts should have a corresponding test here.
*/
describe('compiler compliance', () => {
const angularFiles = setup({
compileAngular: false,
compileAnimations: false,
compileFakeCore: true,
});
describe('elements', () => {
it('should handle SVG', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: \`
test
\`
})
export class MyComponent {}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
// The factory should look like this:
const factory =
'MyComponent.ngFactoryDef = function MyComponent_Factory(t) { return new (t || MyComponent)(); }';
// The template should look like this (where IDENT is a wild card for an identifier):
const template = `
…
consts: [["title", "Hello", ${AttributeMarker.Classes}, "my-app"], ["cx", "20", "cy", "30", "r", "50"]],
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelementStart(0, "div", 0);
$r3$.ɵɵnamespaceSVG();
$r3$.ɵɵelementStart(1, "svg");
$r3$.ɵɵelement(2, "circle", 1);
$r3$.ɵɵelementEnd();
$r3$.ɵɵnamespaceHTML();
$r3$.ɵɵelementStart(3, "p");
$r3$.ɵɵtext(4, "test");
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd();
}
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, factory, 'Incorrect factory');
expectEmit(result.source, template, 'Incorrect template');
});
it('should handle MathML', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: \`
test
\`
})
export class MyComponent {}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
// The factory should look like this:
const factory =
'MyComponent.ngFactoryDef = function MyComponent_Factory(t) { return new (t || MyComponent)(); }';
// The template should look like this (where IDENT is a wild card for an identifier):
const template = `
…
consts: [["title", "Hello", ${AttributeMarker.Classes}, "my-app"]],
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelementStart(0, "div", 0);
$r3$.ɵɵnamespaceMathML();
$r3$.ɵɵelementStart(1, "math");
$r3$.ɵɵelement(2, "infinity");
$r3$.ɵɵelementEnd();
$r3$.ɵɵnamespaceHTML();
$r3$.ɵɵelementStart(3, "p");
$r3$.ɵɵtext(4, "test");
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd();
}
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, factory, 'Incorrect factory');
expectEmit(result.source, template, 'Incorrect template');
});
it('should translate DOM structure', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: \`
Hello World!
\`
})
export class MyComponent {}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
// The factory should look like this:
const factory =
'MyComponent.ngFactoryDef = function MyComponent_Factory(t) { return new (t || MyComponent)(); }';
// The template should look like this (where IDENT is a wild card for an identifier):
const template = `
…
consts: [["title", "Hello", ${AttributeMarker.Classes}, "my-app"]],
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelementStart(0, "div", 0);
$r3$.ɵɵtext(1, "Hello ");
$r3$.ɵɵelementStart(2, "b");
$r3$.ɵɵtext(3, "World");
$r3$.ɵɵelementEnd();
$r3$.ɵɵtext(4, "!");
$r3$.ɵɵelementEnd();
}
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, factory, 'Incorrect factory');
expectEmit(result.source, template, 'Incorrect template');
});
// TODO(https://github.com/angular/angular/issues/24426): We need to support the parser actually
// building the proper attributes based off of xmlns attributes.
xit('should support namespaced attributes', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: \`
Hello World!
\`
})
export class MyComponent {}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
// The factory should look like this:
const factory =
'MyComponent.ngFactoryDef = function MyComponent_Factory(t) { return new (t || MyComponent)(); }';
// The template should look like this (where IDENT is a wild card for an identifier):
const template = `
…
consts: [["class", "my-app", 0, "http://someuri/foo", "foo:bar", "baz", "title", "Hello", 0, "http://someuri/foo", "foo:qux", "quacks"]],
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelementStart(0, "div", 0);
$r3$.ɵɵtext(1, "Hello ");
$r3$.ɵɵelementStart(2, "b");
$r3$.ɵɵtext(3, "World");
$r3$.ɵɵelementEnd();
$r3$.ɵɵtext(4, "!");
$r3$.ɵɵelementEnd();
}
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, factory, 'Incorrect factory');
expectEmit(result.source, template, 'Incorrect template');
});
it('should support ', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: \`in a container\`
})
export class MyComponent {}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
// The template should look like this (where IDENT is a wild card for an identifier):
const template = `
…
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
i0.ɵɵelementContainerStart(0);
i0.ɵɵelementStart(1, "span");
i0.ɵɵtext(2, "in a ");
i0.ɵɵelementEnd();
i0.ɵɵtext(3, "container");
i0.ɵɵelementContainerEnd();
}
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, template, 'Incorrect template');
});
it('should generate self-closing elementContainer instruction for empty ', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: \`\`
})
export class MyComponent {}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
// The template should look like this (where IDENT is a wild card for an identifier):
const template = `
…
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
i0.ɵɵelementContainer(0);
}
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, template, 'Incorrect template');
});
it('should bind to element properties', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: \`\`
})
export class MyComponent {
id = 'one';
}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
const factory =
'MyComponent.ngFactoryDef = function MyComponent_Factory(t) { return new (t || MyComponent)(); }';
const template = `
…
consts: [[${AttributeMarker.Bindings}, "id"]],
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelement(0, "div", 0);
}
if (rf & 2) {
$r3$.ɵɵproperty("id", ctx.id);
}
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, factory, 'Incorrect factory');
expectEmit(result.source, template, 'Incorrect template');
});
it('should reserve slots for pure functions', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: \`\`
})
export class MyComponent {
id = 'one';
}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
///////////////
// TODO(FW-1273): The code generated below is adding extra parens, and we need to stop
// generating those.
//
// For example:
// `$r3$.ɵɵproperty("ternary", (ctx.cond ? $r3$.ɵɵpureFunction1(8, $c0$, ctx.a): $c1$));`
///////////////
const factory =
'MyComponent.ngFactoryDef = function MyComponent_Factory(t) { return new (t || MyComponent)(); }';
const template = `
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelement(0, "div", 0);
$r3$.ɵɵpipe(1,"pipe");
}
if (rf & 2) {
$r3$.ɵɵproperty("ternary", ctx.cond ? $r3$.ɵɵpureFunction1(8, $c0$, ctx.a): $c1$)("pipe", $r3$.ɵɵpipeBind3(1, 4, ctx.value, 1, 2))("and", ctx.cond && $r3$.ɵɵpureFunction1(10, $c0$, ctx.b))("or", ctx.cond || $r3$.ɵɵpureFunction1(12, $c0$, ctx.c));
}
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, factory, 'Incorrect factory');
expectEmit(result.source, template, 'Incorrect template');
});
it('should reserve slots for pure functions in host binding function', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule, Input} from '@angular/core';
@Component({
selector: 'my-component',
template: '...',
host: {
'[@expansionHeight]': \`{
value: getExpandedState(),
params: {
collapsedHeight: collapsedHeight,
expandedHeight: expandedHeight
}
}\`,
'[@expansionWidth]': \`{
value: getExpandedState(),
params: {
collapsedWidth: collapsedWidth,
expandedWidth: expandedWidth
}
}\`
}
})
export class MyComponent {
@Input() expandedHeight: string;
@Input() collapsedHeight: string;
@Input() expandedWidth: string;
@Input() collapsedWidth: string;
getExpandedState() {
return 'expanded';
}
}
@NgModule({
declarations: [MyComponent]
})
export class MyModule {}
`
}
};
const hostBindingsDef = `
const $_c0$ = function (a0, a1) { return { collapsedHeight: a0, expandedHeight: a1 }; };
const $_c1$ = function (a0, a1) { return { value: a0, params: a1 }; };
const $_c2$ = function (a0, a1) { return { collapsedWidth: a0, expandedWidth: a1 }; };
…
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵɵallocHostVars(14);
}
if (rf & 2) {
$r3$.ɵɵupdateSyntheticHostBinding("@expansionHeight",
$r3$.ɵɵpureFunction2(5, $_c1$, ctx.getExpandedState(),
$r3$.ɵɵpureFunction2(2, $_c0$, ctx.collapsedHeight, ctx.expandedHeight)
)
)("@expansionWidth",
$r3$.ɵɵpureFunction2(11, $_c1$, ctx.getExpandedState(),
$r3$.ɵɵpureFunction2(8, $_c2$, ctx.collapsedWidth, ctx.expandedWidth)
)
);
}
},
…
`;
const result = compile(files, angularFiles);
expectEmit(result.source, hostBindingsDef, 'Incorrect "hostBindings" function');
});
it('should bind to class and style names', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: \`\`
})
export class MyComponent {
error = true;
color = 'red';
}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
const factory =
'MyComponent.ngFactoryDef = function MyComponent_Factory(t) { return new (t || MyComponent)(); }';
const template = `
MyComponent.ngComponentDef = i0.ɵɵdefineComponent({type:MyComponent,selectors:[["my-component"]],
decls: 1,
vars: 2,
template: function MyComponent_Template(rf,ctx){
if (rf & 1) {
$r3$.ɵɵelement(0, "div");
}
if (rf & 2) {
$r3$.ɵɵstyleProp("background-color", ctx.color);
$r3$.ɵɵclassProp("error", ctx.error);
}
},
encapsulation: 2
});
`;
const result = compile(files, angularFiles);
expectEmit(result.source, factory, 'Incorrect factory');
expectEmit(result.source, template, 'Incorrect template');
});
it('should de-duplicate attribute arrays', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: \`
\`
})
export class MyComponent {
}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
const template = `
…
consts: [["title", "hi"]],
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelement(0, "div", 0);
$r3$.ɵɵelement(1, "span", 0);
}
…
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, template, 'Incorrect template');
});
});
describe('components & directives', () => {
it('should instantiate directives', () => {
const files = {
app: {
'spec.ts': `
import {Component, Directive, NgModule} from '@angular/core';
@Component({selector: 'child', template: 'child-view'})
export class ChildComponent {}
@Directive({selector: '[some-directive]'})
export class SomeDirective {}
@Component({selector: 'my-component', template: '!'})
export class MyComponent {}
@NgModule({declarations: [ChildComponent, SomeDirective, MyComponent]})
export class MyModule{}
`
}
};
// ChildComponent definition should be:
const ChildComponentDefinition = `
ChildComponent.ngComponentDef = $r3$.ɵɵdefineComponent({
type: ChildComponent,
selectors: [["child"]],
decls: 1,
vars: 0,
template: function ChildComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵtext(0, "child-view");
}
},
encapsulation: 2
});`;
const ChildComponentFactory =
`ChildComponent.ngFactoryDef = function ChildComponent_Factory(t) { return new (t || ChildComponent)(); };`;
// SomeDirective definition should be:
const SomeDirectiveDefinition = `
SomeDirective.ngDirectiveDef = $r3$.ɵɵdefineDirective({
type: SomeDirective,
selectors: [["", "some-directive", ""]]
});
`;
const SomeDirectiveFactory =
`SomeDirective.ngFactoryDef = function SomeDirective_Factory(t) {return new (t || SomeDirective)(); };`;
// MyComponent definition should be:
const MyComponentDefinition = `
…
MyComponent.ngComponentDef = $r3$.ɵɵdefineComponent({
type: MyComponent,
selectors: [["my-component"]],
decls: 2,
vars: 0,
consts: [["some-directive", ""]],
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelement(0, "child", 0);
$r3$.ɵɵtext(1, "!");
}
},
directives: [ChildComponent, SomeDirective],
encapsulation: 2
});
`;
const MyComponentFactory =
`MyComponent.ngFactoryDef = function MyComponent_Factory(t) { return new (t || MyComponent)(); };`;
const result = compile(files, angularFiles);
const source = result.source;
expectEmit(source, ChildComponentDefinition, 'Incorrect ChildComponent.ngComponentDef');
expectEmit(source, ChildComponentFactory, 'Incorrect ChildComponent.ngFactoryDef');
expectEmit(source, SomeDirectiveDefinition, 'Incorrect SomeDirective.ngDirectiveDef');
expectEmit(source, SomeDirectiveFactory, 'Incorrect SomeDirective.ngFactoryDef');
expectEmit(source, MyComponentDefinition, 'Incorrect MyComponentDefinition.ngComponentDef');
expectEmit(source, MyComponentFactory, 'Incorrect MyComponentDefinition.ngFactoryDef');
});
it('should support complex selectors', () => {
const files = {
app: {
'spec.ts': `
import {Directive, NgModule} from '@angular/core';
@Directive({selector: 'div.foo[some-directive]:not([title]):not(.baz)'})
export class SomeDirective {}
@Directive({selector: ':not(span[title]):not(.baz)'})
export class OtherDirective {}
@NgModule({declarations: [SomeDirective, OtherDirective]})
export class MyModule{}
`
}
};
// SomeDirective definition should be:
const SomeDirectiveDefinition = `
SomeDirective.ngDirectiveDef = $r3$.ɵɵdefineDirective({
type: SomeDirective,
selectors: [["div", "some-directive", "", 8, "foo", 3, "title", "", 9, "baz"]]
});
`;
const SomeDirectiveFactory =
`SomeDirective.ngFactoryDef = function SomeDirective_Factory(t) {return new (t || SomeDirective)(); };`;
// OtherDirective definition should be:
const OtherDirectiveDefinition = `
OtherDirective.ngDirectiveDef = $r3$.ɵɵdefineDirective({
type: OtherDirective,
selectors: [["", 5, "span", "title", "", 9, "baz"]]
});
`;
const OtherDirectiveFactory =
`OtherDirective.ngFactoryDef = function OtherDirective_Factory(t) {return new (t || OtherDirective)(); };`;
const result = compile(files, angularFiles);
const source = result.source;
expectEmit(source, SomeDirectiveDefinition, 'Incorrect SomeDirective.ngDirectiveDef');
expectEmit(source, SomeDirectiveFactory, 'Incorrect SomeDirective.ngFactoryDef');
expectEmit(source, OtherDirectiveDefinition, 'Incorrect OtherDirective.ngDirectiveDef');
expectEmit(source, OtherDirectiveFactory, 'Incorrect OtherDirective.ngFactoryDef');
});
it('should support components without selector', () => {
const files = {
app: {
'spec.ts': `
import {Component, Directive, NgModule} from '@angular/core';
@Component({template: ''})
export class EmptyOutletComponent {}
@NgModule({declarations: [EmptyOutletComponent]})
export class MyModule{}
`
}
};
// EmptyOutletComponent definition should be:
const EmptyOutletComponentDefinition = `
…
EmptyOutletComponent.ngComponentDef = $r3$.ɵɵdefineComponent({
type: EmptyOutletComponent,
selectors: [["ng-component"]],
decls: 1,
vars: 0,
template: function EmptyOutletComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelement(0, "router-outlet");
}
},
encapsulation: 2
});
`;
const EmptyOutletComponentFactory =
`EmptyOutletComponent.ngFactoryDef = function EmptyOutletComponent_Factory(t) { return new (t || EmptyOutletComponent)(); };`;
const result = compile(files, angularFiles);
const source = result.source;
expectEmit(
source, EmptyOutletComponentDefinition, 'Incorrect EmptyOutletComponent.ngComponentDef');
expectEmit(
source, EmptyOutletComponentFactory, 'Incorrect EmptyOutletComponent.ngFactoryDef');
});
it('should not treat ElementRef, ViewContainerRef, or ChangeDetectorRef specially when injecting',
() => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule, ElementRef, ChangeDetectorRef, ViewContainerRef} from '@angular/core';
@Component({
selector: 'my-component',
template: ''
})
export class MyComponent {
constructor(public el: ElementRef, public vcr: ViewContainerRef, public cdr: ChangeDetectorRef) {}
}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
const MyComponentDefinition = `
…
MyComponent.ngComponentDef = $r3$.ɵɵdefineComponent({
type: MyComponent,
selectors: [["my-component"]],
decls: 0,
vars: 0,
template: function MyComponent_Template(rf, ctx) {},
encapsulation: 2
});`;
const MyComponentFactory = `MyComponent.ngFactoryDef = function MyComponent_Factory(t) {
return new (t || MyComponent)(
$r3$.ɵɵdirectiveInject($i$.ElementRef), $r3$.ɵɵdirectiveInject($i$.ViewContainerRef),
$r3$.ɵɵdirectiveInject($i$.ChangeDetectorRef));
};`;
const result = compile(files, angularFiles);
const source = result.source;
expectEmit(source, MyComponentDefinition, 'Incorrect MyComponent.ngComponentDef');
expectEmit(source, MyComponentFactory, 'Incorrect MyComponent.ngFactoryDef');
});
it('should support structural directives', () => {
const files = {
app: {
'spec.ts': `
import {Component, Directive, NgModule, TemplateRef} from '@angular/core';
@Directive({selector: '[if]'})
export class IfDirective {
constructor(template: TemplateRef) { }
}
@Component({
selector: 'my-component',
template: '
'*' selector:
\`,
})
class Cmp {}
@NgModule({ declarations: [Cmp] })
class Module {}
`
}
};
const output = `
function Cmp_div_0_Template(rf, ctx) { if (rf & 1) {
$r3$.ɵɵelementStart(0, "div", 2);
$r3$.ɵɵprojection(1);
$r3$.ɵɵelementEnd();
} }
function Cmp_div_1_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelementStart(0, "div", 3);
$r3$.ɵɵtext(1, " No ng-content, no instructions generated. ");
$r3$.ɵɵelementEnd();
}
}
function Cmp_ng_template_2_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵtext(0, " '*' selector: ");
$r3$.ɵɵprojection(1, 1);
}
}
const $_c4$ = [[["span", "title", "tofirst"]], "*"];
…
consts: [["id", "second", ${AttributeMarker.Template}, "ngIf"], ["id", "third", ${AttributeMarker.Template}, "ngIf"], ["id", "second"], ["id", "third"]],
template: function Cmp_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵprojectionDef($_c4$);
$r3$.ɵɵtemplate(0, Cmp_div_0_Template, 2, 0, "div", 0);
$r3$.ɵɵtemplate(1, Cmp_div_1_Template, 2, 0, "div", 1);
$r3$.ɵɵtemplate(2, Cmp_ng_template_2_Template, 2, 0, "ng-template");
}
if (rf & 2) {
$r3$.ɵɵproperty("ngIf", ctx.visible);
$r3$.ɵɵadvance(1);
$r3$.ɵɵproperty("ngIf", ctx.visible);
}
}
`;
const {source} = compile(files, angularFiles);
expectEmit(source, output, 'Invalid content projection instructions generated');
});
it('should support content projection in both the root and nested templates', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
template: \`
'*' selector in a template:
\`,
})
class Cmp {}
@NgModule({ declarations: [Cmp] })
class Module {}
`
}
};
const output = `
function Cmp_ng_template_1_ng_template_1_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵprojection(0, 3);
}
}
function Cmp_ng_template_1_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵprojection(0, 2);
$r3$.ɵɵtemplate(1, Cmp_ng_template_1_ng_template_1_Template, 1, 0, "ng-template");
}
}
function Cmp_ng_template_2_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵtext(0, " '*' selector in a template: ");
$r3$.ɵɵprojection(1, 4);
}
}
const $_c0$ = [[["", "id", "tomainbefore"]], [["", "id", "tomainafter"]], [["", "id", "totemplate"]], [["", "id", "tonestedtemplate"]], "*"];
const $_c1$ = ["[id=toMainBefore]", "[id=toMainAfter]", "[id=toTemplate]", "[id=toNestedTemplate]", "*"];
…
template: function Cmp_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵprojectionDef($_c0$);
$r3$.ɵɵprojection(0);
$r3$.ɵɵtemplate(1, Cmp_ng_template_1_Template, 2, 0, "ng-template");
$r3$.ɵɵtemplate(2, Cmp_ng_template_2_Template, 2, 0, "ng-template");
$r3$.ɵɵprojection(3, 1);
}
}
`;
const {source} = compile(files, angularFiles);
expectEmit(source, output, 'Invalid content projection instructions generated');
});
it('should parse the selector that is passed into ngProjectAs', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'simple',
template: '
'
})
export class SimpleComponent {}
@NgModule({declarations: [SimpleComponent]})
export class MyModule {}
@Component({
selector: 'my-app',
template: ''
})
export class MyApp {}
`
}
};
// Note that the c0 and c1 constants aren't being used in this particular test,
// but they are used in some of the logic that is folded under the ellipsis.
const SimpleComponentDefinition = `
const $_c0$ = [[["", "title", ""]]];
const $_c1$ = ["[title]"];
…
MyApp.ngComponentDef = $r3$.ɵɵdefineComponent({
type: MyApp,
selectors: [["my-app"]],
decls: 2,
vars: 0,
consts: [["ngProjectAs", "[title]", 5, ["", "title", ""]]],
template: function MyApp_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelementStart(0, "simple");
$r3$.ɵɵelement(1, "h1", 0);
$r3$.ɵɵelementEnd();
}
},
encapsulation: 2
})`;
const result = compile(files, angularFiles);
expectEmit(
result.source, SimpleComponentDefinition, 'Incorrect SimpleComponent definition');
});
it('should take the first selector if multiple values are passed into ngProjectAs', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'simple',
template: '
'
})
export class SimpleComponent {}
@NgModule({declarations: [SimpleComponent]})
export class MyModule {}
@Component({
selector: 'my-app',
template: ''
})
export class MyApp {}
`
}
};
// Note that the c0 and c1 constants aren't being used in this particular test,
// but they are used in some of the logic that is folded under the ellipsis.
const SimpleComponentDefinition = `
const $_c0$ = [[["", "title", ""]]];
const $_c1$ = ["[title]"];
…
MyApp.ngComponentDef = $r3$.ɵɵdefineComponent({
type: MyApp,
selectors: [["my-app"]],
decls: 2,
vars: 0,
consts: [["ngProjectAs", "[title],[header]", 5, ["", "title", ""]]],
template: function MyApp_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelementStart(0, "simple");
$r3$.ɵɵelement(1, "h1", 0);
$r3$.ɵɵelementEnd();
}
},
encapsulation: 2
})`;
const result = compile(files, angularFiles);
expectEmit(
result.source, SimpleComponentDefinition, 'Incorrect SimpleComponent definition');
});
});
describe('queries', () => {
const directive = {
'some.directive.ts': `
import {Directive} from '@angular/core';
@Directive({
selector: '[someDir]',
})
export class SomeDirective { }
`
};
it('should support view queries with directives', () => {
const files = {
app: {
...directive,
'view_query.component.ts': `
import {Component, NgModule, ViewChild, ViewChildren} from '@angular/core';
import {SomeDirective} from './some.directive';
@Component({
selector: 'view-query-component',
template: \`
\`
})
export class ViewQueryComponent {
@ViewChild(SomeDirective) someDir: SomeDirective;
@ViewChildren(SomeDirective) someDirs: QueryList;
}
@NgModule({declarations: [SomeDirective, ViewQueryComponent]})
export class MyModule {}
`
}
};
const ViewQueryComponentDefinition = `
…
ViewQueryComponent.ngComponentDef = $r3$.ɵɵdefineComponent({
type: ViewQueryComponent,
selectors: [["view-query-component"]],
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵviewQuery(SomeDirective, true);
$r3$.ɵɵviewQuery(SomeDirective, true);
}
if (rf & 2) {
var $tmp$;
$r3$.ɵɵqueryRefresh(($tmp$ = $r3$.ɵɵloadQuery())) && (ctx.someDir = $tmp$.first);
$r3$.ɵɵqueryRefresh(($tmp$ = $r3$.ɵɵloadQuery())) && (ctx.someDirs = $tmp$);
}
},
decls: 1,
vars: 0,
consts: [["someDir",""]],
template: function ViewQueryComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelement(0, "div", 0);
}
},
directives: function () { return [SomeDirective]; },
encapsulation: 2
});`;
const result = compile(files, angularFiles);
const source = result.source;
expectEmit(source, ViewQueryComponentDefinition, 'Invalid ViewQuery declaration');
});
it('should support view queries with local refs', () => {
const files = {
app: {
'view_query.component.ts': `
import {Component, NgModule, ViewChild, ViewChildren, QueryList} from '@angular/core';
@Component({
selector: 'view-query-component',
template: \`
\`
})
export class ViewQueryComponent {
@ViewChild('myRef') myRef: any;
@ViewChildren('myRef1, myRef2, myRef3') myRefs: QueryList;
}
@NgModule({declarations: [ViewQueryComponent]})
export class MyModule {}
`
}
};
const ViewQueryComponentDefinition = `
const $e0_attrs$ = ["myRef"];
const $e1_attrs$ = ["myRef1", "myRef2", "myRef3"];
…
ViewQueryComponent.ngComponentDef = $r3$.ɵɵdefineComponent({
…
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵviewQuery($e0_attrs$, true);
$r3$.ɵɵviewQuery($e1_attrs$, true);
}
if (rf & 2) {
var $tmp$;
$r3$.ɵɵqueryRefresh(($tmp$ = $r3$.ɵɵloadQuery())) && (ctx.myRef = $tmp$.first);
$r3$.ɵɵqueryRefresh(($tmp$ = $r3$.ɵɵloadQuery())) && (ctx.myRefs = $tmp$);
}
},
…
});`;
const result = compile(files, angularFiles);
const source = result.source;
expectEmit(source, ViewQueryComponentDefinition, 'Invalid ViewQuery declaration');
});
it('should support static view queries', () => {
const files = {
app: {
...directive,
'view_query.component.ts': `
import {Component, NgModule, ViewChild} from '@angular/core';
import {SomeDirective} from './some.directive';
@Component({
selector: 'view-query-component',
template: \`
\`
})
export class ViewQueryComponent {
@ViewChild(SomeDirective, {static: true}) someDir !: SomeDirective;
@ViewChild('foo', {static: false}) foo !: ElementRef;
}
@NgModule({declarations: [SomeDirective, ViewQueryComponent]})
export class MyModule {}
`
}
};
const ViewQueryComponentDefinition = `
const $refs$ = ["foo"];
…
ViewQueryComponent.ngComponentDef = $r3$.ɵɵdefineComponent({
type: ViewQueryComponent,
selectors: [["view-query-component"]],
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵstaticViewQuery(SomeDirective, true);
$r3$.ɵɵviewQuery($refs$, true);
}
if (rf & 2) {
var $tmp$;
$r3$.ɵɵqueryRefresh(($tmp$ = $r3$.ɵɵloadQuery())) && (ctx.someDir = $tmp$.first);
$r3$.ɵɵqueryRefresh(($tmp$ = $r3$.ɵɵloadQuery())) && (ctx.foo = $tmp$.first);
}
},
decls: 1,
vars: 0,
consts: [["someDir",""]],
template: function ViewQueryComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelement(0, "div", 0);
}
},
directives: function () { return [SomeDirective]; },
encapsulation: 2
});`;
const result = compile(files, angularFiles);
const source = result.source;
expectEmit(source, ViewQueryComponentDefinition, 'Invalid ViewQuery declaration');
});
it('should support view queries with read tokens specified', () => {
const files = {
app: {
...directive,
'view_query.component.ts': `
import {Component, NgModule, ViewChild, ViewChildren, QueryList, ElementRef, TemplateRef} from '@angular/core';
import {SomeDirective} from './some.directive';
@Component({
selector: 'view-query-component',
template: \`
\`
})
export class ViewQueryComponent {
@ViewChild('myRef', {read: TemplateRef}) myRef: TemplateRef;
@ViewChildren('myRef1, myRef2, myRef3', {read: ElementRef}) myRefs: QueryList;
@ViewChild(SomeDirective, {read: ElementRef}) someDir: ElementRef;
@ViewChildren(SomeDirective, {read: TemplateRef}) someDirs: QueryList;
}
@NgModule({declarations: [ViewQueryComponent]})
export class MyModule {}
`
}
};
const ViewQueryComponentDefinition = `
const $e0_attrs$ = ["myRef"];
const $e1_attrs$ = ["myRef1", "myRef2", "myRef3"];
…
ViewQueryComponent.ngComponentDef = $r3$.ɵɵdefineComponent({
…
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵviewQuery($e0_attrs$, true, TemplateRef);
$r3$.ɵɵviewQuery(SomeDirective, true, ElementRef);
$r3$.ɵɵviewQuery($e1_attrs$, true, ElementRef);
$r3$.ɵɵviewQuery(SomeDirective, true, TemplateRef);
}
if (rf & 2) {
var $tmp$;
$r3$.ɵɵqueryRefresh(($tmp$ = $r3$.ɵɵloadQuery())) && (ctx.myRef = $tmp$.first);
$r3$.ɵɵqueryRefresh(($tmp$ = $r3$.ɵɵloadQuery())) && (ctx.someDir = $tmp$.first);
$r3$.ɵɵqueryRefresh(($tmp$ = $r3$.ɵɵloadQuery())) && (ctx.myRefs = $tmp$);
$r3$.ɵɵqueryRefresh(($tmp$ = $r3$.ɵɵloadQuery())) && (ctx.someDirs = $tmp$);
}
},
…
});`;
const result = compile(files, angularFiles);
const source = result.source;
expectEmit(source, ViewQueryComponentDefinition, 'Invalid ViewQuery declaration');
});
it('should support content queries with directives', () => {
const files = {
app: {
...directive,
'content_query.ts': `
import {Component, ContentChild, ContentChildren, NgModule, QueryList} from '@angular/core';
import {SomeDirective} from './some.directive';
@Component({
selector: 'content-query-component',
template: \`