diff --git a/packages/core/test/render3/compiler_canonical/compiler_canonical_spec.ts b/packages/core/test/render3/compiler_canonical/compiler_canonical_spec.ts
deleted file mode 100644
index b44e41b93b..0000000000
--- a/packages/core/test/render3/compiler_canonical/compiler_canonical_spec.ts
+++ /dev/null
@@ -1,1628 +0,0 @@
-/**
- * @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 {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
-import * as $r3$ from '../../../src/core_render3_private_export';
-import {renderComponent, toHtml} from '../render_util';
-
-/**
- * NORMATIVE => /NORMATIVE: Designates what the compiler is expected to generate.
- *
- * All local variable names are considered non-normative (informative). They should be
- * wrapped in $ on each end to simplify testing on the compiler side.
- */
-
-describe('compiler specification', () => {
- // Saving type as $boolean$, etc to simplify testing for compiler, as types aren't saved
- type $boolean$ = boolean;
- type $any$ = any;
- type $number$ = number;
-
- describe('elements', () => {
- it('should translate DOM structure', () => {
- type $MyComponent$ = MyComponent;
-
- // Important: keep arrays outside of function to not create new instances.
- const $e0_attrs$ = ['class', 'my-app', 'title', 'Hello'];
-
- @Component({
- selector: 'my-component',
- template: `
Hello World !
`
- })
- class MyComponent {
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyComponent,
- tag: 'my-component',
- factory: () => new MyComponent(),
- template: function(ctx: $MyComponent$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, 'div', $e0_attrs$);
- $r3$.ɵT(1, 'Hello ');
- $r3$.ɵE(2, 'b');
- $r3$.ɵT(3, 'World');
- $r3$.ɵe();
- $r3$.ɵT(4, '!');
- $r3$.ɵe();
- }
- }
- });
- // /NORMATIVE
- }
-
- expect(renderComp(MyComponent))
- .toEqual('Hello World !
');
- });
- });
-
- describe('components & directives', () => {
- it('should instantiate directives', () => {
- type $ChildComponent$ = ChildComponent;
- type $MyComponent$ = MyComponent;
-
- const log: string[] = [];
- @Component({selector: 'child', template: 'child-view'})
- class ChildComponent {
- constructor() { log.push('ChildComponent'); }
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: ChildComponent,
- tag: `child`,
- factory: () => new ChildComponent(),
- template: function(ctx: $ChildComponent$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵT(0, 'child-view');
- }
- }
- });
- // /NORMATIVE
- }
-
- @Directive({
- selector: '[some-directive]',
- })
- class SomeDirective {
- constructor() { log.push('SomeDirective'); }
- // NORMATIVE
- static ngDirectiveDef = $r3$.ɵdefineDirective({
- type: SomeDirective,
- factory: () => new SomeDirective(),
- });
- // /NORMATIVE
- }
-
- // Important: keep arrays outside of function to not create new instances.
- // NORMATIVE
- const $e0_attrs$ = ['some-directive', ''];
- const $e0_dirs$ = [SomeDirective];
- // /NORMATIVE
-
- @Component({selector: 'my-component', template: ` !`})
- class MyComponent {
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyComponent,
- tag: 'my-component',
- factory: () => new MyComponent(),
- template: function(ctx: $MyComponent$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, ChildComponent, $e0_attrs$, $e0_dirs$);
- $r3$.ɵe();
- $r3$.ɵT(3, '!');
- }
- ChildComponent.ngComponentDef.h(1, 0);
- SomeDirective.ngDirectiveDef.h(2, 0);
- $r3$.ɵr(1, 0);
- $r3$.ɵr(2, 0);
- }
- });
- // /NORMATIVE
- }
-
- expect(renderComp(MyComponent)).toEqual('child-view !');
- expect(log).toEqual(['ChildComponent', 'SomeDirective']);
- });
-
- it('should support host bindings', () => {
- type $MyApp$ = MyApp;
-
- @Directive({selector: '[hostBindingDir]'})
- class HostBindingDir {
- @HostBinding('id') dirId = 'some id';
-
- // NORMATIVE
- static ngDirectiveDef = $r3$.ɵdefineDirective({
- type: HostBindingDir,
- factory: function HostBindingDir_Factory() { return new HostBindingDir(); },
- hostBindings: function HostBindingDir_HostBindings(
- dirIndex: $number$, elIndex: $number$) {
- $r3$.ɵp(elIndex, 'id', $r3$.ɵb($r3$.ɵld(dirIndex).dirId));
- }
- });
- // /NORMATIVE
- }
-
- const $e0_attrs$ = ['hostBindingDir', ''];
- const $e0_dirs$ = [HostBindingDir];
-
- @Component({
- selector: 'my-app',
- template: `
-
- `
- })
- class MyApp {
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyApp,
- tag: 'my-app',
- factory: function MyApp_Factory() { return new MyApp(); },
- template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, 'div', $e0_attrs$, $e0_dirs$);
- $r3$.ɵe();
- }
- HostBindingDir.ngDirectiveDef.h(1, 0);
- $r3$.ɵr(1, 0);
- }
- });
- }
-
- expect(renderComp(MyApp)).toEqual(`
`);
- });
-
- it('should support host listeners', () => {
- type $MyApp$ = MyApp;
-
- @Directive({selector: '[hostlistenerDir]'})
- class HostListenerDir {
- @HostListener('click')
- onClick() {}
-
- // NORMATIVE
- static ngDirectiveDef = $r3$.ɵdefineDirective({
- type: HostListenerDir,
- factory: function HostListenerDir_Factory() {
- const $dir$ = new HostListenerDir();
- $r3$.ɵL(
- 'click', function HostListenerDir_click_Handler(event: any) { $dir$.onClick(); });
- return $dir$;
- },
- });
- // /NORMATIVE
- }
-
- const $e0_attrs$ = ['hostListenerDir', ''];
- const $e0_dirs$ = [HostListenerDir];
-
- @Component({
- selector: 'my-app',
- template: `
- Click
- `
- })
- class MyApp {
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyApp,
- tag: 'my-app',
- factory: function MyApp_Factory() { return new MyApp(); },
- template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, 'button', $e0_attrs$, $e0_dirs$);
- $r3$.ɵT(2, 'Click');
- $r3$.ɵe();
- }
- HostListenerDir.ngDirectiveDef.h(1, 0);
- $r3$.ɵr(1, 0);
- }
- });
- }
-
- expect(renderComp(MyApp)).toEqual(`Click `);
- });
-
-
- it('should support setting of host attributes', () => {
- type $MyApp$ = MyApp;
-
- @Directive({selector: '[hostAttributeDir]', host: {'role': 'listbox'}})
- class HostAttributeDir {
- // NORMATIVE
- static ngDirectiveDef = $r3$.ɵdefineDirective({
- type: HostAttributeDir,
- factory: function HostAttributeDir_Factory() { return new HostAttributeDir(); },
- attributes: ['role', 'listbox']
- });
- // /NORMATIVE
- }
-
- const $e0_attrs$ = ['hostAttributeDir', ''];
- const $e0_dirs$ = [HostAttributeDir];
-
- @Component({
- selector: 'my-app',
- template: `
-
- `
- })
- class MyApp {
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyApp,
- tag: 'my-app',
- factory: function MyApp_Factory() { return new MyApp(); },
- template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, 'div', $e0_attrs$, $e0_dirs$);
- $r3$.ɵe();
- }
- HostAttributeDir.ngDirectiveDef.h(1, 0);
- $r3$.ɵr(1, 0);
- }
- });
- }
-
- expect(renderComp(MyApp)).toEqual(`
`);
- });
-
- it('should support bindings of host attributes', () => {
- type $MyApp$ = MyApp;
-
- @Directive({selector: '[hostBindingDir]'})
- class HostBindingDir {
- @HostBinding('attr.aria-label') label = 'some label';
-
- // NORMATIVE
- static ngDirectiveDef = $r3$.ɵdefineDirective({
- type: HostBindingDir,
- factory: function HostBindingDir_Factory() { return new HostBindingDir(); },
- hostBindings: function HostBindingDir_HostBindings(
- dirIndex: $number$, elIndex: $number$) {
- $r3$.ɵa(elIndex, 'aria-label', $r3$.ɵb($r3$.ɵld(dirIndex).label));
- }
- });
- // /NORMATIVE
- }
-
- const $e0_attrs$ = ['hostBindingDir', ''];
- const $e0_dirs$ = [HostBindingDir];
-
- @Component({
- selector: 'my-app',
- template: `
-
- `
- })
- class MyApp {
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyApp,
- tag: 'my-app',
- factory: function MyApp_Factory() { return new MyApp(); },
- template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, 'div', $e0_attrs$, $e0_dirs$);
- $r3$.ɵe();
- }
- HostBindingDir.ngDirectiveDef.h(1, 0);
- $r3$.ɵr(1, 0);
- }
- });
- }
-
- expect(renderComp(MyApp)).toEqual(`
`);
- });
-
- it('should support onPush components', () => {
- type $MyApp$ = MyApp;
- type $MyComp$ = MyComp;
-
- @Component({
- selector: 'my-comp',
- template: `
- {{ name }}
- `,
- changeDetection: ChangeDetectionStrategy.OnPush
- })
- class MyComp {
- @Input() name: string;
-
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyComp,
- tag: 'my-comp',
- factory: function MyComp_Factory() { return new MyComp(); },
- template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵT(0);
- }
- $r3$.ɵt(0, $r3$.ɵb(ctx.name));
- },
- inputs: {name: 'name'},
- changeDetection: ChangeDetectionStrategy.OnPush
- });
- // /NORMATIVE
- }
-
- @Component({
- selector: 'my-app',
- template: `
-
- `
- })
- class MyApp {
- name = 'some name';
-
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyApp,
- tag: 'my-app',
- factory: function MyApp_Factory() { return new MyApp(); },
- template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, MyComp);
- $r3$.ɵe();
- }
- $r3$.ɵp(0, 'name', $r3$.ɵb(ctx.name));
- MyComp.ngComponentDef.h(1, 0);
- $r3$.ɵr(1, 0);
- }
- });
- }
-
- expect(renderComp(MyApp)).toEqual(`some name `);
- });
-
- xit('should support structural directives', () => {
- type $MyComponent$ = MyComponent;
-
- const log: string[] = [];
- @Directive({
- selector: '[if]',
- })
- class IfDirective {
- constructor(template: TemplateRef) { log.push('ifDirective'); }
- // NORMATIVE
- static ngDirectiveDef = $r3$.ɵdefineDirective({
- type: IfDirective,
- factory: () => new IfDirective($r3$.ɵinjectTemplateRef()),
- });
- // /NORMATIVE
- }
-
- // Important: keep arrays outside of function to not create new instances.
- // NORMATIVE
- const $e0_locals$ = ['foo', ''];
- const $c1_dirs$ = [IfDirective];
- // /NORMATIVE
-
- @Component(
- {selector: 'my-component', template: ``})
- class MyComponent {
- salutation = 'Hello';
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyComponent,
- tag: 'my-component',
- factory: () => new MyComponent(),
- template: function(ctx: $MyComponent$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, 'ul', null, null, $e0_locals$);
- $r3$.ɵC(2, $c1_dirs$, C1);
- $r3$.ɵe();
- }
- let $foo$ = $r3$.ɵld(1);
- $r3$.ɵcR(2);
- $r3$.ɵr(3, 2);
- $r3$.ɵcr();
-
- function C1(ctx1: $any$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, 'li');
- $r3$.ɵT(1);
- $r3$.ɵe();
- }
- $r3$.ɵt(1, $r3$.ɵi2('', ctx.salutation, ' ', $foo$, ''));
- }
- }
- });
- // /NORMATIVE
- }
-
- expect(renderComp(MyComponent)).toEqual('child-view !');
- expect(log).toEqual(['ChildComponent', 'SomeDirective']);
- });
-
- describe('value composition', () => {
- type $MyArrayComp$ = MyArrayComp;
-
- @Component({
- selector: 'my-array-comp',
- template: `
- {{ names[0] }} {{ names[1] }}
- `
- })
- class MyArrayComp {
- @Input() names: string[];
-
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyArrayComp,
- tag: 'my-array-comp',
- factory: function MyArrayComp_Factory() { return new MyArrayComp(); },
- template: function MyArrayComp_Template(ctx: $MyArrayComp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵT(0);
- }
- $r3$.ɵt(0, $r3$.ɵi2('', ctx.names[0], ' ', ctx.names[1], ''));
- },
- inputs: {names: 'names'}
- });
- }
-
- it('should support array literals of constants', () => {
- type $MyApp$ = MyApp;
-
- // NORMATIVE
- const $e0_arr$ = ['Nancy', 'Bess'];
- // /NORMATIVE
-
- @Component({
- selector: 'my-app',
- template: `
-
- `
- })
- class MyApp {
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyApp,
- tag: 'my-app',
- factory: function MyApp_Factory() { return new MyApp(); },
- template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, MyArrayComp);
- $r3$.ɵe();
- }
- $r3$.ɵp(0, 'names', cm ? $e0_arr$ : $r3$.ɵNC);
- MyArrayComp.ngComponentDef.h(1, 0);
- $r3$.ɵr(1, 0);
- }
- });
- // /NORMATIVE
- }
-
- expect(renderComp(MyApp)).toEqual(`Nancy Bess `);
- });
-
- it('should support array literals of constants inside function calls', () => {
- type $MyApp$ = MyApp;
-
- // NORMATIVE
- const $e0_ff$ = () => ['Nancy', 'Bess'];
- // /NORMATIVE
-
- @Component({
- selector: 'my-app',
- template: `
-
- `
- })
- class MyApp {
- someFn(arr: string[]): string[] {
- arr[0] = arr[0].toUpperCase();
- return arr;
- }
-
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyApp,
- tag: 'my-app',
- factory: function MyApp_Factory() { return new MyApp(); },
- template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, MyArrayComp);
- $r3$.ɵe();
- }
- $r3$.ɵp(0, 'names', $r3$.ɵb(ctx.someFn($r3$.ɵf0($e0_ff$))));
- MyArrayComp.ngComponentDef.h(1, 0);
- $r3$.ɵr(1, 0);
- }
- });
- // /NORMATIVE
- }
-
- expect(renderComp(MyApp)).toEqual(`NANCY Bess `);
- });
-
- it('should support array literals of constants inside expressions', () => {
- type $MyApp$ = MyApp;
- type $MyComp$ = MyComp;
-
- @Component({selector: 'my-comp', template: `{{ num }}`})
- class MyComp {
- num: number;
-
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyComp,
- tag: 'my-comp',
- factory: function MyComp_Factory() { return new MyComp(); },
- template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵT(0);
- }
- $r3$.ɵt(0, $r3$.ɵb(ctx.num));
- },
- inputs: {num: 'num'}
- });
- }
-
- // NORMATIVE
- const $e0_ff$ = () => ['Nancy', 'Bess'];
- // /NORMATIVE
-
- @Component({
- selector: 'my-app',
- template: `
-
- `
- })
- class MyApp {
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyApp,
- tag: 'my-app',
- factory: function MyApp_Factory() { return new MyApp(); },
- template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, MyComp);
- $r3$.ɵe();
- }
- $r3$.ɵp(0, 'num', $r3$.ɵb($r3$.ɵf0($e0_ff$).length + 1));
- MyComp.ngComponentDef.h(1, 0);
- $r3$.ɵr(1, 0);
- }
- });
- // /NORMATIVE
- }
-
- expect(renderComp(MyApp)).toEqual(`3 `);
- });
-
-
- it('should support array literals', () => {
- type $MyApp$ = MyApp;
-
- // NORMATIVE
- const $e0_ff$ = (v: any) => ['Nancy', v];
- // /NORMATIVE
-
- @Component({
- selector: 'my-app',
- template: `
-
- `
- })
- class MyApp {
- customName = 'Bess';
-
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyApp,
- tag: 'my-app',
- factory: function MyApp_Factory() { return new MyApp(); },
- template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, MyArrayComp);
- $r3$.ɵe();
- }
- $r3$.ɵp(0, 'names', $r3$.ɵb($r3$.ɵf1($e0_ff$, ctx.customName)));
- MyArrayComp.ngComponentDef.h(1, 0);
- $r3$.ɵr(1, 0);
- }
- });
- // /NORMATIVE
- }
-
- expect(renderComp(MyApp)).toEqual(`Nancy Bess `);
- });
-
- it('should support 9+ bindings in array literals', () => {
- type $MyComp$ = MyComp;
-
- @Component({
- selector: 'my-comp',
- template: `
- {{ names[0] }}
- {{ names[1] }}
- {{ names[3] }}
- {{ names[4] }}
- {{ names[5] }}
- {{ names[6] }}
- {{ names[7] }}
- {{ names[8] }}
- {{ names[9] }}
- {{ names[10] }}
- {{ names[11] }}
- `
- })
- class MyComp {
- @Input() names: string[];
-
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyComp,
- tag: 'my-comp',
- factory: function MyComp_Factory() { return new MyComp(); },
- template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵT(0);
- $r3$.ɵT(1);
- $r3$.ɵT(2);
- $r3$.ɵT(3);
- $r3$.ɵT(4);
- $r3$.ɵT(5);
- $r3$.ɵT(6);
- $r3$.ɵT(7);
- $r3$.ɵT(8);
- $r3$.ɵT(9);
- $r3$.ɵT(10);
- $r3$.ɵT(11);
- }
- $r3$.ɵt(0, $r3$.ɵb(ctx.names[0]));
- $r3$.ɵt(1, $r3$.ɵb(ctx.names[1]));
- $r3$.ɵt(2, $r3$.ɵb(ctx.names[2]));
- $r3$.ɵt(3, $r3$.ɵb(ctx.names[3]));
- $r3$.ɵt(4, $r3$.ɵb(ctx.names[4]));
- $r3$.ɵt(5, $r3$.ɵb(ctx.names[5]));
- $r3$.ɵt(6, $r3$.ɵb(ctx.names[6]));
- $r3$.ɵt(7, $r3$.ɵb(ctx.names[7]));
- $r3$.ɵt(8, $r3$.ɵb(ctx.names[8]));
- $r3$.ɵt(9, $r3$.ɵb(ctx.names[9]));
- $r3$.ɵt(10, $r3$.ɵb(ctx.names[10]));
- $r3$.ɵt(11, $r3$.ɵb(ctx.names[11]));
- },
- inputs: {names: 'names'}
- });
- }
-
- // NORMATIVE
- const $e0_ff$ =
- (v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, v7: any,
- v8: any) => ['start-', v0, v1, v2, v3, v4, '-middle-', v5, v6, v7, v8, '-end'];
- // /NORMATIVE
-
- @Component({
- selector: 'my-app',
- template: `
-
-
- `
- })
- class MyApp {
- n0 = 'a';
- n1 = 'b';
- n2 = 'c';
- n3 = 'd';
- n4 = 'e';
- n5 = 'f';
- n6 = 'g';
- n7 = 'h';
- n8 = 'i';
-
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyApp,
- tag: 'my-app',
- factory: function MyApp_Factory() { return new MyApp(); },
- template: function MyApp_Template(c: MyApp, cm: boolean) {
- if (cm) {
- $r3$.ɵE(0, MyComp);
- $r3$.ɵe();
- }
- $r3$.ɵp(
- 0, 'names',
- $r3$.ɵb(
- $r3$.ɵfV($e0_ff$, [c.n0, c.n1, c.n2, c.n3, c.n4, c.n5, c.n6, c.n7, c.n8])));
- MyComp.ngComponentDef.h(1, 0);
- $r3$.ɵr(1, 0);
- }
- });
- // /NORMATIVE
- }
-
- expect(renderComp(MyApp)).toEqual(`start-abcde-middle-fghi-end `);
- });
-
- it('should support object literals', () => {
- type $ObjectComp$ = ObjectComp;
- type $MyApp$ = MyApp;
-
- @Component({
- selector: 'object-comp',
- template: `
- {{ config['duration'] }}
- {{ config.animation }}
- `
- })
- class ObjectComp {
- config: {[key: string]: any};
-
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: ObjectComp,
- tag: 'object-comp',
- factory: function ObjectComp_Factory() { return new ObjectComp(); },
- template: function ObjectComp_Template(ctx: $ObjectComp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, 'p');
- $r3$.ɵT(1);
- $r3$.ɵe();
- $r3$.ɵE(2, 'p');
- $r3$.ɵT(3);
- $r3$.ɵe();
- }
- $r3$.ɵt(1, $r3$.ɵb(ctx.config['duration']));
- $r3$.ɵt(3, $r3$.ɵb(ctx.config.animation));
- },
- inputs: {config: 'config'}
- });
- }
-
- // NORMATIVE
- const $e0_ff$ = (v: any) => { return {'duration': 500, animation: v}; };
- // /NORMATIVE
-
- @Component({
- selector: 'my-app',
- template: `
-
- `
- })
- class MyApp {
- name = 'slide';
-
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyApp,
- tag: 'my-app',
- factory: function MyApp_Factory() { return new MyApp(); },
- template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, ObjectComp);
- $r3$.ɵe();
- }
- $r3$.ɵp(0, 'config', $r3$.ɵb($r3$.ɵf1($e0_ff$, ctx.name)));
- ObjectComp.ngComponentDef.h(1, 0);
- $r3$.ɵr(1, 0);
- }
- });
- // /NORMATIVE
- }
-
- expect(renderComp(MyApp)).toEqual(`500
slide
`);
- });
-
- it('should support expressions nested deeply in object/array literals', () => {
- type $NestedComp$ = NestedComp;
- type $MyApp$ = MyApp;
-
- @Component({
- selector: 'nested-comp',
- template: `
- {{ config.animation }}
- {{config.actions[0].opacity }}
- {{config.actions[1].duration }}
- `
- })
- class NestedComp {
- config: {[key: string]: any};
-
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: NestedComp,
- tag: 'nested-comp',
- factory: function NestedComp_Factory() { return new NestedComp(); },
- template: function NestedComp_Template(ctx: $NestedComp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, 'p');
- $r3$.ɵT(1);
- $r3$.ɵe();
- $r3$.ɵE(2, 'p');
- $r3$.ɵT(3);
- $r3$.ɵe();
- $r3$.ɵE(4, 'p');
- $r3$.ɵT(5);
- $r3$.ɵe();
- }
- $r3$.ɵt(1, $r3$.ɵb(ctx.config.animation));
- $r3$.ɵt(3, $r3$.ɵb(ctx.config.actions[0].opacity));
- $r3$.ɵt(5, $r3$.ɵb(ctx.config.actions[1].duration));
- },
- inputs: {config: 'config'}
- });
- }
-
- // NORMATIVE
- const $e0_ff$ = (v: any) => { return {opacity: 1, duration: v}; };
- const $c0$ = {opacity: 0, duration: 0};
- const $e0_ff_1$ = (v: any) => [$c0$, v];
- const $e0_ff_2$ = (v1: any, v2: any) => { return {animation: v1, actions: v2}; };
- // /NORMATIVE
-
- @Component({
- selector: 'my-app',
- template: `
-
-
- `
- })
- class MyApp {
- name = 'slide';
- duration = 100;
-
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyApp,
- tag: 'my-app',
- factory: function MyApp_Factory() { return new MyApp(); },
- template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, NestedComp);
- $r3$.ɵe();
- }
- $r3$.ɵp(
- 0, 'config', $r3$.ɵf2(
- $e0_ff_2$, ctx.name,
- $r3$.ɵb($r3$.ɵf1($e0_ff_1$, $r3$.ɵf1($e0_ff$, ctx.duration)))));
- NestedComp.ngComponentDef.h(1, 0);
- $r3$.ɵr(1, 0);
- }
- });
- // /NORMATIVE
- }
-
- expect(renderComp(MyApp))
- .toEqual(`slide
0
100
`);
- });
-
- });
-
- it('should support content projection', () => {
- type $SimpleComponent$ = SimpleComponent;
- type $ComplexComponent$ = ComplexComponent;
- type $MyApp$ = MyApp;
-
- @Component({selector: 'simple', template: `
`})
- class SimpleComponent {
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: SimpleComponent,
- tag: 'simple',
- factory: () => new SimpleComponent(),
- template: function(ctx: $SimpleComponent$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵpD(0);
- $r3$.ɵE(1, 'div');
- $r3$.ɵP(2, 0);
- $r3$.ɵe();
- }
- }
- });
- // /NORMATIVE
- }
-
- // NORMATIVE
- const $pD_0$: $r3$.ɵCssSelector[] =
- [[[['span', 'title', 'toFirst'], null]], [[['span', 'title', 'toSecond'], null]]];
- // /NORMATIVE
-
- @Component({
- selector: 'complex',
- template: `
-
-
`
- })
- class ComplexComponent {
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: ComplexComponent,
- tag: 'complex',
- factory: () => new ComplexComponent(),
- template: function(ctx: $ComplexComponent$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵpD(0, $pD_0$);
- $r3$.ɵE(1, 'div', ['id', 'first']);
- $r3$.ɵP(2, 0, 1);
- $r3$.ɵe();
- $r3$.ɵE(3, 'div', ['id', 'second']);
- $r3$.ɵP(4, 0, 2);
- $r3$.ɵe();
- }
- }
- });
- // /NORMATIVE
- }
-
- @Component({
- selector: 'my-app',
- template: `content
- `
- })
- class MyApp {
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyApp,
- tag: 'my-app',
- factory: () => new MyApp(),
- template: function(ctx: $MyApp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, SimpleComponent);
- $r3$.ɵT(2, 'content');
- $r3$.ɵe();
- }
- }
- });
- }
- });
-
- describe('queries', () => {
- let someDir: SomeDirective;
-
- @Directive({
- selector: '[someDir]',
- })
- class SomeDirective {
- static ngDirectiveDef = $r3$.ɵdefineDirective({
- type: SomeDirective,
- factory: function SomeDirective_Factory() { return someDir = new SomeDirective(); },
- features: [$r3$.ɵPublicFeature]
- });
- }
-
- it('should support view queries', () => {
- type $ViewQueryComponent$ = ViewQueryComponent;
-
- // NORMATIVE
- const $e1_attrs$ = ['someDir', ''];
- const $e1_dirs$ = [SomeDirective];
- // /NORMATIVE
-
- @Component({
- selector: 'view-query-component',
- template: `
-
- `
- })
- class ViewQueryComponent {
- @ViewChild(SomeDirective) someDir: SomeDirective;
- @ViewChildren(SomeDirective) someDirList: QueryList;
-
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: ViewQueryComponent,
- tag: 'view-query-component',
- factory: function ViewQueryComponent_Factory() { return new ViewQueryComponent(); },
- template: function ViewQueryComponent_Template(
- ctx: $ViewQueryComponent$, cm: $boolean$) {
- let $tmp$: any;
- if (cm) {
- $r3$.ɵQ(0, SomeDirective, false);
- $r3$.ɵQ(1, SomeDirective, false);
- $r3$.ɵE(2, 'div', $e1_attrs$, $e1_dirs$);
- $r3$.ɵe();
- }
-
- $r3$.ɵqR($tmp$ = $r3$.ɵld>(0)) && (ctx.someDir = $tmp$.first);
- $r3$.ɵqR($tmp$ = $r3$.ɵld>(1)) &&
- (ctx.someDirList = $tmp$ as QueryList);
- SomeDirective.ngDirectiveDef.h(3, 2);
- $r3$.ɵr(3, 2);
- }
- });
- // /NORMATIVE
- }
-
-
- const viewQueryComp = renderComponent(ViewQueryComponent);
- expect(viewQueryComp.someDir).toEqual(someDir);
- expect((viewQueryComp.someDirList as QueryList).toArray()).toEqual([
- someDir !
- ]);
- });
-
- it('should support content queries', () => {
- type $MyApp$ = MyApp;
- type $ContentQueryComponent$ = ContentQueryComponent;
-
- let contentQueryComp: ContentQueryComponent;
-
- @Component({
- selector: 'content-query-component',
- template: `
-
- `
- })
- class ContentQueryComponent {
- @ContentChild(SomeDirective) someDir: SomeDirective;
- @ContentChildren(SomeDirective) someDirList: QueryList;
-
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: ContentQueryComponent,
- tag: 'content-query-component',
- factory: function ContentQueryComponent_Factory() {
- return [
- new ContentQueryComponent(), $r3$.ɵQ(null, SomeDirective, false),
- $r3$.ɵQ(null, SomeDirective, false)
- ];
- },
- hostBindings: function ContentQueryComponent_HostBindings(
- dirIndex: $number$, elIndex: $number$) {
- let $tmp$: any;
- const $instance$ = $r3$.ɵld(dirIndex)[0];
- $r3$.ɵqR($tmp$ = $r3$.ɵld(dirIndex)[1]) && ($instance$.someDir = $tmp$.first);
- $r3$.ɵqR($tmp$ = $r3$.ɵld(dirIndex)[2]) && ($instance$.someDirList = $tmp$);
- },
- template: function ContentQueryComponent_Template(
- ctx: $ContentQueryComponent$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵpD(0);
- $r3$.ɵE(1, 'div');
- $r3$.ɵP(2, 0);
- $r3$.ɵe();
- }
- }
- });
- // /NORMATIVE
- }
-
- const $e2_attrs$ = ['someDir', ''];
- const $e2_dirs$ = [SomeDirective];
-
- @Component({
- selector: 'my-app',
- template: `
-
-
-
- `
- })
- class MyApp {
- // NON-NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyApp,
- tag: 'my-app',
- factory: function MyApp_Factory() { return new MyApp(); },
- template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, ContentQueryComponent);
- contentQueryComp = $r3$.ɵld(1)[0];
- $r3$.ɵE(2, 'div', $e2_attrs$, $e2_dirs$);
- $r3$.ɵe();
- $r3$.ɵe();
- }
- ContentQueryComponent.ngComponentDef.h(1, 0);
- SomeDirective.ngDirectiveDef.h(3, 2);
- $r3$.ɵr(1, 0);
- $r3$.ɵr(3, 2);
- }
- });
- // /NON-NORMATIVE
- }
-
-
- expect(renderComp(MyApp))
- .toEqual(
- ` `);
- expect(contentQueryComp !.someDir).toEqual(someDir !);
- expect((contentQueryComp !.someDirList as QueryList).toArray()).toEqual([
- someDir !
- ]);
- });
-
- });
-
- });
-
- xdescribe('pipes', () => {
- type $MyApp$ = MyApp;
-
- @Pipe({
- name: 'myPipe',
- pure: false,
- })
- class MyPipe implements PipeTransform,
- OnDestroy {
- transform(value: any, ...args: any[]) { throw new Error('Method not implemented.'); }
- ngOnDestroy(): void { throw new Error('Method not implemented.'); }
-
- // NORMATIVE
- static ngPipeDef = $r3$.ɵdefinePipe({
- type: MyPipe,
- factory: function MyPipe_Factory() { return new MyPipe(); },
- pure: false,
- });
- // /NORMATIVE
- }
-
- @Pipe({
- name: 'myPurePipe',
- })
- class MyPurePipe implements PipeTransform {
- transform(value: any, ...args: any[]) { throw new Error('Method not implemented.'); }
-
- // NORMATIVE
- static ngPipeDef = $r3$.ɵdefinePipe({
- type: MyPurePipe,
- factory: function MyPurePipe_Factory() { return new MyPurePipe(); },
- });
- // /NORMATIVE
- }
-
- // NORMATIVE
- const $MyPurePipe_ngPipeDef$ = MyPurePipe.ngPipeDef;
- const $MyPipe_ngPipeDef$ = MyPipe.ngPipeDef;
- // /NORMATIVE
-
- @Component({template: `{{name | myPipe:size | myPurePipe:size }}`})
- class MyApp {
- name = 'World';
- size = 0;
-
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyApp,
- tag: 'my-app',
- factory: function MyApp_Factory() { return new MyApp(); },
- template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵT(0);
- $r3$.ɵPp(1, $MyPurePipe_ngPipeDef$, $MyPurePipe_ngPipeDef$.n());
- $r3$.ɵPp(2, $MyPipe_ngPipeDef$, $MyPipe_ngPipeDef$.n());
- }
- $r3$.ɵt(2, $r3$.ɵi1('', $r3$.ɵpb2(1, $r3$.ɵpb2(2, ctx.name, ctx.size), ctx.size), ''));
- }
- });
- // /NORMATIVE
- }
-
- it('should render pipes', () => {
- // TODO(misko): write a test once pipes runtime is implemented.
- });
- });
-
- describe('local references', () => {
- // TODO(misko): currently disabled until local refs are working
- xit('should translate DOM structure', () => {
- type $MyComponent$ = MyComponent;
-
- @Component({selector: 'my-component', template: ` Hello {{user.value}}!`})
- class MyComponent {
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyComponent,
- tag: 'my-component',
- factory: () => new MyComponent,
- template: function(ctx: $MyComponent$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, 'input', null, null, ['user', '']);
- $r3$.ɵe();
- $r3$.ɵT(2);
- }
- const l1_user = $r3$.ɵld(1);
- $r3$.ɵt(2, $r3$.ɵi1('Hello ', l1_user.value, '!'));
- }
- });
- // NORMATIVE
- }
-
- expect(renderComp(MyComponent))
- .toEqual('Hello World !
');
- });
- });
-
- describe('lifecycle hooks', () => {
- let events: string[] = [];
- let simpleLayout: SimpleLayout;
-
- type $LifecycleComp$ = LifecycleComp;
- type $SimpleLayout$ = SimpleLayout;
-
- beforeEach(() => { events = []; });
-
- @Component({selector: 'lifecycle-comp', template: ``})
- class LifecycleComp {
- @Input('name') nameMin: string;
-
- ngOnChanges() { events.push('changes' + this.nameMin); }
-
- ngOnInit() { events.push('init' + this.nameMin); }
- ngDoCheck() { events.push('check' + this.nameMin); }
-
- ngAfterContentInit() { events.push('content init' + this.nameMin); }
- ngAfterContentChecked() { events.push('content check' + this.nameMin); }
-
- ngAfterViewInit() { events.push('view init' + this.nameMin); }
- ngAfterViewChecked() { events.push('view check' + this.nameMin); }
-
- ngOnDestroy() { events.push(this.nameMin); }
-
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: LifecycleComp,
- tag: 'lifecycle-comp',
- factory: function LifecycleComp_Factory() { return new LifecycleComp(); },
- template: function LifecycleComp_Template(ctx: $LifecycleComp$, cm: $boolean$) {},
- inputs: {nameMin: 'name'},
- inputsPropertyName: {nameMin: 'nameMin'},
- features: [$r3$.ɵNgOnChangesFeature]
- });
- // /NORMATIVE
- }
-
- @Component({
- selector: 'simple-layout',
- template: `
-
-
- `
- })
- class SimpleLayout {
- name1 = '1';
- name2 = '2';
-
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: SimpleLayout,
- tag: 'simple-layout',
- factory: function SimpleLayout_Factory() { return simpleLayout = new SimpleLayout(); },
- template: function SimpleLayout_Template(ctx: $SimpleLayout$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, LifecycleComp);
- $r3$.ɵe();
- $r3$.ɵE(2, LifecycleComp);
- $r3$.ɵe();
- }
- $r3$.ɵp(0, 'name', $r3$.ɵb(ctx.name1));
- $r3$.ɵp(2, 'name', $r3$.ɵb(ctx.name2));
- LifecycleComp.ngComponentDef.h(1, 0);
- LifecycleComp.ngComponentDef.h(3, 2);
- $r3$.ɵr(1, 0);
- $r3$.ɵr(3, 2);
- }
- });
- // /NORMATIVE
- }
-
- it('should gen hooks with a few simple components', () => {
- expect(renderComp(SimpleLayout))
- .toEqual(` `);
- expect(events).toEqual([
- 'changes1', 'init1', 'check1', 'changes2', 'init2', 'check2', 'content init1',
- 'content check1', 'content init2', 'content check2', 'view init1', 'view check1',
- 'view init2', 'view check2'
- ]);
-
- events = [];
- simpleLayout.name1 = '-one';
- simpleLayout.name2 = '-two';
- $r3$.ɵdetectChanges(simpleLayout);
- expect(events).toEqual([
- 'changes-one', 'check-one', 'changes-two', 'check-two', 'content check-one',
- 'content check-two', 'view check-one', 'view check-two'
- ]);
- });
-
- });
-
- it('should inject ChangeDetectorRef', () => {
- type $MyComp$ = MyComp;
- type $MyApp$ = MyApp;
-
- @Component({selector: 'my-comp', template: `{{ value }}`})
- class MyComp {
- value: string;
- constructor(public cdr: ChangeDetectorRef) { this.value = (cdr.constructor as any).name; }
-
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyComp,
- tag: 'my-comp',
- factory: function MyComp_Factory() { return new MyComp($r3$.ɵinjectChangeDetectorRef()); },
- template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵT(0);
- }
- $r3$.ɵt(0, $r3$.ɵb(ctx.value));
- }
- });
- // /NORMATIVE
- }
-
- class MyApp {
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyApp,
- tag: 'my-app',
- factory: function MyApp_Factory() { return new MyApp(); },
- /** */
- template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, MyComp);
- $r3$.ɵe();
- }
- MyComp.ngComponentDef.h(1, 0);
- $r3$.ɵr(1, 0);
- }
- });
- }
-
- const app = renderComponent(MyApp);
- // ChangeDetectorRef is the token, ViewRef is historically the constructor
- expect(toHtml(app)).toEqual('ViewRef ');
- });
-
- describe('template variables', () => {
-
- interface ForOfContext {
- $implicit: any;
- index: number;
- even: boolean;
- odd: boolean;
- }
-
- @Directive({selector: '[forOf]'})
- class ForOfDirective {
- private previous: any[];
-
- constructor(private view: ViewContainerRef, private template: TemplateRef) {}
-
- @Input() forOf: any[];
-
- ngOnChanges(simpleChanges: SimpleChanges) {
- if ('forOf' in simpleChanges) {
- this.update();
- }
- }
-
- ngDoCheck(): void {
- const previous = this.previous;
- const current = this.forOf;
- if (!previous || previous.length != current.length ||
- previous.some((value: any, index: number) => current[index] !== previous[index])) {
- this.update();
- }
- }
-
- private update() {
- // TODO(chuckj): Not implemented yet
- // this.view.clear();
- if (this.forOf) {
- const current = this.forOf;
- for (let i = 0; i < current.length; i++) {
- const context = {$implicit: current[i], index: i, even: i % 2 == 0, odd: i % 2 == 1};
- // TODO(chuckj): Not implemented yet
- // this.view.createEmbeddedView(this.template, context);
- }
- this.previous = [...this.forOf];
- }
- }
-
- // NORMATIVE
- static ngDirectiveDef = $r3$.ɵdefineDirective({
- type: ForOfDirective,
- factory: function ForOfDirective_Factory() {
- return new ForOfDirective($r3$.ɵinjectViewContainerRef(), $r3$.ɵinjectTemplateRef());
- },
- // TODO(chuckj): Enable when ngForOf enabling lands.
- // features: [NgOnChangesFeature(NgForOf)],
- inputs: {forOf: 'forOf'}
- });
- // /NORMATIVE
- }
-
- it('should support a let variable and reference', () => {
- type $MyComponent$ = MyComponent;
-
- interface Item {
- name: string;
- }
-
- // NORMATIVE
- const $c1_dirs$ = [ForOfDirective];
- // /NORMATIVE
-
- @Component({
- selector: 'my-component',
- template: ``
- })
- class MyComponent {
- items = [{name: 'one'}, {name: 'two'}];
-
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyComponent,
- tag: 'my-component',
- factory: function MyComponent_Factory() { return new MyComponent(); },
- template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, 'ul');
- $r3$.ɵC(1, $c1_dirs$, MyComponent_ForOfDirective_Template_1);
- $r3$.ɵe();
- }
- $r3$.ɵp(1, 'forOf', $r3$.ɵb(ctx.items));
- $r3$.ɵcR(1);
- $r3$.ɵr(2, 1);
- $r3$.ɵcr();
-
- function MyComponent_ForOfDirective_Template_1(ctx1: $any$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, 'li');
- $r3$.ɵT(1);
- $r3$.ɵe();
- }
- const $l0_item$ = ctx1.$implicit;
- $r3$.ɵt(1, $r3$.ɵi1('', $l0_item$.name, ''));
- }
- }
- });
- // /NORMATIVE
- }
-
- // TODO(chuckj): update when the changes to enable ngForOf lands.
- expect(renderComp(MyComponent)).toEqual('');
- });
-
- it('should support accessing parent template variables', () => {
- type $MyComponent$ = MyComponent;
-
- interface Info {
- description: string;
- }
- interface Item {
- name: string;
- infos: Info[];
- }
-
- // NORMATIVE
- const $c1_dirs$ = [ForOfDirective];
- // /NORMATIVE
-
- @Component({
- selector: 'my-component',
- template: `
-
-
- {{item.name}}
-
-
- {{item.name}}: {{info.description}}
-
-
-
- `
- })
- class MyComponent {
- items: Item[] = [
- {name: 'one', infos: [{description: '11'}, {description: '12'}]},
- {name: 'two', infos: [{description: '21'}, {description: '22'}]}
- ];
-
- // NORMATIVE
- static ngComponentDef = $r3$.ɵdefineComponent({
- type: MyComponent,
- tag: 'my-component',
- factory: function MyComponent_Factory() { return new MyComponent(); },
- template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, 'ul');
- $r3$.ɵC(1, $c1_dirs$, MyComponent_ForOfDirective_Template_1);
- $r3$.ɵe();
- }
- $r3$.ɵp(1, 'forOf', $r3$.ɵb(ctx.items));
- $r3$.ɵcR(1);
- $r3$.ɵr(2, 1);
- $r3$.ɵcr();
-
- function MyComponent_ForOfDirective_Template_1(ctx1: $any$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, 'li');
- $r3$.ɵE(1, 'div');
- $r3$.ɵT(2);
- $r3$.ɵe();
- $r3$.ɵE(3, 'ul');
- $r3$.ɵC(4, $c1_dirs$, MyComponent_ForOfDirective_ForOfDirective_Template_3);
- $r3$.ɵe();
- $r3$.ɵe();
- }
- const $l0_item$ = ctx1.$implicit;
- $r3$.ɵp(4, 'forOf', $r3$.ɵb($l0_item$.infos));
- $r3$.ɵt(2, $r3$.ɵi1('', $l0_item$.name, ''));
- $r3$.ɵcR(4);
- $r3$.ɵr(5, 4);
- $r3$.ɵcr();
-
- function MyComponent_ForOfDirective_ForOfDirective_Template_3(
- ctx2: $any$, cm: $boolean$) {
- if (cm) {
- $r3$.ɵE(0, 'li');
- $r3$.ɵT(1);
- $r3$.ɵe();
- }
- const $l0_info$ = ctx2.$implicit;
- $r3$.ɵt(1, $r3$.ɵi2(' ', $l0_item$.name, ': ', $l0_info$.description, ' '));
- }
- }
- }
- });
- // /NORMATIVE
- }
- });
- });
-});
-
-xdescribe('NgModule', () => {
-
- interface Injectable {
- scope?: /*InjectorDefType*/ any;
- factory: Function;
- }
-
- function defineInjectable(opts: Injectable): Injectable {
- // This class should be imported from https://github.com/angular/angular/pull/20850
- return opts;
- }
- function defineInjector(opts: any): any {
- // This class should be imported from https://github.com/angular/angular/pull/20850
- return opts;
- }
- it('should convert module', () => {
- @Injectable()
- class Toast {
- constructor(name: String) {}
- // NORMATIVE
- static ngInjectableDef = defineInjectable({
- factory: () => new Toast($r3$.ɵinject(String)),
- });
- // /NORMATIVE
- }
-
- class CommonModule {
- // NORMATIVE
- static ngInjectorDef = defineInjector({});
- // /NORMATIVE
- }
-
- @NgModule({
- providers: [Toast, {provide: String, useValue: 'Hello'}],
- imports: [CommonModule],
- })
- class MyModule {
- constructor(toast: Toast) {}
- // NORMATIVE
- static ngInjectorDef = defineInjector({
- factory: () => new MyModule($r3$.ɵinject(Toast)),
- provider: [
- {provide: Toast, deps: [String]}, // If Toast has metadata generate this line
- Toast, // If Toast has no metadata generate this line.
- {provide: String, useValue: 'Hello'}
- ],
- imports: [CommonModule]
- });
- // /NORMATIVE
- }
-
- @Injectable(/*{MyModule}*/)
- class BurntToast {
- constructor(@Optional() toast: Toast|null, name: String) {}
- // NORMATIVE
- static ngInjectableDef = defineInjectable({
- scope: MyModule,
- factory: () => new BurntToast(
- $r3$.ɵinject(Toast, $r3$.ɵInjectFlags.Optional), $r3$.ɵinject(String)),
- });
- // /NORMATIVE
- }
-
- });
-});
-
-function renderComp(type: $r3$.ɵComponentType): string {
- return toHtml(renderComponent(type));
-}
diff --git a/packages/core/test/render3/compiler_canonical/component_directives_spec.ts b/packages/core/test/render3/compiler_canonical/component_directives_spec.ts
new file mode 100644
index 0000000000..92678172e4
--- /dev/null
+++ b/packages/core/test/render3/compiler_canonical/component_directives_spec.ts
@@ -0,0 +1,844 @@
+/**
+ * @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 {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
+import * as $r3$ from '../../../src/core_render3_private_export';
+import {renderComponent, toHtml} from '../render_util';
+
+/// See: `normative.md`
+describe('components & directives', () => {
+ type $boolean$ = boolean;
+ type $any$ = any;
+ type $number$ = number;
+
+
+ it('should instantiate directives', () => {
+ type $ChildComponent$ = ChildComponent;
+ type $MyComponent$ = MyComponent;
+
+ const log: string[] = [];
+ @Component({selector: 'child', template: 'child-view'})
+ class ChildComponent {
+ constructor() { log.push('ChildComponent'); }
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: ChildComponent,
+ tag: `child`,
+ factory: () => new ChildComponent(),
+ template: function(ctx: $ChildComponent$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵT(0, 'child-view');
+ }
+ }
+ });
+ // /NORMATIVE
+ }
+
+ @Directive({
+ selector: '[some-directive]',
+ })
+ class SomeDirective {
+ constructor() { log.push('SomeDirective'); }
+ // NORMATIVE
+ static ngDirectiveDef = $r3$.ɵdefineDirective({
+ type: SomeDirective,
+ factory: () => new SomeDirective(),
+ });
+ // /NORMATIVE
+ }
+
+ // Important: keep arrays outside of function to not create new instances.
+ // NORMATIVE
+ const $e0_attrs$ = ['some-directive', ''];
+ const $e0_dirs$ = [SomeDirective];
+ // /NORMATIVE
+
+ @Component({selector: 'my-component', template: ` !`})
+ class MyComponent {
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyComponent,
+ tag: 'my-component',
+ factory: () => new MyComponent(),
+ template: function(ctx: $MyComponent$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, ChildComponent, $e0_attrs$, $e0_dirs$);
+ $r3$.ɵe();
+ $r3$.ɵT(3, '!');
+ }
+ ChildComponent.ngComponentDef.h(1, 0);
+ SomeDirective.ngDirectiveDef.h(2, 0);
+ $r3$.ɵr(1, 0);
+ $r3$.ɵr(2, 0);
+ }
+ });
+ // /NORMATIVE
+ }
+
+ expect(renderComp(MyComponent)).toEqual('child-view !');
+ expect(log).toEqual(['ChildComponent', 'SomeDirective']);
+ });
+
+ it('should support host bindings', () => {
+ type $MyApp$ = MyApp;
+
+ @Directive({selector: '[hostBindingDir]'})
+ class HostBindingDir {
+ @HostBinding('id') dirId = 'some id';
+
+ // NORMATIVE
+ static ngDirectiveDef = $r3$.ɵdefineDirective({
+ type: HostBindingDir,
+ factory: function HostBindingDir_Factory() { return new HostBindingDir(); },
+ hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) {
+ $r3$.ɵp(elIndex, 'id', $r3$.ɵb($r3$.ɵld(dirIndex).dirId));
+ }
+ });
+ // /NORMATIVE
+ }
+
+ const $e0_attrs$ = ['hostBindingDir', ''];
+ const $e0_dirs$ = [HostBindingDir];
+
+ @Component({
+ selector: 'my-app',
+ template: `
+
+ `
+ })
+ class MyApp {
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyApp,
+ tag: 'my-app',
+ factory: function MyApp_Factory() { return new MyApp(); },
+ template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, 'div', $e0_attrs$, $e0_dirs$);
+ $r3$.ɵe();
+ }
+ HostBindingDir.ngDirectiveDef.h(1, 0);
+ $r3$.ɵr(1, 0);
+ }
+ });
+ }
+
+ expect(renderComp(MyApp)).toEqual(`
`);
+ });
+
+ it('should support host listeners', () => {
+ type $MyApp$ = MyApp;
+
+ @Directive({selector: '[hostlistenerDir]'})
+ class HostListenerDir {
+ @HostListener('click')
+ onClick() {}
+
+ // NORMATIVE
+ static ngDirectiveDef = $r3$.ɵdefineDirective({
+ type: HostListenerDir,
+ factory: function HostListenerDir_Factory() {
+ const $dir$ = new HostListenerDir();
+ $r3$.ɵL('click', function HostListenerDir_click_Handler(event: any) { $dir$.onClick(); });
+ return $dir$;
+ },
+ });
+ // /NORMATIVE
+ }
+
+ const $e0_attrs$ = ['hostListenerDir', ''];
+ const $e0_dirs$ = [HostListenerDir];
+
+ @Component({
+ selector: 'my-app',
+ template: `
+ Click
+ `
+ })
+ class MyApp {
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyApp,
+ tag: 'my-app',
+ factory: function MyApp_Factory() { return new MyApp(); },
+ template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, 'button', $e0_attrs$, $e0_dirs$);
+ $r3$.ɵT(2, 'Click');
+ $r3$.ɵe();
+ }
+ HostListenerDir.ngDirectiveDef.h(1, 0);
+ $r3$.ɵr(1, 0);
+ }
+ });
+ }
+
+ expect(renderComp(MyApp)).toEqual(`Click `);
+ });
+
+
+ it('should support setting of host attributes', () => {
+ type $MyApp$ = MyApp;
+
+ @Directive({selector: '[hostAttributeDir]', host: {'role': 'listbox'}})
+ class HostAttributeDir {
+ // NORMATIVE
+ static ngDirectiveDef = $r3$.ɵdefineDirective({
+ type: HostAttributeDir,
+ factory: function HostAttributeDir_Factory() { return new HostAttributeDir(); },
+ attributes: ['role', 'listbox']
+ });
+ // /NORMATIVE
+ }
+
+ const $e0_attrs$ = ['hostAttributeDir', ''];
+ const $e0_dirs$ = [HostAttributeDir];
+
+ @Component({
+ selector: 'my-app',
+ template: `
+
+ `
+ })
+ class MyApp {
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyApp,
+ tag: 'my-app',
+ factory: function MyApp_Factory() { return new MyApp(); },
+ template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, 'div', $e0_attrs$, $e0_dirs$);
+ $r3$.ɵe();
+ }
+ HostAttributeDir.ngDirectiveDef.h(1, 0);
+ $r3$.ɵr(1, 0);
+ }
+ });
+ }
+
+ expect(renderComp(MyApp)).toEqual(`
`);
+ });
+
+ it('should support bindings of host attributes', () => {
+ type $MyApp$ = MyApp;
+
+ @Directive({selector: '[hostBindingDir]'})
+ class HostBindingDir {
+ @HostBinding('attr.aria-label') label = 'some label';
+
+ // NORMATIVE
+ static ngDirectiveDef = $r3$.ɵdefineDirective({
+ type: HostBindingDir,
+ factory: function HostBindingDir_Factory() { return new HostBindingDir(); },
+ hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) {
+ $r3$.ɵa(elIndex, 'aria-label', $r3$.ɵb($r3$.ɵld(dirIndex).label));
+ }
+ });
+ // /NORMATIVE
+ }
+
+ const $e0_attrs$ = ['hostBindingDir', ''];
+ const $e0_dirs$ = [HostBindingDir];
+
+ @Component({
+ selector: 'my-app',
+ template: `
+
+ `
+ })
+ class MyApp {
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyApp,
+ tag: 'my-app',
+ factory: function MyApp_Factory() { return new MyApp(); },
+ template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, 'div', $e0_attrs$, $e0_dirs$);
+ $r3$.ɵe();
+ }
+ HostBindingDir.ngDirectiveDef.h(1, 0);
+ $r3$.ɵr(1, 0);
+ }
+ });
+ }
+
+ expect(renderComp(MyApp)).toEqual(`
`);
+ });
+
+ it('should support onPush components', () => {
+ type $MyApp$ = MyApp;
+ type $MyComp$ = MyComp;
+
+ @Component({
+ selector: 'my-comp',
+ template: `
+ {{ name }}
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush
+ })
+ class MyComp {
+ @Input() name: string;
+
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyComp,
+ tag: 'my-comp',
+ factory: function MyComp_Factory() { return new MyComp(); },
+ template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵT(0);
+ }
+ $r3$.ɵt(0, $r3$.ɵb(ctx.name));
+ },
+ inputs: {name: 'name'},
+ changeDetection: ChangeDetectionStrategy.OnPush
+ });
+ // /NORMATIVE
+ }
+
+ @Component({
+ selector: 'my-app',
+ template: `
+
+ `
+ })
+ class MyApp {
+ name = 'some name';
+
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyApp,
+ tag: 'my-app',
+ factory: function MyApp_Factory() { return new MyApp(); },
+ template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, MyComp);
+ $r3$.ɵe();
+ }
+ $r3$.ɵp(0, 'name', $r3$.ɵb(ctx.name));
+ MyComp.ngComponentDef.h(1, 0);
+ $r3$.ɵr(1, 0);
+ }
+ });
+ }
+
+ expect(renderComp(MyApp)).toEqual(`some name `);
+ });
+
+ xit('should support structural directives', () => {
+ type $MyComponent$ = MyComponent;
+
+ const log: string[] = [];
+ @Directive({
+ selector: '[if]',
+ })
+ class IfDirective {
+ constructor(template: TemplateRef) { log.push('ifDirective'); }
+ // NORMATIVE
+ static ngDirectiveDef = $r3$.ɵdefineDirective({
+ type: IfDirective,
+ factory: () => new IfDirective($r3$.ɵinjectTemplateRef()),
+ });
+ // /NORMATIVE
+ }
+
+ // Important: keep arrays outside of function to not create new instances.
+ // NORMATIVE
+ const $e0_locals$ = ['foo', ''];
+ const $c1_dirs$ = [IfDirective];
+ // /NORMATIVE
+
+ @Component(
+ {selector: 'my-component', template: ``})
+ class MyComponent {
+ salutation = 'Hello';
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyComponent,
+ tag: 'my-component',
+ factory: () => new MyComponent(),
+ template: function(ctx: $MyComponent$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, 'ul', null, null, $e0_locals$);
+ $r3$.ɵC(2, $c1_dirs$, C1);
+ $r3$.ɵe();
+ }
+ let $foo$ = $r3$.ɵld(1);
+ $r3$.ɵcR(2);
+ $r3$.ɵr(3, 2);
+ $r3$.ɵcr();
+
+ function C1(ctx1: $any$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, 'li');
+ $r3$.ɵT(1);
+ $r3$.ɵe();
+ }
+ $r3$.ɵt(1, $r3$.ɵi2('', ctx.salutation, ' ', $foo$, ''));
+ }
+ }
+ });
+ // /NORMATIVE
+ }
+
+ expect(renderComp(MyComponent)).toEqual('child-view !');
+ expect(log).toEqual(['ChildComponent', 'SomeDirective']);
+ });
+
+ describe('value composition', () => {
+ type $MyArrayComp$ = MyArrayComp;
+
+ @Component({
+ selector: 'my-array-comp',
+ template: `
+ {{ names[0] }} {{ names[1] }}
+ `
+ })
+ class MyArrayComp {
+ @Input() names: string[];
+
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyArrayComp,
+ tag: 'my-array-comp',
+ factory: function MyArrayComp_Factory() { return new MyArrayComp(); },
+ template: function MyArrayComp_Template(ctx: $MyArrayComp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵT(0);
+ }
+ $r3$.ɵt(0, $r3$.ɵi2('', ctx.names[0], ' ', ctx.names[1], ''));
+ },
+ inputs: {names: 'names'}
+ });
+ }
+
+ it('should support array literals of constants', () => {
+ type $MyApp$ = MyApp;
+
+ // NORMATIVE
+ const $e0_arr$ = ['Nancy', 'Bess'];
+ // /NORMATIVE
+
+ @Component({
+ selector: 'my-app',
+ template: `
+
+ `
+ })
+ class MyApp {
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyApp,
+ tag: 'my-app',
+ factory: function MyApp_Factory() { return new MyApp(); },
+ template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, MyArrayComp);
+ $r3$.ɵe();
+ }
+ $r3$.ɵp(0, 'names', cm ? $e0_arr$ : $r3$.ɵNC);
+ MyArrayComp.ngComponentDef.h(1, 0);
+ $r3$.ɵr(1, 0);
+ }
+ });
+ // /NORMATIVE
+ }
+
+ expect(renderComp(MyApp)).toEqual(`Nancy Bess `);
+ });
+
+ it('should support array literals of constants inside function calls', () => {
+ type $MyApp$ = MyApp;
+
+ // NORMATIVE
+ const $e0_ff$ = () => ['Nancy', 'Bess'];
+ // /NORMATIVE
+
+ @Component({
+ selector: 'my-app',
+ template: `
+
+ `
+ })
+ class MyApp {
+ someFn(arr: string[]): string[] {
+ arr[0] = arr[0].toUpperCase();
+ return arr;
+ }
+
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyApp,
+ tag: 'my-app',
+ factory: function MyApp_Factory() { return new MyApp(); },
+ template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, MyArrayComp);
+ $r3$.ɵe();
+ }
+ $r3$.ɵp(0, 'names', $r3$.ɵb(ctx.someFn($r3$.ɵf0($e0_ff$))));
+ MyArrayComp.ngComponentDef.h(1, 0);
+ $r3$.ɵr(1, 0);
+ }
+ });
+ // /NORMATIVE
+ }
+
+ expect(renderComp(MyApp)).toEqual(`NANCY Bess `);
+ });
+
+ it('should support array literals of constants inside expressions', () => {
+ type $MyApp$ = MyApp;
+ type $MyComp$ = MyComp;
+
+ @Component({selector: 'my-comp', template: `{{ num }}`})
+ class MyComp {
+ num: number;
+
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyComp,
+ tag: 'my-comp',
+ factory: function MyComp_Factory() { return new MyComp(); },
+ template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵT(0);
+ }
+ $r3$.ɵt(0, $r3$.ɵb(ctx.num));
+ },
+ inputs: {num: 'num'}
+ });
+ }
+
+ // NORMATIVE
+ const $e0_ff$ = () => ['Nancy', 'Bess'];
+ // /NORMATIVE
+
+ @Component({
+ selector: 'my-app',
+ template: `
+
+ `
+ })
+ class MyApp {
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyApp,
+ tag: 'my-app',
+ factory: function MyApp_Factory() { return new MyApp(); },
+ template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, MyComp);
+ $r3$.ɵe();
+ }
+ $r3$.ɵp(0, 'num', $r3$.ɵb($r3$.ɵf0($e0_ff$).length + 1));
+ MyComp.ngComponentDef.h(1, 0);
+ $r3$.ɵr(1, 0);
+ }
+ });
+ // /NORMATIVE
+ }
+
+ expect(renderComp(MyApp)).toEqual(`3 `);
+ });
+
+
+ it('should support array literals', () => {
+ type $MyApp$ = MyApp;
+
+ // NORMATIVE
+ const $e0_ff$ = (v: any) => ['Nancy', v];
+ // /NORMATIVE
+
+ @Component({
+ selector: 'my-app',
+ template: `
+
+ `
+ })
+ class MyApp {
+ customName = 'Bess';
+
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyApp,
+ tag: 'my-app',
+ factory: function MyApp_Factory() { return new MyApp(); },
+ template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, MyArrayComp);
+ $r3$.ɵe();
+ }
+ $r3$.ɵp(0, 'names', $r3$.ɵb($r3$.ɵf1($e0_ff$, ctx.customName)));
+ MyArrayComp.ngComponentDef.h(1, 0);
+ $r3$.ɵr(1, 0);
+ }
+ });
+ // /NORMATIVE
+ }
+
+ expect(renderComp(MyApp)).toEqual(`Nancy Bess `);
+ });
+
+ it('should support 9+ bindings in array literals', () => {
+ type $MyComp$ = MyComp;
+
+ @Component({
+ selector: 'my-comp',
+ template: `
+ {{ names[0] }}
+ {{ names[1] }}
+ {{ names[3] }}
+ {{ names[4] }}
+ {{ names[5] }}
+ {{ names[6] }}
+ {{ names[7] }}
+ {{ names[8] }}
+ {{ names[9] }}
+ {{ names[10] }}
+ {{ names[11] }}
+ `
+ })
+ class MyComp {
+ @Input() names: string[];
+
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyComp,
+ tag: 'my-comp',
+ factory: function MyComp_Factory() { return new MyComp(); },
+ template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵT(0);
+ $r3$.ɵT(1);
+ $r3$.ɵT(2);
+ $r3$.ɵT(3);
+ $r3$.ɵT(4);
+ $r3$.ɵT(5);
+ $r3$.ɵT(6);
+ $r3$.ɵT(7);
+ $r3$.ɵT(8);
+ $r3$.ɵT(9);
+ $r3$.ɵT(10);
+ $r3$.ɵT(11);
+ }
+ $r3$.ɵt(0, $r3$.ɵb(ctx.names[0]));
+ $r3$.ɵt(1, $r3$.ɵb(ctx.names[1]));
+ $r3$.ɵt(2, $r3$.ɵb(ctx.names[2]));
+ $r3$.ɵt(3, $r3$.ɵb(ctx.names[3]));
+ $r3$.ɵt(4, $r3$.ɵb(ctx.names[4]));
+ $r3$.ɵt(5, $r3$.ɵb(ctx.names[5]));
+ $r3$.ɵt(6, $r3$.ɵb(ctx.names[6]));
+ $r3$.ɵt(7, $r3$.ɵb(ctx.names[7]));
+ $r3$.ɵt(8, $r3$.ɵb(ctx.names[8]));
+ $r3$.ɵt(9, $r3$.ɵb(ctx.names[9]));
+ $r3$.ɵt(10, $r3$.ɵb(ctx.names[10]));
+ $r3$.ɵt(11, $r3$.ɵb(ctx.names[11]));
+ },
+ inputs: {names: 'names'}
+ });
+ }
+
+ // NORMATIVE
+ const $e0_ff$ =
+ (v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, v7: any,
+ v8: any) => ['start-', v0, v1, v2, v3, v4, '-middle-', v5, v6, v7, v8, '-end'];
+ // /NORMATIVE
+
+ @Component({
+ selector: 'my-app',
+ template: `
+
+
+ `
+ })
+ class MyApp {
+ n0 = 'a';
+ n1 = 'b';
+ n2 = 'c';
+ n3 = 'd';
+ n4 = 'e';
+ n5 = 'f';
+ n6 = 'g';
+ n7 = 'h';
+ n8 = 'i';
+
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyApp,
+ tag: 'my-app',
+ factory: function MyApp_Factory() { return new MyApp(); },
+ template: function MyApp_Template(c: MyApp, cm: boolean) {
+ if (cm) {
+ $r3$.ɵE(0, MyComp);
+ $r3$.ɵe();
+ }
+ $r3$.ɵp(
+ 0, 'names',
+ $r3$.ɵb($r3$.ɵfV($e0_ff$, [c.n0, c.n1, c.n2, c.n3, c.n4, c.n5, c.n6, c.n7, c.n8])));
+ MyComp.ngComponentDef.h(1, 0);
+ $r3$.ɵr(1, 0);
+ }
+ });
+ // /NORMATIVE
+ }
+
+ expect(renderComp(MyApp)).toEqual(`start-abcde-middle-fghi-end `);
+ });
+
+ it('should support object literals', () => {
+ type $ObjectComp$ = ObjectComp;
+ type $MyApp$ = MyApp;
+
+ @Component({
+ selector: 'object-comp',
+ template: `
+ {{ config['duration'] }}
+ {{ config.animation }}
+ `
+ })
+ class ObjectComp {
+ config: {[key: string]: any};
+
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: ObjectComp,
+ tag: 'object-comp',
+ factory: function ObjectComp_Factory() { return new ObjectComp(); },
+ template: function ObjectComp_Template(ctx: $ObjectComp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, 'p');
+ $r3$.ɵT(1);
+ $r3$.ɵe();
+ $r3$.ɵE(2, 'p');
+ $r3$.ɵT(3);
+ $r3$.ɵe();
+ }
+ $r3$.ɵt(1, $r3$.ɵb(ctx.config['duration']));
+ $r3$.ɵt(3, $r3$.ɵb(ctx.config.animation));
+ },
+ inputs: {config: 'config'}
+ });
+ }
+
+ // NORMATIVE
+ const $e0_ff$ = (v: any) => { return {'duration': 500, animation: v}; };
+ // /NORMATIVE
+
+ @Component({
+ selector: 'my-app',
+ template: `
+
+ `
+ })
+ class MyApp {
+ name = 'slide';
+
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyApp,
+ tag: 'my-app',
+ factory: function MyApp_Factory() { return new MyApp(); },
+ template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, ObjectComp);
+ $r3$.ɵe();
+ }
+ $r3$.ɵp(0, 'config', $r3$.ɵb($r3$.ɵf1($e0_ff$, ctx.name)));
+ ObjectComp.ngComponentDef.h(1, 0);
+ $r3$.ɵr(1, 0);
+ }
+ });
+ // /NORMATIVE
+ }
+
+ expect(renderComp(MyApp)).toEqual(`500
slide
`);
+ });
+
+ it('should support expressions nested deeply in object/array literals', () => {
+ type $NestedComp$ = NestedComp;
+ type $MyApp$ = MyApp;
+
+ @Component({
+ selector: 'nested-comp',
+ template: `
+ {{ config.animation }}
+ {{config.actions[0].opacity }}
+ {{config.actions[1].duration }}
+ `
+ })
+ class NestedComp {
+ config: {[key: string]: any};
+
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: NestedComp,
+ tag: 'nested-comp',
+ factory: function NestedComp_Factory() { return new NestedComp(); },
+ template: function NestedComp_Template(ctx: $NestedComp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, 'p');
+ $r3$.ɵT(1);
+ $r3$.ɵe();
+ $r3$.ɵE(2, 'p');
+ $r3$.ɵT(3);
+ $r3$.ɵe();
+ $r3$.ɵE(4, 'p');
+ $r3$.ɵT(5);
+ $r3$.ɵe();
+ }
+ $r3$.ɵt(1, $r3$.ɵb(ctx.config.animation));
+ $r3$.ɵt(3, $r3$.ɵb(ctx.config.actions[0].opacity));
+ $r3$.ɵt(5, $r3$.ɵb(ctx.config.actions[1].duration));
+ },
+ inputs: {config: 'config'}
+ });
+ }
+
+ // NORMATIVE
+ const $e0_ff$ = (v: any) => { return {opacity: 1, duration: v}; };
+ const $c0$ = {opacity: 0, duration: 0};
+ const $e0_ff_1$ = (v: any) => [$c0$, v];
+ const $e0_ff_2$ = (v1: any, v2: any) => { return {animation: v1, actions: v2}; };
+ // /NORMATIVE
+
+ @Component({
+ selector: 'my-app',
+ template: `
+
+
+ `
+ })
+ class MyApp {
+ name = 'slide';
+ duration = 100;
+
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyApp,
+ tag: 'my-app',
+ factory: function MyApp_Factory() { return new MyApp(); },
+ template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, NestedComp);
+ $r3$.ɵe();
+ }
+ $r3$.ɵp(
+ 0, 'config', $r3$.ɵf2(
+ $e0_ff_2$, ctx.name,
+ $r3$.ɵb($r3$.ɵf1($e0_ff_1$, $r3$.ɵf1($e0_ff$, ctx.duration)))));
+ NestedComp.ngComponentDef.h(1, 0);
+ $r3$.ɵr(1, 0);
+ }
+ });
+ // /NORMATIVE
+ }
+
+ expect(renderComp(MyApp))
+ .toEqual(`slide
0
100
`);
+ });
+
+ });
+
+});
+
+function renderComp(type: $r3$.ɵComponentType): string {
+ return toHtml(renderComponent(type));
+}
diff --git a/packages/core/test/render3/compiler_canonical/content_projection_spec.ts b/packages/core/test/render3/compiler_canonical/content_projection_spec.ts
new file mode 100644
index 0000000000..ed64e0bf20
--- /dev/null
+++ b/packages/core/test/render3/compiler_canonical/content_projection_spec.ts
@@ -0,0 +1,94 @@
+/**
+ * @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 {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
+import * as $r3$ from '../../../src/core_render3_private_export';
+import {renderComponent, toHtml} from '../render_util';
+
+/// See: `normative.md`
+describe('content projection', () => {
+ type $boolean$ = boolean;
+
+ it('should support content projection', () => {
+ type $SimpleComponent$ = SimpleComponent;
+ type $ComplexComponent$ = ComplexComponent;
+ type $MyApp$ = MyApp;
+
+ @Component({selector: 'simple', template: `
`})
+ class SimpleComponent {
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: SimpleComponent,
+ tag: 'simple',
+ factory: () => new SimpleComponent(),
+ template: function(ctx: $SimpleComponent$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵpD(0);
+ $r3$.ɵE(1, 'div');
+ $r3$.ɵP(2, 0);
+ $r3$.ɵe();
+ }
+ }
+ });
+ // /NORMATIVE
+ }
+
+ // NORMATIVE
+ const $pD_0$: $r3$.ɵCssSelector[] =
+ [[[['span', 'title', 'toFirst'], null]], [[['span', 'title', 'toSecond'], null]]];
+ // /NORMATIVE
+
+ @Component({
+ selector: 'complex',
+ template: `
+
+
`
+ })
+ class ComplexComponent {
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: ComplexComponent,
+ tag: 'complex',
+ factory: () => new ComplexComponent(),
+ template: function(ctx: $ComplexComponent$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵpD(0, $pD_0$);
+ $r3$.ɵE(1, 'div', ['id', 'first']);
+ $r3$.ɵP(2, 0, 1);
+ $r3$.ɵe();
+ $r3$.ɵE(3, 'div', ['id', 'second']);
+ $r3$.ɵP(4, 0, 2);
+ $r3$.ɵe();
+ }
+ }
+ });
+ // /NORMATIVE
+ }
+
+ @Component({
+ selector: 'my-app',
+ template: `content
+ `
+ })
+ class MyApp {
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyApp,
+ tag: 'my-app',
+ factory: () => new MyApp(),
+ template: function(ctx: $MyApp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, SimpleComponent);
+ $r3$.ɵT(2, 'content');
+ $r3$.ɵe();
+ }
+ }
+ });
+ }
+ });
+
+});
\ No newline at end of file
diff --git a/packages/core/test/render3/compiler_canonical/elements_spec.ts b/packages/core/test/render3/compiler_canonical/elements_spec.ts
new file mode 100644
index 0000000000..e626248543
--- /dev/null
+++ b/packages/core/test/render3/compiler_canonical/elements_spec.ts
@@ -0,0 +1,54 @@
+/**
+ * @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 {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
+import * as $r3$ from '../../../src/core_render3_private_export';
+import {renderComponent, toHtml} from '../render_util';
+
+/// See: `normative.md`
+describe('elements', () => {
+ // Saving type as $boolean$, etc to simplify testing for compiler, as types aren't saved
+ type $boolean$ = boolean;
+ type $any$ = any;
+ type $number$ = number;
+
+ it('should translate DOM structure', () => {
+ type $MyComponent$ = MyComponent;
+
+ // Important: keep arrays outside of function to not create new instances.
+ const $e0_attrs$ = ['class', 'my-app', 'title', 'Hello'];
+
+ @Component({
+ selector: 'my-component',
+ template: `Hello World !
`
+ })
+ class MyComponent {
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyComponent,
+ tag: 'my-component',
+ factory: () => new MyComponent(),
+ template: function(ctx: $MyComponent$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, 'div', $e0_attrs$);
+ $r3$.ɵT(1, 'Hello ');
+ $r3$.ɵE(2, 'b');
+ $r3$.ɵT(3, 'World');
+ $r3$.ɵe();
+ $r3$.ɵT(4, '!');
+ $r3$.ɵe();
+ }
+ }
+ });
+ // /NORMATIVE
+ }
+
+ expect(toHtml(renderComponent(MyComponent)))
+ .toEqual('Hello World !
');
+ });
+});
diff --git a/packages/core/test/render3/compiler_canonical/injection_spec.ts b/packages/core/test/render3/compiler_canonical/injection_spec.ts
new file mode 100644
index 0000000000..463a4a7b60
--- /dev/null
+++ b/packages/core/test/render3/compiler_canonical/injection_spec.ts
@@ -0,0 +1,63 @@
+/**
+ * @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 {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
+import * as $r3$ from '../../../src/core_render3_private_export';
+import {renderComponent, toHtml} from '../render_util';
+
+/// See: `normative.md`
+describe('injection', () => {
+ type $boolean$ = boolean;
+
+ it('should inject ChangeDetectorRef', () => {
+ type $MyComp$ = MyComp;
+ type $MyApp$ = MyApp;
+
+ @Component({selector: 'my-comp', template: `{{ value }}`})
+ class MyComp {
+ value: string;
+ constructor(public cdr: ChangeDetectorRef) { this.value = (cdr.constructor as any).name; }
+
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyComp,
+ tag: 'my-comp',
+ factory: function MyComp_Factory() { return new MyComp($r3$.ɵinjectChangeDetectorRef()); },
+ template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵT(0);
+ }
+ $r3$.ɵt(0, $r3$.ɵb(ctx.value));
+ }
+ });
+ // /NORMATIVE
+ }
+
+ class MyApp {
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyApp,
+ tag: 'my-app',
+ factory: function MyApp_Factory() { return new MyApp(); },
+ /** */
+ template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, MyComp);
+ $r3$.ɵe();
+ }
+ MyComp.ngComponentDef.h(1, 0);
+ $r3$.ɵr(1, 0);
+ }
+ });
+ }
+
+ const app = renderComponent(MyApp);
+ // ChangeDetectorRef is the token, ViewRef is historically the constructor
+ expect(toHtml(app)).toEqual('ViewRef ');
+ });
+
+});
\ No newline at end of file
diff --git a/packages/core/test/render3/compiler_canonical/life_cycle_spec.ts b/packages/core/test/render3/compiler_canonical/life_cycle_spec.ts
new file mode 100644
index 0000000000..1296e9e93f
--- /dev/null
+++ b/packages/core/test/render3/compiler_canonical/life_cycle_spec.ts
@@ -0,0 +1,107 @@
+/**
+ * @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 {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
+import * as $r3$ from '../../../src/core_render3_private_export';
+import {renderComponent, toHtml} from '../render_util';
+
+/// See: `normative.md`
+describe('lifecycle hooks', () => {
+ let events: string[] = [];
+ let simpleLayout: SimpleLayout;
+
+ type $boolean$ = boolean;
+ type $LifecycleComp$ = LifecycleComp;
+ type $SimpleLayout$ = SimpleLayout;
+
+ beforeEach(() => { events = []; });
+
+ @Component({selector: 'lifecycle-comp', template: ``})
+ class LifecycleComp {
+ @Input('name') nameMin: string;
+
+ ngOnChanges() { events.push('changes' + this.nameMin); }
+
+ ngOnInit() { events.push('init' + this.nameMin); }
+ ngDoCheck() { events.push('check' + this.nameMin); }
+
+ ngAfterContentInit() { events.push('content init' + this.nameMin); }
+ ngAfterContentChecked() { events.push('content check' + this.nameMin); }
+
+ ngAfterViewInit() { events.push('view init' + this.nameMin); }
+ ngAfterViewChecked() { events.push('view check' + this.nameMin); }
+
+ ngOnDestroy() { events.push(this.nameMin); }
+
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: LifecycleComp,
+ tag: 'lifecycle-comp',
+ factory: function LifecycleComp_Factory() { return new LifecycleComp(); },
+ template: function LifecycleComp_Template(ctx: $LifecycleComp$, cm: $boolean$) {},
+ inputs: {nameMin: 'name'},
+ inputsPropertyName: {nameMin: 'nameMin'},
+ features: [$r3$.ɵNgOnChangesFeature]
+ });
+ // /NORMATIVE
+ }
+
+ @Component({
+ selector: 'simple-layout',
+ template: `
+
+
+ `
+ })
+ class SimpleLayout {
+ name1 = '1';
+ name2 = '2';
+
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: SimpleLayout,
+ tag: 'simple-layout',
+ factory: function SimpleLayout_Factory() { return simpleLayout = new SimpleLayout(); },
+ template: function SimpleLayout_Template(ctx: $SimpleLayout$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, LifecycleComp);
+ $r3$.ɵe();
+ $r3$.ɵE(2, LifecycleComp);
+ $r3$.ɵe();
+ }
+ $r3$.ɵp(0, 'name', $r3$.ɵb(ctx.name1));
+ $r3$.ɵp(2, 'name', $r3$.ɵb(ctx.name2));
+ LifecycleComp.ngComponentDef.h(1, 0);
+ LifecycleComp.ngComponentDef.h(3, 2);
+ $r3$.ɵr(1, 0);
+ $r3$.ɵr(3, 2);
+ }
+ });
+ // /NORMATIVE
+ }
+
+ it('should gen hooks with a few simple components', () => {
+ expect(toHtml(renderComponent(SimpleLayout)))
+ .toEqual(` `);
+ expect(events).toEqual([
+ 'changes1', 'init1', 'check1', 'changes2', 'init2', 'check2', 'content init1',
+ 'content check1', 'content init2', 'content check2', 'view init1', 'view check1',
+ 'view init2', 'view check2'
+ ]);
+
+ events = [];
+ simpleLayout.name1 = '-one';
+ simpleLayout.name2 = '-two';
+ $r3$.ɵdetectChanges(simpleLayout);
+ expect(events).toEqual([
+ 'changes-one', 'check-one', 'changes-two', 'check-two', 'content check-one',
+ 'content check-two', 'view check-one', 'view check-two'
+ ]);
+ });
+
+});
diff --git a/packages/core/test/render3/compiler_canonical/local_reference_spec.ts b/packages/core/test/render3/compiler_canonical/local_reference_spec.ts
new file mode 100644
index 0000000000..5e5de98243
--- /dev/null
+++ b/packages/core/test/render3/compiler_canonical/local_reference_spec.ts
@@ -0,0 +1,44 @@
+/**
+ * @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 {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
+import * as $r3$ from '../../../src/core_render3_private_export';
+import {renderComponent, toHtml} from '../render_util';
+
+/// See: `normative.md`
+describe('local references', () => {
+ type $boolean$ = boolean;
+
+ // TODO(misko): currently disabled until local refs are working
+ xit('should translate DOM structure', () => {
+ type $MyComponent$ = MyComponent;
+
+ @Component({selector: 'my-component', template: ` Hello {{user.value}}!`})
+ class MyComponent {
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyComponent,
+ tag: 'my-component',
+ factory: () => new MyComponent,
+ template: function(ctx: $MyComponent$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, 'input', null, null, ['user', '']);
+ $r3$.ɵe();
+ $r3$.ɵT(2);
+ }
+ const l1_user = $r3$.ɵld(1);
+ $r3$.ɵt(2, $r3$.ɵi1('Hello ', l1_user.value, '!'));
+ }
+ });
+ // NORMATIVE
+ }
+
+ expect(toHtml(renderComponent(MyComponent)))
+ .toEqual('Hello World !
');
+ });
+});
diff --git a/packages/core/test/render3/compiler_canonical/ng_module_spec.ts b/packages/core/test/render3/compiler_canonical/ng_module_spec.ts
new file mode 100644
index 0000000000..554b5cad39
--- /dev/null
+++ b/packages/core/test/render3/compiler_canonical/ng_module_spec.ts
@@ -0,0 +1,78 @@
+/**
+ * @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 {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
+import * as $r3$ from '../../../src/core_render3_private_export';
+import {renderComponent, toHtml} from '../render_util';
+
+/// See: `normative.md`
+xdescribe('NgModule', () => {
+
+ interface Injectable {
+ scope?: /*InjectorDefType*/ any;
+ factory: Function;
+ }
+
+ function defineInjectable(opts: Injectable): Injectable {
+ // This class should be imported from https://github.com/angular/angular/pull/20850
+ return opts;
+ }
+ function defineInjector(opts: any): any {
+ // This class should be imported from https://github.com/angular/angular/pull/20850
+ return opts;
+ }
+ it('should convert module', () => {
+ @Injectable()
+ class Toast {
+ constructor(name: String) {}
+ // NORMATIVE
+ static ngInjectableDef = defineInjectable({
+ factory: () => new Toast($r3$.ɵinject(String)),
+ });
+ // /NORMATIVE
+ }
+
+ class CommonModule {
+ // NORMATIVE
+ static ngInjectorDef = defineInjector({});
+ // /NORMATIVE
+ }
+
+ @NgModule({
+ providers: [Toast, {provide: String, useValue: 'Hello'}],
+ imports: [CommonModule],
+ })
+ class MyModule {
+ constructor(toast: Toast) {}
+ // NORMATIVE
+ static ngInjectorDef = defineInjector({
+ factory: () => new MyModule($r3$.ɵinject(Toast)),
+ provider: [
+ {provide: Toast, deps: [String]}, // If Toast has metadata generate this line
+ Toast, // If Toast has no metadata generate this line.
+ {provide: String, useValue: 'Hello'}
+ ],
+ imports: [CommonModule]
+ });
+ // /NORMATIVE
+ }
+
+ @Injectable(/*{MyModule}*/)
+ class BurntToast {
+ constructor(@Optional() toast: Toast|null, name: String) {}
+ // NORMATIVE
+ static ngInjectableDef = defineInjectable({
+ scope: MyModule,
+ factory: () => new BurntToast(
+ $r3$.ɵinject(Toast, $r3$.ɵInjectFlags.Optional), $r3$.ɵinject(String)),
+ });
+ // /NORMATIVE
+ }
+
+ });
+});
diff --git a/packages/core/test/render3/compiler_canonical/normative.md b/packages/core/test/render3/compiler_canonical/normative.md
index 96653d9d74..992bf86d3f 100644
--- a/packages/core/test/render3/compiler_canonical/normative.md
+++ b/packages/core/test/render3/compiler_canonical/normative.md
@@ -1,5 +1,11 @@
This folder contains canonical examples of how the Ivy compiler translates annotations into code
- The specs are marked with `NORMATIVE` => `/NORMATIVE` comments which designates what the compiler is expected to generate.
-- All local variable names are considered non-normative (informative).
+- All local variable names are considered non-normative (informative). They should be wrapped in `$` on each end to simplify testing on the compiler side.
+A common trick in spec files is to map types to `$x$` (such as `boolean` => `$boolean$`, etc) to simplify testing for compiler, as types aren't saved. (See bullet above).
+```
+type $boolean$ = boolean;
+type $any$ = any;
+type $number$ = number;
+```
\ No newline at end of file
diff --git a/packages/core/test/render3/compiler_canonical/pipes_spec.ts b/packages/core/test/render3/compiler_canonical/pipes_spec.ts
new file mode 100644
index 0000000000..5cdf0a5ca9
--- /dev/null
+++ b/packages/core/test/render3/compiler_canonical/pipes_spec.ts
@@ -0,0 +1,80 @@
+/**
+ * @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 {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
+import * as $r3$ from '../../../src/core_render3_private_export';
+import {renderComponent, toHtml} from '../render_util';
+
+/// See: `normative.md`
+xdescribe('pipes', () => {
+ type $MyApp$ = MyApp;
+ type $boolean$ = boolean;
+
+ @Pipe({
+ name: 'myPipe',
+ pure: false,
+ })
+ class MyPipe implements PipeTransform,
+ OnDestroy {
+ transform(value: any, ...args: any[]) { throw new Error('Method not implemented.'); }
+ ngOnDestroy(): void { throw new Error('Method not implemented.'); }
+
+ // NORMATIVE
+ static ngPipeDef = $r3$.ɵdefinePipe({
+ type: MyPipe,
+ factory: function MyPipe_Factory() { return new MyPipe(); },
+ pure: false,
+ });
+ // /NORMATIVE
+ }
+
+ @Pipe({
+ name: 'myPurePipe',
+ })
+ class MyPurePipe implements PipeTransform {
+ transform(value: any, ...args: any[]) { throw new Error('Method not implemented.'); }
+
+ // NORMATIVE
+ static ngPipeDef = $r3$.ɵdefinePipe({
+ type: MyPurePipe,
+ factory: function MyPurePipe_Factory() { return new MyPurePipe(); },
+ });
+ // /NORMATIVE
+ }
+
+ // NORMATIVE
+ const $MyPurePipe_ngPipeDef$ = MyPurePipe.ngPipeDef;
+ const $MyPipe_ngPipeDef$ = MyPipe.ngPipeDef;
+ // /NORMATIVE
+
+ @Component({template: `{{name | myPipe:size | myPurePipe:size }}`})
+ class MyApp {
+ name = 'World';
+ size = 0;
+
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyApp,
+ tag: 'my-app',
+ factory: function MyApp_Factory() { return new MyApp(); },
+ template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵT(0);
+ $r3$.ɵPp(1, $MyPurePipe_ngPipeDef$, $MyPurePipe_ngPipeDef$.n());
+ $r3$.ɵPp(2, $MyPipe_ngPipeDef$, $MyPipe_ngPipeDef$.n());
+ }
+ $r3$.ɵt(2, $r3$.ɵi1('', $r3$.ɵpb2(1, $r3$.ɵpb2(2, ctx.name, ctx.size), ctx.size), ''));
+ }
+ });
+ // /NORMATIVE
+ }
+
+ it('should render pipes', () => {
+ // TODO(misko): write a test once pipes runtime is implemented.
+ });
+});
diff --git a/packages/core/test/render3/compiler_canonical/query_spec.ts b/packages/core/test/render3/compiler_canonical/query_spec.ts
new file mode 100644
index 0000000000..90bd835415
--- /dev/null
+++ b/packages/core/test/render3/compiler_canonical/query_spec.ts
@@ -0,0 +1,168 @@
+/**
+ * @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 {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
+import * as $r3$ from '../../../src/core_render3_private_export';
+import {renderComponent, toHtml} from '../render_util';
+
+/// See: `normative.md`
+describe('queries', () => {
+ type $boolean$ = boolean;
+ type $number$ = number;
+ let someDir: SomeDirective;
+
+ @Directive({
+ selector: '[someDir]',
+ })
+ class SomeDirective {
+ static ngDirectiveDef = $r3$.ɵdefineDirective({
+ type: SomeDirective,
+ factory: function SomeDirective_Factory() { return someDir = new SomeDirective(); },
+ features: [$r3$.ɵPublicFeature]
+ });
+ }
+
+ it('should support view queries', () => {
+ type $ViewQueryComponent$ = ViewQueryComponent;
+
+ // NORMATIVE
+ const $e1_attrs$ = ['someDir', ''];
+ const $e1_dirs$ = [SomeDirective];
+ // /NORMATIVE
+
+ @Component({
+ selector: 'view-query-component',
+ template: `
+
+ `
+ })
+ class ViewQueryComponent {
+ @ViewChild(SomeDirective) someDir: SomeDirective;
+ @ViewChildren(SomeDirective) someDirList: QueryList;
+
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: ViewQueryComponent,
+ tag: 'view-query-component',
+ factory: function ViewQueryComponent_Factory() { return new ViewQueryComponent(); },
+ template: function ViewQueryComponent_Template(ctx: $ViewQueryComponent$, cm: $boolean$) {
+ let $tmp$: any;
+ if (cm) {
+ $r3$.ɵQ(0, SomeDirective, false);
+ $r3$.ɵQ(1, SomeDirective, false);
+ $r3$.ɵE(2, 'div', $e1_attrs$, $e1_dirs$);
+ $r3$.ɵe();
+ }
+
+ $r3$.ɵqR($tmp$ = $r3$.ɵld>(0)) && (ctx.someDir = $tmp$.first);
+ $r3$.ɵqR($tmp$ = $r3$.ɵld>(1)) &&
+ (ctx.someDirList = $tmp$ as QueryList);
+ SomeDirective.ngDirectiveDef.h(3, 2);
+ $r3$.ɵr(3, 2);
+ }
+ });
+ // /NORMATIVE
+ }
+
+
+ const viewQueryComp = renderComponent(ViewQueryComponent);
+ expect(viewQueryComp.someDir).toEqual(someDir);
+ expect((viewQueryComp.someDirList as QueryList).toArray()).toEqual([someDir !]);
+ });
+
+ it('should support content queries', () => {
+ type $MyApp$ = MyApp;
+ type $ContentQueryComponent$ = ContentQueryComponent;
+
+ let contentQueryComp: ContentQueryComponent;
+
+ @Component({
+ selector: 'content-query-component',
+ template: `
+
+ `
+ })
+ class ContentQueryComponent {
+ @ContentChild(SomeDirective) someDir: SomeDirective;
+ @ContentChildren(SomeDirective) someDirList: QueryList;
+
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: ContentQueryComponent,
+ tag: 'content-query-component',
+ factory: function ContentQueryComponent_Factory() {
+ return [
+ new ContentQueryComponent(), $r3$.ɵQ(null, SomeDirective, false),
+ $r3$.ɵQ(null, SomeDirective, false)
+ ];
+ },
+ hostBindings: function ContentQueryComponent_HostBindings(
+ dirIndex: $number$, elIndex: $number$) {
+ let $tmp$: any;
+ const $instance$ = $r3$.ɵld(dirIndex)[0];
+ $r3$.ɵqR($tmp$ = $r3$.ɵld(dirIndex)[1]) && ($instance$.someDir = $tmp$.first);
+ $r3$.ɵqR($tmp$ = $r3$.ɵld(dirIndex)[2]) && ($instance$.someDirList = $tmp$);
+ },
+ template: function ContentQueryComponent_Template(
+ ctx: $ContentQueryComponent$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵpD(0);
+ $r3$.ɵE(1, 'div');
+ $r3$.ɵP(2, 0);
+ $r3$.ɵe();
+ }
+ }
+ });
+ // /NORMATIVE
+ }
+
+ const $e2_attrs$ = ['someDir', ''];
+ const $e2_dirs$ = [SomeDirective];
+
+ @Component({
+ selector: 'my-app',
+ template: `
+
+
+
+ `
+ })
+ class MyApp {
+ // NON-NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyApp,
+ tag: 'my-app',
+ factory: function MyApp_Factory() { return new MyApp(); },
+ template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, ContentQueryComponent);
+ contentQueryComp = $r3$.ɵld(1)[0];
+ $r3$.ɵE(2, 'div', $e2_attrs$, $e2_dirs$);
+ $r3$.ɵe();
+ $r3$.ɵe();
+ }
+ ContentQueryComponent.ngComponentDef.h(1, 0);
+ SomeDirective.ngDirectiveDef.h(3, 2);
+ $r3$.ɵr(1, 0);
+ $r3$.ɵr(3, 2);
+ }
+ });
+ // /NON-NORMATIVE
+ }
+
+
+ expect(toHtml(renderComponent(MyApp)))
+ .toEqual(
+ ` `);
+ expect(contentQueryComp !.someDir).toEqual(someDir !);
+ expect((contentQueryComp !.someDirList as QueryList).toArray()).toEqual([
+ someDir !
+ ]);
+ });
+
+});
diff --git a/packages/core/test/render3/compiler_canonical/small_app_spec.ts b/packages/core/test/render3/compiler_canonical/small_app_spec.ts
index c551db697f..4c094cb3e6 100644
--- a/packages/core/test/render3/compiler_canonical/small_app_spec.ts
+++ b/packages/core/test/render3/compiler_canonical/small_app_spec.ts
@@ -12,6 +12,7 @@ import {withBody} from '@angular/core/testing';
import * as r3 from '../../../src/render3/index';
+/// See: `normative.md`
// TODO: remove once https://github.com/angular/angular/pull/22005 lands
diff --git a/packages/core/test/render3/compiler_canonical/template_variables_spec.ts b/packages/core/test/render3/compiler_canonical/template_variables_spec.ts
new file mode 100644
index 0000000000..209282d091
--- /dev/null
+++ b/packages/core/test/render3/compiler_canonical/template_variables_spec.ts
@@ -0,0 +1,213 @@
+/**
+ * @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 {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
+import * as $r3$ from '../../../src/core_render3_private_export';
+import {renderComponent, toHtml} from '../render_util';
+
+/// See: `normative.md`
+describe('template variables', () => {
+ type $boolean$ = boolean;
+ type $any$ = any;
+ type $number$ = number;
+
+ interface ForOfContext {
+ $implicit: any;
+ index: number;
+ even: boolean;
+ odd: boolean;
+ }
+
+ @Directive({selector: '[forOf]'})
+ class ForOfDirective {
+ private previous: any[];
+
+ constructor(private view: ViewContainerRef, private template: TemplateRef) {}
+
+ @Input() forOf: any[];
+
+ ngOnChanges(simpleChanges: SimpleChanges) {
+ if ('forOf' in simpleChanges) {
+ this.update();
+ }
+ }
+
+ ngDoCheck(): void {
+ const previous = this.previous;
+ const current = this.forOf;
+ if (!previous || previous.length != current.length ||
+ previous.some((value: any, index: number) => current[index] !== previous[index])) {
+ this.update();
+ }
+ }
+
+ private update() {
+ // TODO(chuckj): Not implemented yet
+ // this.view.clear();
+ if (this.forOf) {
+ const current = this.forOf;
+ for (let i = 0; i < current.length; i++) {
+ const context = {$implicit: current[i], index: i, even: i % 2 == 0, odd: i % 2 == 1};
+ // TODO(chuckj): Not implemented yet
+ // this.view.createEmbeddedView(this.template, context);
+ }
+ this.previous = [...this.forOf];
+ }
+ }
+
+ // NORMATIVE
+ static ngDirectiveDef = $r3$.ɵdefineDirective({
+ type: ForOfDirective,
+ factory: function ForOfDirective_Factory() {
+ return new ForOfDirective($r3$.ɵinjectViewContainerRef(), $r3$.ɵinjectTemplateRef());
+ },
+ // TODO(chuckj): Enable when ngForOf enabling lands.
+ // features: [NgOnChangesFeature(NgForOf)],
+ inputs: {forOf: 'forOf'}
+ });
+ // /NORMATIVE
+ }
+
+ it('should support a let variable and reference', () => {
+ type $MyComponent$ = MyComponent;
+
+ interface Item {
+ name: string;
+ }
+
+ // NORMATIVE
+ const $c1_dirs$ = [ForOfDirective];
+ // /NORMATIVE
+
+ @Component({
+ selector: 'my-component',
+ template: ``
+ })
+ class MyComponent {
+ items = [{name: 'one'}, {name: 'two'}];
+
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyComponent,
+ tag: 'my-component',
+ factory: function MyComponent_Factory() { return new MyComponent(); },
+ template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, 'ul');
+ $r3$.ɵC(1, $c1_dirs$, MyComponent_ForOfDirective_Template_1);
+ $r3$.ɵe();
+ }
+ $r3$.ɵp(1, 'forOf', $r3$.ɵb(ctx.items));
+ $r3$.ɵcR(1);
+ $r3$.ɵr(2, 1);
+ $r3$.ɵcr();
+
+ function MyComponent_ForOfDirective_Template_1(ctx1: $any$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, 'li');
+ $r3$.ɵT(1);
+ $r3$.ɵe();
+ }
+ const $l0_item$ = ctx1.$implicit;
+ $r3$.ɵt(1, $r3$.ɵi1('', $l0_item$.name, ''));
+ }
+ }
+ });
+ // /NORMATIVE
+ }
+
+ // TODO(chuckj): update when the changes to enable ngForOf lands.
+ expect(toHtml(renderComponent(MyComponent))).toEqual('');
+ });
+
+ it('should support accessing parent template variables', () => {
+ type $MyComponent$ = MyComponent;
+
+ interface Info {
+ description: string;
+ }
+ interface Item {
+ name: string;
+ infos: Info[];
+ }
+
+ // NORMATIVE
+ const $c1_dirs$ = [ForOfDirective];
+ // /NORMATIVE
+
+ @Component({
+ selector: 'my-component',
+ template: `
+
+
+ {{item.name}}
+
+
+ {{item.name}}: {{info.description}}
+
+
+
+ `
+ })
+ class MyComponent {
+ items: Item[] = [
+ {name: 'one', infos: [{description: '11'}, {description: '12'}]},
+ {name: 'two', infos: [{description: '21'}, {description: '22'}]}
+ ];
+
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyComponent,
+ tag: 'my-component',
+ factory: function MyComponent_Factory() { return new MyComponent(); },
+ template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, 'ul');
+ $r3$.ɵC(1, $c1_dirs$, MyComponent_ForOfDirective_Template_1);
+ $r3$.ɵe();
+ }
+ $r3$.ɵp(1, 'forOf', $r3$.ɵb(ctx.items));
+ $r3$.ɵcR(1);
+ $r3$.ɵr(2, 1);
+ $r3$.ɵcr();
+
+ function MyComponent_ForOfDirective_Template_1(ctx1: $any$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, 'li');
+ $r3$.ɵE(1, 'div');
+ $r3$.ɵT(2);
+ $r3$.ɵe();
+ $r3$.ɵE(3, 'ul');
+ $r3$.ɵC(4, $c1_dirs$, MyComponent_ForOfDirective_ForOfDirective_Template_3);
+ $r3$.ɵe();
+ $r3$.ɵe();
+ }
+ const $l0_item$ = ctx1.$implicit;
+ $r3$.ɵp(4, 'forOf', $r3$.ɵb($l0_item$.infos));
+ $r3$.ɵt(2, $r3$.ɵi1('', $l0_item$.name, ''));
+ $r3$.ɵcR(4);
+ $r3$.ɵr(5, 4);
+ $r3$.ɵcr();
+
+ function MyComponent_ForOfDirective_ForOfDirective_Template_3(
+ ctx2: $any$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, 'li');
+ $r3$.ɵT(1);
+ $r3$.ɵe();
+ }
+ const $l0_info$ = ctx2.$implicit;
+ $r3$.ɵt(1, $r3$.ɵi2(' ', $l0_item$.name, ': ', $l0_info$.description, ' '));
+ }
+ }
+ }
+ });
+ // /NORMATIVE
+ }
+ });
+});