test(ivy): port TemplateRef tests over to acceptance tests (#30443)
- Moves template ref tests from render3 unit tests to acceptance tests. - Marks tests testing ivy specific changes as `onlyInIvy`. - Deletes old render3 unit tests that are no longer necessary PR Close #30443
This commit is contained in:
parent
53f356427f
commit
35c1750fcc
|
@ -9,6 +9,7 @@
|
||||||
import {Component, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core';
|
import {Component, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core';
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
|
import {onlyInIvy} from '@angular/private/testing';
|
||||||
|
|
||||||
describe('TemplateRef', () => {
|
describe('TemplateRef', () => {
|
||||||
describe('rootNodes', () => {
|
describe('rootNodes', () => {
|
||||||
|
@ -52,6 +53,117 @@ describe('TemplateRef', () => {
|
||||||
|
|
||||||
expect(rootNodeTextContent).toEqual(['Header', 'Item one', 'Item two']);
|
expect(rootNodeTextContent).toEqual(['Header', 'Item one', 'Item two']);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
|
it('should return root render nodes for an embedded view instance', () => {
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<ng-template #templateRef>
|
||||||
|
<div></div>
|
||||||
|
some text
|
||||||
|
<span></span>
|
||||||
|
</ng-template>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
@ViewChild('templateRef')
|
||||||
|
templateRef !: TemplateRef<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const embeddedView = fixture.componentInstance.templateRef.createEmbeddedView({});
|
||||||
|
expect(embeddedView.rootNodes.length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is different as compared to the view engine implementation which returns a comment node
|
||||||
|
* in this case:
|
||||||
|
* https://stackblitz.com/edit/angular-uiqry6?file=src/app/app.component.ts
|
||||||
|
*
|
||||||
|
* Returning a comment node for a template ref with no nodes is wrong is fixed in Ivy.
|
||||||
|
*/
|
||||||
|
onlyInIvy('Fixed: Ivy no longer adds a comment node in this case.')
|
||||||
|
.it('should return an empty array for embedded view with no nodes', () => {
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<ng-template #templateRef></ng-template>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
@ViewChild('templateRef')
|
||||||
|
templateRef !: TemplateRef<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const embeddedView = fixture.componentInstance.templateRef.createEmbeddedView({});
|
||||||
|
expect(embeddedView.rootNodes.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not descend into containers when retrieving root nodes', () => {
|
||||||
|
/**
|
||||||
|
* NOTE: In VE, if `SUFFIX` text node below is _not_ present, VE will add an
|
||||||
|
* additional `<!---->` comment, thus being slightly different than Ivy.
|
||||||
|
* (resulting in 1 root node in Ivy and 2 in VE).
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<ng-template #templateRef><ng-template [ngIf]="true">text</ng-template>SUFFIX</ng-template>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
@ViewChild('templateRef')
|
||||||
|
templateRef !: TemplateRef<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const embeddedView = fixture.componentInstance.templateRef.createEmbeddedView({});
|
||||||
|
expect(embeddedView.rootNodes.length).toBe(2);
|
||||||
|
expect(embeddedView.rootNodes[0].nodeType).toBe(Node.COMMENT_NODE);
|
||||||
|
expect(embeddedView.rootNodes[1].nodeType).toBe(Node.TEXT_NODE);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contrary to containers (<ng-template>) we _do_ descend into element containers
|
||||||
|
* (<ng-container>)
|
||||||
|
*/
|
||||||
|
it('should descend into element containers when retrieving root nodes', () => {
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<ng-template #templateRef>
|
||||||
|
<ng-container>text</ng-container>
|
||||||
|
</ng-template>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
@ViewChild('templateRef')
|
||||||
|
templateRef !: TemplateRef<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const embeddedView = fixture.componentInstance.templateRef.createEmbeddedView({});
|
||||||
|
|
||||||
|
expect(embeddedView.rootNodes.length).toBe(2);
|
||||||
|
expect(embeddedView.rootNodes[0].nodeType).toBe(Node.COMMENT_NODE);
|
||||||
|
expect(embeddedView.rootNodes[1].nodeType).toBe(Node.TEXT_NODE);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,178 +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 {TemplateRef} from '@angular/core';
|
|
||||||
|
|
||||||
import {AttributeMarker, RenderFlags, ΔdefineDirective} from '../../src/render3/index';
|
|
||||||
import {Δbind, ΔdirectiveInject, Δelement, ΔelementContainerEnd, ΔelementContainerStart, ΔelementProperty, Δtemplate, Δtext} from '../../src/render3/instructions/all';
|
|
||||||
|
|
||||||
import {NgIf} from './common_with_def';
|
|
||||||
import {ComponentFixture, createComponent, getDirectiveOnNode} from './render_util';
|
|
||||||
|
|
||||||
describe('TemplateRef', () => {
|
|
||||||
|
|
||||||
describe('rootNodes', () => {
|
|
||||||
|
|
||||||
class DirectiveWithTplRef {
|
|
||||||
static ngDirectiveDef = ΔdefineDirective({
|
|
||||||
type: DirectiveWithTplRef,
|
|
||||||
selectors: [['', 'tplRef', '']],
|
|
||||||
factory: () => new DirectiveWithTplRef(ΔdirectiveInject(TemplateRef as any))
|
|
||||||
});
|
|
||||||
|
|
||||||
// injecting a ViewContainerRef to create a dynamic container in which embedded views will be
|
|
||||||
// created
|
|
||||||
constructor(public tplRef: TemplateRef<{}>) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should return root render nodes for an embedded view instance', () => {
|
|
||||||
let directiveWithTplRef: DirectiveWithTplRef;
|
|
||||||
|
|
||||||
function embeddedTemplate(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'div');
|
|
||||||
Δtext(1, 'some text');
|
|
||||||
Δelement(2, 'span');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
<ng-template tplRef>
|
|
||||||
<div></div>
|
|
||||||
some text
|
|
||||||
<span></span>
|
|
||||||
</ng-template>
|
|
||||||
*/
|
|
||||||
const AppComponent = createComponent('app-cmp', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δtemplate(0, embeddedTemplate, 3, 0, 'ng-template', ['tplRef', '']);
|
|
||||||
directiveWithTplRef = getDirectiveOnNode(0, 0);
|
|
||||||
}
|
|
||||||
}, 1, 0, [DirectiveWithTplRef]);
|
|
||||||
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(AppComponent);
|
|
||||||
expect(directiveWithTplRef !).toBeDefined();
|
|
||||||
|
|
||||||
const viewRef = directiveWithTplRef !.tplRef.createEmbeddedView({});
|
|
||||||
expect(viewRef.rootNodes.length).toBe(3);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is different as compared to the view engine implementation which returns a comment node
|
|
||||||
* in this case:
|
|
||||||
* https://stackblitz.com/edit/angular-uiqry6?file=src/app/app.component.ts
|
|
||||||
*
|
|
||||||
* Returning a comment node for a template ref with no nodes is wrong and should be fixed in
|
|
||||||
* ivy.
|
|
||||||
*/
|
|
||||||
it('should return an empty array for embedded view with no nodes', () => {
|
|
||||||
let directiveWithTplRef: DirectiveWithTplRef;
|
|
||||||
|
|
||||||
/*
|
|
||||||
<ng-template tplRef></ng-template>
|
|
||||||
*/
|
|
||||||
const AppComponent = createComponent('app-cmp', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δtemplate(0, () => {}, 0, 0, 'ng-template', ['tplRef', '']);
|
|
||||||
directiveWithTplRef = getDirectiveOnNode(0, 0);
|
|
||||||
}
|
|
||||||
}, 1, 0, [DirectiveWithTplRef]);
|
|
||||||
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(AppComponent);
|
|
||||||
expect(directiveWithTplRef !).toBeDefined();
|
|
||||||
|
|
||||||
const viewRef = directiveWithTplRef !.tplRef.createEmbeddedView({});
|
|
||||||
expect(viewRef.rootNodes.length).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is somehow surprising but the current view engine don't descend into containers when
|
|
||||||
* getting root nodes of an embedded view:
|
|
||||||
* https://stackblitz.com/edit/angular-z8zev7?file=src/app/app.component.ts
|
|
||||||
*/
|
|
||||||
it('should not descend into containers when retrieving root nodes', () => {
|
|
||||||
let directiveWithTplRef: DirectiveWithTplRef;
|
|
||||||
|
|
||||||
function ngIfTemplate(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δtext(0, 'text');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function embeddedTemplate(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δtemplate(0, ngIfTemplate, 1, 0, 'ng-template', [AttributeMarker.Bindings, 'ngIf']);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'ngIf', Δbind(ctx.showing));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
<ng-template tplRef><ng-template [ngIf]="true">text</ng-template></ng-template>
|
|
||||||
*/
|
|
||||||
const AppComponent = createComponent('app-cmp', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δtemplate(0, embeddedTemplate, 1, 1, 'ng-template', ['tplRef', '']);
|
|
||||||
directiveWithTplRef = getDirectiveOnNode(0, 0);
|
|
||||||
}
|
|
||||||
}, 1, 0, [DirectiveWithTplRef, NgIf]);
|
|
||||||
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(AppComponent);
|
|
||||||
expect(directiveWithTplRef !).toBeDefined();
|
|
||||||
|
|
||||||
const viewRef = directiveWithTplRef !.tplRef.createEmbeddedView({});
|
|
||||||
|
|
||||||
// assert that we've got a comment node (only!) corresponding to <ng-template [ngIf]="true">
|
|
||||||
expect(viewRef.rootNodes.length).toBe(1);
|
|
||||||
expect(viewRef.rootNodes[0].nodeType).toBe(8);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Contrary to containers (<ng-template>) we _do_ descend into element containers
|
|
||||||
* (<ng-container):
|
|
||||||
* https://stackblitz.com/edit/angular-yovmmp?file=src/app/app.component.ts
|
|
||||||
*/
|
|
||||||
it('should descend into element containers when retrieving root nodes', () => {
|
|
||||||
let directiveWithTplRef: DirectiveWithTplRef;
|
|
||||||
|
|
||||||
function embeddedTemplate(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ΔelementContainerStart(0);
|
|
||||||
{ Δtext(1, 'text'); }
|
|
||||||
ΔelementContainerEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
<ng-template tplRef><ng-container>text</ng-container></ng-template>
|
|
||||||
*/
|
|
||||||
const AppComponent = createComponent('app-cmp', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δtemplate(0, embeddedTemplate, 2, 0, 'ng-template', ['tplRef', '']);
|
|
||||||
directiveWithTplRef = getDirectiveOnNode(0, 0);
|
|
||||||
}
|
|
||||||
}, 1, 0, [DirectiveWithTplRef]);
|
|
||||||
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(AppComponent);
|
|
||||||
expect(directiveWithTplRef !).toBeDefined();
|
|
||||||
|
|
||||||
const viewRef = directiveWithTplRef !.tplRef.createEmbeddedView({});
|
|
||||||
|
|
||||||
expect(viewRef.rootNodes.length).toBe(2);
|
|
||||||
expect(viewRef.rootNodes[0].nodeType)
|
|
||||||
.toBe(8); // a comment node (only!) corresponding to <ng-container>
|
|
||||||
expect(viewRef.rootNodes[1].nodeType).toBe(3); // a text node
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in New Issue