1
2function leftAsNumber(target) {
3  var left = getComputedStyle(target).left;
4  return Number(left.substring(0, left.length - 2));
5}
6
7suite('effect', function() {
8  // Test normalize.
9  test('Normalize keyframes with all offsets specified but not sorted by offset. Some offsets are out of [0, 1] range.', function() {
10    var normalizedKeyframes;
11    assert.throws(function() {
12      normalizedKeyframes = normalizeKeyframes([
13        {offset: 0},
14        {offset: -1},
15        {offset: 1},
16        {offset: 0.5},
17        {offset: 2}
18      ]);
19    });
20  });
21
22  test('Normalize keyframes with some offsets not specified, and not sorted by offset.', function() {
23    assert.throws(function() {
24      normalizeKeyframes([
25        {offset: 0.5},
26        {offset: 0},
27        {offset: 0.8},
28        {},
29        {offset: 1}
30      ]);
31    });
32  });
33
34  test('Normalize keyframes with some offsets not specified, and not sorted by offset. Out of order keyframes are out of [0, 1] range.', function() {
35    assert.throws(function() {
36      normalizeKeyframes([
37        {offset: 0},
38        {offset: -1},
39        {offset: 0.5},
40        {},
41        {offset: 1}
42      ]);
43    });
44  });
45
46  test('Normalize keyframes with some offsets not specified, but sorted by offset where specified. Some offsets are out of [0, 1] range.', function() {
47    var normalizedKeyframes;
48    assert.doesNotThrow(function() {
49      normalizedKeyframes = normalizeKeyframes([
50        {offset: -1},
51        {offset: 0},
52        {offset: 0.5},
53        {},
54        {},
55        {offset: 2}
56      ]);
57    });
58    assert.equal(normalizedKeyframes.length, 4);
59    assert.closeTo(normalizedKeyframes[0].offset, 0, 0.001);
60    assert.closeTo(normalizedKeyframes[1].offset, 0.5, 0.001);
61    assert.closeTo(normalizedKeyframes[2].offset, 0.75, 0.001);
62    assert.closeTo(normalizedKeyframes[3].offset, 1, 0.001);
63  });
64
65  test('Normalize keyframes with some offsets not specified, but sorted by offset where specified. All specified offsets in [0, 1] range.', function() {
66    var normalizedKeyframes;
67    assert.doesNotThrow(function() {
68      normalizedKeyframes = normalizeKeyframes([
69        {left: '0px', offset: 0},
70        {left: '10px'},
71        {left: '20px'},
72        {left: '30px', offset: 0.6},
73        {left: '40px'},
74        {left: '50px'}
75      ]);
76    });
77    assert.equal(normalizedKeyframes.length, 6);
78    assert.closeTo(normalizedKeyframes[0].offset, 0, 0.001);
79    assert.equal(normalizedKeyframes[0].left, '0px');
80    assert.closeTo(normalizedKeyframes[1].offset, 0.2, 0.001);
81    assert.equal(normalizedKeyframes[1].left, '10px');
82    assert.closeTo(normalizedKeyframes[2].offset, 0.4, 0.001);
83    assert.equal(normalizedKeyframes[2].left, '20px');
84    assert.closeTo(normalizedKeyframes[3].offset, 0.6, 0.001);
85    assert.equal(normalizedKeyframes[3].left, '30px');
86    assert.closeTo(normalizedKeyframes[4].offset, 0.8, 0.001);
87    assert.equal(normalizedKeyframes[4].left, '40px');
88    assert.closeTo(normalizedKeyframes[5].offset, 1, 0.001);
89    assert.equal(normalizedKeyframes[5].left, '50px');
90  });
91
92  test('Normalize keyframes with no offsets specified.', function() {
93    var normalizedKeyframes;
94    assert.doesNotThrow(function() {
95      normalizedKeyframes = normalizeKeyframes([
96        {left: '0px'},
97        {left: '10px'},
98        {left: '20px'},
99        {left: '30px'},
100        {left: '40px'}
101      ]);
102    });
103    assert.equal(normalizedKeyframes.length, 5);
104    assert.closeTo(normalizedKeyframes[0].offset, 0, 0.001);
105    assert.equal(normalizedKeyframes[0].left, '0px');
106    assert.closeTo(normalizedKeyframes[1].offset, 0.25, 0.001);
107    assert.equal(normalizedKeyframes[1].left, '10px');
108    assert.closeTo(normalizedKeyframes[2].offset, 0.5, 0.001);
109    assert.equal(normalizedKeyframes[2].left, '20px');
110    assert.closeTo(normalizedKeyframes[3].offset, 0.75, 0.001);
111    assert.equal(normalizedKeyframes[3].left, '30px');
112    assert.closeTo(normalizedKeyframes[4].offset, 1, 0.001);
113    assert.equal(normalizedKeyframes[4].left, '40px');
114  });
115
116  test('Normalize keyframes where a keyframe has an offset that is not a number.', function() {
117    assert.throws(function() {
118      normalizeKeyframes([
119        {offset: 0},
120        {offset: 'one'},
121        {offset: 1}
122      ]);
123    });
124  });
125
126  test('Normalize keyframes where a keyframe has an offset that is a numeric string.', function() {
127    var normalizedKeyframes;
128    assert.doesNotThrow(function() {
129      normalizedKeyframes = normalizeKeyframes([
130        {offset: 0},
131        {offset: '0.5'},
132        {offset: 1}
133      ]);
134    });
135    assert.equal(normalizedKeyframes.length, 3);
136    assert.closeTo(normalizedKeyframes[0].offset, 0, 0.001);
137    assert.closeTo(normalizedKeyframes[1].offset, 0.5, 0.001);
138    assert.closeTo(normalizedKeyframes[2].offset, 1, 0.001);
139  });
140
141  test('Normalize keyframes where some keyframes have easings.', function() {
142    var normalizedKeyframes;
143    assert.doesNotThrow(function() {
144      normalizedKeyframes = normalizeKeyframes([
145        {left: '0px', easing: 'ease-in'},
146        {left: '10px'},
147        {left: '0px'}
148      ]);
149    });
150  });
151
152  test('Normalize keyframes with invalid specified easing.', function() {
153    var normalizedKeyframes;
154    assert.doesNotThrow(function() {
155      normalizedKeyframes = normalizeKeyframes([
156        {left: '0px', easing: 'easy-peasy'},
157        {left: '10px'},
158        {left: '0px'}
159      ]);
160    });
161    assert.equal('' + normalizedKeyframes[0].easing, 'function (x) { return x; }');
162  });
163
164  test('Normalize keyframes where some properties are given non-string, non-number values.', function() {
165    var normalizedKeyframes;
166    assert.doesNotThrow(function() {
167      normalizedKeyframes = normalizeKeyframes([
168        {left: {}},
169        {left: '100px'},
170        {left: []}
171      ]);
172    });
173    assert(normalizedKeyframes.length, 3);
174    assert.equal(normalizedKeyframes[0].left, '[object Object]');
175    assert.equal(normalizedKeyframes[1].left, '100px');
176    assert.equal(normalizedKeyframes[2].left, '');
177  });
178
179  test('Normalize input that is not an array.', function() {
180    assert.throws(function() {
181      normalizeKeyframes(10);
182    });
183  });
184
185  test('Normalize an empty array.', function() {
186    var normalizedKeyframes;
187    assert.doesNotThrow(function() {
188      normalizedKeyframes = normalizeKeyframes([]);
189    });
190    assert.deepEqual(normalizedKeyframes, []);
191  });
192
193  test('Normalize null.', function() {
194    var normalizedKeyframes;
195    assert.doesNotThrow(function() {
196      normalizedKeyframes = normalizeKeyframes(null);
197    });
198    assert.deepEqual(normalizedKeyframes, []);
199  });
200
201  test('Normalize shorthands.', function() {
202    var normalizedKeyframes;
203    assert.doesNotThrow(function() {
204      normalizedKeyframes = normalizeKeyframes([{borderColor: 'purple green orange blue'}, {borderColor: 'red'}]);
205    });
206    assert.equal(normalizedKeyframes[0].borderTopColor, 'purple');
207    assert.equal(normalizedKeyframes[0].borderRightColor, 'green');
208    assert.equal(normalizedKeyframes[0].borderBottomColor, 'orange');
209    assert.equal(normalizedKeyframes[0].borderLeftColor, 'blue');
210    assert.equal(normalizedKeyframes[1].borderTopColor, 'red');
211    assert.equal(normalizedKeyframes[1].borderRightColor, 'red');
212    assert.equal(normalizedKeyframes[1].borderBottomColor, 'red');
213    assert.equal(normalizedKeyframes[1].borderLeftColor, 'red');
214
215    assert.doesNotThrow(function() {
216      normalizedKeyframes = normalizeKeyframes([{font: 'italic bold 20pt / 200% serif'}, {font: 'italic normal bold 50pt serif'}]);
217    });
218    assert.equal(normalizedKeyframes[0].fontStyle, 'italic');
219    assert.equal(normalizedKeyframes[0].fontVariant, 'normal');
220    assert.equal(normalizedKeyframes[0].fontWeight, '700');
221    assert.equal(normalizedKeyframes[0].fontSize, '20pt');
222    assert.equal(normalizedKeyframes[0].lineHeight, '200%');
223    assert.equal(normalizedKeyframes[0].fontFamily, 'serif');
224    assert.equal(normalizedKeyframes[1].fontStyle, 'italic');
225    assert.equal(normalizedKeyframes[1].fontVariant, 'normal');
226    assert.equal(normalizedKeyframes[1].fontWeight, '700');
227    assert.equal(normalizedKeyframes[1].fontSize, '50pt');
228    assert.equal(normalizedKeyframes[1].lineHeight, 'normal');
229    assert.equal(normalizedKeyframes[1].fontFamily, 'serif');
230  });
231
232  // Test makePropertySpecificKeyframeGroups.
233  test('Make property specific keyframe groups for a simple effect with one property.', function() {
234    var groups;
235    assert.doesNotThrow(function() {
236      groups = makePropertySpecificKeyframeGroups(normalizeKeyframes([
237        {left: '0px'},
238        {left: '200px', offset: 0.3},
239        {left: '0px'}
240      ]));
241    });
242    assert.equal(Object.getOwnPropertyNames(groups).length, 1);
243    assert.equal(groups.left.length, 3);
244    assert.closeTo(groups.left[0].offset, 0, 0.001);
245    assert.equal(groups.left[0].value, '0px');
246    assert.closeTo(groups.left[1].offset, 0.3, 0.001);
247    assert.equal(groups.left[1].value, '200px');
248    assert.closeTo(groups.left[2].offset, 1, 0.001);
249    assert.equal(groups.left[2].value, '0px');
250  });
251
252  test('Make property specific keyframe groups for an effect with three properties.', function() {
253    var groups;
254    assert.doesNotThrow(function() {
255      groups = makePropertySpecificKeyframeGroups(normalizeKeyframes([
256        {left: '0px', top: '200px', opacity: 1},
257        {left: '200px', top: '0px'},
258        {left: '0px', top: '200px', opacity: 0},
259        {top: '0px', opacity: 1},
260        {left: '200px', top: '200px', opacity: 0}
261      ]));
262    });
263    assert.equal(Object.getOwnPropertyNames(groups).length, 3);
264
265    assert.equal(groups.left.length, 4);
266    assert.closeTo(groups.left[0].offset, 0, 0.001);
267    assert.equal(groups.left[0].value, '0px');
268    assert.closeTo(groups.left[1].offset, 0.25, 0.001);
269    assert.equal(groups.left[1].value, '200px');
270    assert.closeTo(groups.left[2].offset, 0.5, 0.001);
271    assert.equal(groups.left[2].value, '0px');
272    assert.closeTo(groups.left[3].offset, 1, 0.001);
273    assert.equal(groups.left[3].value, '200px');
274
275    assert.equal(groups.top.length, 5);
276    assert.closeTo(groups.top[0].offset, 0, 0.001);
277    assert.equal(groups.top[0].value, '200px');
278    assert.closeTo(groups.top[1].offset, 0.25, 0.001);
279    assert.equal(groups.top[1].value, '0px');
280    assert.closeTo(groups.top[2].offset, 0.5, 0.001);
281    assert.equal(groups.top[2].value, '200px');
282    assert.closeTo(groups.top[3].offset, 0.75, 0.001);
283    assert.equal(groups.top[3].value, '0px');
284    assert.closeTo(groups.top[4].offset, 1, 0.001);
285    assert.equal(groups.top[4].value, '200px');
286
287    assert.equal(groups.opacity.length, 4);
288    assert.closeTo(groups.opacity[0].offset, 0, 0.001);
289    assert.equal(groups.opacity[0].value, 1);
290    assert.closeTo(groups.opacity[1].offset, 0.5, 0.001);
291    assert.equal(groups.opacity[1].value, 0);
292    assert.closeTo(groups.opacity[2].offset, 0.75, 0.001);
293    assert.equal(groups.opacity[2].value, 1);
294    assert.closeTo(groups.opacity[3].offset, 1, 0.001);
295    assert.equal(groups.opacity[3].value, 0);
296  });
297
298  test('Make property specific keyframes when the offset of the last keyframe is specified but not equal to 1.', function() {
299    assert.throws(function() {
300      makePropertySpecificKeyframeGroups(normalizeKeyframes([
301        {left: '0px', offset: 0},
302        {left: '20px'},
303        {left: '30px', offset: 0.9}
304      ]));
305    });
306  });
307
308  test('Make property specific keyframes when no properties are animated, and the offset of the last keyframe is specified but not equal to 1.', function() {
309    var groups;
310    assert.doesNotThrow(function() {
311      groups = makePropertySpecificKeyframeGroups(normalizeKeyframes([
312        {offset: 0},
313        {},
314        {offset: 0.9}
315      ]));
316    });
317    assert.equal(Object.getOwnPropertyNames(groups).length, 0);
318  });
319
320  test('Make property specific keyframes when a property appears in some keyframes, but not in the last keyframe.', function() {
321    assert.throws(function() {
322      makePropertySpecificKeyframeGroups(normalizeKeyframes([
323        {left: '0px', top: '0px'},
324        {left: '10px', top: '10px'},
325        {top: '20px'}
326      ]));
327    });
328  });
329
330  test('Make property specific keyframes when a property appears in some keyframes, but not in the first keyframe.', function() {
331    assert.throws(function() {
332      makePropertySpecificKeyframeGroups(normalizeKeyframes([
333        {left: '0px'},
334        {left: '10px', top: '10px'},
335        {left: '20px', top: '20px'}
336      ]));
337    });
338  });
339
340  test('Make property specific keyframes where two properties are animated. One property in a keyframe with offset 1. One property in the last keyframe, with no offset.', function() {
341    var groups;
342    assert.doesNotThrow(function() {
343      groups = makePropertySpecificKeyframeGroups(normalizeKeyframes([
344        {left: '0px', top: '0px', offset: 0},
345        {left: '20px', offset: 1},
346        {top: '20px'}
347      ]));
348    });
349    assert.equal(Object.getOwnPropertyNames(groups).length, 2);
350  });
351
352  test('Make property specific keyframes where two properties are animated. One property in a keyframe with offset 0. One property in the first keyframe, with no offset.', function() {
353    var groups;
354    assert.doesNotThrow(function() {
355      groups = makePropertySpecificKeyframeGroups(normalizeKeyframes([
356        {top: '0px'},
357        {left: '0px', offset: 0},
358        {left: '20px', top: '20px', offset: 1}
359      ]));
360    });
361    assert.equal(Object.getOwnPropertyNames(groups).length, 2);
362  });
363
364  // Test per-keyframe easings.
365  test('Apply keyframe easings.', function() {
366    var target1 = document.createElement('div');
367    var target2 = document.createElement('div');
368    target1.style.position = 'absolute';
369    target2.style.position = 'absolute';
370    document.body.appendChild(target1);
371    document.body.appendChild(target2);
372
373    var player1 = target1.animate(
374        [
375          {left: '0px'},
376          {left: '50px', offset: 0.25},
377          {left: '0px'}
378        ],
379        {duration: 4000, fill: 'forwards'});
380    var player2 = target2.animate(
381        [
382          {left: '0px', easing: 'ease-in'},
383          {left: '50px', offset: 0.25},
384          {left: '0px'}
385        ],
386        {duration: 4000, fill: 'forwards'});
387
388    tick(0);
389    assert.equal(leftAsNumber(target1), 0);
390    assert.equal(leftAsNumber(target2), 0);
391    tick(250);
392    assert.closeTo(leftAsNumber(target1), 12.5, 1);
393    assert.closeTo(leftAsNumber(target2), 4.65, 1);
394    tick(500);
395    assert.closeTo(leftAsNumber(target1), 25, 1);
396    assert.closeTo(leftAsNumber(target2), 15.25, 1);
397    tick(1000);
398    assert.equal(leftAsNumber(target1), 50);
399    assert.equal(leftAsNumber(target2), 50);
400
401    tick(2500);
402    assert.equal(leftAsNumber(target1), 25);
403    assert.equal(leftAsNumber(target2), 25);
404    tick(4000);
405    assert.equal(leftAsNumber(target1), 0);
406    assert.equal(leftAsNumber(target2), 0);
407  });
408
409  // Test makeInterpolations.
410  test('Make interpolations for a simple effect with one property.', function() {
411    var interpolations;
412    assert.doesNotThrow(function() {
413      interpolations = makeInterpolations(makePropertySpecificKeyframeGroups(normalizeKeyframes([
414        {left: '0px'},
415        {left: '200px', offset: 0.3},
416        {left: '0px'}
417      ])));
418    });
419    assert.equal(interpolations.length, 2);
420
421    assert.closeTo(interpolations[0].startTime, 0, 0.001);
422    assert.closeTo(interpolations[0].endTime, 0.3, 0.001);
423    assert.equal(interpolations[0].property, 'left');
424    assert.equal(typeof interpolations[0].interpolation, 'function');
425
426    assert.closeTo(interpolations[1].startTime, 0.3, 0.001);
427    assert.closeTo(interpolations[1].endTime, 1, 0.001);
428    assert.equal(interpolations[1].property, 'left');
429    assert.equal(typeof interpolations[1].interpolation, 'function');
430  });
431});
432
433suite('effect-convertEffectInput', function() {
434  setup(function() {
435    this.target = document.createElement('div');
436    this.target.style.position = 'absolute';
437    document.documentElement.appendChild(this.target);
438  });
439  teardown(function() {
440    if (this.target.parent)
441      this.target.removeChild(this.target);
442  });
443
444  test('Convert effect input for a simple effect with one property.', function() {
445    var effectFunction;
446    assert.doesNotThrow(function() {
447      effectFunction = webAnimations1.convertEffectInput([
448        {left: '0px'},
449        {left: '200px', offset: 0.3},
450        {left: '100px'}
451      ]);
452    });
453
454    effectFunction(this.target, 0);
455    assert.closeTo(leftAsNumber(this.target), 0, 0.001);
456    effectFunction(this.target, 0.075);
457    assert.closeTo(leftAsNumber(this.target), 50, 0.001);
458    effectFunction(this.target, 0.15);
459    assert.closeTo(leftAsNumber(this.target), 100, 0.001);
460    effectFunction(this.target, 0.65);
461    assert.closeTo(leftAsNumber(this.target), 150, 0.001);
462    effectFunction(this.target, 1);
463    assert.closeTo(leftAsNumber(this.target), 100, 0.001);
464    effectFunction(this.target, 2);
465    assert.closeTo(leftAsNumber(this.target), -42.856, 0.01);
466  });
467
468  test('Convert effect input where one property is animated and the property has two keyframes at offset 1.', function() {
469    var effectFunction;
470    assert.doesNotThrow(function() {
471      effectFunction = webAnimations1.convertEffectInput([
472        {left: '0px', offset: 0},
473        {left: '20px', offset: 1},
474        {left: '30px'}
475      ]);
476    });
477    effectFunction(this.target, 1);
478    assert.equal(getComputedStyle(this.target).left, '30px');
479    effectFunction(this.target, 2);
480    assert.equal(getComputedStyle(this.target).left, '30px');
481  });
482
483  test('Convert effect input and apply effect at fraction null.', function() {
484    var effectFunction;
485    var underlying = getComputedStyle(this.target).left;
486    assert.doesNotThrow(function() {
487      effectFunction = webAnimations1.convertEffectInput([
488        {left: '0px'},
489        {left: '100px'}
490      ]);
491    });
492
493    effectFunction(this.target, 1);
494    assert.equal(getComputedStyle(this.target).left, '100px');
495    effectFunction(this.target, null);
496    assert.equal(getComputedStyle(this.target).left, underlying);
497  });
498});
499