From aeb99645bba5da16b490c74220479ebb97905e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemela=CC=88?= Date: Thu, 16 Mar 2017 22:52:20 -0700 Subject: [PATCH] test(animations): test various combinations of animations with host bindings (#15251) PR Close #15251 --- .../animation/animation_integration_spec.ts | 267 ++++++++++++++++-- 1 file changed, 236 insertions(+), 31 deletions(-) diff --git a/packages/core/test/animation/animation_integration_spec.ts b/packages/core/test/animation/animation_integration_spec.ts index b1c320b9df..ed6ef269c2 100644 --- a/packages/core/test/animation/animation_integration_spec.ts +++ b/packages/core/test/animation/animation_integration_spec.ts @@ -162,42 +162,247 @@ export function main() { const cmp2 = TestBed.createComponent(Cmp2); }); + describe('host bindings', () => { + it('should trigger a state change animation from state => state on the component host element', + fakeAsync(() => { + @Component({ + selector: 'my-cmp', + template: '...', + animations: [trigger( + 'myAnimation', + [transition( + 'a => b', + [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])], + }) + class Cmp { + @HostBinding('@myAnimation') + exp = 'a'; + } - it('should trigger a state change animation from void => state on the component host element', - () => { - @Component({ - selector: 'my-cmp', - template: '...', - animations: [trigger( - 'myAnimation', - [transition( - 'a => b', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])], - }) - class Cmp { - @HostBinding('@myAnimation') - get binding() { return this.exp ? 'b' : 'a'; } - exp: any = false; - } + TestBed.configureTestingModule({declarations: [Cmp]}); - TestBed.configureTestingModule({declarations: [Cmp]}); + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(0); - const engine = TestBed.get(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp = false; - fixture.detectChanges(); - engine.flush(); - expect(getLog().length).toEqual(0); + cmp.exp = 'b'; + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(1); - cmp.exp = true; - fixture.detectChanges(); - engine.flush(); - expect(getLog().length).toEqual(1); + const data = getLog().pop(); + expect(data.element).toEqual(fixture.elementRef.nativeElement); + expect(data.keyframes).toEqual([{offset: 0, opacity: '0'}, {offset: 1, opacity: '1'}]); + })); - const data = getLog().pop(); - expect(data.element).toEqual(fixture.elementRef.nativeElement); - expect(data.keyframes).toEqual([{offset: 0, opacity: '0'}, {offset: 1, opacity: '1'}]); - }); + // nonAnimationRenderer => animationRenderer + it('should trigger a leave animation when the inner components host binding updates', + fakeAsync(() => { + @Component({ + selector: 'parent-cmp', + template: ` + + ` + }) + class ParentCmp { + public exp = true; + } + + @Component({ + selector: 'child-cmp', + template: '...', + animations: [trigger( + 'host', + [transition( + ':leave', [style({opacity: 1}), animate(1000, style({opacity: 0}))])])] + }) + class ChildCmp { + @HostBinding('@host') public hostAnimation = true; + } + + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); + + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(ParentCmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(0); + + cmp.exp = false; + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.children.length).toBe(1); + + engine.flush(); + expect(getLog().length).toEqual(1); + + const [player] = getLog(); + expect(player.keyframes).toEqual([ + {opacity: '1', offset: 0}, + {opacity: '0', offset: 1}, + ]); + + flushMicrotasks(); + expect(fixture.debugElement.nativeElement.children.length).toBe(0); + })); + + // animationRenderer => nonAnimationRenderer + it('should trigger a leave animation when the outer components element binding updates on the host component element', + fakeAsync(() => { + @Component({ + selector: 'parent-cmp', + animations: [trigger( + 'host', + [transition( + ':leave', [style({opacity: 1}), animate(1000, style({opacity: 0}))])])], + template: ` + + ` + }) + class ParentCmp { + public exp = true; + } + + @Component({ + selector: 'child-cmp', + template: '...', + }) + class ChildCmp { + } + + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); + + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(ParentCmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(0); + + cmp.exp = false; + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.children.length).toBe(1); + + engine.flush(); + expect(getLog().length).toEqual(1); + + const [player] = getLog(); + expect(player.keyframes).toEqual([ + {opacity: '1', offset: 0}, + {opacity: '0', offset: 1}, + ]); + + flushMicrotasks(); + expect(fixture.debugElement.nativeElement.children.length).toBe(0); + })); + + // animationRenderer => animationRenderer + it('should trigger a leave animation when both the inner and outer components trigger on the same element', + fakeAsync(() => { + @Component({ + selector: 'parent-cmp', + animations: [trigger( + 'host', + [transition( + ':leave', + [style({height: '100px'}), animate(1000, style({height: '0px'}))])])], + template: ` + + ` + }) + class ParentCmp { + public exp = true; + } + + @Component({ + selector: 'child-cmp', + template: '...', + animations: [trigger( + 'host', [transition( + ':leave', + [style({width: '100px'}), animate(1000, style({width: '0px'}))])])] + }) + class ChildCmp { + @HostBinding('@host') public hostAnimation = true; + } + + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); + + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(ParentCmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(0); + + cmp.exp = false; + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.children.length).toBe(1); + + engine.flush(); + expect(getLog().length).toEqual(2); + + const [p1, p2] = getLog(); + expect(p1.keyframes).toEqual([ + {height: '100px', offset: 0}, + {height: '0px', offset: 1}, + ]); + + expect(p2.keyframes).toEqual([ + {width: '100px', offset: 0}, + {width: '0px', offset: 1}, + ]); + + flushMicrotasks(); + expect(fixture.debugElement.nativeElement.children.length).toBe(0); + })); + + it('should not throw when the host element is removed and no animation triggers', + fakeAsync(() => { + @Component({ + selector: 'parent-cmp', + template: ` + + ` + }) + class ParentCmp { + public exp = true; + } + + @Component({ + selector: 'child-cmp', + template: '...', + animations: [trigger('host', [transition('a => b', [style({height: '100px'})])])], + }) + class ChildCmp { + @HostBinding('@host') public hostAnimation = 'a'; + } + + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); + + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(ParentCmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.children.length).toBe(1); + + engine.flush(); + expect(getLog().length).toEqual(0); + + cmp.exp = false; + fixture.detectChanges(); + engine.flush(); + flushMicrotasks(); + expect(getLog().length).toEqual(0); + expect(fixture.debugElement.nativeElement.children.length).toBe(0); + + flushMicrotasks(); + expect(fixture.debugElement.nativeElement.children.length).toBe(0); + })); + }); it('should cancel and merge in mid-animation styles into the follow-up animation', () => { @Component({