test(ivy): Add acceptance tests for view insertion (#30625)
This work is being done ahead of changes to how view insertion is done in Ivy in accordance with [this design document](https://hackmd.io/Ae3W_2pOQlKouu9YNy1t6A?view). The idea is to make sure we have acceptance tests ahead of that change. PR Close #30625
This commit is contained in:
parent
9d9c9e43e5
commit
17d87d4e10
|
@ -0,0 +1,252 @@
|
||||||
|
/**
|
||||||
|
* @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 {CommonModule} from '@angular/common';
|
||||||
|
import {ChangeDetectorRef, Component, EmbeddedViewRef, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core';
|
||||||
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
import {By} from '@angular/platform-browser';
|
||||||
|
|
||||||
|
describe('view insertion', () => {
|
||||||
|
describe('of a simple template', () => {
|
||||||
|
it('should insert into an empty container, at the front, in the middle, and at the end', () => {
|
||||||
|
let _counter = 0;
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'increment-comp',
|
||||||
|
template: `<span>created{{counter}}</span>`,
|
||||||
|
})
|
||||||
|
class IncrementComp {
|
||||||
|
counter = _counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<ng-template #simple><increment-comp></increment-comp></ng-template>
|
||||||
|
<div #container></div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
@ViewChild('container', {read: ViewContainerRef})
|
||||||
|
container: ViewContainerRef = null !;
|
||||||
|
|
||||||
|
@ViewChild('simple', {read: TemplateRef})
|
||||||
|
simple: TemplateRef<any> = null !;
|
||||||
|
|
||||||
|
view0: EmbeddedViewRef<any> = null !;
|
||||||
|
view1: EmbeddedViewRef<any> = null !;
|
||||||
|
view2: EmbeddedViewRef<any> = null !;
|
||||||
|
view3: EmbeddedViewRef<any> = null !;
|
||||||
|
|
||||||
|
constructor(public changeDetector: ChangeDetectorRef) {}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
// insert at the front
|
||||||
|
this.view1 = this.container.createEmbeddedView(this.simple); // "created0"
|
||||||
|
|
||||||
|
// insert at the front again
|
||||||
|
this.view0 = this.container.createEmbeddedView(this.simple, {}, 0); // "created1"
|
||||||
|
|
||||||
|
// insert at the end
|
||||||
|
this.view3 = this.container.createEmbeddedView(this.simple); // "created2"
|
||||||
|
|
||||||
|
// insert in the middle
|
||||||
|
this.view2 = this.container.createEmbeddedView(this.simple, {}, 2); // "created3"
|
||||||
|
|
||||||
|
// We need to run change detection here to avoid
|
||||||
|
// ExpressionChangedAfterItHasBeenCheckedError because of the value updating in
|
||||||
|
// increment-comp
|
||||||
|
this.changeDetector.detectChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App, IncrementComp],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
|
||||||
|
expect(app.container.indexOf(app.view0)).toBe(0);
|
||||||
|
expect(app.container.indexOf(app.view1)).toBe(1);
|
||||||
|
expect(app.container.indexOf(app.view2)).toBe(2);
|
||||||
|
expect(app.container.indexOf(app.view3)).toBe(3);
|
||||||
|
// The text in each component differs based on *when* it was created.
|
||||||
|
expect(fixture.nativeElement.textContent).toBe('created1created0created3created2');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('of an empty template', () => {
|
||||||
|
it('should insert into an empty container, at the front, in the middle, and at the end', () => {
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<ng-template #empty></ng-template>
|
||||||
|
<div #container></div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
@ViewChild('container', {read: ViewContainerRef})
|
||||||
|
container: ViewContainerRef = null !;
|
||||||
|
|
||||||
|
@ViewChild('empty', {read: TemplateRef})
|
||||||
|
empty: TemplateRef<any> = null !;
|
||||||
|
|
||||||
|
view0: EmbeddedViewRef<any> = null !;
|
||||||
|
view1: EmbeddedViewRef<any> = null !;
|
||||||
|
view2: EmbeddedViewRef<any> = null !;
|
||||||
|
view3: EmbeddedViewRef<any> = null !;
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
// insert at the front
|
||||||
|
this.view1 = this.container.createEmbeddedView(this.empty);
|
||||||
|
|
||||||
|
// insert at the front again
|
||||||
|
this.view0 = this.container.createEmbeddedView(this.empty, {}, 0);
|
||||||
|
|
||||||
|
// insert at the end
|
||||||
|
this.view3 = this.container.createEmbeddedView(this.empty);
|
||||||
|
|
||||||
|
// insert in the middle
|
||||||
|
this.view2 = this.container.createEmbeddedView(this.empty, {}, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
|
||||||
|
expect(app.container.indexOf(app.view0)).toBe(0);
|
||||||
|
expect(app.container.indexOf(app.view1)).toBe(1);
|
||||||
|
expect(app.container.indexOf(app.view2)).toBe(2);
|
||||||
|
expect(app.container.indexOf(app.view3)).toBe(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('of an ng-content projection', () => {
|
||||||
|
it('should insert into an empty container, at the front, in the middle, and at the end', () => {
|
||||||
|
@Component({
|
||||||
|
selector: 'comp',
|
||||||
|
template: `
|
||||||
|
<ng-template #projection><ng-content></ng-content></ng-template>
|
||||||
|
<div #container></div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class Comp {
|
||||||
|
@ViewChild('container', {read: ViewContainerRef})
|
||||||
|
container: ViewContainerRef = null !;
|
||||||
|
|
||||||
|
@ViewChild('projection', {read: TemplateRef})
|
||||||
|
projection: TemplateRef<any> = null !;
|
||||||
|
|
||||||
|
view0: EmbeddedViewRef<any> = null !;
|
||||||
|
view1: EmbeddedViewRef<any> = null !;
|
||||||
|
view2: EmbeddedViewRef<any> = null !;
|
||||||
|
view3: EmbeddedViewRef<any> = null !;
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
// insert at the front
|
||||||
|
this.view1 = this.container.createEmbeddedView(this.projection);
|
||||||
|
|
||||||
|
// insert at the front again
|
||||||
|
this.view0 = this.container.createEmbeddedView(this.projection, {}, 0);
|
||||||
|
|
||||||
|
// insert at the end
|
||||||
|
this.view3 = this.container.createEmbeddedView(this.projection);
|
||||||
|
|
||||||
|
// insert in the middle
|
||||||
|
this.view2 = this.container.createEmbeddedView(this.projection, {}, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<comp>test</comp>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App, Comp],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const comp = fixture.debugElement.query(By.directive(Comp)).injector.get(Comp);
|
||||||
|
|
||||||
|
expect(comp.container.indexOf(comp.view0)).toBe(0);
|
||||||
|
expect(comp.container.indexOf(comp.view1)).toBe(1);
|
||||||
|
expect(comp.container.indexOf(comp.view2)).toBe(2);
|
||||||
|
expect(comp.container.indexOf(comp.view3)).toBe(3);
|
||||||
|
|
||||||
|
// Both ViewEngine and Ivy only honor one of the inserted ng-content components, even though
|
||||||
|
// all are inserted.
|
||||||
|
expect(fixture.nativeElement.textContent).toBe('test');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('of another container like ngIf', () => {
|
||||||
|
it('should insert into an empty container, at the front, in the middle, and at the end', () => {
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<ng-template #subContainer><div class="dynamic" *ngIf="true">test</div></ng-template>
|
||||||
|
<div #container></div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
@ViewChild('container', {read: ViewContainerRef})
|
||||||
|
container: ViewContainerRef = null !;
|
||||||
|
|
||||||
|
@ViewChild('subContainer', {read: TemplateRef})
|
||||||
|
subContainer: TemplateRef<any> = null !;
|
||||||
|
|
||||||
|
view0: EmbeddedViewRef<any> = null !;
|
||||||
|
view1: EmbeddedViewRef<any> = null !;
|
||||||
|
view2: EmbeddedViewRef<any> = null !;
|
||||||
|
view3: EmbeddedViewRef<any> = null !;
|
||||||
|
|
||||||
|
constructor(public changeDetectorRef: ChangeDetectorRef) {}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
// insert at the front
|
||||||
|
this.view1 = this.container.createEmbeddedView(this.subContainer, null, 0);
|
||||||
|
|
||||||
|
// insert at the front again
|
||||||
|
this.view0 = this.container.createEmbeddedView(this.subContainer, null, 0);
|
||||||
|
|
||||||
|
// insert at the end
|
||||||
|
this.view3 = this.container.createEmbeddedView(this.subContainer, null, 2);
|
||||||
|
|
||||||
|
// insert in the middle
|
||||||
|
this.view2 = this.container.createEmbeddedView(this.subContainer, null, 2);
|
||||||
|
|
||||||
|
// We need to run change detection here to avoid
|
||||||
|
// ExpressionChangedAfterItHasBeenCheckedError because of the value getting passed to ngIf
|
||||||
|
// in the template.
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App],
|
||||||
|
imports: [CommonModule],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
|
||||||
|
expect(app.container.indexOf(app.view0)).toBe(0);
|
||||||
|
expect(app.container.indexOf(app.view1)).toBe(1);
|
||||||
|
expect(app.container.indexOf(app.view2)).toBe(2);
|
||||||
|
expect(app.container.indexOf(app.view3)).toBe(3);
|
||||||
|
|
||||||
|
expect(fixture.debugElement.queryAll(By.css('div.dynamic')).length).toBe(4);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue