Prior to this change, we were unable to match directives using `ng-template` tags (for example the following selector would not work even though there might be some <ng-template>s in a template: `ng-template[directiveA]`. As a result, that broke some components that relies on such selectors to work. In order to resolve the problem, we now pass tag name to the `template` instruction (where we passed `null` before) and this tag name is used for matching at runtime. This update should also help support projecting containers, because the tag name is required to properly match such elements. PR Close #27636
666 lines
20 KiB
TypeScript
666 lines
20 KiB
TypeScript
/**
|
|
* @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 {ViewEncapsulation, createInjector, defineInjectable, defineInjector} from '../../src/core';
|
|
|
|
import {AttributeMarker, ComponentFactory, LifecycleHooksFeature, defineComponent, directiveInject, markDirty, template, getRenderedText} from '../../src/render3/index';
|
|
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, nextContext, text, textBinding, tick} from '../../src/render3/instructions';
|
|
import {ComponentDef, RenderFlags} from '../../src/render3/interfaces/definition';
|
|
|
|
import {NgIf} from './common_with_def';
|
|
import {getRendererFactory2} from './imported_renderer2';
|
|
import {ComponentFixture, MockRendererFactory, containerEl, createComponent, renderComponent, renderToHtml, requestAnimationFrame, toHtml} from './render_util';
|
|
|
|
describe('component', () => {
|
|
class CounterComponent {
|
|
count = 0;
|
|
|
|
increment() { this.count++; }
|
|
|
|
static ngComponentDef = defineComponent({
|
|
type: CounterComponent,
|
|
encapsulation: ViewEncapsulation.None,
|
|
selectors: [['counter']],
|
|
consts: 1,
|
|
vars: 1,
|
|
template: function(rf: RenderFlags, ctx: CounterComponent) {
|
|
if (rf & RenderFlags.Create) {
|
|
text(0);
|
|
}
|
|
if (rf & RenderFlags.Update) {
|
|
textBinding(0, bind(ctx.count));
|
|
}
|
|
},
|
|
factory: () => new CounterComponent,
|
|
inputs: {count: 'count'},
|
|
});
|
|
}
|
|
|
|
describe('renderComponent', () => {
|
|
it('should render on initial call', () => {
|
|
renderComponent(CounterComponent);
|
|
expect(toHtml(containerEl)).toEqual('0');
|
|
});
|
|
|
|
it('should re-render on input change or method invocation', () => {
|
|
const component = renderComponent(CounterComponent);
|
|
expect(toHtml(containerEl)).toEqual('0');
|
|
component.count = 123;
|
|
markDirty(component);
|
|
expect(toHtml(containerEl)).toEqual('0');
|
|
requestAnimationFrame.flush();
|
|
expect(toHtml(containerEl)).toEqual('123');
|
|
component.increment();
|
|
markDirty(component);
|
|
expect(toHtml(containerEl)).toEqual('123');
|
|
requestAnimationFrame.flush();
|
|
expect(toHtml(containerEl)).toEqual('124');
|
|
});
|
|
|
|
class MyService {
|
|
constructor(public value: string) {}
|
|
static ngInjectableDef =
|
|
defineInjectable({providedIn: 'root', factory: () => new MyService('no-injector')});
|
|
}
|
|
class MyComponent {
|
|
constructor(public myService: MyService) {}
|
|
static ngComponentDef = defineComponent({
|
|
type: MyComponent,
|
|
encapsulation: ViewEncapsulation.None,
|
|
selectors: [['my-component']],
|
|
factory: () => new MyComponent(directiveInject(MyService)),
|
|
consts: 1,
|
|
vars: 1,
|
|
template: function(fs: RenderFlags, ctx: MyComponent) {
|
|
if (fs & RenderFlags.Create) {
|
|
text(0);
|
|
}
|
|
if (fs & RenderFlags.Update) {
|
|
textBinding(0, bind(ctx.myService.value));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
class MyModule {
|
|
static ngInjectorDef = defineInjector({
|
|
factory: () => new MyModule(),
|
|
providers: [{provide: MyService, useValue: new MyService('injector')}]
|
|
});
|
|
}
|
|
|
|
it('should support bootstrapping without injector', () => {
|
|
const fixture = new ComponentFixture(MyComponent);
|
|
expect(fixture.html).toEqual('no-injector');
|
|
});
|
|
|
|
it('should support bootstrapping with injector', () => {
|
|
const fixture = new ComponentFixture(MyComponent, {injector: createInjector(MyModule)});
|
|
expect(fixture.html).toEqual('injector');
|
|
});
|
|
|
|
});
|
|
|
|
it('should instantiate components at high indices', () => {
|
|
|
|
// {{ name }}
|
|
class Comp {
|
|
// @Input
|
|
name = '';
|
|
|
|
static ngComponentDef = defineComponent({
|
|
type: Comp,
|
|
selectors: [['comp']],
|
|
factory: () => new Comp(),
|
|
consts: 1,
|
|
vars: 1,
|
|
template: (rf: RenderFlags, ctx: Comp) => {
|
|
if (rf & RenderFlags.Create) {
|
|
text(0);
|
|
}
|
|
if (rf & RenderFlags.Update) {
|
|
textBinding(0, bind(ctx.name));
|
|
}
|
|
},
|
|
inputs: {name: 'name'}
|
|
});
|
|
}
|
|
|
|
// Artificially inflating the slot IDs of this app component to mimic an app
|
|
// with a very large view
|
|
const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
|
|
if (rf & RenderFlags.Create) {
|
|
element(4097, 'comp');
|
|
}
|
|
if (rf & RenderFlags.Update) {
|
|
elementProperty(4097, 'name', bind(ctx.name));
|
|
}
|
|
}, 4098, 1, [Comp]);
|
|
|
|
const fixture = new ComponentFixture(App);
|
|
expect(fixture.html).toEqual('<comp></comp>');
|
|
|
|
fixture.component.name = 'some name';
|
|
fixture.update();
|
|
expect(fixture.html).toEqual('<comp>some name</comp>');
|
|
});
|
|
|
|
});
|
|
|
|
it('should not invoke renderer destroy method for embedded views', () => {
|
|
let comp: Comp;
|
|
|
|
function MyComponent_div_Template_2(rf: any, ctx: any) {
|
|
if (rf & RenderFlags.Create) {
|
|
elementStart(0, 'div');
|
|
text(1, 'Child view');
|
|
elementEnd();
|
|
}
|
|
}
|
|
|
|
class Comp {
|
|
visible = true;
|
|
|
|
static ngComponentDef = defineComponent({
|
|
type: Comp,
|
|
selectors: [['comp']],
|
|
consts: 3,
|
|
vars: 1,
|
|
factory: () => {
|
|
comp = new Comp();
|
|
return comp;
|
|
},
|
|
directives: [NgIf],
|
|
/**
|
|
* <div>Root view</div>
|
|
* <div *ngIf="visible">Child view</div>
|
|
*/
|
|
template: function(rf: RenderFlags, ctx: Comp) {
|
|
if (rf & RenderFlags.Create) {
|
|
elementStart(0, 'div');
|
|
text(1, 'Root view');
|
|
elementEnd();
|
|
template(2, MyComponent_div_Template_2, 2, 0, null, [AttributeMarker.SelectOnly, 'ngIf']);
|
|
}
|
|
if (rf & RenderFlags.Update) {
|
|
elementProperty(2, 'ngIf', bind(ctx.visible));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
const rendererFactory = new MockRendererFactory(['destroy']);
|
|
const fixture = new ComponentFixture(Comp, {rendererFactory});
|
|
|
|
comp !.visible = false;
|
|
fixture.update();
|
|
|
|
comp !.visible = true;
|
|
fixture.update();
|
|
|
|
const renderer = rendererFactory.lastRenderer !;
|
|
const destroySpy = renderer.spies['destroy'];
|
|
|
|
// we should never see `destroy` method being called
|
|
// in case child views are created/removed
|
|
expect(destroySpy.calls.count()).toBe(0);
|
|
});
|
|
|
|
describe('component with a container', () => {
|
|
|
|
function showItems(rf: RenderFlags, ctx: {items: string[]}) {
|
|
if (rf & RenderFlags.Create) {
|
|
container(0);
|
|
}
|
|
if (rf & RenderFlags.Update) {
|
|
containerRefreshStart(0);
|
|
{
|
|
for (const item of ctx.items) {
|
|
const rf0 = embeddedViewStart(0, 1, 1);
|
|
{
|
|
if (rf0 & RenderFlags.Create) {
|
|
text(0);
|
|
}
|
|
if (rf0 & RenderFlags.Update) {
|
|
textBinding(0, bind(item));
|
|
}
|
|
}
|
|
embeddedViewEnd();
|
|
}
|
|
}
|
|
containerRefreshEnd();
|
|
}
|
|
}
|
|
|
|
class WrapperComponent {
|
|
// TODO(issue/24571): remove '!'.
|
|
items !: string[];
|
|
static ngComponentDef = defineComponent({
|
|
type: WrapperComponent,
|
|
encapsulation: ViewEncapsulation.None,
|
|
selectors: [['wrapper']],
|
|
consts: 1,
|
|
vars: 0,
|
|
template: function ChildComponentTemplate(rf: RenderFlags, ctx: {items: string[]}) {
|
|
if (rf & RenderFlags.Create) {
|
|
container(0);
|
|
}
|
|
if (rf & RenderFlags.Update) {
|
|
containerRefreshStart(0);
|
|
{
|
|
const rf0 = embeddedViewStart(0, 1, 0);
|
|
{ showItems(rf0, {items: ctx.items}); }
|
|
embeddedViewEnd();
|
|
}
|
|
containerRefreshEnd();
|
|
}
|
|
},
|
|
factory: () => new WrapperComponent,
|
|
inputs: {items: 'items'}
|
|
});
|
|
}
|
|
|
|
function template(rf: RenderFlags, ctx: {items: string[]}) {
|
|
if (rf & RenderFlags.Create) {
|
|
element(0, 'wrapper');
|
|
}
|
|
if (rf & RenderFlags.Update) {
|
|
elementProperty(0, 'items', bind(ctx.items));
|
|
}
|
|
}
|
|
|
|
const defs = [WrapperComponent];
|
|
|
|
it('should re-render on input change', () => {
|
|
const ctx: {items: string[]} = {items: ['a']};
|
|
expect(renderToHtml(template, ctx, 1, 1, defs)).toEqual('<wrapper>a</wrapper>');
|
|
|
|
ctx.items = [...ctx.items, 'b'];
|
|
expect(renderToHtml(template, ctx, 1, 1, defs)).toEqual('<wrapper>ab</wrapper>');
|
|
});
|
|
|
|
});
|
|
|
|
// TODO: add tests with Native once tests are run in real browser (domino doesn't support shadow
|
|
// root)
|
|
describe('encapsulation', () => {
|
|
class WrapperComponent {
|
|
static ngComponentDef = defineComponent({
|
|
type: WrapperComponent,
|
|
encapsulation: ViewEncapsulation.None,
|
|
selectors: [['wrapper']],
|
|
consts: 1,
|
|
vars: 0,
|
|
template: function(rf: RenderFlags, ctx: WrapperComponent) {
|
|
if (rf & RenderFlags.Create) {
|
|
element(0, 'encapsulated');
|
|
}
|
|
},
|
|
factory: () => new WrapperComponent,
|
|
directives: () => [EncapsulatedComponent]
|
|
});
|
|
}
|
|
|
|
class EncapsulatedComponent {
|
|
static ngComponentDef = defineComponent({
|
|
type: EncapsulatedComponent,
|
|
selectors: [['encapsulated']],
|
|
consts: 2,
|
|
vars: 0,
|
|
template: function(rf: RenderFlags, ctx: EncapsulatedComponent) {
|
|
if (rf & RenderFlags.Create) {
|
|
text(0, 'foo');
|
|
element(1, 'leaf');
|
|
}
|
|
},
|
|
factory: () => new EncapsulatedComponent,
|
|
encapsulation: ViewEncapsulation.Emulated,
|
|
styles: [],
|
|
data: {},
|
|
directives: () => [LeafComponent]
|
|
});
|
|
}
|
|
|
|
class LeafComponent {
|
|
static ngComponentDef = defineComponent({
|
|
type: LeafComponent,
|
|
encapsulation: ViewEncapsulation.None,
|
|
selectors: [['leaf']],
|
|
consts: 2,
|
|
vars: 0,
|
|
template: function(rf: RenderFlags, ctx: LeafComponent) {
|
|
if (rf & RenderFlags.Create) {
|
|
elementStart(0, 'span');
|
|
{ text(1, 'bar'); }
|
|
elementEnd();
|
|
}
|
|
},
|
|
factory: () => new LeafComponent,
|
|
});
|
|
}
|
|
|
|
it('should encapsulate children, but not host nor grand children', () => {
|
|
renderComponent(WrapperComponent, {rendererFactory: getRendererFactory2(document)});
|
|
expect(containerEl.outerHTML)
|
|
.toMatch(
|
|
/<div host=""><encapsulated _nghost-c(\d+)="">foo<leaf _ngcontent-c\1=""><span>bar<\/span><\/leaf><\/encapsulated><\/div>/);
|
|
});
|
|
|
|
it('should encapsulate host', () => {
|
|
renderComponent(EncapsulatedComponent, {rendererFactory: getRendererFactory2(document)});
|
|
expect(containerEl.outerHTML)
|
|
.toMatch(
|
|
/<div host="" _nghost-c(\d+)="">foo<leaf _ngcontent-c\1=""><span>bar<\/span><\/leaf><\/div>/);
|
|
});
|
|
|
|
it('should encapsulate host and children with different attributes', () => {
|
|
class WrapperComponentWith {
|
|
static ngComponentDef = defineComponent({
|
|
type: WrapperComponentWith,
|
|
selectors: [['wrapper']],
|
|
consts: 1,
|
|
vars: 0,
|
|
template: function(rf: RenderFlags, ctx: WrapperComponentWith) {
|
|
if (rf & RenderFlags.Create) {
|
|
element(0, 'leaf');
|
|
}
|
|
},
|
|
factory: () => new WrapperComponentWith,
|
|
encapsulation: ViewEncapsulation.Emulated,
|
|
styles: [],
|
|
data: {},
|
|
directives: () => [LeafComponentwith]
|
|
});
|
|
}
|
|
|
|
class LeafComponentwith {
|
|
static ngComponentDef = defineComponent({
|
|
type: LeafComponentwith,
|
|
selectors: [['leaf']],
|
|
consts: 2,
|
|
vars: 0,
|
|
template: function(rf: RenderFlags, ctx: LeafComponentwith) {
|
|
if (rf & RenderFlags.Create) {
|
|
elementStart(0, 'span');
|
|
{ text(1, 'bar'); }
|
|
elementEnd();
|
|
}
|
|
},
|
|
factory: () => new LeafComponentwith,
|
|
encapsulation: ViewEncapsulation.Emulated,
|
|
styles: [],
|
|
data: {},
|
|
});
|
|
}
|
|
|
|
renderComponent(WrapperComponentWith, {rendererFactory: getRendererFactory2(document)});
|
|
expect(containerEl.outerHTML)
|
|
.toMatch(
|
|
/<div host="" _nghost-c(\d+)=""><leaf _ngcontent-c\1="" _nghost-c(\d+)=""><span _ngcontent-c\2="">bar<\/span><\/leaf><\/div>/);
|
|
});
|
|
|
|
});
|
|
|
|
describe('recursive components', () => {
|
|
let events: string[];
|
|
let count: number;
|
|
|
|
beforeEach(() => {
|
|
events = [];
|
|
count = 0;
|
|
});
|
|
|
|
class TreeNode {
|
|
constructor(
|
|
public value: number, public depth: number, public left: TreeNode|null,
|
|
public right: TreeNode|null) {}
|
|
}
|
|
|
|
/**
|
|
* {{ data.value }}
|
|
*
|
|
* % if (data.left != null) {
|
|
* <tree-comp [data]="data.left"></tree-comp>
|
|
* % }
|
|
* % if (data.right != null) {
|
|
* <tree-comp [data]="data.right"></tree-comp>
|
|
* % }
|
|
*/
|
|
class TreeComponent {
|
|
data: TreeNode = _buildTree(0);
|
|
|
|
ngDoCheck() { events.push('check' + this.data.value); }
|
|
|
|
ngOnDestroy() { events.push('destroy' + this.data.value); }
|
|
|
|
static ngComponentDef = defineComponent({
|
|
type: TreeComponent,
|
|
encapsulation: ViewEncapsulation.None,
|
|
selectors: [['tree-comp']],
|
|
factory: () => new TreeComponent(),
|
|
consts: 3,
|
|
vars: 1,
|
|
template: (rf: RenderFlags, ctx: TreeComponent) => {
|
|
if (rf & RenderFlags.Create) {
|
|
text(0);
|
|
container(1);
|
|
container(2);
|
|
}
|
|
if (rf & RenderFlags.Update) {
|
|
textBinding(0, bind(ctx.data.value));
|
|
containerRefreshStart(1);
|
|
{
|
|
if (ctx.data.left != null) {
|
|
let rf0 = embeddedViewStart(0, 1, 1);
|
|
if (rf0 & RenderFlags.Create) {
|
|
element(0, 'tree-comp');
|
|
}
|
|
if (rf0 & RenderFlags.Update) {
|
|
elementProperty(0, 'data', bind(ctx.data.left));
|
|
}
|
|
embeddedViewEnd();
|
|
}
|
|
}
|
|
containerRefreshEnd();
|
|
containerRefreshStart(2);
|
|
{
|
|
if (ctx.data.right != null) {
|
|
let rf0 = embeddedViewStart(0, 1, 1);
|
|
if (rf0 & RenderFlags.Create) {
|
|
element(0, 'tree-comp');
|
|
}
|
|
if (rf0 & RenderFlags.Update) {
|
|
elementProperty(0, 'data', bind(ctx.data.right));
|
|
}
|
|
embeddedViewEnd();
|
|
}
|
|
}
|
|
containerRefreshEnd();
|
|
}
|
|
},
|
|
inputs: {data: 'data'}
|
|
});
|
|
}
|
|
|
|
(TreeComponent.ngComponentDef as ComponentDef<TreeComponent>).directiveDefs =
|
|
() => [TreeComponent.ngComponentDef];
|
|
|
|
/**
|
|
* {{ data.value }}
|
|
* <ng-if-tree [data]="data.left" *ngIf="data.left"></ng-if-tree>
|
|
* <ng-if-tree [data]="data.right" *ngIf="data.right"></ng-if-tree>
|
|
*/
|
|
class NgIfTree {
|
|
data: TreeNode = _buildTree(0);
|
|
|
|
ngOnDestroy() { events.push('destroy' + this.data.value); }
|
|
|
|
static ngComponentDef = defineComponent({
|
|
type: NgIfTree,
|
|
encapsulation: ViewEncapsulation.None,
|
|
selectors: [['ng-if-tree']],
|
|
factory: () => new NgIfTree(),
|
|
consts: 3,
|
|
vars: 3,
|
|
template: (rf: RenderFlags, ctx: NgIfTree) => {
|
|
|
|
if (rf & RenderFlags.Create) {
|
|
text(0);
|
|
template(1, IfTemplate, 1, 1, 'ng-if-tree', [AttributeMarker.SelectOnly, 'ngIf']);
|
|
template(2, IfTemplate2, 1, 1, 'ng-if-tree', [AttributeMarker.SelectOnly, 'ngIf']);
|
|
}
|
|
if (rf & RenderFlags.Update) {
|
|
textBinding(0, bind(ctx.data.value));
|
|
elementProperty(1, 'ngIf', bind(ctx.data.left));
|
|
elementProperty(2, 'ngIf', bind(ctx.data.right));
|
|
}
|
|
|
|
},
|
|
inputs: {data: 'data'},
|
|
});
|
|
}
|
|
|
|
function IfTemplate(rf: RenderFlags, left: any) {
|
|
if (rf & RenderFlags.Create) {
|
|
elementStart(0, 'ng-if-tree');
|
|
elementEnd();
|
|
}
|
|
if (rf & RenderFlags.Update) {
|
|
const parent = nextContext();
|
|
elementProperty(0, 'data', bind(parent.data.left));
|
|
}
|
|
}
|
|
|
|
function IfTemplate2(rf: RenderFlags, right: any) {
|
|
if (rf & RenderFlags.Create) {
|
|
elementStart(0, 'ng-if-tree');
|
|
elementEnd();
|
|
}
|
|
if (rf & RenderFlags.Update) {
|
|
const parent = nextContext();
|
|
elementProperty(0, 'data', bind(parent.data.right));
|
|
}
|
|
}
|
|
|
|
(NgIfTree.ngComponentDef as ComponentDef<NgIfTree>).directiveDefs =
|
|
() => [NgIfTree.ngComponentDef, NgIf.ngDirectiveDef];
|
|
|
|
function _buildTree(currDepth: number): TreeNode {
|
|
const children = currDepth < 2 ? _buildTree(currDepth + 1) : null;
|
|
const children2 = currDepth < 2 ? _buildTree(currDepth + 1) : null;
|
|
return new TreeNode(count++, currDepth, children, children2);
|
|
}
|
|
|
|
it('should check each component just once', () => {
|
|
const comp = renderComponent(TreeComponent, {hostFeatures: [LifecycleHooksFeature]});
|
|
expect(getRenderedText(comp)).toEqual('6201534');
|
|
expect(events).toEqual(['check6', 'check2', 'check0', 'check1', 'check5', 'check3', 'check4']);
|
|
|
|
events = [];
|
|
tick(comp);
|
|
expect(events).toEqual(['check6', 'check2', 'check0', 'check1', 'check5', 'check3', 'check4']);
|
|
});
|
|
|
|
// This tests that the view tree is set up properly for recursive components
|
|
it('should call onDestroys properly', () => {
|
|
|
|
/**
|
|
* % if (!skipContent) {
|
|
* <tree-comp></tree-comp>
|
|
* % }
|
|
*/
|
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
if (rf & RenderFlags.Create) {
|
|
container(0);
|
|
}
|
|
if (rf & RenderFlags.Update) {
|
|
containerRefreshStart(0);
|
|
if (!ctx.skipContent) {
|
|
const rf0 = embeddedViewStart(0, 1, 0);
|
|
if (rf0 & RenderFlags.Create) {
|
|
elementStart(0, 'tree-comp');
|
|
elementEnd();
|
|
}
|
|
embeddedViewEnd();
|
|
}
|
|
containerRefreshEnd();
|
|
}
|
|
}, 1, 0, [TreeComponent]);
|
|
|
|
const fixture = new ComponentFixture(App);
|
|
expect(getRenderedText(fixture.component)).toEqual('6201534');
|
|
|
|
events = [];
|
|
fixture.component.skipContent = true;
|
|
fixture.update();
|
|
expect(events).toEqual(
|
|
['destroy0', 'destroy1', 'destroy2', 'destroy3', 'destroy4', 'destroy5', 'destroy6']);
|
|
});
|
|
|
|
it('should call onDestroys properly with ngIf', () => {
|
|
/**
|
|
* % if (!skipContent) {
|
|
* <ng-if-tree></ng-if-tree>
|
|
* % }
|
|
*/
|
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
if (rf & RenderFlags.Create) {
|
|
container(0);
|
|
}
|
|
if (rf & RenderFlags.Update) {
|
|
containerRefreshStart(0);
|
|
if (!ctx.skipContent) {
|
|
const rf0 = embeddedViewStart(0, 1, 0);
|
|
if (rf0 & RenderFlags.Create) {
|
|
elementStart(0, 'ng-if-tree');
|
|
elementEnd();
|
|
}
|
|
embeddedViewEnd();
|
|
}
|
|
containerRefreshEnd();
|
|
}
|
|
}, 1, 0, [NgIfTree]);
|
|
|
|
const fixture = new ComponentFixture(App);
|
|
expect(getRenderedText(fixture.component)).toEqual('6201534');
|
|
|
|
events = [];
|
|
fixture.component.skipContent = true;
|
|
fixture.update();
|
|
expect(events).toEqual(
|
|
['destroy0', 'destroy1', 'destroy2', 'destroy3', 'destroy4', 'destroy5', 'destroy6']);
|
|
});
|
|
|
|
it('should map inputs minified & unminified names', async() => {
|
|
class TestInputsComponent {
|
|
// TODO(issue/24571): remove '!'.
|
|
minifiedName !: string;
|
|
static ngComponentDef = defineComponent({
|
|
type: TestInputsComponent,
|
|
encapsulation: ViewEncapsulation.None,
|
|
selectors: [['test-inputs']],
|
|
inputs: {minifiedName: 'unminifiedName'},
|
|
consts: 0,
|
|
vars: 0,
|
|
factory: () => new TestInputsComponent(),
|
|
template: function(rf: RenderFlags, ctx: TestInputsComponent): void {
|
|
// Template not needed for this test
|
|
}
|
|
});
|
|
}
|
|
|
|
const testInputsComponentFactory = new ComponentFactory(TestInputsComponent.ngComponentDef);
|
|
|
|
expect([
|
|
{propName: 'minifiedName', templateName: 'unminifiedName'}
|
|
]).toEqual(testInputsComponentFactory.inputs);
|
|
|
|
});
|
|
|
|
});
|