1032 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			1032 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
/**
 | 
						|
 * @license
 | 
						|
 * Copyright Google Inc. All Rights Reserved.
 | 
						|
 *
 | 
						|
 * Use of this source code is governed by an MIT-style license that can be
 | 
						|
 * found in the LICENSE file at https://angular.io/license
 | 
						|
 */
 | 
						|
import {AUTO_STYLE, AnimationMetadata, AnimationMetadataType, animate, animation, group, keyframes, query, sequence, state, style, transition, trigger, useAnimation, ɵStyleData} from '@angular/animations';
 | 
						|
import {AnimationOptions} from '@angular/core/src/animation/dsl';
 | 
						|
 | 
						|
import {Animation} from '../../src/dsl/animation';
 | 
						|
import {buildAnimationAst} from '../../src/dsl/animation_ast_builder';
 | 
						|
import {AnimationTimelineInstruction} from '../../src/dsl/animation_timeline_instruction';
 | 
						|
import {ElementInstructionMap} from '../../src/dsl/element_instruction_map';
 | 
						|
import {MockAnimationDriver} from '../../testing';
 | 
						|
 | 
						|
function createDiv() {
 | 
						|
  return document.createElement('div');
 | 
						|
}
 | 
						|
 | 
						|
{
 | 
						|
  describe('Animation', () => {
 | 
						|
    // these tests are only mean't to be run within the DOM (for now)
 | 
						|
    if (typeof Element == 'undefined') return;
 | 
						|
 | 
						|
    let rootElement: any;
 | 
						|
    let subElement1: any;
 | 
						|
    let subElement2: any;
 | 
						|
 | 
						|
    beforeEach(() => {
 | 
						|
      rootElement = createDiv();
 | 
						|
      subElement1 = createDiv();
 | 
						|
      subElement2 = createDiv();
 | 
						|
      document.body.appendChild(rootElement);
 | 
						|
      rootElement.appendChild(subElement1);
 | 
						|
      rootElement.appendChild(subElement2);
 | 
						|
    });
 | 
						|
 | 
						|
    afterEach(() => { document.body.removeChild(rootElement); });
 | 
						|
 | 
						|
    describe('validation', () => {
 | 
						|
      it('should throw an error if one or more but not all keyframes() styles contain offsets',
 | 
						|
         () => {
 | 
						|
           const steps = animate(1000, keyframes([
 | 
						|
                                   style({opacity: 0}),
 | 
						|
                                   style({opacity: 1, offset: 1}),
 | 
						|
                                 ]));
 | 
						|
 | 
						|
           expect(() => { validateAndThrowAnimationSequence(steps); })
 | 
						|
               .toThrowError(
 | 
						|
                   /Not all style\(\) steps within the declared keyframes\(\) contain offsets/);
 | 
						|
         });
 | 
						|
 | 
						|
      it('should throw an error if not all offsets are between 0 and 1', () => {
 | 
						|
        let steps = animate(1000, keyframes([
 | 
						|
                              style({opacity: 0, offset: -1}),
 | 
						|
                              style({opacity: 1, offset: 1}),
 | 
						|
                            ]));
 | 
						|
 | 
						|
        expect(() => {
 | 
						|
          validateAndThrowAnimationSequence(steps);
 | 
						|
        }).toThrowError(/Please ensure that all keyframe offsets are between 0 and 1/);
 | 
						|
 | 
						|
        steps = animate(1000, keyframes([
 | 
						|
                          style({opacity: 0, offset: 0}),
 | 
						|
                          style({opacity: 1, offset: 1.1}),
 | 
						|
                        ]));
 | 
						|
 | 
						|
        expect(() => {
 | 
						|
          validateAndThrowAnimationSequence(steps);
 | 
						|
        }).toThrowError(/Please ensure that all keyframe offsets are between 0 and 1/);
 | 
						|
      });
 | 
						|
 | 
						|
      it('should throw an error if a smaller offset shows up after a bigger one', () => {
 | 
						|
        let steps = animate(1000, keyframes([
 | 
						|
                              style({opacity: 0, offset: 1}),
 | 
						|
                              style({opacity: 1, offset: 0}),
 | 
						|
                            ]));
 | 
						|
 | 
						|
        expect(() => {
 | 
						|
          validateAndThrowAnimationSequence(steps);
 | 
						|
        }).toThrowError(/Please ensure that all keyframe offsets are in order/);
 | 
						|
      });
 | 
						|
 | 
						|
      it('should throw an error if any styles overlap during parallel animations', () => {
 | 
						|
        const steps = group([
 | 
						|
          sequence([
 | 
						|
            // 0 -> 2000ms
 | 
						|
            style({opacity: 0}), animate('500ms', style({opacity: .25})),
 | 
						|
            animate('500ms', style({opacity: .5})), animate('500ms', style({opacity: .75})),
 | 
						|
            animate('500ms', style({opacity: 1}))
 | 
						|
          ]),
 | 
						|
          animate('1s 500ms', keyframes([
 | 
						|
                    // 0 -> 1500ms
 | 
						|
                    style({width: 0}),
 | 
						|
                    style({opacity: 1, width: 1000}),
 | 
						|
                  ]))
 | 
						|
        ]);
 | 
						|
 | 
						|
        expect(() => { validateAndThrowAnimationSequence(steps); })
 | 
						|
            .toThrowError(
 | 
						|
                /The CSS property "opacity" that exists between the times of "0ms" and "2000ms" is also being animated in a parallel animation between the times of "0ms" and "1500ms"/);
 | 
						|
      });
 | 
						|
 | 
						|
      it('should not throw an error if animations overlap in different query levels within different transitions',
 | 
						|
         () => {
 | 
						|
           const steps = trigger('myAnimation', [
 | 
						|
             transition('a => b', group([
 | 
						|
                          query('h1', animate('1s', style({opacity: 0}))),
 | 
						|
                          query('h2', animate('1s', style({opacity: 1}))),
 | 
						|
                        ])),
 | 
						|
 | 
						|
             transition('b => a', group([
 | 
						|
                          query('h1', animate('1s', style({opacity: 0}))),
 | 
						|
                          query('h2', animate('1s', style({opacity: 1}))),
 | 
						|
                        ])),
 | 
						|
           ]);
 | 
						|
 | 
						|
           expect(() => validateAndThrowAnimationSequence(steps)).not.toThrow();
 | 
						|
         });
 | 
						|
 | 
						|
      it('should not allow triggers to be defined with a prefixed `@` symbol', () => {
 | 
						|
        const steps = trigger('@foo', []);
 | 
						|
 | 
						|
        expect(() => validateAndThrowAnimationSequence(steps))
 | 
						|
            .toThrowError(
 | 
						|
                /animation triggers cannot be prefixed with an `@` sign \(e\.g\. trigger\('@foo', \[...\]\)\)/);
 | 
						|
      });
 | 
						|
 | 
						|
      it('should throw an error if an animation time is invalid', () => {
 | 
						|
        const steps = [animate('500xs', style({opacity: 1}))];
 | 
						|
 | 
						|
        expect(() => {
 | 
						|
          validateAndThrowAnimationSequence(steps);
 | 
						|
        }).toThrowError(/The provided timing value "500xs" is invalid/);
 | 
						|
 | 
						|
        const steps2 = [animate('500ms 500ms 500ms ease-out', style({opacity: 1}))];
 | 
						|
 | 
						|
        expect(() => {
 | 
						|
          validateAndThrowAnimationSequence(steps2);
 | 
						|
        }).toThrowError(/The provided timing value "500ms 500ms 500ms ease-out" is invalid/);
 | 
						|
      });
 | 
						|
 | 
						|
      it('should throw if negative durations are used', () => {
 | 
						|
        const steps = [animate(-1000, style({opacity: 1}))];
 | 
						|
 | 
						|
        expect(() => {
 | 
						|
          validateAndThrowAnimationSequence(steps);
 | 
						|
        }).toThrowError(/Duration values below 0 are not allowed for this animation step/);
 | 
						|
 | 
						|
        const steps2 = [animate('-1s', style({opacity: 1}))];
 | 
						|
 | 
						|
        expect(() => {
 | 
						|
          validateAndThrowAnimationSequence(steps2);
 | 
						|
        }).toThrowError(/Duration values below 0 are not allowed for this animation step/);
 | 
						|
      });
 | 
						|
 | 
						|
      it('should throw if negative delays are used', () => {
 | 
						|
        const steps = [animate('1s -500ms', style({opacity: 1}))];
 | 
						|
 | 
						|
        expect(() => {
 | 
						|
          validateAndThrowAnimationSequence(steps);
 | 
						|
        }).toThrowError(/Delay values below 0 are not allowed for this animation step/);
 | 
						|
 | 
						|
        const steps2 = [animate('1s -0.5s', style({opacity: 1}))];
 | 
						|
 | 
						|
        expect(() => {
 | 
						|
          validateAndThrowAnimationSequence(steps2);
 | 
						|
        }).toThrowError(/Delay values below 0 are not allowed for this animation step/);
 | 
						|
      });
 | 
						|
 | 
						|
      it('should throw if keyframes() is not used inside of animate()', () => {
 | 
						|
        const steps = [keyframes([])];
 | 
						|
 | 
						|
        expect(() => {
 | 
						|
          validateAndThrowAnimationSequence(steps);
 | 
						|
        }).toThrowError(/keyframes\(\) must be placed inside of a call to animate\(\)/);
 | 
						|
 | 
						|
        const steps2 = [group([keyframes([])])];
 | 
						|
 | 
						|
        expect(() => {
 | 
						|
          validateAndThrowAnimationSequence(steps2);
 | 
						|
        }).toThrowError(/keyframes\(\) must be placed inside of a call to animate\(\)/);
 | 
						|
      });
 | 
						|
 | 
						|
      it('should throw if dynamic style substitutions are used without defaults within state() definitions',
 | 
						|
         () => {
 | 
						|
           const steps = [
 | 
						|
             state('final', style({
 | 
						|
                     'width': '{{ one }}px',
 | 
						|
                     'borderRadius': '{{ two }}px {{ three }}px',
 | 
						|
                   })),
 | 
						|
           ];
 | 
						|
 | 
						|
           expect(() => { validateAndThrowAnimationSequence(steps); })
 | 
						|
               .toThrowError(
 | 
						|
                   /state\("final", ...\) must define default values for all the following style substitutions: one, two, three/);
 | 
						|
 | 
						|
           const steps2 = [state(
 | 
						|
               'panfinal', style({
 | 
						|
                 'color': '{{ greyColor }}',
 | 
						|
                 'borderColor': '1px solid {{ greyColor }}',
 | 
						|
                 'backgroundColor': '{{ redColor }}',
 | 
						|
               }),
 | 
						|
               {params: {redColor: 'maroon'}})];
 | 
						|
 | 
						|
           expect(() => { validateAndThrowAnimationSequence(steps2); })
 | 
						|
               .toThrowError(
 | 
						|
                   /state\("panfinal", ...\) must define default values for all the following style substitutions: greyColor/);
 | 
						|
         });
 | 
						|
 | 
						|
      it('should throw an error if an invalid CSS property is used in the animation', () => {
 | 
						|
        const steps = [animate(1000, style({abc: '500px'}))];
 | 
						|
 | 
						|
        expect(() => { validateAndThrowAnimationSequence(steps); })
 | 
						|
            .toThrowError(
 | 
						|
                /The provided animation property "abc" is not a supported CSS property for animations/);
 | 
						|
      });
 | 
						|
 | 
						|
      it('should allow a vendor-prefixed property to be used in an animation sequence without throwing an error',
 | 
						|
         () => {
 | 
						|
           const steps = [
 | 
						|
             style({webkitTransform: 'translateX(0px)'}),
 | 
						|
             animate(1000, style({webkitTransform: 'translateX(100px)'}))
 | 
						|
           ];
 | 
						|
 | 
						|
           expect(() => validateAndThrowAnimationSequence(steps)).not.toThrow();
 | 
						|
         });
 | 
						|
 | 
						|
      it('should allow for old CSS properties (like transform) to be auto-prefixed by webkit',
 | 
						|
         () => {
 | 
						|
           const steps = [
 | 
						|
             style({transform: 'translateX(-100px)'}),
 | 
						|
             animate(1000, style({transform: 'translateX(500px)'}))
 | 
						|
           ];
 | 
						|
 | 
						|
           expect(() => validateAndThrowAnimationSequence(steps)).not.toThrow();
 | 
						|
         });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('keyframe building', () => {
 | 
						|
      describe('style() / animate()', () => {
 | 
						|
        it('should produce a balanced series of keyframes given a sequence of animate steps',
 | 
						|
           () => {
 | 
						|
             const steps = [
 | 
						|
               style({width: 0}), animate(1000, style({height: 50})),
 | 
						|
               animate(1000, style({width: 100})), animate(1000, style({height: 150})),
 | 
						|
               animate(1000, style({width: 200}))
 | 
						|
             ];
 | 
						|
 | 
						|
             const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
             expect(players[0].keyframes).toEqual([
 | 
						|
               {height: AUTO_STYLE, width: 0, offset: 0},
 | 
						|
               {height: 50, width: 0, offset: .25},
 | 
						|
               {height: 50, width: 100, offset: .5},
 | 
						|
               {height: 150, width: 100, offset: .75},
 | 
						|
               {height: 150, width: 200, offset: 1},
 | 
						|
             ]);
 | 
						|
           });
 | 
						|
 | 
						|
        it('should fill in missing starting steps when a starting `style()` value is not used',
 | 
						|
           () => {
 | 
						|
             const steps = [animate(1000, style({width: 999}))];
 | 
						|
 | 
						|
             const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
             expect(players[0].keyframes).toEqual([
 | 
						|
               {width: AUTO_STYLE, offset: 0}, {width: 999, offset: 1}
 | 
						|
             ]);
 | 
						|
           });
 | 
						|
 | 
						|
        it('should merge successive style() calls together before an animate() call', () => {
 | 
						|
          const steps = [
 | 
						|
            style({width: 0}), style({height: 0}), style({width: 200}), style({opacity: 0}),
 | 
						|
            animate(1000, style({width: 100, height: 400, opacity: 1}))
 | 
						|
          ];
 | 
						|
 | 
						|
          const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
          expect(players[0].keyframes).toEqual([
 | 
						|
            {width: 200, height: 0, opacity: 0, offset: 0},
 | 
						|
            {width: 100, height: 400, opacity: 1, offset: 1}
 | 
						|
          ]);
 | 
						|
        });
 | 
						|
 | 
						|
        it('should not merge in successive style() calls to the previous animate() keyframe',
 | 
						|
           () => {
 | 
						|
             const steps = [
 | 
						|
               style({opacity: 0}), animate(1000, style({opacity: .5})), style({opacity: .6}),
 | 
						|
               animate(1000, style({opacity: 1}))
 | 
						|
             ];
 | 
						|
 | 
						|
             const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
             const keyframes = humanizeOffsets(players[0].keyframes, 4);
 | 
						|
 | 
						|
             expect(keyframes).toEqual([
 | 
						|
               {opacity: 0, offset: 0},
 | 
						|
               {opacity: .5, offset: .4998},
 | 
						|
               {opacity: .6, offset: .5002},
 | 
						|
               {opacity: 1, offset: 1},
 | 
						|
             ]);
 | 
						|
           });
 | 
						|
 | 
						|
        it('should support an easing value that uses cubic-bezier(...)', () => {
 | 
						|
          const steps = [
 | 
						|
            style({opacity: 0}),
 | 
						|
            animate('1s cubic-bezier(.29, .55 ,.53 ,1.53)', style({opacity: 1}))
 | 
						|
          ];
 | 
						|
 | 
						|
          const player = invokeAnimationSequence(rootElement, steps)[0];
 | 
						|
          const firstKeyframe = player.keyframes[0];
 | 
						|
          const firstKeyframeEasing = firstKeyframe['easing'] as string;
 | 
						|
          expect(firstKeyframeEasing.replace(/\s+/g, '')).toEqual('cubic-bezier(.29,.55,.53,1.53)');
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      describe('sequence()', () => {
 | 
						|
        it('should not produce extra timelines when multiple sequences are used within each other',
 | 
						|
           () => {
 | 
						|
             const steps = [
 | 
						|
               style({width: 0}),
 | 
						|
               animate(1000, style({width: 100})),
 | 
						|
               sequence([
 | 
						|
                 animate(1000, style({width: 200})),
 | 
						|
                 sequence([
 | 
						|
                   animate(1000, style({width: 300})),
 | 
						|
                 ]),
 | 
						|
               ]),
 | 
						|
               animate(1000, style({width: 400})),
 | 
						|
               sequence([
 | 
						|
                 animate(1000, style({width: 500})),
 | 
						|
               ]),
 | 
						|
             ];
 | 
						|
 | 
						|
             const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
             expect(players.length).toEqual(1);
 | 
						|
 | 
						|
             const player = players[0];
 | 
						|
             expect(player.keyframes).toEqual([
 | 
						|
               {width: 0, offset: 0}, {width: 100, offset: .2}, {width: 200, offset: .4},
 | 
						|
               {width: 300, offset: .6}, {width: 400, offset: .8}, {width: 500, offset: 1}
 | 
						|
             ]);
 | 
						|
           });
 | 
						|
 | 
						|
        it('should create a new timeline after a sequence if group() or keyframe() commands are used within',
 | 
						|
           () => {
 | 
						|
             const steps = [
 | 
						|
               style({width: 100, height: 100}), animate(1000, style({width: 150, height: 150})),
 | 
						|
               sequence([
 | 
						|
                 group([
 | 
						|
                   animate(1000, style({height: 200})),
 | 
						|
                 ]),
 | 
						|
                 animate(1000, keyframes([style({width: 180}), style({width: 200})]))
 | 
						|
               ]),
 | 
						|
               animate(1000, style({width: 500, height: 500}))
 | 
						|
             ];
 | 
						|
 | 
						|
             const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
             expect(players.length).toEqual(4);
 | 
						|
 | 
						|
             const finalPlayer = players[players.length - 1];
 | 
						|
             expect(finalPlayer.keyframes).toEqual([
 | 
						|
               {width: 200, height: 200, offset: 0}, {width: 500, height: 500, offset: 1}
 | 
						|
             ]);
 | 
						|
           });
 | 
						|
 | 
						|
        it('should push the start of a sequence if a delay option is provided', () => {
 | 
						|
          const steps = [
 | 
						|
            style({width: '0px'}), animate(1000, style({width: '100px'})),
 | 
						|
            sequence(
 | 
						|
                [
 | 
						|
                  animate(1000, style({width: '200px'})),
 | 
						|
                ],
 | 
						|
                {delay: 500})
 | 
						|
          ];
 | 
						|
 | 
						|
          const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
          const finalPlayer = players[players.length - 1];
 | 
						|
          expect(finalPlayer.keyframes).toEqual([
 | 
						|
            {width: '100px', offset: 0},
 | 
						|
            {width: '200px', offset: 1},
 | 
						|
          ]);
 | 
						|
          expect(finalPlayer.delay).toEqual(1500);
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      describe('subtitutions', () => {
 | 
						|
        it('should allow params to be subtituted even if they are not defaulted in a reusable animation',
 | 
						|
           () => {
 | 
						|
             const myAnimation = animation([
 | 
						|
               style({left: '{{ start }}'}),
 | 
						|
               animate(1000, style({left: '{{ end }}'})),
 | 
						|
             ]);
 | 
						|
 | 
						|
             const steps = [
 | 
						|
               useAnimation(myAnimation, {params: {start: '0px', end: '100px'}}),
 | 
						|
             ];
 | 
						|
 | 
						|
             const players = invokeAnimationSequence(rootElement, steps, {});
 | 
						|
             expect(players.length).toEqual(1);
 | 
						|
             const player = players[0];
 | 
						|
 | 
						|
             expect(player.keyframes).toEqual([
 | 
						|
               {left: '0px', offset: 0},
 | 
						|
               {left: '100px', offset: 1},
 | 
						|
             ]);
 | 
						|
           });
 | 
						|
 | 
						|
        it('should substitute in timing values', () => {
 | 
						|
          function makeAnimation(exp: string, options: {[key: string]: any}) {
 | 
						|
            const steps = [style({opacity: 0}), animate(exp, style({opacity: 1}))];
 | 
						|
            return invokeAnimationSequence(rootElement, steps, options);
 | 
						|
          }
 | 
						|
 | 
						|
          let players = makeAnimation('{{ duration }}', buildParams({duration: '1234ms'}));
 | 
						|
          expect(players[0].duration).toEqual(1234);
 | 
						|
 | 
						|
          players = makeAnimation('{{ duration }}', buildParams({duration: '9s 2s'}));
 | 
						|
          expect(players[0].duration).toEqual(11000);
 | 
						|
 | 
						|
          players = makeAnimation('{{ duration }} 1s', buildParams({duration: '1.5s'}));
 | 
						|
          expect(players[0].duration).toEqual(2500);
 | 
						|
 | 
						|
          players = makeAnimation(
 | 
						|
              '{{ duration }} {{ delay }}', buildParams({duration: '1s', delay: '2s'}));
 | 
						|
          expect(players[0].duration).toEqual(3000);
 | 
						|
        });
 | 
						|
 | 
						|
        it('should allow multiple substitutions to occur within the same style value', () => {
 | 
						|
          const steps = [
 | 
						|
            style({borderRadius: '100px 100px'}),
 | 
						|
            animate(1000, style({borderRadius: '{{ one }}px {{ two }}'})),
 | 
						|
          ];
 | 
						|
          const players =
 | 
						|
              invokeAnimationSequence(rootElement, steps, buildParams({one: '200', two: '400px'}));
 | 
						|
          expect(players[0].keyframes).toEqual([
 | 
						|
            {offset: 0, borderRadius: '100px 100px'}, {offset: 1, borderRadius: '200px 400px'}
 | 
						|
          ]);
 | 
						|
        });
 | 
						|
 | 
						|
        it('should substitute in values that are defined as parameters for inner areas of a sequence',
 | 
						|
           () => {
 | 
						|
             const steps = sequence(
 | 
						|
                 [
 | 
						|
                   sequence(
 | 
						|
                       [
 | 
						|
                         sequence(
 | 
						|
                             [
 | 
						|
                               style({height: '{{ x0 }}px'}),
 | 
						|
                               animate(1000, style({height: '{{ x2 }}px'})),
 | 
						|
                             ],
 | 
						|
                             buildParams({x2: '{{ x1 }}3'})),
 | 
						|
                       ],
 | 
						|
                       buildParams({x1: '{{ x0 }}2'})),
 | 
						|
                 ],
 | 
						|
                 buildParams({x0: '1'}));
 | 
						|
 | 
						|
             const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
             expect(players.length).toEqual(1);
 | 
						|
             const [player] = players;
 | 
						|
             expect(player.keyframes).toEqual([
 | 
						|
               {offset: 0, height: '1px'}, {offset: 1, height: '123px'}
 | 
						|
             ]);
 | 
						|
           });
 | 
						|
 | 
						|
        it('should substitute in values that are defined as parameters for reusable animations',
 | 
						|
           () => {
 | 
						|
             const anim = animation([
 | 
						|
               style({height: '{{ start }}'}),
 | 
						|
               animate(1000, style({height: '{{ end }}'})),
 | 
						|
             ]);
 | 
						|
 | 
						|
             const steps = sequence(
 | 
						|
                 [
 | 
						|
                   sequence(
 | 
						|
                       [
 | 
						|
                         useAnimation(anim, buildParams({start: '{{ a }}', end: '{{ b }}'})),
 | 
						|
                       ],
 | 
						|
                       buildParams({a: '100px', b: '200px'})),
 | 
						|
                 ],
 | 
						|
                 buildParams({a: '0px'}));
 | 
						|
 | 
						|
             const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
             expect(players.length).toEqual(1);
 | 
						|
             const [player] = players;
 | 
						|
             expect(player.keyframes).toEqual([
 | 
						|
               {offset: 0, height: '100px'}, {offset: 1, height: '200px'}
 | 
						|
             ]);
 | 
						|
           });
 | 
						|
 | 
						|
        it('should throw an error when an input variable is not provided when invoked and is not a default value',
 | 
						|
           () => {
 | 
						|
             expect(() => invokeAnimationSequence(rootElement, [style({color: '{{ color }}'})]))
 | 
						|
                 .toThrowError(/Please provide a value for the animation param color/);
 | 
						|
 | 
						|
             expect(
 | 
						|
                 () => invokeAnimationSequence(
 | 
						|
                     rootElement,
 | 
						|
                     [
 | 
						|
                       style({color: '{{ start }}'}),
 | 
						|
                       animate('{{ time }}', style({color: '{{ end }}'})),
 | 
						|
                     ],
 | 
						|
                     buildParams({start: 'blue', end: 'red'})))
 | 
						|
                 .toThrowError(/Please provide a value for the animation param time/);
 | 
						|
           });
 | 
						|
      });
 | 
						|
 | 
						|
      describe('keyframes()', () => {
 | 
						|
        it('should produce a sub timeline when `keyframes()` is used within a sequence', () => {
 | 
						|
          const steps = [
 | 
						|
            animate(1000, style({opacity: .5})), animate(1000, style({opacity: 1})),
 | 
						|
            animate(
 | 
						|
                1000, keyframes([style({height: 0}), style({height: 100}), style({height: 50})])),
 | 
						|
            animate(1000, style({height: 0, opacity: 0}))
 | 
						|
          ];
 | 
						|
 | 
						|
          const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
          expect(players.length).toEqual(3);
 | 
						|
 | 
						|
          const player0 = players[0];
 | 
						|
          expect(player0.delay).toEqual(0);
 | 
						|
          expect(player0.keyframes).toEqual([
 | 
						|
            {opacity: AUTO_STYLE, offset: 0},
 | 
						|
            {opacity: .5, offset: .5},
 | 
						|
            {opacity: 1, offset: 1},
 | 
						|
          ]);
 | 
						|
 | 
						|
          const subPlayer = players[1];
 | 
						|
          expect(subPlayer.delay).toEqual(2000);
 | 
						|
          expect(subPlayer.keyframes).toEqual([
 | 
						|
            {height: 0, offset: 0},
 | 
						|
            {height: 100, offset: .5},
 | 
						|
            {height: 50, offset: 1},
 | 
						|
          ]);
 | 
						|
 | 
						|
          const player1 = players[2];
 | 
						|
          expect(player1.delay).toEqual(3000);
 | 
						|
          expect(player1.keyframes).toEqual([
 | 
						|
            {opacity: 1, height: 50, offset: 0}, {opacity: 0, height: 0, offset: 1}
 | 
						|
          ]);
 | 
						|
        });
 | 
						|
 | 
						|
        it('should propagate inner keyframe style data to the parent timeline if used afterwards',
 | 
						|
           () => {
 | 
						|
             const steps = [
 | 
						|
               style({opacity: 0}), animate(1000, style({opacity: .5})),
 | 
						|
               animate(1000, style({opacity: 1})), animate(1000, keyframes([
 | 
						|
                                                             style({color: 'red'}),
 | 
						|
                                                             style({color: 'blue'}),
 | 
						|
                                                           ])),
 | 
						|
               animate(1000, style({color: 'green', opacity: 0}))
 | 
						|
             ];
 | 
						|
 | 
						|
             const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
             const finalPlayer = players[players.length - 1];
 | 
						|
             expect(finalPlayer.keyframes).toEqual([
 | 
						|
               {opacity: 1, color: 'blue', offset: 0}, {opacity: 0, color: 'green', offset: 1}
 | 
						|
             ]);
 | 
						|
           });
 | 
						|
 | 
						|
        it('should feed in starting data into inner keyframes if used in an style step beforehand',
 | 
						|
           () => {
 | 
						|
             const steps = [
 | 
						|
               animate(1000, style({opacity: .5})), animate(1000, keyframes([
 | 
						|
                                                              style({opacity: .8, offset: .5}),
 | 
						|
                                                              style({opacity: 1, offset: 1}),
 | 
						|
                                                            ]))
 | 
						|
             ];
 | 
						|
 | 
						|
             const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
             expect(players.length).toEqual(2);
 | 
						|
 | 
						|
             const topPlayer = players[0];
 | 
						|
             expect(topPlayer.keyframes).toEqual([
 | 
						|
               {opacity: AUTO_STYLE, offset: 0}, {opacity: .5, offset: 1}
 | 
						|
             ]);
 | 
						|
 | 
						|
             const subPlayer = players[1];
 | 
						|
             expect(subPlayer.keyframes).toEqual([
 | 
						|
               {opacity: .5, offset: 0}, {opacity: .8, offset: 0.5}, {opacity: 1, offset: 1}
 | 
						|
             ]);
 | 
						|
           });
 | 
						|
 | 
						|
        it('should set the easing value as an easing value for the entire timeline', () => {
 | 
						|
          const steps = [
 | 
						|
            style({opacity: 0}), animate(1000, style({opacity: .5})),
 | 
						|
            animate(
 | 
						|
                '1s ease-out',
 | 
						|
                keyframes([style({opacity: .8, offset: .5}), style({opacity: 1, offset: 1})]))
 | 
						|
          ];
 | 
						|
 | 
						|
          const player = invokeAnimationSequence(rootElement, steps)[1];
 | 
						|
          expect(player.easing).toEqual('ease-out');
 | 
						|
        });
 | 
						|
 | 
						|
        it('should combine the starting time + the given delay as the delay value for the animated keyframes',
 | 
						|
           () => {
 | 
						|
             const steps = [
 | 
						|
               style({opacity: 0}), animate(500, style({opacity: .5})),
 | 
						|
               animate(
 | 
						|
                   '1s 2s ease-out',
 | 
						|
                   keyframes([style({opacity: .8, offset: .5}), style({opacity: 1, offset: 1})]))
 | 
						|
             ];
 | 
						|
 | 
						|
             const player = invokeAnimationSequence(rootElement, steps)[1];
 | 
						|
             expect(player.delay).toEqual(2500);
 | 
						|
           });
 | 
						|
 | 
						|
        it('should not leak in additional styles used later on after keyframe styles have already been declared',
 | 
						|
           () => {
 | 
						|
             const steps = [
 | 
						|
               animate(1000, style({height: '50px'})),
 | 
						|
               animate(2000, keyframes([
 | 
						|
                         style({left: '0', top: '0', offset: 0}),
 | 
						|
                         style({left: '40%', top: '50%', offset: .33}),
 | 
						|
                         style({left: '60%', top: '80%', offset: .66}),
 | 
						|
                         style({left: 'calc(100% - 100px)', top: '100%', offset: 1}),
 | 
						|
                       ])),
 | 
						|
               group([animate('2s', style({width: '200px'}))]),
 | 
						|
               animate('2s', style({height: '300px'})),
 | 
						|
               group([animate('2s', style({height: '500px', width: '500px'}))])
 | 
						|
             ];
 | 
						|
 | 
						|
             const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
             expect(players.length).toEqual(5);
 | 
						|
 | 
						|
             const firstPlayerKeyframes = players[0].keyframes;
 | 
						|
             expect(firstPlayerKeyframes[0]['width']).toBeFalsy();
 | 
						|
             expect(firstPlayerKeyframes[1]['width']).toBeFalsy();
 | 
						|
             expect(firstPlayerKeyframes[0]['height']).toEqual(AUTO_STYLE);
 | 
						|
             expect(firstPlayerKeyframes[1]['height']).toEqual('50px');
 | 
						|
 | 
						|
             const keyframePlayerKeyframes = players[1].keyframes;
 | 
						|
             expect(keyframePlayerKeyframes[0]['width']).toBeFalsy();
 | 
						|
             expect(keyframePlayerKeyframes[0]['height']).toBeFalsy();
 | 
						|
 | 
						|
             const groupPlayerKeyframes = players[2].keyframes;
 | 
						|
             expect(groupPlayerKeyframes[0]['width']).toEqual(AUTO_STYLE);
 | 
						|
             expect(groupPlayerKeyframes[1]['width']).toEqual('200px');
 | 
						|
             expect(groupPlayerKeyframes[0]['height']).toBeFalsy();
 | 
						|
             expect(groupPlayerKeyframes[1]['height']).toBeFalsy();
 | 
						|
 | 
						|
             const secondToFinalAnimatePlayerKeyframes = players[3].keyframes;
 | 
						|
             expect(secondToFinalAnimatePlayerKeyframes[0]['width']).toBeFalsy();
 | 
						|
             expect(secondToFinalAnimatePlayerKeyframes[1]['width']).toBeFalsy();
 | 
						|
             expect(secondToFinalAnimatePlayerKeyframes[0]['height']).toEqual('50px');
 | 
						|
             expect(secondToFinalAnimatePlayerKeyframes[1]['height']).toEqual('300px');
 | 
						|
 | 
						|
             const finalAnimatePlayerKeyframes = players[4].keyframes;
 | 
						|
             expect(finalAnimatePlayerKeyframes[0]['width']).toEqual('200px');
 | 
						|
             expect(finalAnimatePlayerKeyframes[1]['width']).toEqual('500px');
 | 
						|
             expect(finalAnimatePlayerKeyframes[0]['height']).toEqual('300px');
 | 
						|
             expect(finalAnimatePlayerKeyframes[1]['height']).toEqual('500px');
 | 
						|
           });
 | 
						|
 | 
						|
        it('should respect offsets if provided directly within the style data', () => {
 | 
						|
          const steps = animate(1000, keyframes([
 | 
						|
                                  style({opacity: 0, offset: 0}), style({opacity: .6, offset: .6}),
 | 
						|
                                  style({opacity: 1, offset: 1})
 | 
						|
                                ]));
 | 
						|
 | 
						|
          const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
          expect(players.length).toEqual(1);
 | 
						|
          const player = players[0];
 | 
						|
 | 
						|
          expect(player.keyframes).toEqual([
 | 
						|
            {opacity: 0, offset: 0}, {opacity: .6, offset: .6}, {opacity: 1, offset: 1}
 | 
						|
          ]);
 | 
						|
        });
 | 
						|
 | 
						|
        it('should respect offsets if provided directly within the style metadata type', () => {
 | 
						|
          const steps =
 | 
						|
              animate(1000, keyframes([
 | 
						|
                        {type: AnimationMetadataType.Style, offset: 0, styles: {opacity: 0}},
 | 
						|
                        {type: AnimationMetadataType.Style, offset: .4, styles: {opacity: .4}},
 | 
						|
                        {type: AnimationMetadataType.Style, offset: 1, styles: {opacity: 1}},
 | 
						|
                      ]));
 | 
						|
 | 
						|
          const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
          expect(players.length).toEqual(1);
 | 
						|
          const player = players[0];
 | 
						|
 | 
						|
          expect(player.keyframes).toEqual([
 | 
						|
            {opacity: 0, offset: 0}, {opacity: .4, offset: .4}, {opacity: 1, offset: 1}
 | 
						|
          ]);
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      describe('group()', () => {
 | 
						|
        it('should properly tally style data within a group() for use in a follow-up animate() step',
 | 
						|
           () => {
 | 
						|
             const steps = [
 | 
						|
               style({width: 0, height: 0}), animate(1000, style({width: 20, height: 50})),
 | 
						|
               group([animate('1s 1s', style({width: 200})), animate('1s', style({height: 500}))]),
 | 
						|
               animate(1000, style({width: 1000, height: 1000}))
 | 
						|
             ];
 | 
						|
 | 
						|
             const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
             expect(players.length).toEqual(4);
 | 
						|
 | 
						|
             const player0 = players[0];
 | 
						|
             expect(player0.duration).toEqual(1000);
 | 
						|
             expect(player0.keyframes).toEqual([
 | 
						|
               {width: 0, height: 0, offset: 0}, {width: 20, height: 50, offset: 1}
 | 
						|
             ]);
 | 
						|
 | 
						|
             const gPlayer1 = players[1];
 | 
						|
             expect(gPlayer1.duration).toEqual(2000);
 | 
						|
             expect(gPlayer1.delay).toEqual(1000);
 | 
						|
             expect(gPlayer1.keyframes).toEqual([
 | 
						|
               {width: 20, offset: 0}, {width: 20, offset: .5}, {width: 200, offset: 1}
 | 
						|
             ]);
 | 
						|
 | 
						|
             const gPlayer2 = players[2];
 | 
						|
             expect(gPlayer2.duration).toEqual(1000);
 | 
						|
             expect(gPlayer2.delay).toEqual(1000);
 | 
						|
             expect(gPlayer2.keyframes).toEqual([
 | 
						|
               {height: 50, offset: 0}, {height: 500, offset: 1}
 | 
						|
             ]);
 | 
						|
 | 
						|
             const player1 = players[3];
 | 
						|
             expect(player1.duration).toEqual(1000);
 | 
						|
             expect(player1.delay).toEqual(3000);
 | 
						|
             expect(player1.keyframes).toEqual([
 | 
						|
               {width: 200, height: 500, offset: 0}, {width: 1000, height: 1000, offset: 1}
 | 
						|
             ]);
 | 
						|
           });
 | 
						|
 | 
						|
        it('should support groups with nested sequences', () => {
 | 
						|
          const steps = [group([
 | 
						|
            sequence([
 | 
						|
              style({opacity: 0}),
 | 
						|
              animate(1000, style({opacity: 1})),
 | 
						|
            ]),
 | 
						|
            sequence([
 | 
						|
              style({width: 0}),
 | 
						|
              animate(1000, style({width: 200})),
 | 
						|
            ])
 | 
						|
          ])];
 | 
						|
 | 
						|
          const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
          expect(players.length).toEqual(2);
 | 
						|
 | 
						|
          const gPlayer1 = players[0];
 | 
						|
          expect(gPlayer1.delay).toEqual(0);
 | 
						|
          expect(gPlayer1.keyframes).toEqual([
 | 
						|
            {opacity: 0, offset: 0},
 | 
						|
            {opacity: 1, offset: 1},
 | 
						|
          ]);
 | 
						|
 | 
						|
          const gPlayer2 = players[1];
 | 
						|
          expect(gPlayer1.delay).toEqual(0);
 | 
						|
          expect(gPlayer2.keyframes).toEqual([{width: 0, offset: 0}, {width: 200, offset: 1}]);
 | 
						|
        });
 | 
						|
 | 
						|
        it('should respect delays after group entries', () => {
 | 
						|
          const steps = [
 | 
						|
            style({width: 0, height: 0}), animate(1000, style({width: 50, height: 50})), group([
 | 
						|
              animate(1000, style({width: 100})),
 | 
						|
              animate(1000, style({height: 100})),
 | 
						|
            ]),
 | 
						|
            animate('1s 1s', style({height: 200, width: 200}))
 | 
						|
          ];
 | 
						|
 | 
						|
          const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
          expect(players.length).toEqual(4);
 | 
						|
 | 
						|
          const finalPlayer = players[players.length - 1];
 | 
						|
          expect(finalPlayer.delay).toEqual(2000);
 | 
						|
          expect(finalPlayer.duration).toEqual(2000);
 | 
						|
          expect(finalPlayer.keyframes).toEqual([
 | 
						|
            {width: 100, height: 100, offset: 0},
 | 
						|
            {width: 100, height: 100, offset: .5},
 | 
						|
            {width: 200, height: 200, offset: 1},
 | 
						|
          ]);
 | 
						|
        });
 | 
						|
 | 
						|
        it('should respect delays after multiple calls to group()', () => {
 | 
						|
          const steps = [
 | 
						|
            group([animate('2s', style({opacity: 1})), animate('2s', style({width: '100px'}))]),
 | 
						|
            animate(2000, style({width: 0, opacity: 0})),
 | 
						|
            group([animate('2s', style({opacity: 1})), animate('2s', style({width: '200px'}))]),
 | 
						|
            animate(2000, style({width: 0, opacity: 0}))
 | 
						|
          ];
 | 
						|
 | 
						|
          const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
          const middlePlayer = players[2];
 | 
						|
          expect(middlePlayer.delay).toEqual(2000);
 | 
						|
          expect(middlePlayer.duration).toEqual(2000);
 | 
						|
 | 
						|
          const finalPlayer = players[players.length - 1];
 | 
						|
          expect(finalPlayer.delay).toEqual(6000);
 | 
						|
          expect(finalPlayer.duration).toEqual(2000);
 | 
						|
        });
 | 
						|
 | 
						|
        it('should push the start of a group if a delay option is provided', () => {
 | 
						|
          const steps = [
 | 
						|
            style({width: '0px', height: '0px'}),
 | 
						|
            animate(1500, style({width: '100px', height: '100px'})),
 | 
						|
            group(
 | 
						|
                [
 | 
						|
                  animate(1000, style({width: '200px'})),
 | 
						|
                  animate(2000, style({height: '200px'})),
 | 
						|
                ],
 | 
						|
                {delay: 300})
 | 
						|
          ];
 | 
						|
 | 
						|
          const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
          const finalWidthPlayer = players[players.length - 2];
 | 
						|
          const finalHeightPlayer = players[players.length - 1];
 | 
						|
 | 
						|
          expect(finalWidthPlayer.delay).toEqual(1800);
 | 
						|
          expect(finalWidthPlayer.keyframes).toEqual([
 | 
						|
            {width: '100px', offset: 0},
 | 
						|
            {width: '200px', offset: 1},
 | 
						|
          ]);
 | 
						|
 | 
						|
          expect(finalHeightPlayer.delay).toEqual(1800);
 | 
						|
          expect(finalHeightPlayer.keyframes).toEqual([
 | 
						|
            {height: '100px', offset: 0},
 | 
						|
            {height: '200px', offset: 1},
 | 
						|
          ]);
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      describe('query()', () => {
 | 
						|
        it('should delay the query operation if a delay option is provided', () => {
 | 
						|
          const steps = [
 | 
						|
            style({opacity: 0}), animate(1000, style({opacity: 1})),
 | 
						|
            query(
 | 
						|
                'div',
 | 
						|
                [
 | 
						|
                  style({width: 0}),
 | 
						|
                  animate(500, style({width: 200})),
 | 
						|
                ],
 | 
						|
                {delay: 200})
 | 
						|
          ];
 | 
						|
 | 
						|
          const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
          const finalPlayer = players[players.length - 1];
 | 
						|
          expect(finalPlayer.delay).toEqual(1200);
 | 
						|
        });
 | 
						|
 | 
						|
        it('should throw an error when an animation query returns zero elements', () => {
 | 
						|
          const steps =
 | 
						|
              [query('somethingFake', [style({opacity: 0}), animate(1000, style({opacity: 1}))])];
 | 
						|
 | 
						|
          expect(() => { invokeAnimationSequence(rootElement, steps); })
 | 
						|
              .toThrowError(
 | 
						|
                  /`query\("somethingFake"\)` returned zero elements\. \(Use `query\("somethingFake", \{ optional: true \}\)` if you wish to allow this\.\)/);
 | 
						|
        });
 | 
						|
 | 
						|
        it('should allow a query to be skipped if it is set as optional and returns zero elements',
 | 
						|
           () => {
 | 
						|
             const steps = [query(
 | 
						|
                 'somethingFake', [style({opacity: 0}), animate(1000, style({opacity: 1}))],
 | 
						|
                 {optional: true})];
 | 
						|
 | 
						|
             expect(() => { invokeAnimationSequence(rootElement, steps); }).not.toThrow();
 | 
						|
 | 
						|
             const steps2 = [query(
 | 
						|
                 'fakeSomethings', [style({opacity: 0}), animate(1000, style({opacity: 1}))],
 | 
						|
                 {optional: true})];
 | 
						|
 | 
						|
             expect(() => { invokeAnimationSequence(rootElement, steps2); }).not.toThrow();
 | 
						|
           });
 | 
						|
 | 
						|
        it('should delay the query operation if a delay option is provided', () => {
 | 
						|
          const steps = [
 | 
						|
            style({opacity: 0}), animate(1300, style({opacity: 1})),
 | 
						|
            query(
 | 
						|
                'div',
 | 
						|
                [
 | 
						|
                  style({width: 0}),
 | 
						|
                  animate(500, style({width: 200})),
 | 
						|
                ],
 | 
						|
                {delay: 300})
 | 
						|
          ];
 | 
						|
 | 
						|
          const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
          const fp1 = players[players.length - 2];
 | 
						|
          const fp2 = players[players.length - 1];
 | 
						|
          expect(fp1.delay).toEqual(1600);
 | 
						|
          expect(fp2.delay).toEqual(1600);
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      describe('timing values', () => {
 | 
						|
        it('should properly combine an easing value with a delay into a set of three keyframes',
 | 
						|
           () => {
 | 
						|
             const steps: AnimationMetadata[] =
 | 
						|
                 [style({opacity: 0}), animate('3s 1s ease-out', style({opacity: 1}))];
 | 
						|
 | 
						|
             const player = invokeAnimationSequence(rootElement, steps)[0];
 | 
						|
             expect(player.keyframes).toEqual([
 | 
						|
               {opacity: 0, offset: 0}, {opacity: 0, offset: .25, easing: 'ease-out'},
 | 
						|
               {opacity: 1, offset: 1}
 | 
						|
             ]);
 | 
						|
           });
 | 
						|
 | 
						|
        it('should allow easing values to exist for each animate() step', () => {
 | 
						|
          const steps: AnimationMetadata[] = [
 | 
						|
            style({width: 0}), animate('1s linear', style({width: 10})),
 | 
						|
            animate('2s ease-out', style({width: 20})), animate('1s ease-in', style({width: 30}))
 | 
						|
          ];
 | 
						|
 | 
						|
          const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
          expect(players.length).toEqual(1);
 | 
						|
 | 
						|
          const player = players[0];
 | 
						|
          expect(player.keyframes).toEqual([
 | 
						|
            {width: 0, offset: 0, easing: 'linear'}, {width: 10, offset: .25, easing: 'ease-out'},
 | 
						|
            {width: 20, offset: .75, easing: 'ease-in'}, {width: 30, offset: 1}
 | 
						|
          ]);
 | 
						|
        });
 | 
						|
 | 
						|
        it('should produce a top-level timeline only for the duration that is set as before a group kicks in',
 | 
						|
           () => {
 | 
						|
             const steps: AnimationMetadata[] = [
 | 
						|
               style({width: 0, height: 0, opacity: 0}),
 | 
						|
               animate('1s', style({width: 100, height: 100, opacity: .2})), group([
 | 
						|
                 animate('500ms 1s', style({width: 500})), animate('1s', style({height: 500})),
 | 
						|
                 sequence([
 | 
						|
                   animate(500, style({opacity: .5})),
 | 
						|
                   animate(500, style({opacity: .6})),
 | 
						|
                   animate(500, style({opacity: .7})),
 | 
						|
                   animate(500, style({opacity: 1})),
 | 
						|
                 ])
 | 
						|
               ])
 | 
						|
             ];
 | 
						|
 | 
						|
             const player = invokeAnimationSequence(rootElement, steps)[0];
 | 
						|
             expect(player.duration).toEqual(1000);
 | 
						|
             expect(player.delay).toEqual(0);
 | 
						|
           });
 | 
						|
 | 
						|
        it('should offset group() and keyframe() timelines with a delay which is the current time of the previous player when called',
 | 
						|
           () => {
 | 
						|
             const steps: AnimationMetadata[] = [
 | 
						|
               style({width: 0, height: 0}),
 | 
						|
               animate('1500ms linear', style({width: 10, height: 10})), group([
 | 
						|
                 animate(1000, style({width: 500, height: 500})),
 | 
						|
                 animate(2000, style({width: 500, height: 500}))
 | 
						|
               ]),
 | 
						|
               animate(1000, keyframes([
 | 
						|
                         style({width: 200}),
 | 
						|
                         style({width: 500}),
 | 
						|
                       ]))
 | 
						|
             ];
 | 
						|
 | 
						|
             const players = invokeAnimationSequence(rootElement, steps);
 | 
						|
             expect(players[0].delay).toEqual(0);     // top-level animation
 | 
						|
             expect(players[1].delay).toEqual(1500);  // first entry in group()
 | 
						|
             expect(players[2].delay).toEqual(1500);  // second entry in group()
 | 
						|
             expect(players[3].delay).toEqual(3500);  // animate(...keyframes())
 | 
						|
           });
 | 
						|
      });
 | 
						|
 | 
						|
      describe('state based data', () => {
 | 
						|
        it('should create an empty animation if there are zero animation steps', () => {
 | 
						|
          const steps: AnimationMetadata[] = [];
 | 
						|
 | 
						|
          const fromStyles: ɵStyleData[] = [{background: 'blue', height: 100}];
 | 
						|
 | 
						|
          const toStyles: ɵStyleData[] = [{background: 'red'}];
 | 
						|
 | 
						|
          const player = invokeAnimationSequence(rootElement, steps, {}, fromStyles, toStyles)[0];
 | 
						|
          expect(player.duration).toEqual(0);
 | 
						|
          expect(player.keyframes).toEqual([]);
 | 
						|
        });
 | 
						|
 | 
						|
        it('should produce an animation from start to end between the to and from styles if there are animate steps in between',
 | 
						|
           () => {
 | 
						|
             const steps: AnimationMetadata[] = [animate(1000)];
 | 
						|
 | 
						|
             const fromStyles: ɵStyleData[] = [{background: 'blue', height: 100}];
 | 
						|
 | 
						|
             const toStyles: ɵStyleData[] = [{background: 'red'}];
 | 
						|
 | 
						|
             const players = invokeAnimationSequence(rootElement, steps, {}, fromStyles, toStyles);
 | 
						|
             expect(players[0].keyframes).toEqual([
 | 
						|
               {background: 'blue', height: 100, offset: 0},
 | 
						|
               {background: 'red', height: AUTO_STYLE, offset: 1}
 | 
						|
             ]);
 | 
						|
           });
 | 
						|
 | 
						|
        it('should produce an animation from start to end between the to and from styles if there are animate steps in between with an easing value',
 | 
						|
           () => {
 | 
						|
             const steps: AnimationMetadata[] = [animate('1s ease-out')];
 | 
						|
 | 
						|
             const fromStyles: ɵStyleData[] = [{background: 'blue'}];
 | 
						|
 | 
						|
             const toStyles: ɵStyleData[] = [{background: 'red'}];
 | 
						|
 | 
						|
             const players = invokeAnimationSequence(rootElement, steps, {}, fromStyles, toStyles);
 | 
						|
             expect(players[0].keyframes).toEqual([
 | 
						|
               {background: 'blue', offset: 0, easing: 'ease-out'},
 | 
						|
               {background: 'red', offset: 1}
 | 
						|
             ]);
 | 
						|
           });
 | 
						|
      });
 | 
						|
    });
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
function humanizeOffsets(keyframes: ɵStyleData[], digits: number = 3): ɵStyleData[] {
 | 
						|
  return keyframes.map(keyframe => {
 | 
						|
    keyframe['offset'] = Number(parseFloat(<any>keyframe['offset']).toFixed(digits));
 | 
						|
    return keyframe;
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
function invokeAnimationSequence(
 | 
						|
    element: any, steps: AnimationMetadata | AnimationMetadata[], locals: {[key: string]: any} = {},
 | 
						|
    startingStyles: ɵStyleData[] = [], destinationStyles: ɵStyleData[] = [],
 | 
						|
    subInstructions?: ElementInstructionMap): AnimationTimelineInstruction[] {
 | 
						|
  const driver = new MockAnimationDriver();
 | 
						|
  return new Animation(driver, steps)
 | 
						|
      .buildTimelines(element, startingStyles, destinationStyles, locals, subInstructions);
 | 
						|
}
 | 
						|
 | 
						|
function validateAndThrowAnimationSequence(steps: AnimationMetadata | AnimationMetadata[]) {
 | 
						|
  const driver = new MockAnimationDriver();
 | 
						|
  const errors: any[] = [];
 | 
						|
  const ast = buildAnimationAst(driver, steps, errors);
 | 
						|
  if (errors.length) {
 | 
						|
    throw new Error(errors.join('\n'));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
function buildParams(params: {[name: string]: any}): AnimationOptions {
 | 
						|
  return <AnimationOptions>{params};
 | 
						|
}
 |