From 1f1e77b641e602a1dd3abd5464163e3b82b1683a Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Fri, 21 Dec 2018 11:53:18 -0800 Subject: [PATCH] =?UTF-8?q?fix(ivy):=20don=E2=80=99t=20publish=20animation?= =?UTF-8?q?=20bindings=20as=20attributes=20(#27805)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of the animation tests have been failing because animation gets triggered multiple times. The reason for this is that the compiler was generating static attribute bindings in addition to dynamic bindings. This created multiple writes to the animation render which failed the tests. PR Close #27805 --- .../test/compliance/r3_view_compiler_spec.ts | 66 ++ .../r3_view_compiler_styling_spec.ts | 3 +- .../compiler/src/render3/view/template.ts | 39 +- packages/core/src/render3/instructions.ts | 5 +- .../animation/animation_integration_spec.ts | 960 +++++++++--------- .../animation_query_integration_spec.ts | 232 +++-- .../animation_router_integration_spec.ts | 792 +++++++-------- ...ns_with_web_animations_integration_spec.ts | 87 +- .../test/animation_renderer_spec.ts | 47 +- 9 files changed, 1151 insertions(+), 1080 deletions(-) diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_spec.ts index 013d28e5b9..7c973a01cc 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_spec.ts @@ -120,4 +120,70 @@ describe('r3_view_compiler', () => { expectEmit(result.source, bV_call, 'Incorrect bV call'); }); }); + + describe('animations', () => { + it('should keep @attr but suppress [@attr]', () => { + const files: MockDirectory = { + app: { + 'example.ts': ` + import {Component, NgModule} from '@angular/core'; + + @Component({ + selector: 'my-app', + template: '
' + }) + export class MyApp { + } + + @NgModule({declarations: [MyApp]}) + export class MyModule {}` + } + }; + + const template = ` + const _c0 = ["@attrOnly", ""]; + // ... + template: function MyApp_Template(rf, ctx) { + if (rf & 1) { + $i0$.ɵelement(0, "div", _c0); + // ... + } + // ... + }`; + const result = compile(files, angularFiles); + expectEmit(result.source, template, 'Incorrect initialization attributes'); + }); + + it('should dedup multiple [@event] listeners', () => { + const files: MockDirectory = { + app: { + 'example.ts': ` + import {Component, NgModule} from '@angular/core'; + + @Component({ + selector: 'my-app', + template: '
' + }) + export class MyApp { + } + + @NgModule({declarations: [MyApp]}) + export class MyModule {}` + } + }; + + const template = ` + const _c0 = [1, "mySelector"]; + // ... + template: function MyApp_Template(rf, ctx) { + if (rf & 1) { + $i0$.ɵelementStart(0, "div", _c0); + // ... + } + // ... + }`; + const result = compile(files, angularFiles); + expectEmit(result.source, template, 'Incorrect initialization attributes'); + }); + }); }); diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts index 3827ff7294..9a99dc7f98 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts @@ -214,7 +214,6 @@ describe('compiler compliance: styling', () => { }; const template = ` - const $e0_attrs$ = ["@foo", ""]; const $e1_attrs$ = ["@bar", ""]; const $e2_attrs$ = ["@baz", ""]; … @@ -224,7 +223,7 @@ describe('compiler compliance: styling', () => { vars: 1, template: function MyComponent_Template(rf, $ctx$) { if (rf & 1) { - $r3$.ɵelement(0, "div", $e0_attrs$); + $r3$.ɵelement(0, "div"); $r3$.ɵelement(1, "div", $e1_attrs$); $r3$.ɵelement(2, "div", $e2_attrs$); } diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts index 188c0e4712..293b32571c 100644 --- a/packages/compiler/src/render3/view/template.ts +++ b/packages/compiler/src/render3/view/template.ts @@ -10,7 +10,7 @@ import {flatten, sanitizeIdentifier} from '../../compile_metadata'; import {BindingForm, BuiltinFunctionCall, LocalResolver, convertActionBinding, convertPropertyBinding} from '../../compiler_util/expression_converter'; import {ConstantPool} from '../../constant_pool'; import * as core from '../../core'; -import {AST, AstMemoryEfficientTransformer, BindingPipe, BindingType, FunctionCall, ImplicitReceiver, Interpolation, LiteralArray, LiteralMap, LiteralPrimitive, ParsedEventType, PropertyRead} from '../../expression_parser/ast'; +import {AST, ASTWithSource, AstMemoryEfficientTransformer, BindingPipe, BindingType, FunctionCall, ImplicitReceiver, Interpolation, LiteralArray, LiteralMap, LiteralPrimitive, ParsedEventType, PropertyRead} from '../../expression_parser/ast'; import {Lexer} from '../../expression_parser/lexer'; import {Parser} from '../../expression_parser/parser'; import * as i18n from '../../i18n/i18n_ast'; @@ -967,6 +967,29 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver styles?: StylingBuilder): o.Expression[] { const attrExprs: o.Expression[] = []; const nonSyntheticInputs: t.BoundAttribute[] = []; + const alreadySeen = new Set(); + + function isASTWithSource(ast: AST): ast is ASTWithSource { + return ast instanceof ASTWithSource; + } + + function isLiteralPrimitive(ast: AST): ast is LiteralPrimitive { + return ast instanceof LiteralPrimitive; + } + + function addAttrExpr(key: string | number, value?: o.Expression): void { + if (typeof key === 'string') { + if (!alreadySeen.has(key)) { + attrExprs.push(o.literal(key)); + if (value !== undefined) { + attrExprs.push(value); + } + alreadySeen.add(key); + } + } else { + attrExprs.push(o.literal(key)); + } + } if (inputs.length) { const EMPTY_STRING_EXPR = asLiteral(''); @@ -976,7 +999,13 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver // may be supported differently in future versions of angular. However, // @triggers should always just be treated as regular attributes (it's up // to the renderer to detect and use them in a special way). - attrExprs.push(asLiteral(prepareSyntheticAttributeName(input.name)), EMPTY_STRING_EXPR); + const valueExp = input.value; + if (isASTWithSource(valueExp)) { + const literal = valueExp.ast; + if (isLiteralPrimitive(literal) && literal.value === undefined) { + addAttrExpr(prepareSyntheticAttributeName(input.name), EMPTY_STRING_EXPR); + } + } } else { nonSyntheticInputs.push(input); } @@ -991,9 +1020,9 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver } if (nonSyntheticInputs.length || outputs.length) { - attrExprs.push(o.literal(core.AttributeMarker.SelectOnly)); - nonSyntheticInputs.forEach((i: t.BoundAttribute) => attrExprs.push(asLiteral(i.name))); - outputs.forEach((o: t.BoundEvent) => attrExprs.push(asLiteral(o.name))); + addAttrExpr(core.AttributeMarker.SelectOnly); + nonSyntheticInputs.forEach((i: t.BoundAttribute) => addAttrExpr(i.name)); + outputs.forEach((o: t.BoundEvent) => addAttrExpr(o.name)); } return attrExprs; diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index 6ed84dd888..03ffe747ec 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -394,8 +394,9 @@ function renderComponentOrTemplate( hostView: LView, context: T, templateFn?: ComponentTemplate) { const rendererFactory = hostView[RENDERER_FACTORY]; const oldView = enterView(hostView, hostView[HOST_NODE]); + const normalExecutionPath = !getCheckNoChangesMode(); try { - if (rendererFactory.begin) { + if (normalExecutionPath && rendererFactory.begin) { rendererFactory.begin(); } @@ -414,7 +415,7 @@ function renderComponentOrTemplate( templateFn && templateFn(RenderFlags.Update, context !); refreshDescendantViews(hostView); } finally { - if (rendererFactory.end) { + if (normalExecutionPath && rendererFactory.end) { rendererFactory.end(); } leaveView(oldView); diff --git a/packages/core/test/animation/animation_integration_spec.ts b/packages/core/test/animation/animation_integration_spec.ts index 7c0734fb82..aca4fdda3c 100644 --- a/packages/core/test/animation/animation_integration_spec.ts +++ b/packages/core/test/animation/animation_integration_spec.ts @@ -109,83 +109,81 @@ const DEFAULT_COMPONENT_ID = '1'; expect(cmp.status).toEqual('done'); })); - fixmeIvy('unknown').it( - 'should always run .start callbacks before .done callbacks even for noop animations', - fakeAsync(() => { - @Component({ - selector: 'cmp', - template: ` + it('should always run .start callbacks before .done callbacks even for noop animations', + fakeAsync(() => { + @Component({ + selector: 'cmp', + template: `
`, - animations: [ - trigger( - 'myAnimation', - [ - transition('* => go', []), - ]), - ] - }) - class Cmp { - exp: any = false; - log: string[] = []; - cb(status: string) { this.log.push(status); } - } + animations: [ + trigger( + 'myAnimation', + [ + transition('* => go', []), + ]), + ] + }) + class Cmp { + exp: any = false; + log: string[] = []; + cb(status: string) { this.log.push(status); } + } - TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp = 'go'; - fixture.detectChanges(); - expect(cmp.log).toEqual([]); + TestBed.configureTestingModule({declarations: [Cmp]}); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = 'go'; + fixture.detectChanges(); + expect(cmp.log).toEqual([]); - flushMicrotasks(); - expect(cmp.log).toEqual(['start', 'done']); - })); + flushMicrotasks(); + expect(cmp.log).toEqual(['start', 'done']); + })); - fixmeIvy('unknown').it( - 'should emit the correct totalTime value for a noop-animation', fakeAsync(() => { - @Component({ - selector: 'cmp', - template: ` + it('should emit the correct totalTime value for a noop-animation', fakeAsync(() => { + @Component({ + selector: 'cmp', + template: `
`, - animations: [ - trigger( - 'myAnimation', - [ - transition( - '* => go', - [ - animate('1s', style({opacity: 0})), - ]), - ]), - ] - }) - class Cmp { - exp: any = false; - log: AnimationEvent[] = []; - cb(event: AnimationEvent) { this.log.push(event); } - } + animations: [ + trigger( + 'myAnimation', + [ + transition( + '* => go', + [ + animate('1s', style({opacity: 0})), + ]), + ]), + ] + }) + class Cmp { + exp: any = false; + log: AnimationEvent[] = []; + cb(event: AnimationEvent) { this.log.push(event); } + } - TestBed.configureTestingModule({ - declarations: [Cmp], - providers: [ - {provide: AnimationDriver, useClass: NoopAnimationDriver}, - ], - }); + TestBed.configureTestingModule({ + declarations: [Cmp], + providers: [ + {provide: AnimationDriver, useClass: NoopAnimationDriver}, + ], + }); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp = 'go'; - fixture.detectChanges(); - expect(cmp.log).toEqual([]); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = 'go'; + fixture.detectChanges(); + expect(cmp.log).toEqual([]); - flushMicrotasks(); - expect(cmp.log.length).toEqual(2); - const [start, end] = cmp.log; - expect(start.totalTime).toEqual(1000); - expect(end.totalTime).toEqual(1000); - })); + flushMicrotasks(); + expect(cmp.log.length).toEqual(2); + const [start, end] = cmp.log; + expect(start.totalTime).toEqual(1000); + expect(end.totalTime).toEqual(1000); + })); }); describe('component fixture integration', () => { @@ -299,7 +297,7 @@ const DEFAULT_COMPONENT_ID = '1'; }); describe('animation triggers', () => { - fixmeIvy('unknown').it('should trigger a state change animation from void => state', () => { + it('should trigger a state change animation from void => state', () => { @Component({ selector: 'if-cmp', template: ` @@ -329,53 +327,51 @@ const DEFAULT_COMPONENT_ID = '1'; ]); }); - fixmeIvy('unknown').it( - 'should allow a transition to use a function to determine what method to run', () => { - let valueToMatch = ''; - let capturedElement: any; - const transitionFn = (fromState: string, toState: string, element: any) => { - capturedElement = element; - return toState == valueToMatch; - }; + it('should allow a transition to use a function to determine what method to run', () => { + let valueToMatch = ''; + let capturedElement: any; + const transitionFn = (fromState: string, toState: string, element: any) => { + capturedElement = element; + return toState == valueToMatch; + }; - @Component({ - selector: 'if-cmp', - template: '
', - animations: [ - trigger( - 'myAnimation', - [transition( - transitionFn, [style({opacity: 0}), animate(1234, style({opacity: 1}))])]), - ] - }) - class Cmp { - @ViewChild('element') - element: any; - exp: any = ''; - } + @Component({ + selector: 'if-cmp', + template: '
', + animations: [ + trigger('myAnimation', [transition( + transitionFn, + [style({opacity: 0}), animate(1234, style({opacity: 1}))])]), + ] + }) + class Cmp { + @ViewChild('element') + element: any; + exp: any = ''; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - valueToMatch = cmp.exp = 'something'; - fixture.detectChanges(); - const element = cmp.element.nativeElement; + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + valueToMatch = cmp.exp = 'something'; + fixture.detectChanges(); + const element = cmp.element.nativeElement; - let players = getLog(); - expect(players.length).toEqual(1); - let [p1] = players; - expect(p1.totalTime).toEqual(1234); - expect(capturedElement).toEqual(element); - resetLog(); + let players = getLog(); + expect(players.length).toEqual(1); + let [p1] = players; + expect(p1.totalTime).toEqual(1234); + expect(capturedElement).toEqual(element); + resetLog(); - valueToMatch = 'something-else'; - cmp.exp = 'this-wont-match'; - fixture.detectChanges(); + valueToMatch = 'something-else'; + cmp.exp = 'this-wont-match'; + fixture.detectChanges(); - players = getLog(); - expect(players.length).toEqual(0); - }); + players = getLog(); + expect(players.length).toEqual(0); + }); fixmeIvy('unknown').it( 'should allow a transition to use a function to determine what method to run and expose any parameter values', @@ -573,105 +569,102 @@ const DEFAULT_COMPONENT_ID = '1'; expect(cmp.log).toEqual(['myAnimation-start', 'myAnimation-done']); })); - fixmeIvy('unknown').it( - 'should only turn a view removal as into `void` state transition', () => { - @Component({ - selector: 'if-cmp', - template: ` + it('should only turn a view removal as into `void` state transition', () => { + @Component({ + selector: 'if-cmp', + template: `
`, - animations: [trigger( - 'myAnimation', - [ - transition( - 'void <=> *', - [style({width: '0px'}), animate(1000, style({width: '100px'}))]), - transition( - '* => *', - [style({height: '0px'}), animate(1000, style({height: '100px'}))]), - ])] - }) - class Cmp { - exp1: any = false; - exp2: any = false; - } + animations: [trigger( + 'myAnimation', + [ + transition( + 'void <=> *', [style({width: '0px'}), animate(1000, style({width: '100px'}))]), + transition( + '* => *', [style({height: '0px'}), animate(1000, style({height: '100px'}))]), + ])] + }) + class Cmp { + exp1: any = false; + exp2: any = false; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.get(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - function resetState() { - cmp.exp2 = 'something'; - fixture.detectChanges(); - engine.flush(); - resetLog(); - } + function resetState() { + cmp.exp2 = 'something'; + fixture.detectChanges(); + engine.flush(); + resetLog(); + } - cmp.exp1 = true; - cmp.exp2 = null; + cmp.exp1 = true; + cmp.exp2 = null; - fixture.detectChanges(); - engine.flush(); + fixture.detectChanges(); + engine.flush(); - expect(getLog().pop() !.keyframes).toEqual([ - {offset: 0, width: '0px'}, {offset: 1, width: '100px'} - ]); + expect(getLog().pop() !.keyframes).toEqual([ + {offset: 0, width: '0px'}, {offset: 1, width: '100px'} + ]); - resetState(); - cmp.exp2 = false; + resetState(); + cmp.exp2 = false; - fixture.detectChanges(); - engine.flush(); + fixture.detectChanges(); + engine.flush(); - expect(getLog().pop() !.keyframes).toEqual([ - {offset: 0, height: '0px'}, {offset: 1, height: '100px'} - ]); + expect(getLog().pop() !.keyframes).toEqual([ + {offset: 0, height: '0px'}, {offset: 1, height: '100px'} + ]); - resetState(); - cmp.exp2 = 0; + resetState(); + cmp.exp2 = 0; - fixture.detectChanges(); - engine.flush(); + fixture.detectChanges(); + engine.flush(); - expect(getLog().pop() !.keyframes).toEqual([ - {offset: 0, height: '0px'}, {offset: 1, height: '100px'} - ]); + expect(getLog().pop() !.keyframes).toEqual([ + {offset: 0, height: '0px'}, {offset: 1, height: '100px'} + ]); - resetState(); - cmp.exp2 = ''; + resetState(); + cmp.exp2 = ''; - fixture.detectChanges(); - engine.flush(); + fixture.detectChanges(); + engine.flush(); - expect(getLog().pop() !.keyframes).toEqual([ - {offset: 0, height: '0px'}, {offset: 1, height: '100px'} - ]); + expect(getLog().pop() !.keyframes).toEqual([ + {offset: 0, height: '0px'}, {offset: 1, height: '100px'} + ]); - resetState(); - cmp.exp2 = undefined; + resetState(); + cmp.exp2 = undefined; - fixture.detectChanges(); - engine.flush(); + fixture.detectChanges(); + engine.flush(); - expect(getLog().pop() !.keyframes).toEqual([ - {offset: 0, height: '0px'}, {offset: 1, height: '100px'} - ]); + expect(getLog().pop() !.keyframes).toEqual([ + {offset: 0, height: '0px'}, {offset: 1, height: '100px'} + ]); - resetState(); - cmp.exp1 = false; - cmp.exp2 = 'abc'; + resetState(); + cmp.exp1 = false; + cmp.exp2 = 'abc'; - fixture.detectChanges(); - engine.flush(); + fixture.detectChanges(); + engine.flush(); - expect(getLog().pop() !.keyframes).toEqual([ - {offset: 0, width: '0px'}, {offset: 1, width: '100px'} - ]); - }); + expect(getLog().pop() !.keyframes).toEqual([ + {offset: 0, width: '0px'}, {offset: 1, width: '100px'} + ]); + }); - fixmeIvy('unknown').it('should stringify boolean triggers to `1` and `0`', () => { + it('should stringify boolean triggers to `1` and `0`', () => { @Component({ selector: 'if-cmp', template: ` @@ -1577,61 +1570,62 @@ const DEFAULT_COMPONENT_ID = '1'; } }); - it('should animate removals of nodes to the `void` state for each animation trigger, but treat all auto styles as pre styles', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + fixmeIvy('unknown').it( + 'should animate removals of nodes to the `void` state for each animation trigger, but treat all auto styles as pre styles', + () => { + @Component({ + selector: 'ani-cmp', + template: `
`, - animations: [ - trigger( - 'trig1', [transition('state => void', [animate(1000, style({opacity: 0}))])]), - trigger('trig2', [transition(':leave', [animate(1000, style({width: '0px'}))])]) - ] - }) - class Cmp { - public exp = true; - public exp2 = 'state'; - } + animations: [ + trigger( + 'trig1', [transition('state => void', [animate(1000, style({opacity: 0}))])]), + trigger('trig2', [transition(':leave', [animate(1000, style({width: '0px'}))])]) + ] + }) + class Cmp { + public exp = true; + public exp2 = 'state'; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.get(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp = true; - fixture.detectChanges(); - engine.flush(); - resetLog(); + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = true; + fixture.detectChanges(); + engine.flush(); + resetLog(); - const element = getDOM().querySelector(fixture.nativeElement, '.ng-if'); - assertHasParent(element, true); + const element = getDOM().querySelector(fixture.nativeElement, '.ng-if'); + assertHasParent(element, true); - cmp.exp = false; - fixture.detectChanges(); - engine.flush(); + cmp.exp = false; + fixture.detectChanges(); + engine.flush(); - assertHasParent(element, true); + assertHasParent(element, true); - expect(getLog().length).toEqual(2); + expect(getLog().length).toEqual(2); - const player2 = getLog().pop() !; - const player1 = getLog().pop() !; + const player2 = getLog().pop() !; + const player1 = getLog().pop() !; - expect(player2.keyframes).toEqual([ - {width: PRE_STYLE, offset: 0}, - {width: '0px', offset: 1}, - ]); + expect(player2.keyframes).toEqual([ + {width: PRE_STYLE, offset: 0}, + {width: '0px', offset: 1}, + ]); - expect(player1.keyframes).toEqual([ - {opacity: PRE_STYLE, offset: 0}, {opacity: '0', offset: 1} - ]); + expect(player1.keyframes).toEqual([ + {opacity: PRE_STYLE, offset: 0}, {opacity: '0', offset: 1} + ]); - player2.finish(); - player1.finish(); - assertHasParent(element, false); - }); + player2.finish(); + player1.finish(); + assertHasParent(element, false); + }); it('should properly cancel all existing animations when a removal occurs', () => { @Component({ @@ -1977,48 +1971,49 @@ const DEFAULT_COMPONENT_ID = '1'; expect(players[0].duration).toEqual(5678); }); - it('should not render animations when the object expression value is the same as it was previously', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + fixmeIvy('unknown').it( + 'should not render animations when the object expression value is the same as it was previously', + () => { + @Component({ + selector: 'ani-cmp', + template: `
`, - animations: [ - trigger( - 'myAnimation', - [ - transition('* => *', [animate(1234, style({opacity: 0}))]), - ]), - ] - }) - class Cmp { - public exp: any; - public params: any; - } + animations: [ + trigger( + 'myAnimation', + [ + transition('* => *', [animate(1234, style({opacity: 0}))]), + ]), + ] + }) + class Cmp { + public exp: any; + public params: any; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.get(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = '1'; - cmp.params = {}; - fixture.detectChanges(); - engine.flush(); - let players = getLog(); - expect(players.length).toEqual(1); - expect(players[0].duration).toEqual(1234); - resetLog(); + cmp.exp = '1'; + cmp.params = {}; + fixture.detectChanges(); + engine.flush(); + let players = getLog(); + expect(players.length).toEqual(1); + expect(players[0].duration).toEqual(1234); + resetLog(); - cmp.exp = '1'; - cmp.params = {}; - fixture.detectChanges(); - engine.flush(); - players = getLog(); - expect(players.length).toEqual(0); - }); + cmp.exp = '1'; + cmp.params = {}; + fixture.detectChanges(); + engine.flush(); + players = getLog(); + expect(players.length).toEqual(0); + }); fixmeIvy('unknown').it( 'should update the final state styles when params update even if the expression hasn\'t changed', @@ -2459,88 +2454,85 @@ const DEFAULT_COMPONENT_ID = '1'; }); describe('animation listeners', () => { - fixmeIvy('unknown').it( - 'should trigger a `start` state change listener for when the animation changes state from void => state', - fakeAsync(() => { - @Component({ - selector: 'if-cmp', - template: ` + it('should trigger a `start` state change listener for when the animation changes state from void => state', + fakeAsync(() => { + @Component({ + selector: 'if-cmp', + template: `
`, - animations: [trigger( - 'myAnimation', - [transition( - 'void => *', - [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])], - }) - class Cmp { - exp: any = false; - // TODO(issue/24571): remove '!'. - event !: AnimationEvent; + animations: [trigger( + 'myAnimation', + [transition( + 'void => *', + [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])], + }) + class Cmp { + exp: any = false; + // TODO(issue/24571): remove '!'. + event !: AnimationEvent; - callback = (event: any) => { this.event = event; }; - } + callback = (event: any) => { this.event = event; }; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.get(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp = 'true'; - fixture.detectChanges(); - flushMicrotasks(); + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = 'true'; + fixture.detectChanges(); + flushMicrotasks(); - expect(cmp.event.triggerName).toEqual('myAnimation'); - expect(cmp.event.phaseName).toEqual('start'); - expect(cmp.event.totalTime).toEqual(500); - expect(cmp.event.fromState).toEqual('void'); - expect(cmp.event.toState).toEqual('true'); - })); + expect(cmp.event.triggerName).toEqual('myAnimation'); + expect(cmp.event.phaseName).toEqual('start'); + expect(cmp.event.totalTime).toEqual(500); + expect(cmp.event.fromState).toEqual('void'); + expect(cmp.event.toState).toEqual('true'); + })); - fixmeIvy('unknown').it( - 'should trigger a `done` state change listener for when the animation changes state from a => b', - fakeAsync(() => { - @Component({ - selector: 'if-cmp', - template: ` + it('should trigger a `done` state change listener for when the animation changes state from a => b', + fakeAsync(() => { + @Component({ + selector: 'if-cmp', + template: `
`, - animations: [trigger( - 'myAnimation123', - [transition( - '* => b', - [style({'opacity': '0'}), animate(999, style({'opacity': '1'}))])])], - }) - class Cmp { - exp: any = false; - // TODO(issue/24571): remove '!'. - event !: AnimationEvent; + animations: [trigger( + 'myAnimation123', + [transition( + '* => b', [style({'opacity': '0'}), animate(999, style({'opacity': '1'}))])])], + }) + class Cmp { + exp: any = false; + // TODO(issue/24571): remove '!'. + event !: AnimationEvent; - callback = (event: any) => { this.event = event; }; - } + callback = (event: any) => { this.event = event; }; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.get(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = 'b'; - fixture.detectChanges(); - engine.flush(); + cmp.exp = 'b'; + fixture.detectChanges(); + engine.flush(); - expect(cmp.event).toBeFalsy(); + expect(cmp.event).toBeFalsy(); - const player = engine.players.pop(); - player.finish(); - flushMicrotasks(); + const player = engine.players.pop(); + player.finish(); + flushMicrotasks(); - expect(cmp.event.triggerName).toEqual('myAnimation123'); - expect(cmp.event.phaseName).toEqual('done'); - expect(cmp.event.totalTime).toEqual(999); - expect(cmp.event.fromState).toEqual('void'); - expect(cmp.event.toState).toEqual('b'); - })); + expect(cmp.event.triggerName).toEqual('myAnimation123'); + expect(cmp.event.phaseName).toEqual('done'); + expect(cmp.event.totalTime).toEqual(999); + expect(cmp.event.fromState).toEqual('void'); + expect(cmp.event.toState).toEqual('b'); + })); it('should handle callbacks for multiple triggers running simultaneously', fakeAsync(() => { @Component({ @@ -2767,42 +2759,41 @@ const DEFAULT_COMPONENT_ID = '1'; expect(cmp.event.toState).toEqual('TRUE'); })); - fixmeIvy('unknown').it( - 'should always fire callbacks even when a transition is not detected', fakeAsync(() => { - @Component({ - selector: 'my-cmp', - template: ` + it('should always fire callbacks even when a transition is not detected', fakeAsync(() => { + @Component({ + selector: 'my-cmp', + template: `
`, - animations: [trigger('myAnimation', [])] - }) - class Cmp { - // TODO(issue/24571): remove '!'. - exp !: string; - log: any[] = []; - callback = (event: any) => this.log.push(`${event.phaseName} => ${event.toState}`); - } + animations: [trigger('myAnimation', [])] + }) + class Cmp { + // TODO(issue/24571): remove '!'. + exp !: string; + log: any[] = []; + callback = (event: any) => this.log.push(`${event.phaseName} => ${event.toState}`); + } - TestBed.configureTestingModule({ - providers: [{provide: AnimationDriver, useClass: NoopAnimationDriver}], - declarations: [Cmp] - }); + TestBed.configureTestingModule({ + providers: [{provide: AnimationDriver, useClass: NoopAnimationDriver}], + declarations: [Cmp] + }); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = 'a'; - fixture.detectChanges(); - flushMicrotasks(); - expect(cmp.log).toEqual(['start => a', 'done => a']); + cmp.exp = 'a'; + fixture.detectChanges(); + flushMicrotasks(); + expect(cmp.log).toEqual(['start => a', 'done => a']); - cmp.log = []; - cmp.exp = 'b'; - fixture.detectChanges(); - flushMicrotasks(); + cmp.log = []; + cmp.exp = 'b'; + fixture.detectChanges(); + flushMicrotasks(); - expect(cmp.log).toEqual(['start => b', 'done => b']); - })); + expect(cmp.log).toEqual(['start => b', 'done => b']); + })); it('should fire callback events for leave animations even if there is no leave transition', fakeAsync(() => { @@ -2844,11 +2835,10 @@ const DEFAULT_COMPONENT_ID = '1'; expect(cmp.log).toEqual(['start => void', 'done => void']); })); - fixmeIvy('unknown').it( - 'should fire callbacks on a sub animation once it starts and finishes', fakeAsync(() => { - @Component({ - selector: 'my-cmp', - template: ` + it('should fire callbacks on a sub animation once it starts and finishes', fakeAsync(() => { + @Component({ + selector: 'my-cmp', + template: `
`, - animations: [ - trigger( - 'parent', - [ - transition( - '* => go', - [ - style({width: '0px'}), - animate(1000, style({width: '100px'})), - query( - '.child', - [ - animateChild({duration: '1s'}), - ]), - animate(1000, style({width: '0px'})), - ]), - ]), - trigger( - 'child', - [ - transition( - '* => go', - [ - style({height: '0px'}), - animate(1000, style({height: '100px'})), - ]), - ]) - ] - }) - class Cmp { - log: string[] = []; - // TODO(issue/24571): remove '!'. - exp1 !: string; - // TODO(issue/24571): remove '!'. - exp2 !: string; + animations: [ + trigger( + 'parent', + [ + transition( + '* => go', + [ + style({width: '0px'}), + animate(1000, style({width: '100px'})), + query( + '.child', + [ + animateChild({duration: '1s'}), + ]), + animate(1000, style({width: '0px'})), + ]), + ]), + trigger( + 'child', + [ + transition( + '* => go', + [ + style({height: '0px'}), + animate(1000, style({height: '100px'})), + ]), + ]) + ] + }) + class Cmp { + log: string[] = []; + // TODO(issue/24571): remove '!'. + exp1 !: string; + // TODO(issue/24571): remove '!'. + exp2 !: string; - cb(name: string, event: AnimationEvent) { this.log.push(name); } - } + cb(name: string, event: AnimationEvent) { this.log.push(name); } + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.get(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp1 = 'go'; - cmp.exp2 = 'go'; - fixture.detectChanges(); - engine.flush(); - flushMicrotasks(); + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp1 = 'go'; + cmp.exp2 = 'go'; + fixture.detectChanges(); + engine.flush(); + flushMicrotasks(); - expect(cmp.log).toEqual(['parent-start', 'child-start']); - cmp.log = []; + expect(cmp.log).toEqual(['parent-start', 'child-start']); + cmp.log = []; - const players = getLog(); - expect(players.length).toEqual(3); - const [p1, p2, p3] = players; + const players = getLog(); + expect(players.length).toEqual(3); + const [p1, p2, p3] = players; - p1.finish(); - flushMicrotasks(); - expect(cmp.log).toEqual([]); + p1.finish(); + flushMicrotasks(); + expect(cmp.log).toEqual([]); - p2.finish(); - flushMicrotasks(); - expect(cmp.log).toEqual([]); + p2.finish(); + flushMicrotasks(); + expect(cmp.log).toEqual([]); - p3.finish(); - flushMicrotasks(); - expect(cmp.log).toEqual(['parent-done', 'child-done']); - })); + p3.finish(); + flushMicrotasks(); + expect(cmp.log).toEqual(['parent-done', 'child-done']); + })); fixmeIvy('unknown').it( 'should fire callbacks and collect the correct the totalTime and element details for any queried sub animations', @@ -3597,64 +3587,63 @@ const DEFAULT_COMPONENT_ID = '1'; /only state\(\) and transition\(\) definitions can sit inside of a trigger\(\)/); }); - fixmeIvy('unknown').it( - 'should combine multiple errors together into one exception when an animation fails to be built', - () => { - @Component({ - selector: 'if-cmp', - template: ` + it('should combine multiple errors together into one exception when an animation fails to be built', + () => { + @Component({ + selector: 'if-cmp', + template: `
`, - animations: [ - trigger( - 'foo', - [ - transition(':enter', []), - transition( - '* => *', - [ - query('foo', animate(1000, style({background: 'red'}))), - ]), - ]), - trigger( - 'bar', - [ - transition(':enter', []), - transition( - '* => *', - [ - query('bar', animate(1000, style({background: 'blue'}))), - ]), - ]), - ] - }) - class Cmp { - fooExp: any = false; - barExp: any = false; - } + animations: [ + trigger( + 'foo', + [ + transition(':enter', []), + transition( + '* => *', + [ + query('foo', animate(1000, style({background: 'red'}))), + ]), + ]), + trigger( + 'bar', + [ + transition(':enter', []), + transition( + '* => *', + [ + query('bar', animate(1000, style({background: 'blue'}))), + ]), + ]), + ] + }) + class Cmp { + fooExp: any = false; + barExp: 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(); + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); - cmp.fooExp = 'go'; - cmp.barExp = 'go'; + cmp.fooExp = 'go'; + cmp.barExp = 'go'; - let errorMsg: string = ''; - try { - fixture.detectChanges(); - } catch (e) { - errorMsg = e.message; - } + let errorMsg: string = ''; + try { + fixture.detectChanges(); + } catch (e) { + errorMsg = e.message; + } - expect(errorMsg).toMatch(/@foo has failed due to:/); - expect(errorMsg).toMatch(/`query\("foo"\)` returned zero elements/); - expect(errorMsg).toMatch(/@bar has failed due to:/); - expect(errorMsg).toMatch(/`query\("bar"\)` returned zero elements/); - }); + expect(errorMsg).toMatch(/@foo has failed due to:/); + expect(errorMsg).toMatch(/`query\("foo"\)` returned zero elements/); + expect(errorMsg).toMatch(/@bar has failed due to:/); + expect(errorMsg).toMatch(/`query\("bar"\)` returned zero elements/); + }); it('should not throw an error if styles overlap in separate transitions', () => { @Component({ @@ -3741,18 +3730,17 @@ const DEFAULT_COMPONENT_ID = '1'; }); }); - fixmeIvy('unknown').it( - 'should throw when using an @prop binding without the animation module', () => { - @Component({template: `
`}) - class Cmp { - } + it('should throw when using an @prop binding without the animation module', () => { + @Component({template: `
`}) + class Cmp { + } - TestBed.configureTestingModule({declarations: [Cmp]}); - const comp = TestBed.createComponent(Cmp); - expect(() => comp.detectChanges()) - .toThrowError( - 'Found the synthetic property @myAnimation. Please include either "BrowserAnimationsModule" or "NoopAnimationsModule" in your application.'); - }); + TestBed.configureTestingModule({declarations: [Cmp]}); + const comp = TestBed.createComponent(Cmp); + expect(() => comp.detectChanges()) + .toThrowError( + 'Found the synthetic property @myAnimation. Please include either "BrowserAnimationsModule" or "NoopAnimationsModule" in your application.'); + }); it('should throw when using an @prop listener without the animation module', () => { @Component({template: `
`}) diff --git a/packages/core/test/animation/animation_query_integration_spec.ts b/packages/core/test/animation/animation_query_integration_spec.ts index f30cf1d597..5586803080 100644 --- a/packages/core/test/animation/animation_query_integration_spec.ts +++ b/packages/core/test/animation/animation_query_integration_spec.ts @@ -853,12 +853,11 @@ import {HostListener} from '../../src/metadata/directives'; }); }); - fixmeIvy('unknown').it( - 'should cleanup :enter and :leave artifacts from nodes when any animation sequences fail to be built', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should cleanup :enter and :leave artifacts from nodes when any animation sequences fail to be built', + () => { + @Component({ + selector: 'ani-cmp', + template: `
{{ item }} @@ -866,56 +865,56 @@ import {HostListener} from '../../src/metadata/directives';
Leave!
`, - animations: [ - trigger( - 'myAnimation', - [ - transition('* => 0', []), - transition( - '* => *', - [ - query( - '.child:enter', - [ - style({opacity: 0}), - animate(1000, style({opacity: 1})), - ]), - query( - '.incorrect-child:leave', - [ - animate(1000, style({opacity: 0})), - ]), - ]), - ]), - ] - }) - class Cmp { - @ViewChild('container') public container: any; - public items: any[] = []; - } + animations: [ + trigger( + 'myAnimation', + [ + transition('* => 0', []), + transition( + '* => *', + [ + query( + '.child:enter', + [ + style({opacity: 0}), + animate(1000, style({opacity: 1})), + ]), + query( + '.incorrect-child:leave', + [ + animate(1000, style({opacity: 0})), + ]), + ]), + ]), + ] + }) + class Cmp { + @ViewChild('container') public container: any; + public items: any[] = []; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.get(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.items = []; - fixture.detectChanges(); + cmp.items = []; + fixture.detectChanges(); - cmp.items = [0, 1, 2, 3, 4]; + cmp.items = [0, 1, 2, 3, 4]; - expect(() => { fixture.detectChanges(); }).toThrow(); + expect(() => { fixture.detectChanges(); }).toThrow(); - const children = cmp.container.nativeElement.querySelectorAll('.child'); - expect(children.length).toEqual(5); + const children = cmp.container.nativeElement.querySelectorAll('.child'); + expect(children.length).toEqual(5); - for (let i = 0; i < children.length; i++) { - let child = children[i]; - expect(child.classList.contains(ENTER_CLASSNAME)).toBe(false); - expect(child.classList.contains(LEAVE_CLASSNAME)).toBe(false); - } - }); + for (let i = 0; i < children.length; i++) { + let child = children[i]; + expect(child.classList.contains(ENTER_CLASSNAME)).toBe(false); + expect(child.classList.contains(LEAVE_CLASSNAME)).toBe(false); + } + }); it('should find elements that have been removed via :leave', () => { @Component({ @@ -2520,9 +2519,8 @@ import {HostListener} from '../../src/metadata/directives'; expect(p4.element.classList.contains('d')); }); - fixmeIvy('unknown').it( - 'should collect multiple root levels of :enter and :leave nodes', () => { - @Component({ + it('should collect multiple root levels of :enter and :leave nodes', () => { + @Component({ selector: 'ani-cmp', animations: [ trigger('pageAnimation', [ @@ -2557,88 +2555,88 @@ import {HostListener} from '../../src/metadata/directives'; ` }) class Cmp { - get title() { - if (this.page1) { - return 'hello from page1'; - } - return 'greetings from page2'; - } - - page1 = false; - page2 = false; - loading = false; - - get status() { - if (this.loading) return 'loading'; - if (this.page1) return 'page1'; - if (this.page2) return 'page2'; - return ''; - } + get title() { + if (this.page1) { + return 'hello from page1'; } + return 'greetings from page2'; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + page1 = false; + page2 = false; + loading = false; - const engine = TestBed.get(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.loading = true; - fixture.detectChanges(); - engine.flush(); + get status() { + if (this.loading) return 'loading'; + if (this.page1) return 'page1'; + if (this.page2) return 'page2'; + return ''; + } + } - let players = getLog(); - resetLog(); - cancelAllPlayers(players); + TestBed.configureTestingModule({declarations: [Cmp]}); - cmp.page1 = true; - cmp.loading = false; - fixture.detectChanges(); - engine.flush(); + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.loading = true; + fixture.detectChanges(); + engine.flush(); - let p1: MockAnimationPlayer; - let p2: MockAnimationPlayer; - let p3: MockAnimationPlayer; + let players = getLog(); + resetLog(); + cancelAllPlayers(players); - players = getLog(); - expect(players.length).toEqual(3); - [p1, p2, p3] = players; + cmp.page1 = true; + cmp.loading = false; + fixture.detectChanges(); + engine.flush(); - expect(p1.element.classList.contains('loading')).toBe(true); - expect(p2.element.classList.contains('title')).toBe(true); - expect(p3.element.classList.contains('page1')).toBe(true); + let p1: MockAnimationPlayer; + let p2: MockAnimationPlayer; + let p3: MockAnimationPlayer; - resetLog(); - cancelAllPlayers(players); + players = getLog(); + expect(players.length).toEqual(3); + [p1, p2, p3] = players; - cmp.page1 = false; - cmp.loading = true; - fixture.detectChanges(); + expect(p1.element.classList.contains('loading')).toBe(true); + expect(p2.element.classList.contains('title')).toBe(true); + expect(p3.element.classList.contains('page1')).toBe(true); - players = getLog(); - cancelAllPlayers(players); + resetLog(); + cancelAllPlayers(players); - expect(players.length).toEqual(3); - [p1, p2, p3] = players; + cmp.page1 = false; + cmp.loading = true; + fixture.detectChanges(); - expect(p1.element.classList.contains('title')).toBe(true); - expect(p2.element.classList.contains('page1')).toBe(true); - expect(p3.element.classList.contains('loading')).toBe(true); + players = getLog(); + cancelAllPlayers(players); - resetLog(); - cancelAllPlayers(players); + expect(players.length).toEqual(3); + [p1, p2, p3] = players; - cmp.page2 = true; - cmp.loading = false; - fixture.detectChanges(); - engine.flush(); + expect(p1.element.classList.contains('title')).toBe(true); + expect(p2.element.classList.contains('page1')).toBe(true); + expect(p3.element.classList.contains('loading')).toBe(true); - players = getLog(); - expect(players.length).toEqual(3); - [p1, p2, p3] = players; + resetLog(); + cancelAllPlayers(players); - expect(p1.element.classList.contains('loading')).toBe(true); - expect(p2.element.classList.contains('title')).toBe(true); - expect(p3.element.classList.contains('page2')).toBe(true); - }); + cmp.page2 = true; + cmp.loading = false; + fixture.detectChanges(); + engine.flush(); + + players = getLog(); + expect(players.length).toEqual(3); + [p1, p2, p3] = players; + + expect(p1.element.classList.contains('loading')).toBe(true); + expect(p2.element.classList.contains('title')).toBe(true); + expect(p3.element.classList.contains('page2')).toBe(true); + }); it('should emulate leave animation callbacks for all sub elements that have leave triggers within the component', fakeAsync(() => { diff --git a/packages/core/test/animation/animation_router_integration_spec.ts b/packages/core/test/animation/animation_router_integration_spec.ts index 2dbc90591e..e904623ec9 100644 --- a/packages/core/test/animation/animation_router_integration_spec.ts +++ b/packages/core/test/animation/animation_router_integration_spec.ts @@ -34,354 +34,349 @@ import {RouterTestingModule} from '@angular/router/testing'; }); }); - fixmeIvy('unknown').it( - 'should query the old and new routes via :leave and :enter', fakeAsync(() => { - @Component({ - animations: [ - trigger( - 'routerAnimations', - [ - transition( - 'page1 => page2', - [ - query(':leave', animateChild()), - query(':enter', animateChild()), - ]), - ]), - ], - template: ` + it('should query the old and new routes via :leave and :enter', fakeAsync(() => { + @Component({ + animations: [ + trigger( + 'routerAnimations', + [ + transition( + 'page1 => page2', + [ + query(':leave', animateChild()), + query(':enter', animateChild()), + ]), + ]), + ], + template: `
` - }) - class ContainerCmp { - constructor(public router: Router) {} + }) + class ContainerCmp { + constructor(public router: Router) {} - prepareRouteAnimation(r: RouterOutlet) { - const animation = r.activatedRouteData['animation']; - const value = animation ? animation['value'] : null; - return value; - } - } + prepareRouteAnimation(r: RouterOutlet) { + const animation = r.activatedRouteData['animation']; + const value = animation ? animation['value'] : null; + return value; + } + } - @Component({ - selector: 'page1', - template: `page1`, - animations: [ - trigger( - 'page1Animation', - [ - transition( - ':leave', - [ - style({width: '200px'}), - animate(1000, style({width: '0px'})), - ]), - ]), - ] - }) - class Page1Cmp { - @HostBinding('@page1Animation') public doAnimate = true; - } + @Component({ + selector: 'page1', + template: `page1`, + animations: [ + trigger( + 'page1Animation', + [ + transition( + ':leave', + [ + style({width: '200px'}), + animate(1000, style({width: '0px'})), + ]), + ]), + ] + }) + class Page1Cmp { + @HostBinding('@page1Animation') public doAnimate = true; + } - @Component({ - selector: 'page2', - template: `page2`, - animations: [ - trigger( - 'page2Animation', - [ - transition( - ':enter', - [ - style({opacity: 0}), - animate(1000, style({opacity: 1})), - ]), - ]), - ] - }) - class Page2Cmp { - @HostBinding('@page2Animation') public doAnimate = true; - } + @Component({ + selector: 'page2', + template: `page2`, + animations: [ + trigger( + 'page2Animation', + [ + transition( + ':enter', + [ + style({opacity: 0}), + animate(1000, style({opacity: 1})), + ]), + ]), + ] + }) + class Page2Cmp { + @HostBinding('@page2Animation') public doAnimate = true; + } - TestBed.configureTestingModule({ - declarations: [Page1Cmp, Page2Cmp, ContainerCmp], - imports: [RouterTestingModule.withRoutes([ - {path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')}, - {path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')} - ])] - }); + TestBed.configureTestingModule({ + declarations: [Page1Cmp, Page2Cmp, ContainerCmp], + imports: [RouterTestingModule.withRoutes([ + {path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')}, + {path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')} + ])] + }); - const engine = TestBed.get(ɵAnimationEngine); - const fixture = TestBed.createComponent(ContainerCmp); - const cmp = fixture.componentInstance; - cmp.router.initialNavigation(); - tick(); - fixture.detectChanges(); - engine.flush(); + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(ContainerCmp); + const cmp = fixture.componentInstance; + cmp.router.initialNavigation(); + tick(); + fixture.detectChanges(); + engine.flush(); - cmp.router.navigateByUrl('/page1'); - tick(); - fixture.detectChanges(); - engine.flush(); + cmp.router.navigateByUrl('/page1'); + tick(); + fixture.detectChanges(); + engine.flush(); - cmp.router.navigateByUrl('/page2'); - tick(); - fixture.detectChanges(); - engine.flush(); + cmp.router.navigateByUrl('/page2'); + tick(); + fixture.detectChanges(); + engine.flush(); - const player = engine.players[0] !; - const groupPlayer = player.getRealPlayer() as AnimationGroupPlayer; - const players = groupPlayer.players as MockAnimationPlayer[]; + const player = engine.players[0] !; + const groupPlayer = player.getRealPlayer() as AnimationGroupPlayer; + const players = groupPlayer.players as MockAnimationPlayer[]; - expect(players.length).toEqual(2); - const [p1, p2] = players; + expect(players.length).toEqual(2); + const [p1, p2] = players; - expect(p1.duration).toEqual(1000); - expect(p1.keyframes).toEqual([ - {offset: 0, width: '200px'}, - {offset: 1, width: '0px'}, - ]); + expect(p1.duration).toEqual(1000); + expect(p1.keyframes).toEqual([ + {offset: 0, width: '200px'}, + {offset: 1, width: '0px'}, + ]); - expect(p2.duration).toEqual(2000); - expect(p2.keyframes).toEqual([ - {offset: 0, opacity: '0'}, - {offset: .5, opacity: '0'}, - {offset: 1, opacity: '1'}, - ]); - })); + expect(p2.duration).toEqual(2000); + expect(p2.keyframes).toEqual([ + {offset: 0, opacity: '0'}, + {offset: .5, opacity: '0'}, + {offset: 1, opacity: '1'}, + ]); + })); - fixmeIvy('unknown').it( - 'should allow inner enter animations to be emulated within a routed item', fakeAsync(() => { - @Component({ - animations: [ - trigger( - 'routerAnimations', - [ - transition( - 'page1 => page2', - [ - query(':enter', animateChild()), - ]), - ]), - ], - template: ` + it('should allow inner enter animations to be emulated within a routed item', fakeAsync(() => { + @Component({ + animations: [ + trigger( + 'routerAnimations', + [ + transition( + 'page1 => page2', + [ + query(':enter', animateChild()), + ]), + ]), + ], + template: `
` - }) - class ContainerCmp { - constructor(public router: Router) {} + }) + class ContainerCmp { + constructor(public router: Router) {} - prepareRouteAnimation(r: RouterOutlet) { - const animation = r.activatedRouteData['animation']; - const value = animation ? animation['value'] : null; - return value; - } - } + prepareRouteAnimation(r: RouterOutlet) { + const animation = r.activatedRouteData['animation']; + const value = animation ? animation['value'] : null; + return value; + } + } - @Component({selector: 'page1', template: `page1`, animations: []}) - class Page1Cmp { - } + @Component({selector: 'page1', template: `page1`, animations: []}) + class Page1Cmp { + } - @Component({ - selector: 'page2', - template: ` + @Component({ + selector: 'page2', + template: `

Page 2

`, - animations: [ - trigger( - 'page2Animation', - [ - transition( - ':enter', - [query('.if-one', animateChild()), query('.if-two', animateChild())]), - ]), - trigger( - 'ifAnimation', - [transition( - ':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))])]) - ] - }) - class Page2Cmp { - @HostBinding('@page2Animation') public doAnimate = true; + animations: [ + trigger( + 'page2Animation', + [ + transition( + ':enter', + [query('.if-one', animateChild()), query('.if-two', animateChild())]), + ]), + trigger( + 'ifAnimation', + [transition( + ':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))])]) + ] + }) + class Page2Cmp { + @HostBinding('@page2Animation') public doAnimate = true; - public exp = true; - } + public exp = true; + } - TestBed.configureTestingModule({ - declarations: [Page1Cmp, Page2Cmp, ContainerCmp], - imports: [RouterTestingModule.withRoutes([ - {path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')}, - {path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')} - ])] - }); + TestBed.configureTestingModule({ + declarations: [Page1Cmp, Page2Cmp, ContainerCmp], + imports: [RouterTestingModule.withRoutes([ + {path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')}, + {path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')} + ])] + }); - const engine = TestBed.get(ɵAnimationEngine); - const fixture = TestBed.createComponent(ContainerCmp); - const cmp = fixture.componentInstance; - cmp.router.initialNavigation(); - tick(); - fixture.detectChanges(); - engine.flush(); + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(ContainerCmp); + const cmp = fixture.componentInstance; + cmp.router.initialNavigation(); + tick(); + fixture.detectChanges(); + engine.flush(); - cmp.router.navigateByUrl('/page1'); - tick(); - fixture.detectChanges(); - engine.flush(); + cmp.router.navigateByUrl('/page1'); + tick(); + fixture.detectChanges(); + engine.flush(); - cmp.router.navigateByUrl('/page2'); - tick(); - fixture.detectChanges(); - engine.flush(); + cmp.router.navigateByUrl('/page2'); + tick(); + fixture.detectChanges(); + engine.flush(); - const player = engine.players[0] !; - const groupPlayer = player.getRealPlayer() as AnimationGroupPlayer; - const players = groupPlayer.players as MockAnimationPlayer[]; + const player = engine.players[0] !; + const groupPlayer = player.getRealPlayer() as AnimationGroupPlayer; + const players = groupPlayer.players as MockAnimationPlayer[]; - expect(players.length).toEqual(2); - const [p1, p2] = players; + expect(players.length).toEqual(2); + const [p1, p2] = players; - expect(p1.keyframes).toEqual([ - {offset: 0, opacity: '0'}, - {offset: 1, opacity: '1'}, - ]); + expect(p1.keyframes).toEqual([ + {offset: 0, opacity: '0'}, + {offset: 1, opacity: '1'}, + ]); - expect(p2.keyframes).toEqual([ - {offset: 0, opacity: '0'}, - {offset: .5, opacity: '0'}, - {offset: 1, opacity: '1'}, - ]); - })); + expect(p2.keyframes).toEqual([ + {offset: 0, opacity: '0'}, + {offset: .5, opacity: '0'}, + {offset: 1, opacity: '1'}, + ]); + })); - fixmeIvy('unknown').it( - 'should allow inner leave animations to be emulated within a routed item', fakeAsync(() => { - @Component({ - animations: [ - trigger( - 'routerAnimations', - [ - transition( - 'page1 => page2', - [ - query(':leave', animateChild()), - ]), - ]), - ], - template: ` + it('should allow inner leave animations to be emulated within a routed item', fakeAsync(() => { + @Component({ + animations: [ + trigger( + 'routerAnimations', + [ + transition( + 'page1 => page2', + [ + query(':leave', animateChild()), + ]), + ]), + ], + template: `
` - }) - class ContainerCmp { - constructor(public router: Router) {} + }) + class ContainerCmp { + constructor(public router: Router) {} - prepareRouteAnimation(r: RouterOutlet) { - const animation = r.activatedRouteData['animation']; - const value = animation ? animation['value'] : null; - return value; - } - } + prepareRouteAnimation(r: RouterOutlet) { + const animation = r.activatedRouteData['animation']; + const value = animation ? animation['value'] : null; + return value; + } + } - @Component({ - selector: 'page1', - template: ` + @Component({ + selector: 'page1', + template: `

Page 1

`, - animations: [ - trigger( - 'page1Animation', - [ - transition( - ':leave', - [query('.if-one', animateChild()), query('.if-two', animateChild())]), - ]), - trigger( - 'ifAnimation', - [transition( - ':leave', [style({opacity: 1}), animate(1000, style({opacity: 0}))])]), - ] - }) - class Page1Cmp { - @HostBinding('@page1Animation') public doAnimate = true; + animations: [ + trigger( + 'page1Animation', + [ + transition( + ':leave', + [query('.if-one', animateChild()), query('.if-two', animateChild())]), + ]), + trigger( + 'ifAnimation', + [transition(':leave', [style({opacity: 1}), animate(1000, style({opacity: 0}))])]), + ] + }) + class Page1Cmp { + @HostBinding('@page1Animation') public doAnimate = true; - public exp = true; - } + public exp = true; + } - @Component({selector: 'page2', template: `page2`, animations: []}) - class Page2Cmp { - } + @Component({selector: 'page2', template: `page2`, animations: []}) + class Page2Cmp { + } - TestBed.configureTestingModule({ - declarations: [Page1Cmp, Page2Cmp, ContainerCmp], - imports: [RouterTestingModule.withRoutes([ - {path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')}, - {path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')} - ])] - }); + TestBed.configureTestingModule({ + declarations: [Page1Cmp, Page2Cmp, ContainerCmp], + imports: [RouterTestingModule.withRoutes([ + {path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')}, + {path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')} + ])] + }); - const engine = TestBed.get(ɵAnimationEngine); - const fixture = TestBed.createComponent(ContainerCmp); - const cmp = fixture.componentInstance; - cmp.router.initialNavigation(); - tick(); - fixture.detectChanges(); - engine.flush(); + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(ContainerCmp); + const cmp = fixture.componentInstance; + cmp.router.initialNavigation(); + tick(); + fixture.detectChanges(); + engine.flush(); - cmp.router.navigateByUrl('/page1'); - tick(); - fixture.detectChanges(); - engine.flush(); + cmp.router.navigateByUrl('/page1'); + tick(); + fixture.detectChanges(); + engine.flush(); - cmp.router.navigateByUrl('/page2'); - tick(); - fixture.detectChanges(); - engine.flush(); + cmp.router.navigateByUrl('/page2'); + tick(); + fixture.detectChanges(); + engine.flush(); - const player = engine.players[0] !; - const groupPlayer = player.getRealPlayer() as AnimationGroupPlayer; - const players = groupPlayer.players as MockAnimationPlayer[]; + const player = engine.players[0] !; + const groupPlayer = player.getRealPlayer() as AnimationGroupPlayer; + const players = groupPlayer.players as MockAnimationPlayer[]; - expect(players.length).toEqual(2); - const [p1, p2] = players; + expect(players.length).toEqual(2); + const [p1, p2] = players; - expect(p1.keyframes).toEqual([ - {offset: 0, opacity: '1'}, - {offset: 1, opacity: '0'}, - ]); + expect(p1.keyframes).toEqual([ + {offset: 0, opacity: '1'}, + {offset: 1, opacity: '0'}, + ]); - expect(p2.keyframes).toEqual([ - {offset: 0, opacity: '1'}, - {offset: .5, opacity: '1'}, - {offset: 1, opacity: '0'}, - ]); - })); + expect(p2.keyframes).toEqual([ + {offset: 0, opacity: '1'}, + {offset: .5, opacity: '1'}, + {offset: 1, opacity: '0'}, + ]); + })); - fixmeIvy('unknown').it( - 'should properly collect :enter / :leave router nodes even when another non-router *template component is within the trigger boundaries', - fakeAsync(() => { - @Component({ - selector: 'ani-cmp', - animations: [ - trigger( - 'pageAnimation', - [ - transition( - 'page1 => page2', - [ - query('.router-container :leave', animate('1s', style({opacity: 0}))), - query('.router-container :enter', animate('1s', style({opacity: 1}))), - ]), - ]), - ], - template: ` + it('should properly collect :enter / :leave router nodes even when another non-router *template component is within the trigger boundaries', + fakeAsync(() => { + @Component({ + selector: 'ani-cmp', + animations: [ + trigger( + 'pageAnimation', + [ + transition( + 'page1 => page2', + [ + query('.router-container :leave', animate('1s', style({opacity: 0}))), + query('.router-container :enter', animate('1s', style({opacity: 1}))), + ]), + ]), + ], + template: `
@@ -394,139 +389,136 @@ import {RouterTestingModule} from '@angular/router/testing';
` - }) - class ContainerCmp { - loading = false; + }) + class ContainerCmp { + loading = false; - constructor(public router: Router) {} + constructor(public router: Router) {} - prepRoute(outlet: any) { return outlet.activatedRouteData['animation']; } - } + prepRoute(outlet: any) { return outlet.activatedRouteData['animation']; } + } - @Component({selector: 'page1', template: `page1`}) - class Page1Cmp { - } + @Component({selector: 'page1', template: `page1`}) + class Page1Cmp { + } - @Component({selector: 'page2', template: `page2`}) - class Page2Cmp { - } + @Component({selector: 'page2', template: `page2`}) + class Page2Cmp { + } - TestBed.configureTestingModule({ - declarations: [Page1Cmp, Page2Cmp, ContainerCmp], - imports: [RouterTestingModule.withRoutes([ - {path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')}, - {path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')} - ])] - }); + TestBed.configureTestingModule({ + declarations: [Page1Cmp, Page2Cmp, ContainerCmp], + imports: [RouterTestingModule.withRoutes([ + {path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')}, + {path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')} + ])] + }); - const engine = TestBed.get(ɵAnimationEngine); - const fixture = TestBed.createComponent(ContainerCmp); - const cmp = fixture.componentInstance; - cmp.router.initialNavigation(); - tick(); - fixture.detectChanges(); - engine.flush(); + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(ContainerCmp); + const cmp = fixture.componentInstance; + cmp.router.initialNavigation(); + tick(); + fixture.detectChanges(); + engine.flush(); - cmp.router.navigateByUrl('/page1'); - tick(); - cmp.loading = true; - fixture.detectChanges(); - engine.flush(); + cmp.router.navigateByUrl('/page1'); + tick(); + cmp.loading = true; + fixture.detectChanges(); + engine.flush(); - cmp.router.navigateByUrl('/page2'); - tick(); - cmp.loading = false; - fixture.detectChanges(); - engine.flush(); + cmp.router.navigateByUrl('/page2'); + tick(); + cmp.loading = false; + fixture.detectChanges(); + engine.flush(); - const players = engine.players; - expect(players.length).toEqual(1); - const [p1] = players; + const players = engine.players; + expect(players.length).toEqual(1); + const [p1] = players; - const innerPlayers = p1.getRealPlayer().players; - expect(innerPlayers.length).toEqual(2); + const innerPlayers = p1.getRealPlayer().players; + expect(innerPlayers.length).toEqual(2); - const [ip1, ip2] = innerPlayers; - expect(ip1.element.innerText).toEqual('page1'); - expect(ip2.element.innerText).toEqual('page2'); - })); + const [ip1, ip2] = innerPlayers; + expect(ip1.element.innerText).toEqual('page1'); + expect(ip2.element.innerText).toEqual('page2'); + })); - fixmeIvy('unknown').it( - 'should allow a recursive set of :leave animations to occur for nested routes', - fakeAsync(() => { - @Component( - {selector: 'ani-cmp', template: ''}) - class ContainerCmp { - constructor(private _router: Router) {} - log: string[] = []; + it('should allow a recursive set of :leave animations to occur for nested routes', + fakeAsync(() => { + @Component({selector: 'ani-cmp', template: ''}) + class ContainerCmp { + constructor(private _router: Router) {} + log: string[] = []; - enter() { this._router.navigateByUrl('/(recur:recur/nested)'); } + enter() { this._router.navigateByUrl('/(recur:recur/nested)'); } - leave() { this._router.navigateByUrl('/'); } - } + leave() { this._router.navigateByUrl('/'); } + } - @Component({ - selector: 'recur-page', - template: 'Depth: {{ depth }} \n ', - animations: [ - trigger( - 'pageAnimations', - [ - transition( - ':leave', [group([ - sequence([style({opacity: 1}), animate('1s', style({opacity: 0}))]), - query('@*', animateChild(), {optional: true}) - ])]), - ]), - ] - }) - class RecurPageCmp { - @HostBinding('@pageAnimations') public animatePage = true; + @Component({ + selector: 'recur-page', + template: 'Depth: {{ depth }} \n ', + animations: [ + trigger( + 'pageAnimations', + [ + transition(':leave', [group([ + sequence([style({opacity: 1}), animate('1s', style({opacity: 0}))]), + query('@*', animateChild(), {optional: true}) + ])]), + ]), + ] + }) + class RecurPageCmp { + @HostBinding('@pageAnimations') public animatePage = true; - @HostBinding('attr.data-depth') public depth = 0; + @HostBinding('attr.data-depth') public depth = 0; - constructor(private container: ContainerCmp, private route: ActivatedRoute) { - this.route.data.subscribe(data => { - this.container.log.push(`DEPTH ${data.depth}`); - this.depth = data.depth; - }); - } - } + constructor(private container: ContainerCmp, private route: ActivatedRoute) { + this.route.data.subscribe(data => { + this.container.log.push(`DEPTH ${data.depth}`); + this.depth = data.depth; + }); + } + } - TestBed.configureTestingModule({ - declarations: [ContainerCmp, RecurPageCmp], - imports: [RouterTestingModule.withRoutes([{ - path: 'recur', - component: RecurPageCmp, - outlet: 'recur', - data: {depth: 0}, - children: [{path: 'nested', component: RecurPageCmp, data: {depth: 1}}] - }])] - }); + TestBed.configureTestingModule({ + declarations: [ContainerCmp, RecurPageCmp], + imports: [RouterTestingModule.withRoutes([{ + path: 'recur', + component: RecurPageCmp, + outlet: 'recur', + data: {depth: 0}, + children: [{path: 'nested', component: RecurPageCmp, data: {depth: 1}}] + }])] + }); - const fixture = TestBed.createComponent(ContainerCmp); - const cmp = fixture.componentInstance; - cmp.enter(); - tick(); - fixture.detectChanges(); - flushMicrotasks(); + const fixture = TestBed.createComponent(ContainerCmp); + const cmp = fixture.componentInstance; + cmp.enter(); + tick(); + fixture.detectChanges(); + flushMicrotasks(); - expect(cmp.log).toEqual([ - 'DEPTH 0', - 'DEPTH 1', - ]); + expect(cmp.log).toEqual([ + 'DEPTH 0', + 'DEPTH 1', + ]); - cmp.leave(); - tick(); - fixture.detectChanges(); + cmp.leave(); + tick(); + fixture.detectChanges(); - const players = getLog(); - expect(players.length).toEqual(2); + const players = getLog(); + expect(players.length).toEqual(2); - const [p1, p2] = players; - expect(p1.element.getAttribute('data-depth')).toEqual('0'); - expect(p2.element.getAttribute('data-depth')).toEqual('1'); - })); + const [p1, p2] = players; + expect(p1.element.getAttribute('data-depth')).toEqual('0'); + expect(p2.element.getAttribute('data-depth')).toEqual('1'); + })); }); }); diff --git a/packages/core/test/animation/animations_with_web_animations_integration_spec.ts b/packages/core/test/animation/animations_with_web_animations_integration_spec.ts index 7b2ec1ec2a..e510e7bd2d 100644 --- a/packages/core/test/animation/animations_with_web_animations_integration_spec.ts +++ b/packages/core/test/animation/animations_with_web_animations_integration_spec.ts @@ -242,10 +242,9 @@ import {fixmeIvy} from '@angular/private/testing'; ]); }); - fixmeIvy('unknown').it( - 'should treat * styles as ! for queried items that are collected in a container that is being removed', - () => { - @Component({ + it('should treat * styles as ! for queried items that are collected in a container that is being removed', + () => { + @Component({ selector: 'my-app', styles: [` .list .outer { @@ -286,58 +285,58 @@ import {fixmeIvy} from '@angular/private/testing'; ] }) class Cmp { - items: any[] = []; + items: any[] = []; - get exp() { return this.items.length ? 'full' : 'empty'; } + get exp() { return this.items.length ? 'full' : 'empty'; } - empty() { this.items = []; } + empty() { this.items = []; } - full() { this.items = [0, 1, 2, 3, 4]; } - } + full() { this.items = [0, 1, 2, 3, 4]; } + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.get(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.get(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.empty(); - fixture.detectChanges(); - let player = engine.players[0] !as TransitionAnimationPlayer; - player.finish(); + cmp.empty(); + fixture.detectChanges(); + let player = engine.players[0] !as TransitionAnimationPlayer; + player.finish(); - cmp.full(); - fixture.detectChanges(); + cmp.full(); + fixture.detectChanges(); - player = engine.players[0] !as TransitionAnimationPlayer; - let queriedPlayers = (player.getRealPlayer() as AnimationGroupPlayer).players; - expect(queriedPlayers.length).toEqual(5); + player = engine.players[0] !as TransitionAnimationPlayer; + let queriedPlayers = (player.getRealPlayer() as AnimationGroupPlayer).players; + expect(queriedPlayers.length).toEqual(5); - let i = 0; - for (i = 0; i < queriedPlayers.length; i++) { - let player = queriedPlayers[i] as ɵWebAnimationsPlayer; - expect(player.keyframes).toEqual([ - {height: '0px', offset: 0}, - {height: '50px', offset: 1}, - ]); - player.finish(); - } + let i = 0; + for (i = 0; i < queriedPlayers.length; i++) { + let player = queriedPlayers[i] as ɵWebAnimationsPlayer; + expect(player.keyframes).toEqual([ + {height: '0px', offset: 0}, + {height: '50px', offset: 1}, + ]); + player.finish(); + } - cmp.empty(); - fixture.detectChanges(); + cmp.empty(); + fixture.detectChanges(); - player = engine.players[0] !as TransitionAnimationPlayer; - queriedPlayers = (player.getRealPlayer() as AnimationGroupPlayer).players; - expect(queriedPlayers.length).toEqual(5); + player = engine.players[0] !as TransitionAnimationPlayer; + queriedPlayers = (player.getRealPlayer() as AnimationGroupPlayer).players; + expect(queriedPlayers.length).toEqual(5); - for (i = 0; i < queriedPlayers.length; i++) { - let player = queriedPlayers[i] as ɵWebAnimationsPlayer; - expect(player.keyframes).toEqual([ - {height: '50px', offset: 0}, - {height: '0px', offset: 1}, - ]); - } - }); + for (i = 0; i < queriedPlayers.length; i++) { + let player = queriedPlayers[i] as ɵWebAnimationsPlayer; + expect(player.keyframes).toEqual([ + {height: '50px', offset: 0}, + {height: '0px', offset: 1}, + ]); + } + }); it('should compute intermediate styles properly when an animation is cancelled', () => { @Component({ diff --git a/packages/platform-browser/animations/test/animation_renderer_spec.ts b/packages/platform-browser/animations/test/animation_renderer_spec.ts index 733aa52763..2f26069c0f 100644 --- a/packages/platform-browser/animations/test/animation_renderer_spec.ts +++ b/packages/platform-browser/animations/test/animation_renderer_spec.ts @@ -279,36 +279,35 @@ import {el} from '../../testing/src/browser_util'; }); }); - fixmeIvy(`FW-802: Animation 'start' and 'end' hooks are invoked twice`) - .it('should provide hooks at the start and end of change detection', () => { - @Component({ - selector: 'my-cmp', - template: ` + it('should provide hooks at the start and end of change detection', () => { + @Component({ + selector: 'my-cmp', + template: `
`, - animations: [trigger('myAnimation', [])] - }) - class Cmp { - public exp: any; - } + animations: [trigger('myAnimation', [])] + }) + class Cmp { + public exp: any; + } - TestBed.configureTestingModule({ - providers: [{provide: AnimationEngine, useClass: InjectableAnimationEngine}], - declarations: [Cmp] - }); + TestBed.configureTestingModule({ + providers: [{provide: AnimationEngine, useClass: InjectableAnimationEngine}], + declarations: [Cmp] + }); - const renderer = TestBed.get(RendererFactory2) as ExtendedAnimationRendererFactory; - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const renderer = TestBed.get(RendererFactory2) as ExtendedAnimationRendererFactory; + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - renderer.log = []; - fixture.detectChanges(); - expect(renderer.log).toEqual(['begin', 'end']); + renderer.log = []; + fixture.detectChanges(); + expect(renderer.log).toEqual(['begin', 'end']); - renderer.log = []; - fixture.detectChanges(); - expect(renderer.log).toEqual(['begin', 'end']); - }); + renderer.log = []; + fixture.detectChanges(); + expect(renderer.log).toEqual(['begin', 'end']); + }); }); })();