test(ivy): add afterViewInit acceptance tests (#30445)

Migrates afterViewInit and afterViewChecked render3 tests to acceptance tests

PR Close #30445
This commit is contained in:
Ben Lesh 2019-05-13 21:52:00 -07:00 committed by Jason Aden
parent a5e06ba629
commit 257e9646d0
2 changed files with 678 additions and 852 deletions

View File

@ -1209,3 +1209,681 @@ describe('afterContentChecked', () => {
]);
});
});
describe('afterViewInit', () => {
it('should be called on creation and not in update mode', () => {
let afterViewInitCalls = 0;
@Component({
selector: 'comp',
template: `<p>test</p>`,
})
class Comp {
ngAfterViewInit() { afterViewInitCalls++; }
}
@Component({template: `<comp></comp>`})
class App {
}
TestBed.configureTestingModule({
declarations: [App, Comp],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
// two updates
fixture.detectChanges();
fixture.detectChanges();
expect(afterViewInitCalls).toBe(1);
});
it('should be called on root component in creation mode', () => {
let afterViewInitCalls = 0;
@Component({template: `<p>test</p>`})
class App {
ngAfterViewInit() { afterViewInitCalls++; }
}
TestBed.configureTestingModule({
declarations: [App],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
// two updates
fixture.detectChanges();
fixture.detectChanges();
expect(afterViewInitCalls).toBe(1);
});
it('should be called every time a view is initialized with ngIf', () => {
const events: string[] = [];
@Component({
selector: 'comp',
template: `<p>test</p>`,
})
class Comp {
ngAfterViewInit() { events.push('comp'); }
}
@Component({
template: `<comp *ngIf="show"></comp>`,
})
class App {
show = true;
ngAfterViewInit() { events.push('app'); }
}
TestBed.configureTestingModule({
declarations: [App, Comp],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(events).toEqual(['comp', 'app']);
fixture.componentInstance.show = false;
fixture.detectChanges();
expect(events).toEqual(['comp', 'app']);
fixture.componentInstance.show = true;
fixture.detectChanges();
expect(events).toEqual(['comp', 'app', 'comp']);
});
it('should be called in children before parents', () => {
const events: string[] = [];
@Component({
selector: 'parent',
template: `<child [name]=name></child>`,
})
class Parent {
@Input()
name = '';
ngAfterViewInit() { events.push('parent ' + this.name); }
}
@Component({
selector: 'child',
template: `<p>test</p>`,
})
class Child {
@Input()
name = '';
ngAfterViewInit() { events.push('child of parent ' + this.name); }
}
@Component({
template: `
<parent name="1"></parent>
<parent name="2"></parent>
`
})
class App {
ngAfterViewInit() { events.push('app'); }
}
TestBed.configureTestingModule({
declarations: [App, Parent, Child],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(events).toEqual([
'child of parent 1',
'child of parent 2',
'parent 1',
'parent 2',
'app',
]);
});
it('should be called in projected components before their hosts', () => {
const events: string[] = [];
@Component({
selector: 'projected',
template: `<p>test</p>`,
})
class Projected {
@Input()
name = '';
ngAfterViewInit() { events.push('projected ' + this.name); }
}
@Component({
selector: 'comp',
template: `<ng-content></ng-content>`,
})
class Comp {
@Input()
name = '';
ngAfterViewInit() { events.push('comp ' + this.name); }
}
@Component({
template: `
<comp name="1"><projected name="1"></projected></comp>
<comp name="2"><projected name="2"></projected></comp>
`
})
class App {
ngAfterViewInit() { events.push('app'); }
}
TestBed.configureTestingModule({
declarations: [App, Comp, Projected],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(events).toEqual([
'projected 1',
'comp 1',
'projected 2',
'comp 2',
'app',
]);
});
it('should call afterViewInit in content children and host before next host', () => {
const events: string[] = [];
@Component({
selector: 'projected-child',
template: `<p>test</p>`,
})
class ProjectedChild {
@Input()
name = '';
ngAfterViewInit() { events.push('child of projected ' + this.name); }
}
@Component({
selector: 'projected',
template: `<projected-child [name]="name"></projected-child>`,
})
class Projected {
@Input()
name = '';
ngAfterViewInit() { events.push('projected ' + this.name); }
}
@Component({
selector: 'comp',
template: `<div><ng-content></ng-content></div>`,
})
class Comp {
@Input()
name = '';
ngAfterViewInit() { events.push('comp ' + this.name); }
}
@Component({
template: `
<comp name="1"><projected name="1"></projected></comp>
<comp name="2"><projected name="2"></projected></comp>
`
})
class App {
ngAfterViewInit() { events.push('app'); }
}
TestBed.configureTestingModule({
declarations: [App, Comp, Projected, ProjectedChild],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(events).toEqual([
'child of projected 1',
'child of projected 2',
'projected 1',
'comp 1',
'projected 2',
'comp 2',
'app',
]);
});
it('should be called in correct order with ngFor', () => {
const events: string[] = [];
@Component({
selector: 'comp',
template: `<p>test</p>`,
})
class Comp {
@Input()
name = '';
ngAfterViewInit() { events.push('comp ' + this.name); }
}
@Component({
template: `
<comp name="4"></comp>
<comp *ngFor="let number of numbers" [name]="number"></comp>
<comp name="5"></comp>
`
})
class App {
numbers = [0, 1, 2, 3];
ngAfterViewInit() { events.push('app'); }
}
TestBed.configureTestingModule({
declarations: [App, Comp],
imports: [CommonModule],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(events).toEqual([
'comp 0',
'comp 1',
'comp 2',
'comp 3',
'comp 4',
'comp 5',
'app',
]);
});
it('should be called in correct order with for loops with children', () => {
const events: string[] = [];
@Component({
selector: 'child',
template: `<p>test</p>`,
})
class Child {
@Input()
name = '';
ngAfterViewInit() { events.push('child of parent ' + this.name); }
}
@Component({
selector: 'parent',
template: `<child [name]="name"></child>`,
})
class Parent {
@Input()
name = '';
ngAfterViewInit() { events.push('parent ' + this.name); }
}
@Component({
template: `
<parent name="4"></parent>
<parent *ngFor="let number of numbers" [name]="number"></parent>
<parent name="5"></parent>
`
})
class App {
numbers = [0, 1, 2, 3];
ngAfterViewInit() { events.push('app'); }
}
TestBed.configureTestingModule({
declarations: [App, Parent, Child],
imports: [CommonModule],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(events).toEqual([
'child of parent 0',
'parent 0',
'child of parent 1',
'parent 1',
'child of parent 2',
'parent 2',
'child of parent 3',
'parent 3',
'child of parent 4',
'child of parent 5',
'parent 4',
'parent 5',
'app',
]);
});
it('should be called on directives after component', () => {
const events: string[] = [];
@Directive({
selector: '[dir]',
})
class Dir {
@Input('dir')
name = '';
ngAfterViewInit() { events.push('dir ' + this.name); }
}
@Component({
selector: 'comp',
template: `<p>test</p>`,
})
class Comp {
@Input()
name = '';
ngAfterViewInit() { events.push('comp ' + this.name); }
}
@Component({
template: `
<comp name="1" dir="1"></comp>
<comp name="2" dir="2"></comp>
`
})
class App {
ngAfterViewInit() { events.push('app'); }
}
TestBed.configureTestingModule({
declarations: [App, Comp, Dir],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(events).toEqual([
'comp 1',
'dir 1',
'comp 2',
'dir 2',
'app',
]);
});
it('should be called on directives on an element', () => {
const events: string[] = [];
@Directive({
selector: '[dir]',
})
class Dir {
@Input('dir')
name = '';
ngAfterViewInit() { events.push('dir ' + this.name); }
}
@Component({
template: `
<div dir="1"></div>
<div dir="2"></div>
`
})
class App {
ngAfterViewInit() { events.push('app'); }
}
TestBed.configureTestingModule({
declarations: [App, Dir],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(events).toEqual([
'dir 1',
'dir 2',
'app',
]);
});
});
describe('ngAfterViewChecked', () => {
it('should call ngAfterViewChecked every update', () => {
let afterViewCheckedCalls = 0;
@Component({
selector: 'comp',
template: `<p>test</p>`,
})
class Comp {
ngAfterViewChecked() { afterViewCheckedCalls++; }
}
@Component({template: `<comp></comp>`})
class App {
}
TestBed.configureTestingModule({
declarations: [App, Comp],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(afterViewCheckedCalls).toBe(1);
fixture.detectChanges();
expect(afterViewCheckedCalls).toBe(2);
fixture.detectChanges();
expect(afterViewCheckedCalls).toBe(3);
});
it('should be called on root component', () => {
let afterViewCheckedCalls = 0;
@Component({template: `<p>test</p>`})
class App {
ngAfterViewChecked() { afterViewCheckedCalls++; }
}
TestBed.configureTestingModule({
declarations: [App],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(afterViewCheckedCalls).toBe(1);
fixture.detectChanges();
expect(afterViewCheckedCalls).toBe(2);
fixture.detectChanges();
expect(afterViewCheckedCalls).toBe(3);
});
it('should call ngAfterViewChecked with bindings', () => {
let afterViewCheckedCalls = 0;
@Component({
selector: 'comp',
template: `<p>{{value}}</p>`,
})
class Comp {
@Input()
value = '';
ngAfterViewChecked() { afterViewCheckedCalls++; }
}
@Component({template: `<comp [value]="value"></comp>`})
class App {
value = 1;
}
TestBed.configureTestingModule({
declarations: [App, Comp],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(afterViewCheckedCalls).toBe(1);
fixture.componentInstance.value = 1337;
fixture.detectChanges();
expect(afterViewCheckedCalls).toBe(2);
});
it('should be called in correct order with for loops with children', () => {
const events: string[] = [];
@Component({
selector: 'child',
template: `<p>test</p>`,
})
class Child {
@Input()
name = '';
ngAfterViewChecked() { events.push('child of parent ' + this.name); }
}
@Component({
selector: 'parent',
template: `<child [name]="name"></child>`,
})
class Parent {
@Input()
name = '';
ngAfterViewChecked() { events.push('parent ' + this.name); }
}
@Component({
template: `
<parent name="4"></parent>
<parent *ngFor="let number of numbers" [name]="number"></parent>
<parent name="5"></parent>
`
})
class App {
numbers = [0, 1, 2, 3];
ngAfterViewChecked() { events.push('app'); }
}
TestBed.configureTestingModule({
declarations: [App, Parent, Child],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(events).toEqual([
'child of parent 0',
'parent 0',
'child of parent 1',
'parent 1',
'child of parent 2',
'parent 2',
'child of parent 3',
'parent 3',
'child of parent 4',
'child of parent 5',
'parent 4',
'parent 5',
'app',
]);
});
it('should be called on directives after component', () => {
const events: string[] = [];
@Directive({
selector: '[dir]',
})
class Dir {
@Input('dir')
name = '';
ngAfterViewChecked() { events.push('dir ' + this.name); }
}
@Component({
selector: 'comp',
template: `<p>test</p>`,
})
class Comp {
@Input()
name = '';
ngAfterViewChecked() { events.push('comp ' + this.name); }
}
@Component({
template: `
<comp name="1" dir="1"></comp>
<comp name="2" dir="2"></comp>
`
})
class App {
ngAfterViewChecked() { events.push('app'); }
}
TestBed.configureTestingModule({
declarations: [App, Comp, Dir],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(events).toEqual([
'comp 1',
'dir 1',
'comp 2',
'dir 2',
'app',
]);
});
it('should be called on directives on an element', () => {
const events: string[] = [];
@Directive({
selector: '[dir]',
})
class Dir {
@Input('dir')
name = '';
ngAfterViewChecked() { events.push('dir ' + this.name); }
}
@Component({
template: `
<div dir="1"></div>
<div dir="2"></div>
`
})
class App {
ngAfterViewChecked() { events.push('app'); }
}
TestBed.configureTestingModule({
declarations: [App, Dir],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(events).toEqual([
'dir 1',
'dir 2',
'app',
]);
});
});

View File

@ -116,858 +116,6 @@ describe('lifecycles', () => {
});
});
describe('afterContentInit', () => {
let events: string[];
let allEvents: string[];
beforeEach(() => {
events = [];
allEvents = [];
});
let Comp = createAfterContentInitComp('comp', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ΔprojectionDef();
Δprojection(0);
}
}, 1);
let Parent = createAfterContentInitComp('parent', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ΔprojectionDef();
ΔelementStart(0, 'comp');
{ Δprojection(1); }
ΔelementEnd();
}
if (rf & RenderFlags.Update) {
ΔelementProperty(0, 'val', Δbind(ctx.val));
}
}, 2, 1, [Comp]);
let ProjectedComp = createAfterContentInitComp('projected', (rf: RenderFlags, ctx: any) => {
if (rf & RenderFlags.Create) {
ΔprojectionDef();
Δprojection(0);
}
}, 1);
function createAfterContentInitComp(
name: string, template: ComponentTemplate<any>, consts: number = 0, vars: number = 0,
directives: any[] = []) {
return class Component {
val: string = '';
ngAfterContentInit() {
events.push(`${name}${this.val}`);
allEvents.push(`${name}${this.val} init`);
}
ngAfterContentChecked() { allEvents.push(`${name}${this.val} check`); }
static ngComponentDef = ΔdefineComponent({
type: Component,
selectors: [[name]],
factory: () => new Component(),
consts: consts,
vars: vars,
inputs: {val: 'val'},
template: template,
directives: directives
});
};
}
class Directive {
ngAfterContentInit() { events.push('init'); }
ngAfterContentChecked() { events.push('check'); }
static ngDirectiveDef = ΔdefineDirective(
{type: Directive, selectors: [['', 'dir', '']], factory: () => new Directive()});
}
function ForLoopWithChildrenTemplate(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ΔelementStart(0, 'parent');
{ Δtext(1, 'content'); }
ΔelementEnd();
Δcontainer(2);
ΔelementStart(3, 'parent');
{ Δtext(4, 'content'); }
ΔelementEnd();
}
if (rf & RenderFlags.Update) {
ΔelementProperty(0, 'val', 1);
Δselect(3);
ΔelementProperty(3, 'val', 4);
ΔcontainerRefreshStart(2);
{
for (let i = 2; i < 4; i++) {
let rf1 = ΔembeddedViewStart(0, 2, 0);
if (rf1 & RenderFlags.Create) {
ΔelementStart(0, 'parent');
{ Δtext(1, 'content'); }
ΔelementEnd();
}
if (rf1 & RenderFlags.Update) {
ΔelementProperty(0, 'val', i);
}
ΔembeddedViewEnd();
}
}
ΔcontainerRefreshEnd();
}
}
const directives = [Comp, Parent, ProjectedComp, Directive];
it('should be called only in creation mode', () => {
/** <comp>content</comp> */
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ΔelementStart(0, 'comp');
{ Δtext(1, 'content'); }
ΔelementEnd();
}
}, 2, 0, directives);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['comp']);
fixture.update();
expect(events).toEqual(['comp']);
});
it('should be called on root component in creation mode', () => {
const comp = renderComponent(Comp, {hostFeatures: [LifecycleHooksFeature]});
expect(events).toEqual(['comp']);
markDirty(comp);
requestAnimationFrame.flush();
expect(events).toEqual(['comp']);
});
it('should be called on every init (if blocks)', () => {
/**
* % if (!skip) {
* <comp>content</comp>
* % }
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δcontainer(0);
}
if (rf & RenderFlags.Update) {
ΔcontainerRefreshStart(0);
{
if (!ctx.skip) {
let rf1 = ΔembeddedViewStart(0, 2, 0);
if (rf1 & RenderFlags.Create) {
ΔelementStart(0, 'comp');
{ Δtext(1, 'content'); }
ΔelementEnd();
}
ΔembeddedViewEnd();
}
}
ΔcontainerRefreshEnd();
}
}, 1, 0, directives);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['comp']);
fixture.component.skip = true;
fixture.update();
expect(events).toEqual(['comp']);
fixture.component.skip = false;
fixture.update();
expect(events).toEqual(['comp', 'comp']);
});
it('should be called in parents before children', () => {
/**
* <parent>content</parent>
*
* parent template: <comp><ng-content></ng-content></comp>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ΔelementStart(0, 'parent');
{ Δtext(1, 'content'); }
ΔelementEnd();
}
}, 2, 0, directives);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['parent', 'comp']);
});
it('should be called breadth-first in entire parent subtree before any children', () => {
/**
* <parent [val]="1">content</parent>
* <parent [val]="2">content</parent>
*
* parent template: <comp [val]="val"><ng-content></ng-content></comp>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ΔelementStart(0, 'parent');
{ Δtext(1, 'content'); }
ΔelementEnd();
ΔelementStart(2, 'parent');
{ Δtext(3, 'content'); }
ΔelementEnd();
}
if (rf & RenderFlags.Update) {
ΔelementProperty(0, 'val', 1);
Δselect(2);
ΔelementProperty(2, 'val', 2);
}
}, 4, 0, directives);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['parent1', 'parent2', 'comp1', 'comp2']);
});
it('should be called in projected components before their hosts', () => {
/**
* <parent>
* <projected>content</projected>
* </parent>
*
* parent template:
* <comp><ng-content></ng-content></comp>
*
* projected comp: <ng-content></ng-content>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ΔelementStart(0, 'parent');
{
ΔelementStart(1, 'projected');
{ Δtext(2, 'content'); }
ΔelementEnd();
}
ΔelementEnd();
}
}, 3, 0, directives);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['projected', 'parent', 'comp']);
});
it('should be called in projected components and hosts before children', () => {
/**
* <parent [val]="1">
* <projected [val]="1">content</projected>
* </parent>
* * <parent [val]="2">
* <projected [val]="2">content</projected>
* </parent>
*
* parent template:
* <comp [val]="val"><ng-content></ng-content></comp>
*
* projected comp: <ng-content></ng-content>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ΔelementStart(0, 'parent');
{
ΔelementStart(1, 'projected');
{ Δtext(2, 'content'); }
ΔelementEnd();
}
ΔelementEnd();
ΔelementStart(3, 'parent');
{
ΔelementStart(4, 'projected');
{ Δtext(5, 'content'); }
ΔelementEnd();
}
ΔelementEnd();
}
if (rf & RenderFlags.Update) {
ΔelementProperty(0, 'val', 1);
Δselect(1);
ΔelementProperty(1, 'val', 1);
Δselect(3);
ΔelementProperty(3, 'val', 2);
Δselect(4);
ΔelementProperty(4, 'val', 2);
}
}, 6, 0, directives);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['projected1', 'parent1', 'projected2', 'parent2', 'comp1', 'comp2']);
});
it('should be called in correct order in a for loop', () => {
/**
* <comp [val]="1">content</comp>
* % for(let i = 2; i < 4; i++) {
* <comp [val]="i">content</comp>
* % }
* <comp [val]="4">content</comp>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ΔelementStart(0, 'comp');
{ Δtext(1, 'content'); }
ΔelementEnd();
Δcontainer(2);
ΔelementStart(3, 'comp');
{ Δtext(4, 'content'); }
ΔelementEnd();
}
if (rf & RenderFlags.Update) {
ΔelementProperty(0, 'val', 1);
Δselect(3);
ΔelementProperty(3, 'val', 4);
ΔcontainerRefreshStart(2);
{
for (let i = 2; i < 4; i++) {
let rf1 = ΔembeddedViewStart(0, 2, 0);
if (rf1 & RenderFlags.Create) {
ΔelementStart(0, 'comp');
{ Δtext(1, 'content'); }
ΔelementEnd();
}
if (rf1 & RenderFlags.Update) {
ΔelementProperty(0, 'val', i);
}
ΔembeddedViewEnd();
}
}
ΔcontainerRefreshEnd();
}
}, 5, 0, directives);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['comp2', 'comp3', 'comp1', 'comp4']);
});
it('should be called in correct order in a for loop with children', () => {
/**
* <parent [val]="1">content</parent>
* % for(let i = 2; i < 4; i++) {
* <parent [val]="i">content</parent>
* % }
* <parent [val]="4">content</parent>
*/
renderToHtml(ForLoopWithChildrenTemplate, {}, 5, 0, directives);
expect(events).toEqual(
['parent2', 'comp2', 'parent3', 'comp3', 'parent1', 'parent4', 'comp1', 'comp4']);
});
describe('ngAfterContentChecked', () => {
it('should be called every change detection run after afterContentInit', () => {
/** <comp>content</comp> */
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ΔelementStart(0, 'comp');
{ Δtext(1, 'content'); }
ΔelementEnd();
}
}, 2, 0, directives);
const fixture = new ComponentFixture(App);
expect(allEvents).toEqual(['comp init', 'comp check']);
fixture.update();
expect(allEvents).toEqual(['comp init', 'comp check', 'comp check']);
});
it('should be called on root component', () => {
const comp = renderComponent(Comp, {hostFeatures: [LifecycleHooksFeature]});
expect(allEvents).toEqual(['comp init', 'comp check']);
markDirty(comp);
requestAnimationFrame.flush();
expect(allEvents).toEqual(['comp init', 'comp check', 'comp check']);
});
});
describe('directives', () => {
it('should be called on directives after component', () => {
/** <comp directive></comp> */
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δelement(0, 'comp', ['dir', '']);
}
}, 1, 0, directives);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['comp', 'init', 'check']);
});
it('should be called on directives on an element', () => {
/** <div directive></div> */
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δelement(0, 'div', ['dir', '']);
}
}, 1, 0, directives);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['init', 'check']);
});
});
});
describe('afterViewInit', () => {
let events: string[];
let allEvents: string[];
beforeEach(() => {
events = [];
allEvents = [];
});
let Comp = createAfterViewInitComponent('comp', (rf: RenderFlags, ctx: any) => {
if (rf & RenderFlags.Create) {
ΔprojectionDef();
ΔelementStart(0, 'div');
{ Δprojection(1); }
ΔelementEnd();
}
}, 2);
let Parent = createAfterViewInitComponent('parent', getParentTemplate('comp'), 1, 1, [Comp]);
let ProjectedComp = createAfterViewInitComponent('projected', (rf: RenderFlags, ctx: any) => {
if (rf & RenderFlags.Create) {
Δtext(0, 'content');
}
}, 1);
function createAfterViewInitComponent(
name: string, template: ComponentTemplate<any>, consts: number, vars: number = 0,
directives: any[] = []) {
return class Component {
val: string = '';
ngAfterViewInit() {
if (!this.val) this.val = '';
events.push(`${name}${this.val}`);
allEvents.push(`${name}${this.val} init`);
}
ngAfterViewChecked() { allEvents.push(`${name}${this.val} check`); }
static ngComponentDef = ΔdefineComponent({
type: Component,
selectors: [[name]],
consts: consts,
vars: vars,
factory: () => new Component(),
inputs: {val: 'val'},
template: template,
directives: directives
});
};
}
class Directive {
ngAfterViewInit() { events.push('init'); }
ngAfterViewChecked() { events.push('check'); }
static ngDirectiveDef = ΔdefineDirective(
{type: Directive, selectors: [['', 'dir', '']], factory: () => new Directive()});
}
const defs = [Comp, Parent, ProjectedComp, Directive];
it('should be called on init and not in update mode', () => {
/** <comp></comp> */
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δelement(0, 'comp');
}
}, 1, 0, defs);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['comp']);
fixture.update();
expect(events).toEqual(['comp']);
});
it('should be called on root component in creation mode', () => {
const comp = renderComponent(Comp, {hostFeatures: [LifecycleHooksFeature]});
expect(events).toEqual(['comp']);
markDirty(comp);
requestAnimationFrame.flush();
expect(events).toEqual(['comp']);
});
it('should be called every time a view is initialized (if block)', () => {
/*
* % if (!skip) {
* <comp></comp>
* % }
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δcontainer(0);
}
if (rf & RenderFlags.Update) {
ΔcontainerRefreshStart(0);
{
if (!ctx.skip) {
let rf1 = ΔembeddedViewStart(0, 1, 0);
if (rf1 & RenderFlags.Create) {
Δelement(0, 'comp');
}
ΔembeddedViewEnd();
}
}
ΔcontainerRefreshEnd();
}
}, 1, 0, defs);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['comp']);
fixture.component.skip = true;
fixture.update();
expect(events).toEqual(['comp']);
fixture.component.skip = false;
fixture.update();
expect(events).toEqual(['comp', 'comp']);
});
it('should be called in children before parents', () => {
/**
* <parent></parent>
*
* parent temp: <comp></comp>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δelement(0, 'parent');
}
}, 1, 0, defs);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['comp', 'parent']);
});
it('should be called for entire subtree before being called in any parent view comps', () => {
/**
* <parent [val]="1"></parent>
* <parent [val]="2"></parent>
*
* parent temp: <comp [val]="val"></comp>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δelement(0, 'parent');
Δelement(1, 'parent');
}
if (rf & RenderFlags.Update) {
ΔelementProperty(0, 'val', 1);
Δselect(1);
ΔelementProperty(1, 'val', 2);
}
}, 2, 0, defs);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['comp1', 'comp2', 'parent1', 'parent2']);
});
it('should be called in projected components before their hosts', () => {
/**
* <comp>
* <projected></projected>
* </comp>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ΔelementStart(0, 'comp');
{ Δelement(1, 'projected'); }
ΔelementEnd();
}
}, 2, 0, defs);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['projected', 'comp']);
});
it('should call afterViewInit in content children and host before next host', () => {
/**
* <comp [val]="1">
* <projected [val]="1"></projected>
* </comp>
* <comp [val]="2">
* <projected [val]="2"></projected>
* </comp>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ΔelementStart(0, 'comp');
{ Δelement(1, 'projected'); }
ΔelementEnd();
ΔelementStart(2, 'comp');
{ Δelement(3, 'projected'); }
ΔelementEnd();
}
if (rf & RenderFlags.Update) {
ΔelementProperty(0, 'val', 1);
Δselect(1);
ΔelementProperty(1, 'val', 1);
Δselect(2);
ΔelementProperty(2, 'val', 2);
Δselect(3);
ΔelementProperty(3, 'val', 2);
}
}, 4, 0, defs);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['projected1', 'comp1', 'projected2', 'comp2']);
});
it('should call afterViewInit in content children and hosts before parents', () => {
/*
* <comp [val]="val">
* <projected [val]="val"></projected>
* </comp>
*/
const ParentComp = createAfterViewInitComponent('parent', (rf: RenderFlags, ctx: any) => {
if (rf & RenderFlags.Create) {
ΔelementStart(0, 'comp');
{ Δelement(1, 'projected'); }
ΔelementEnd();
}
if (rf & RenderFlags.Update) {
ΔelementProperty(0, 'val', Δbind(ctx.val));
Δselect(1);
ΔelementProperty(1, 'val', Δbind(ctx.val));
}
}, 2, 2, [Comp, ProjectedComp]);
/**
* <parent [val]="1"></parent>
* <parent [val]="2"></parent>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δelement(0, 'parent');
Δelement(1, 'parent');
}
if (rf & RenderFlags.Update) {
ΔelementProperty(0, 'val', 1);
Δselect(1);
ΔelementProperty(1, 'val', 2);
}
}, 2, 0, [ParentComp]);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['projected1', 'comp1', 'projected2', 'comp2', 'parent1', 'parent2']);
});
it('should be called in correct order with for loops', () => {
/**
* <comp [val]="1"></comp>
* % for (let i = 0; i < 4; i++) {
* <comp [val]="i"></comp>
* % }
* <comp [val]="4"></comp>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δelement(0, 'comp');
Δcontainer(1);
Δelement(2, 'comp');
}
if (rf & RenderFlags.Update) {
ΔelementProperty(0, 'val', 1);
Δselect(2);
ΔelementProperty(2, 'val', 4);
ΔcontainerRefreshStart(1);
{
for (let i = 2; i < 4; i++) {
let rf1 = ΔembeddedViewStart(0, 1, 0);
if (rf1 & RenderFlags.Create) {
Δelement(0, 'comp');
}
if (rf1 & RenderFlags.Update) {
ΔelementProperty(0, 'val', i);
}
ΔembeddedViewEnd();
}
}
ΔcontainerRefreshEnd();
}
}, 3, 0, defs);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['comp2', 'comp3', 'comp1', 'comp4']);
});
it('should be called in correct order with for loops with children', () => {
/**
* <parent [val]="1"></parent>
* % for(let i = 0; i < 4; i++) {
* <parent [val]="i"></parent>
* % }
* <parent [val]="4"></parent>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δelement(0, 'parent');
Δcontainer(1);
Δelement(2, 'parent');
}
if (rf & RenderFlags.Update) {
ΔelementProperty(0, 'val', 1);
Δselect(2);
ΔelementProperty(2, 'val', 4);
ΔcontainerRefreshStart(1);
{
for (let i = 2; i < 4; i++) {
let rf1 = ΔembeddedViewStart(0, 1, 0);
if (rf1 & RenderFlags.Create) {
Δelement(0, 'parent');
}
if (rf1 & RenderFlags.Update) {
ΔelementProperty(0, 'val', i);
}
ΔembeddedViewEnd();
}
}
ΔcontainerRefreshEnd();
}
}, 3, 0, defs);
const fixture = new ComponentFixture(App);
expect(events).toEqual(
['comp2', 'parent2', 'comp3', 'parent3', 'comp1', 'comp4', 'parent1', 'parent4']);
});
describe('ngAfterViewChecked', () => {
it('should call ngAfterViewChecked every update', () => {
/** <comp></comp> */
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δelement(0, 'comp');
}
}, 1, 0, defs);
const fixture = new ComponentFixture(App);
expect(allEvents).toEqual(['comp init', 'comp check']);
fixture.update();
expect(allEvents).toEqual(['comp init', 'comp check', 'comp check']);
});
it('should be called on root component', () => {
const comp = renderComponent(Comp, {hostFeatures: [LifecycleHooksFeature]});
expect(allEvents).toEqual(['comp init', 'comp check']);
markDirty(comp);
requestAnimationFrame.flush();
expect(allEvents).toEqual(['comp init', 'comp check', 'comp check']);
});
it('should call ngAfterViewChecked with bindings', () => {
/** <comp [val]="myVal"></comp> */
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δelement(0, 'comp');
}
if (rf & RenderFlags.Update) {
ΔelementProperty(0, 'val', Δbind(ctx.myVal));
}
}, 1, 1, defs);
const fixture = new ComponentFixture(App);
expect(allEvents).toEqual(['comp init', 'comp check']);
fixture.component.myVal = 2;
fixture.update();
expect(allEvents).toEqual(['comp init', 'comp check', 'comp2 check']);
});
it('should be called in correct order with for loops with children', () => {
/**
* <parent [val]="1"></parent>
* % for(let i = 0; i < 4; i++) {
* <parent [val]="i"></parent>
* % }
* <parent [val]="4"></parent>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δelement(0, 'parent');
Δcontainer(1);
Δelement(2, 'parent');
}
if (rf & RenderFlags.Update) {
ΔelementProperty(0, 'val', 1);
Δselect(2);
ΔelementProperty(2, 'val', 4);
ΔcontainerRefreshStart(1);
{
for (let i = 2; i < 4; i++) {
let rf1 = ΔembeddedViewStart(0, 1, 0);
if (rf1 & RenderFlags.Create) {
Δelement(0, 'parent');
}
if (rf1 & RenderFlags.Update) {
ΔelementProperty(0, 'val', i);
}
ΔembeddedViewEnd();
}
}
ΔcontainerRefreshEnd();
}
}, 3, 0, defs);
const fixture = new ComponentFixture(App);
expect(allEvents).toEqual([
'comp2 init', 'comp2 check', 'parent2 init', 'parent2 check', 'comp3 init', 'comp3 check',
'parent3 init', 'parent3 check', 'comp1 init', 'comp1 check', 'comp4 init', 'comp4 check',
'parent1 init', 'parent1 check', 'parent4 init', 'parent4 check'
]);
});
});
describe('directives', () => {
it('should be called on directives after component', () => {
/** <comp directive></comp> */
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δelement(0, 'comp', ['dir', '']);
}
}, 1, 0, defs);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['comp', 'init', 'check']);
});
it('should be called on directives on an element', () => {
/** <div directive></div> */
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δelement(0, 'div', ['dir', '']);
}
}, 1, 0, defs);
const fixture = new ComponentFixture(App);
expect(events).toEqual(['init', 'check']);
});
});
});
describe('onDestroy', () => {
let events: string[];