diff --git a/packages/core/test/acceptance/lifecycle_spec.ts b/packages/core/test/acceptance/lifecycle_spec.ts index a99d80120a..ba58110df6 100644 --- a/packages/core/test/acceptance/lifecycle_spec.ts +++ b/packages/core/test/acceptance/lifecycle_spec.ts @@ -10,6 +10,7 @@ import {CommonModule} from '@angular/common'; import {Component, ComponentFactoryResolver, Directive, Input, NgModule, OnChanges, SimpleChanges, ViewChild, ViewContainerRef} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; +import {onlyInIvy} from '@angular/private/testing'; describe('ngOnChanges', () => { it('should correctly support updating one Input among many', () => { @@ -1656,7 +1657,7 @@ describe('afterViewInit', () => { }); }); -describe('ngAfterViewChecked', () => { +describe('afterViewChecked', () => { it('should call ngAfterViewChecked every update', () => { let afterViewCheckedCalls = 0; @@ -1887,3 +1888,566 @@ describe('ngAfterViewChecked', () => { }); }); + +describe('onDestroy', () => { + + + it('should call destroy when view is removed', () => { + let destroyCalled = 0; + + @Component({ + selector: 'comp', + template: `

test

`, + }) + class Comp { + ngOnDestroy() { destroyCalled++; } + } + + @Component({ + template: ``, + }) + class App { + show = true; + } + + TestBed.configureTestingModule({ + declarations: [App, Comp], + imports: [CommonModule], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(destroyCalled).toBe(0); + + fixture.componentInstance.show = false; + fixture.detectChanges(); + + expect(destroyCalled).toBe(1); + + fixture.componentInstance.show = true; + fixture.detectChanges(); + + expect(destroyCalled).toBe(1); + + fixture.componentInstance.show = false; + fixture.detectChanges(); + + expect(destroyCalled).toBe(2); + }); + + it('should call destroy when multiple views are removed', () => { + const events: string[] = []; + + @Component({ + selector: 'comp', + template: `

test

`, + }) + class Comp { + @Input() + name = ''; + + ngOnDestroy() { events.push('comp ' + this.name); } + } + + @Component({ + template: ` +
+ + +
+ ` + }) + class App { + show = true; + } + + TestBed.configureTestingModule({ + declarations: [App, Comp], + imports: [CommonModule], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(events).toEqual([]); + + fixture.componentInstance.show = false; + fixture.detectChanges(); + + expect(events).toEqual(['comp 1', 'comp 2']); + }); + + it('should be called in child components before parent components', () => { + const events: string[] = []; + + @Component({ + selector: 'child', + template: `

test

`, + }) + class Child { + @Input() + name = ''; + + ngOnDestroy() { events.push('child of parent ' + this.name); } + } + + @Component({ + selector: 'parent', + template: ``, + }) + class Parent { + @Input() + name = ''; + ngOnDestroy() { events.push('parent ' + this.name); } + } + + @Component({ + template: ` +
+ + +
+ ` + }) + class App { + show = true; + } + + TestBed.configureTestingModule({ + declarations: [App, Parent, Child], + imports: [CommonModule], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(events).toEqual([]); + + fixture.componentInstance.show = false; + fixture.detectChanges(); + + expect(events).toEqual([ + 'child of parent 1', + 'child of parent 2', + 'parent 1', + 'parent 2', + ]); + }); + + it('should be called bottom up with children nested 2 levels deep', () => { + const events: string[] = []; + + @Component({ + selector: 'child', + template: `

test

`, + }) + class Child { + @Input() + name = ''; + + ngOnDestroy() { events.push('child ' + this.name); } + } + + @Component({ + selector: 'parent', + template: ``, + }) + class Parent { + @Input() + name = ''; + ngOnDestroy() { events.push('parent ' + this.name); } + } + + @Component({ + selector: 'grandparent', + template: ``, + }) + class Grandparent { + @Input() + name = ''; + + ngOnDestroy() { events.push('grandparent ' + this.name); } + } + @Component({ + template: ` +
+ + +
+ ` + }) + class App { + show = true; + } + + TestBed.configureTestingModule({ + declarations: [App, Grandparent, Parent, Child], + imports: [CommonModule], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(events).toEqual([]); + + fixture.componentInstance.show = false; + fixture.detectChanges(); + + expect(events).toEqual([ + 'child 1', + 'parent 1', + 'child 2', + 'parent 2', + 'grandparent 1', + 'grandparent 2', + ]); + }); + + it('should be called in projected components before their hosts', () => { + const events: string[] = []; + + @Component({ + selector: 'projected', + template: `

test

`, + }) + class Projected { + @Input() + name = ''; + + ngOnDestroy() { events.push('projected ' + this.name); } + } + + @Component({ + selector: 'comp', + template: `
`, + }) + class Comp { + @Input() + name = ''; + + ngOnDestroy() { events.push('comp ' + this.name); } + } + + @Component({ + template: ` +
+ + + + + + +
+ ` + }) + class App { + show = true; + } + + TestBed.configureTestingModule({ + declarations: [App, Comp, Projected], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(events).toEqual([]); + + fixture.componentInstance.show = false; + fixture.detectChanges(); + + expect(events).toEqual([ + 'projected 1', + 'comp 1', + 'projected 2', + 'comp 2', + ]); + }); + + + it('should be called in consistent order if views are removed and re-added', () => { + const events: string[] = []; + + @Component({ + selector: 'comp', + template: `

test

`, + }) + class Comp { + @Input() + name = ''; + + ngOnDestroy() { events.push('comp ' + this.name); } + } + + @Component({ + template: ` +
+ + + +
+ ` + }) + class App { + showAll = true; + showMiddle = true; + } + + TestBed.configureTestingModule({ + declarations: [App, Comp], + imports: [CommonModule], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(events).toEqual([]); + + fixture.componentInstance.showMiddle = false; + fixture.detectChanges(); + expect(events).toEqual(['comp 2']); + + fixture.componentInstance.showAll = false; + fixture.detectChanges(); + expect(events).toEqual([ + 'comp 2', + 'comp 1', + 'comp 3', + ]); + + fixture.componentInstance.showAll = true; + fixture.componentInstance.showMiddle = true; + fixture.detectChanges(); + expect(events).toEqual([ + 'comp 2', + 'comp 1', + 'comp 3', + ]); + + fixture.componentInstance.showAll = false; + fixture.detectChanges(); + expect(events).toEqual([ + 'comp 2', + 'comp 1', + 'comp 3', + 'comp 2', + 'comp 1', + 'comp 3', + ]); + }); + + it('should be called on every iteration of a destroyed for loop', () => { + const events: string[] = []; + + @Component({ + selector: 'comp', + template: `

test

`, + }) + class Comp { + @Input() + name = ''; + + ngOnDestroy() { events.push('comp ' + this.name); } + } + + @Component({ + template: ` +
+ +
+ ` + }) + class App { + show = true; + numbers = [0, 1, 2, 3]; + } + + TestBed.configureTestingModule({ + declarations: [App, Comp], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(events).toEqual([]); + + fixture.componentInstance.show = false; + fixture.detectChanges(); + + expect(events).toEqual([ + 'comp 0', + 'comp 1', + 'comp 2', + 'comp 3', + ]); + + fixture.componentInstance.show = true; + fixture.detectChanges(); + + expect(events).toEqual([ + 'comp 0', + 'comp 1', + 'comp 2', + 'comp 3', + ]); + + fixture.componentInstance.numbers.splice(1, 1); + fixture.detectChanges(); + expect(events).toEqual([ + 'comp 0', + 'comp 1', + 'comp 2', + 'comp 3', + 'comp 1', + ]); + + fixture.componentInstance.show = false; + fixture.detectChanges(); + expect(events).toEqual([ + 'comp 0', + 'comp 1', + 'comp 2', + 'comp 3', + 'comp 1', + 'comp 0', + 'comp 2', + 'comp 3', + ]); + }); + + it('should call destroy properly if view also has listeners', () => { + const events: string[] = []; + + @Component({ + selector: 'comp', + template: `

test

`, + }) + class Comp { + ngOnDestroy() { events.push('comp'); } + } + @Component({ + template: ` +
+ + + +
+ ` + }) + class App { + show = true; + + clicksToButton1 = 0; + + clicksToButton2 = 0; + + handleClick1() { this.clicksToButton1++; } + + handleClick2() { this.clicksToButton2++; } + } + + TestBed.configureTestingModule({ + declarations: [App, Comp], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + const buttons = fixture.debugElement.queryAll(By.css('button')); + buttons.forEach(button => button.nativeElement.click()); + + expect(fixture.componentInstance.clicksToButton1).toBe(1); + expect(fixture.componentInstance.clicksToButton2).toBe(1); + expect(events).toEqual([]); + + fixture.componentInstance.show = false; + fixture.detectChanges(); + + buttons.forEach(button => button.nativeElement.click()); + expect(fixture.componentInstance.clicksToButton1).toBe(1); + expect(fixture.componentInstance.clicksToButton2).toBe(1); + + expect(events).toEqual(['comp']); + }); + + onlyInIvy( + 'View Engine has the opposite behavior, where it calls destroy on the directives first, then the components') + .it('should be called on directives after component', () => { + const events: string[] = []; + + @Directive({ + selector: '[dir]', + }) + class Dir { + @Input('dir') + name = ''; + + ngOnDestroy() { events.push('dir ' + this.name); } + } + + @Component({ + selector: 'comp', + template: `

test

`, + }) + class Comp { + @Input() + name = ''; + + ngOnDestroy() { events.push('comp ' + this.name); } + } + + @Component({ + template: ` +
+ + +
+ ` + }) + class App { + show = true; + } + + TestBed.configureTestingModule({ + declarations: [App, Dir, Comp], + imports: [CommonModule], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(events).toEqual([]); + + fixture.componentInstance.show = false; + fixture.detectChanges(); + + expect(events).toEqual([ + 'comp 1', + 'dir 1', + 'comp 2', + 'dir 2', + ]); + }); + + it('should be called on directives on an element', () => { + const events: string[] = []; + + @Directive({ + selector: '[dir]', + }) + class Dir { + ngOnDestroy() { events.push('dir'); } + } + + @Component({template: `

`}) + class App { + show = true; + } + + TestBed.configureTestingModule({ + declarations: [App, Dir], + imports: [CommonModule], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(events).toEqual([]); + + fixture.componentInstance.show = false; + fixture.detectChanges(); + + expect(events).toEqual(['dir']); + }); +}); diff --git a/packages/core/test/render3/lifecycle_spec.ts b/packages/core/test/render3/lifecycle_spec.ts index 0989f3db3a..858dc5a269 100644 --- a/packages/core/test/render3/lifecycle_spec.ts +++ b/packages/core/test/render3/lifecycle_spec.ts @@ -116,573 +116,6 @@ describe('lifecycles', () => { }); }); - describe('onDestroy', () => { - let events: string[]; - - beforeEach(() => { events = []; }); - - let Comp = createOnDestroyComponent('comp', (rf: RenderFlags, ctx: any) => { - if (rf & RenderFlags.Create) { - ΔprojectionDef(); - Δprojection(0); - } - }, 1); - let Parent = createOnDestroyComponent('parent', getParentTemplate('comp'), 1, 1, [Comp]); - - function createOnDestroyComponent( - name: string, template: ComponentTemplate, consts: number = 0, vars: number = 0, - directives: any[] = []) { - return class Component { - val: string = ''; - ngOnDestroy() { events.push(`${name}${this.val}`); } - - static ngComponentDef = ΔdefineComponent({ - type: Component, - selectors: [[name]], - factory: () => new Component(), - consts: consts, - vars: vars, - inputs: {val: 'val'}, - template: template, - directives: directives - }); - }; - } - - let Grandparent = createOnDestroyComponent('grandparent', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δelement(0, 'parent'); - } - }, 1, 0, [Parent]); - - const ProjectedComp = createOnDestroyComponent('projected', (rf: RenderFlags, ctx: any) => {}); - - class Directive { - ngOnDestroy() { events.push('dir'); } - - static ngDirectiveDef = ΔdefineDirective( - {type: Directive, selectors: [['', 'dir', '']], factory: () => new Directive()}); - } - - const defs = [Comp, Parent, Grandparent, ProjectedComp, Directive]; - - it('should call destroy when view is removed', () => { - /** - * % if (!skip) { - * - * % } - */ - - 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); - - fixture.component.skip = true; - fixture.update(); - expect(events).toEqual(['comp']); - }); - - it('should call destroy when multiple views are removed', () => { - /** - * % if (!skip) { - * - * - * % } - */ - 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, 2); - if (rf1 & RenderFlags.Create) { - Δelement(0, 'comp'); - Δelement(1, 'comp'); - } - if (rf1 & RenderFlags.Update) { - ΔelementProperty(0, 'val', Δbind('1')); - Δselect(1); - ΔelementProperty(1, 'val', Δbind('2')); - } - ΔembeddedViewEnd(); - } - } - ΔcontainerRefreshEnd(); - } - }, 1, 0, defs); - - const fixture = new ComponentFixture(App); - - fixture.component.skip = true; - fixture.update(); - expect(events).toEqual(['comp1', 'comp2']); - }); - - it('should be called in child components before parent components', () => { - /** - * % if (!skip) { - * - * % } - * - * parent template: - */ - - 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, 'parent'); - } - ΔembeddedViewEnd(); - } - } - ΔcontainerRefreshEnd(); - } - }, 1, 0, defs); - - const fixture = new ComponentFixture(App); - - fixture.component.skip = true; - fixture.update(); - expect(events).toEqual(['comp', 'parent']); - }); - - it('should be called bottom up with children nested 2 levels deep', () => { - /** - * % if (!skip) { - * - * % } - * - * grandparent template: - * parent template: - */ - 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, 'grandparent'); - } - ΔembeddedViewEnd(); - } - } - ΔcontainerRefreshEnd(); - } - }, 1, 0, defs); - - const fixture = new ComponentFixture(App); - - fixture.component.skip = true; - fixture.update(); - expect(events).toEqual(['comp', 'parent', 'grandparent']); - }); - - it('should be called in projected components before their hosts', () => { - /** - * % if (!skip) { - * - * - * - * - * - * - * } - */ - 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, 4, 0); - if (rf1 & RenderFlags.Create) { - ΔelementStart(0, 'comp'); - { Δelement(1, 'projected'); } - ΔelementEnd(); - ΔelementStart(2, 'comp'); - { Δelement(3, 'projected'); } - ΔelementEnd(); - } - if (rf1 & RenderFlags.Update) { - ΔelementProperty(0, 'val', 1); - Δselect(1); - ΔelementProperty(1, 'val', 1); - Δselect(2); - ΔelementProperty(2, 'val', 2); - Δselect(3); - ΔelementProperty(3, 'val', 2); - } - ΔembeddedViewEnd(); - } - } - ΔcontainerRefreshEnd(); - } - }, 1, 0, defs); - - const fixture = new ComponentFixture(App); - - fixture.component.skip = true; - fixture.update(); - expect(events).toEqual(['projected1', 'comp1', 'projected2', 'comp2']); - }); - - - it('should be called in consistent order if views are removed and re-added', () => { - /** - * % if (condition) { - * - * % if (condition2) { - * - * % } - * - * % } - */ - - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δcontainer(0); - } - if (rf & RenderFlags.Update) { - ΔcontainerRefreshStart(0); - { - if (ctx.condition) { - let rf1 = ΔembeddedViewStart(0, 3, 2); - if (rf1 & RenderFlags.Create) { - Δelement(0, 'comp'); - Δcontainer(1); - Δelement(2, 'comp'); - } - if (rf1 & RenderFlags.Update) { - ΔelementProperty(0, 'val', Δbind('1')); - Δselect(2); - ΔelementProperty(2, 'val', Δbind('3')); - ΔcontainerRefreshStart(1); - { - if (ctx.condition2) { - let rf2 = ΔembeddedViewStart(0, 1, 1); - if (rf2 & RenderFlags.Create) { - Δelement(0, 'comp'); - } - if (rf2 & RenderFlags.Update) { - ΔelementProperty(0, 'val', Δbind('2')); - } - ΔembeddedViewEnd(); - } - } - ΔcontainerRefreshEnd(); - } - ΔembeddedViewEnd(); - } - } - ΔcontainerRefreshEnd(); - } - }, 1, 0, defs); - - const fixture = new ComponentFixture(App); - fixture.component.condition = true; - fixture.component.condition2 = true; - fixture.update(); - - // remove all views - fixture.component.condition = false; - fixture.update(); - - /** - * Current angular will process in this same order (root is the top-level removed view): - * - * root.child (comp1 view) onDestroy: null - * root.child.next (container) -> embeddedView - * embeddedView.child (comp2 view) onDestroy: null - * embeddedView onDestroy: [comp2] - * root.child.next.next (comp3 view) onDestroy: null - * root onDestroy: [comp1, comp3] - */ - expect(events).toEqual(['comp2', 'comp1', 'comp3']); - - events = []; - // remove inner view - fixture.component.condition = true; - fixture.component.condition2 = false; - fixture.update(); - - // remove outer view - fixture.component.condition = false; - fixture.update(); - expect(events).toEqual(['comp1', 'comp3']); - - events = []; - // restore both views - fixture.component.condition = true; - fixture.component.condition2 = true; - fixture.update(); - - // remove both views - fixture.component.condition = false; - fixture.update(); - expect(events).toEqual(['comp2', 'comp1', 'comp3']); - }); - - it('should be called in every iteration of a destroyed for loop', () => { - /** - * % if (condition) { - * - * % for (let i = 2; i < len; i++) { - * - * % } - * - * % } - */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δcontainer(0); - } - if (rf & RenderFlags.Update) { - ΔcontainerRefreshStart(0); - { - if (ctx.condition) { - let rf1 = ΔembeddedViewStart(0, 3, 2); - if (rf1 & RenderFlags.Create) { - Δelement(0, 'comp'); - Δcontainer(1); - Δelement(2, 'comp'); - } - if (rf1 & RenderFlags.Update) { - ΔelementProperty(0, 'val', Δbind('1')); - Δselect(2); - ΔelementProperty(2, 'val', Δbind('5')); - ΔcontainerRefreshStart(1); - { - for (let j = 2; j < ctx.len; j++) { - let rf2 = ΔembeddedViewStart(0, 1, 1); - if (rf2 & RenderFlags.Create) { - Δelement(0, 'comp'); - } - if (rf2 & RenderFlags.Update) { - ΔelementProperty(0, 'val', Δbind(j)); - } - ΔembeddedViewEnd(); - } - } - ΔcontainerRefreshEnd(); - } - ΔembeddedViewEnd(); - } - } - ΔcontainerRefreshEnd(); - } - }, 1, 0, defs); - - const fixture = new ComponentFixture(App); - fixture.component.condition = true; - fixture.component.len = 5; - fixture.update(); - - fixture.component.condition = false; - fixture.update(); - - /** - * Current angular will process in this same order (root is the top-level removed view): - * - * root.child (comp1 view) onDestroy: null - * root.child.next (container) -> embeddedView (children[0].data) - * embeddedView.child (comp2 view) onDestroy: null - * embeddedView onDestroy: [comp2] - * embeddedView.next.child (comp3 view) onDestroy: null - * embeddedView.next onDestroy: [comp3] - * embeddedView.next.next.child (comp4 view) onDestroy: null - * embeddedView.next.next onDestroy: [comp4] - * embeddedView.next.next -> container -> root - * root onDestroy: [comp1, comp5] - */ - expect(events).toEqual(['comp2', 'comp3', 'comp4', 'comp1', 'comp5']); - - events = []; - fixture.component.condition = true; - fixture.component.len = 4; - fixture.update(); - - fixture.component.condition = false; - fixture.update(); - expect(events).toEqual(['comp2', 'comp3', 'comp1', 'comp5']); - - events = []; - fixture.component.condition = true; - fixture.component.len = 5; - fixture.update(); - - fixture.component.condition = false; - fixture.update(); - expect(events).toEqual(['comp2', 'comp3', 'comp4', 'comp1', 'comp5']); - }); - - it('should call destroy properly if view also has listeners', () => { - /** - * % if (condition) { - * - * - * - * % } - */ - function Template(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δcontainer(0); - } - if (rf & RenderFlags.Update) { - ΔcontainerRefreshStart(0); - { - if (ctx.condition) { - let rf1 = ΔembeddedViewStart(0, 5, 0); - if (rf1 & RenderFlags.Create) { - ΔelementStart(0, 'button'); - { - Δlistener('click', ctx.onClick.bind(ctx)); - Δtext(1, 'Click me'); - } - ΔelementEnd(); - Δelement(2, 'comp'); - ΔelementStart(3, 'button'); - { - Δlistener('click', ctx.onClick.bind(ctx)); - Δtext(4, 'Click me'); - } - ΔelementEnd(); - } - ΔembeddedViewEnd(); - } - } - ΔcontainerRefreshEnd(); - } - } - - class App { - counter = 0; - condition = true; - onClick() { this.counter++; } - } - - const ctx: {counter: number} = new App(); - renderToHtml(Template, ctx, 1, 0, defs); - - const buttons = containerEl.querySelectorAll('button') !; - buttons[0].click(); - expect(ctx.counter).toEqual(1); - buttons[1].click(); - expect(ctx.counter).toEqual(2); - - renderToHtml(Template, {condition: false}, 1, 0, defs); - - buttons[0].click(); - buttons[1].click(); - expect(events).toEqual(['comp']); - expect(ctx.counter).toEqual(2); - }); - - it('should be called on directives after component', () => { - /** - * % if (condition) { - * - * % } - */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δcontainer(0); - } - if (rf & RenderFlags.Update) { - ΔcontainerRefreshStart(0); - { - if (ctx.condition) { - let rf1 = ΔembeddedViewStart(0, 1, 0); - if (rf1 & RenderFlags.Create) { - Δelement(0, 'comp', ['dir', '']); - } - ΔembeddedViewEnd(); - } - } - ΔcontainerRefreshEnd(); - } - }, 1, 0, defs); - - const fixture = new ComponentFixture(App); - fixture.component.condition = true; - fixture.update(); - expect(events).toEqual([]); - - fixture.component.condition = false; - fixture.update(); - expect(events).toEqual(['comp', 'dir']); - - }); - - it('should be called on directives on an element', () => { - /** - * % if (condition) { - *
- * % } - */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δcontainer(0); - } - if (rf & RenderFlags.Update) { - ΔcontainerRefreshStart(0); - { - if (ctx.condition) { - let rf1 = ΔembeddedViewStart(0, 1, 0); - if (rf1 & RenderFlags.Create) { - Δelement(0, 'div', ['dir', '']); - } - ΔembeddedViewEnd(); - } - } - ΔcontainerRefreshEnd(); - } - }, 1, 0, defs); - - const fixture = new ComponentFixture(App); - fixture.component.condition = true; - fixture.update(); - expect(events).toEqual([]); - - fixture.component.condition = false; - fixture.update(); - expect(events).toEqual(['dir']); - }); - - }); - describe('onChanges', () => { let events: ({type: string, name: string, [key: string]: any})[];