| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @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
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  | import {AnimationOptions, animate, state, style, transition} from '@angular/animations'; | 
					
						
							|  |  |  | import {AnimationTransitionInstruction} from '@angular/animations/browser/src/dsl/animation_transition_instruction'; | 
					
						
							|  |  |  | import {AnimationTrigger} from '@angular/animations/browser/src/dsl/animation_trigger'; | 
					
						
							| 
									
										
										
										
											2017-02-22 15:14:49 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-02 15:45:48 -07:00
										 |  |  | import {MockAnimationDriver} from '../../testing'; | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  | import {makeTrigger} from '../shared'; | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | export function main() { | 
					
						
							|  |  |  |   describe('AnimationTrigger', () => { | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |     // these tests are only mean't to be run within the DOM (for now)
 | 
					
						
							|  |  |  |     if (typeof Element == 'undefined') return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     let element: any; | 
					
						
							|  |  |  |     beforeEach(() => { | 
					
						
							|  |  |  |       element = document.createElement('div'); | 
					
						
							|  |  |  |       document.body.appendChild(element); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     afterEach(() => { document.body.removeChild(element); }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  |     describe('trigger validation', () => { | 
					
						
							|  |  |  |       it('should group errors together for an animation trigger', () => { | 
					
						
							|  |  |  |         expect(() => { | 
					
						
							| 
									
										
										
										
											2017-02-22 15:14:49 -08:00
										 |  |  |           makeTrigger('myTrigger', [transition('12345', animate(3333))]); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  |         }).toThrowError(/Animation parsing for the myTrigger trigger have failed/); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should throw an error when a transition within a trigger contains an invalid expression', | 
					
						
							|  |  |  |          () => { | 
					
						
							| 
									
										
										
										
											2017-02-22 15:14:49 -08:00
										 |  |  |            expect( | 
					
						
							|  |  |  |                () => { makeTrigger('name', [transition('somethingThatIsWrong', animate(3333))]); }) | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  |                .toThrowError( | 
					
						
							|  |  |  |                    /- The provided transition expression "somethingThatIsWrong" is not supported/); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should throw an error if an animation alias is used that is not yet supported', () => { | 
					
						
							|  |  |  |         expect(() => { | 
					
						
							| 
									
										
										
										
											2017-02-22 15:14:49 -08:00
										 |  |  |           makeTrigger('name', [transition(':angular', animate(3333))]); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  |         }).toThrowError(/- The transition alias value ":angular" is not supported/); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     describe('trigger usage', () => { | 
					
						
							|  |  |  |       it('should construct a trigger based on the states and transition data', () => { | 
					
						
							| 
									
										
										
										
											2017-02-22 15:14:49 -08:00
										 |  |  |         const result = makeTrigger('name', [ | 
					
						
							| 
									
										
										
										
											2017-08-07 11:40:04 -07:00
										 |  |  |           state('on', style({width: 0})), | 
					
						
							|  |  |  |           state('off', style({width: 100})), | 
					
						
							|  |  |  |           transition('on => off', animate(1000)), | 
					
						
							|  |  |  |           transition('off => on', animate(1000)), | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  |         ]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-07 11:40:04 -07:00
										 |  |  |         expect(result.states['on'].buildStyles({}, [])).toEqual({width: 0}); | 
					
						
							|  |  |  |         expect(result.states['off'].buildStyles({}, [])).toEqual({width: 100}); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  |         expect(result.transitionFactories.length).toEqual(2); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-15 09:24:09 -07:00
										 |  |  |       it('should allow multiple state values to use the same styles', () => { | 
					
						
							|  |  |  |         const result = makeTrigger('name', [ | 
					
						
							|  |  |  |           state('on, off', style({width: 50})), transition('on => off', animate(1000)), | 
					
						
							|  |  |  |           transition('off => on', animate(1000)) | 
					
						
							|  |  |  |         ]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-07 11:40:04 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         expect(result.states['on'].buildStyles({}, [])).toEqual({width: 50}); | 
					
						
							|  |  |  |         expect(result.states['off'].buildStyles({}, [])).toEqual({width: 50}); | 
					
						
							| 
									
										
										
										
											2017-03-15 09:24:09 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  |       it('should find the first transition that matches', () => { | 
					
						
							| 
									
										
										
										
											2017-02-22 15:14:49 -08:00
										 |  |  |         const result = makeTrigger( | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  |             'name', [transition('a => b', animate(1234)), transition('b => c', animate(5678))]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |         const trans = buildTransition(result, element, 'b', 'c') !; | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  |         expect(trans.timelines.length).toEqual(1); | 
					
						
							|  |  |  |         const timeline = trans.timelines[0]; | 
					
						
							|  |  |  |         expect(timeline.duration).toEqual(5678); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should find a transition with a `*` value', () => { | 
					
						
							| 
									
										
										
										
											2017-02-22 15:14:49 -08:00
										 |  |  |         const result = makeTrigger('name', [ | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  |           transition('* => b', animate(1234)), transition('b => *', animate(5678)), | 
					
						
							|  |  |  |           transition('* => *', animate(9999)) | 
					
						
							|  |  |  |         ]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |         let trans = buildTransition(result, element, 'b', 'c') !; | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  |         expect(trans.timelines[0].duration).toEqual(5678); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |         trans = buildTransition(result, element, 'a', 'b') !; | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  |         expect(trans.timelines[0].duration).toEqual(1234); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |         trans = buildTransition(result, element, 'c', 'c') !; | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  |         expect(trans.timelines[0].duration).toEqual(9999); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should null when no results are found', () => { | 
					
						
							| 
									
										
										
										
											2017-02-22 15:14:49 -08:00
										 |  |  |         const result = makeTrigger('name', [transition('a => b', animate(1111))]); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |         const trigger = result.matchTransition('b', 'a'); | 
					
						
							|  |  |  |         expect(trigger).toBeFalsy(); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |       it('should support bi-directional transition expressions', () => { | 
					
						
							|  |  |  |         const result = makeTrigger('name', [transition('a <=> b', animate(2222))]); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |         const t1 = buildTransition(result, element, 'a', 'b') !; | 
					
						
							|  |  |  |         expect(t1.timelines[0].duration).toEqual(2222); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const t2 = buildTransition(result, element, 'b', 'a') !; | 
					
						
							|  |  |  |         expect(t2.timelines[0].duration).toEqual(2222); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should support multiple transition statements in one string', () => { | 
					
						
							|  |  |  |         const result = makeTrigger('name', [transition('a => b, b => a, c => *', animate(1234))]); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |         const t1 = buildTransition(result, element, 'a', 'b') !; | 
					
						
							|  |  |  |         expect(t1.timelines[0].duration).toEqual(1234); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |         const t2 = buildTransition(result, element, 'b', 'a') !; | 
					
						
							|  |  |  |         expect(t2.timelines[0].duration).toEqual(1234); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |         const t3 = buildTransition(result, element, 'c', 'a') !; | 
					
						
							|  |  |  |         expect(t3.timelines[0].duration).toEqual(1234); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |       describe('params', () => { | 
					
						
							|  |  |  |         it('should support transition-level animation variable params', () => { | 
					
						
							|  |  |  |           const result = makeTrigger( | 
					
						
							|  |  |  |               'name', | 
					
						
							|  |  |  |               [transition( | 
					
						
							|  |  |  |                   'a => b', [style({height: '{{ a }}'}), animate(1000, style({height: '{{ b }}'}))], | 
					
						
							|  |  |  |                   buildParams({a: '100px', b: '200px'}))]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           const trans = buildTransition(result, element, 'a', 'b') !; | 
					
						
							|  |  |  |           const keyframes = trans.timelines[0].keyframes; | 
					
						
							|  |  |  |           expect(keyframes).toEqual([{height: '100px', offset: 0}, {height: '200px', offset: 1}]); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |         it('should subtitute variable params provided directly within the transition match', () => { | 
					
						
							|  |  |  |           const result = makeTrigger( | 
					
						
							|  |  |  |               'name', | 
					
						
							|  |  |  |               [transition( | 
					
						
							|  |  |  |                   'a => b', [style({height: '{{ a }}'}), animate(1000, style({height: '{{ b }}'}))], | 
					
						
							|  |  |  |                   buildParams({a: '100px', b: '200px'}))]); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-07 11:40:04 -07:00
										 |  |  |           const trans = buildTransition(result, element, 'a', 'b', {}, buildParams({a: '300px'})) !; | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |           const keyframes = trans.timelines[0].keyframes; | 
					
						
							|  |  |  |           expect(keyframes).toEqual([{height: '300px', offset: 0}, {height: '200px', offset: 1}]); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |       it('should match `true` and `false` given boolean values', () => { | 
					
						
							|  |  |  |         const result = makeTrigger('name', [ | 
					
						
							|  |  |  |           state('false', style({color: 'red'})), state('true', style({color: 'green'})), | 
					
						
							|  |  |  |           transition('true <=> false', animate(1234)) | 
					
						
							|  |  |  |         ]); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |         const trans = buildTransition(result, element, false, true) !; | 
					
						
							|  |  |  |         expect(trans.timelines[0].duration).toEqual(1234); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |       it('should match `1` and `0` given boolean values', () => { | 
					
						
							|  |  |  |         const result = makeTrigger('name', [ | 
					
						
							|  |  |  |           state('0', style({color: 'red'})), state('1', style({color: 'green'})), | 
					
						
							|  |  |  |           transition('1 <=> 0', animate(4567)) | 
					
						
							|  |  |  |         ]); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |         const trans = buildTransition(result, element, false, true) !; | 
					
						
							|  |  |  |         expect(trans.timelines[0].duration).toEqual(4567); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |       it('should match `true` and `false` state styles on a `1 <=> 0` boolean transition given boolean values', | 
					
						
							|  |  |  |          () => { | 
					
						
							|  |  |  |            const result = makeTrigger('name', [ | 
					
						
							|  |  |  |              state('false', style({color: 'red'})), state('true', style({color: 'green'})), | 
					
						
							|  |  |  |              transition('1 <=> 0', animate(4567)) | 
					
						
							|  |  |  |            ]); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |            const trans = buildTransition(result, element, false, true) !; | 
					
						
							|  |  |  |            expect(trans.timelines[0].keyframes).toEqual([ | 
					
						
							|  |  |  |              {offset: 0, color: 'red'}, {offset: 1, color: 'green'} | 
					
						
							| 
									
										
										
										
											2017-07-27 16:13:16 -07:00
										 |  |  |            ]); | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |          }); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |       it('should match `1` and `0` state styles on a `true <=> false` boolean transition given boolean values', | 
					
						
							|  |  |  |          () => { | 
					
						
							|  |  |  |            const result = makeTrigger('name', [ | 
					
						
							|  |  |  |              state('0', style({color: 'orange'})), state('1', style({color: 'blue'})), | 
					
						
							|  |  |  |              transition('true <=> false', animate(4567)) | 
					
						
							|  |  |  |            ]); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |            const trans = buildTransition(result, element, false, true) !; | 
					
						
							|  |  |  |            expect(trans.timelines[0].keyframes).toEqual([ | 
					
						
							|  |  |  |              {offset: 0, color: 'orange'}, {offset: 1, color: 'blue'} | 
					
						
							| 
									
										
										
										
											2017-07-27 16:13:16 -07:00
										 |  |  |            ]); | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |          }); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |       describe('aliases', () => { | 
					
						
							|  |  |  |         it('should alias the :enter transition as void => *', () => { | 
					
						
							| 
									
										
										
										
											2017-02-22 15:14:49 -08:00
										 |  |  |           const result = makeTrigger('name', [transition(':enter', animate(3333))]); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |           const trans = buildTransition(result, element, 'void', 'something') !; | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  |           expect(trans.timelines[0].duration).toEqual(3333); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         it('should alias the :leave transition as * => void', () => { | 
					
						
							| 
									
										
										
										
											2017-02-22 15:14:49 -08:00
										 |  |  |           const result = makeTrigger('name', [transition(':leave', animate(3333))]); | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |           const trans = buildTransition(result, element, 'something', 'void') !; | 
					
						
							| 
									
										
										
										
											2017-01-26 11:16:51 -08:00
										 |  |  |           expect(trans.timelines[0].duration).toEqual(3333); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | function buildTransition( | 
					
						
							|  |  |  |     trigger: AnimationTrigger, element: any, fromState: any, toState: any, | 
					
						
							| 
									
										
										
										
											2017-08-07 11:40:04 -07:00
										 |  |  |     fromOptions?: AnimationOptions, toOptions?: AnimationOptions): AnimationTransitionInstruction| | 
					
						
							|  |  |  |     null { | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |   const trans = trigger.matchTransition(fromState, toState) !; | 
					
						
							|  |  |  |   if (trans) { | 
					
						
							| 
									
										
										
										
											2017-05-02 15:45:48 -07:00
										 |  |  |     const driver = new MockAnimationDriver(); | 
					
						
							| 
									
										
										
										
											2017-08-07 11:40:04 -07:00
										 |  |  |     return trans.build(driver, element, fromState, toState, fromOptions, toOptions) !; | 
					
						
							| 
									
										
										
										
											2017-04-26 10:44:28 -07:00
										 |  |  |   } | 
					
						
							|  |  |  |   return null; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function buildParams(params: {[name: string]: any}): AnimationOptions { | 
					
						
							|  |  |  |   return <AnimationOptions>{params}; | 
					
						
							|  |  |  | } |