OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2015 Google Inc. All rights reserved. | 2 * Copyright (C) 2015 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
43 * generated according to each interpolation method's handling of values | 43 * generated according to each interpolation method's handling of values |
44 * that don't interpolate. | 44 * that don't interpolate. |
45 * - assertComposition({property, underlying, [addFrom], [addTo], [replaceFrom]
, [replaceTo]}, [{at: fraction, is: value}]) | 45 * - assertComposition({property, underlying, [addFrom], [addTo], [replaceFrom]
, [replaceTo]}, [{at: fraction, is: value}]) |
46 * Similar to assertInterpolation() instead using only the Web Animations
API | 46 * Similar to assertInterpolation() instead using only the Web Animations
API |
47 * to animate composite specified keyframes (add or replace) on top of | 47 * to animate composite specified keyframes (add or replace) on top of |
48 * an underlying value. | 48 * an underlying value. |
49 * Exactly one of (addFrom, replaceFrom) must be specified. | 49 * Exactly one of (addFrom, replaceFrom) must be specified. |
50 * Exactly one of (addTo, replaceTo) must be specified. | 50 * Exactly one of (addTo, replaceTo) must be specified. |
51 * - afterTest(callback) | 51 * - afterTest(callback) |
52 * Calls callback after all the tests have executed. | 52 * Calls callback after all the tests have executed. |
| 53 * |
| 54 * The following object is exported: |
| 55 * - neutralKeyframe |
| 56 * Can be used as the from/to value to use a neutral keyframe. |
53 */ | 57 */ |
54 'use strict'; | 58 'use strict'; |
55 (function() { | 59 (function() { |
56 var interpolationTests = []; | 60 var interpolationTests = []; |
57 var compositionTests = []; | 61 var compositionTests = []; |
58 var cssAnimationsData = { | 62 var cssAnimationsData = { |
59 sharedStyle: null, | 63 sharedStyle: null, |
60 nextID: 0, | 64 nextID: 0, |
61 }; | 65 }; |
62 var webAnimationsEnabled = typeof Element.prototype.animate === 'function'; | 66 var webAnimationsEnabled = typeof Element.prototype.animate === 'function'; |
63 var expectNoInterpolation = {}; | 67 var expectNoInterpolation = {}; |
64 var afterTestHook = function() {}; | 68 var afterTestHook = function() {}; |
| 69 var neutralKeyframe = {}; |
| 70 function isNeutralKeyframe(keyframe) { |
| 71 return keyframe === neutralKeyframe; |
| 72 } |
65 | 73 |
66 var cssAnimationsInterpolation = { | 74 var cssAnimationsInterpolation = { |
67 name: 'CSS Animations', | 75 name: 'CSS Animations', |
68 supportsProperty: function() {return true;}, | 76 supportsProperty: function() {return true;}, |
69 supportsValue: function() {return true;}, | 77 supportsValue: function() {return true;}, |
70 setup: function() {}, | 78 setup: function() {}, |
71 nonInterpolationExpectations: function(from, to) { | 79 nonInterpolationExpectations: function(from, to) { |
72 return expectFlip(from, to, 0.5); | 80 return expectFlip(from, to, 0.5); |
73 }, | 81 }, |
74 interpolate: function(property, from, to, at, target) { | 82 interpolate: function(property, from, to, at, target) { |
75 var id = cssAnimationsData.nextID++; | 83 var id = cssAnimationsData.nextID++; |
76 if (!cssAnimationsData.sharedStyle) { | 84 if (!cssAnimationsData.sharedStyle) { |
77 cssAnimationsData.sharedStyle = createElement(document.body, 'style'); | 85 cssAnimationsData.sharedStyle = createElement(document.body, 'style'); |
78 } | 86 } |
79 cssAnimationsData.sharedStyle.textContent += '' + | 87 cssAnimationsData.sharedStyle.textContent += '' + |
80 '@keyframes animation' + id + ' {' + | 88 '@keyframes animation' + id + ' {' + |
81 'from {' + property + ': ' + from + ';}' + | 89 (isNeutralKeyframe(from) ? '' : `from {${property}: ${from};}`) + |
82 'to {' + property + ': ' + to + ';}' + | 90 (isNeutralKeyframe(to) ? '' : `to {${property}: ${to};}`) + |
83 '}'; | 91 '}'; |
84 target.style.animationName = 'animation' + id; | 92 target.style.animationName = 'animation' + id; |
85 target.style.animationDuration = '2e10s'; | 93 target.style.animationDuration = '2e10s'; |
86 target.style.animationDelay = '-1e10s'; | 94 target.style.animationDelay = '-1e10s'; |
87 target.style.animationTimingFunction = createEasing(at); | 95 target.style.animationTimingFunction = createEasing(at); |
88 }, | 96 }, |
89 rebaseline: false, | 97 rebaseline: false, |
90 }; | 98 }; |
91 | 99 |
92 var cssTransitionsInterpolation = { | 100 var cssTransitionsInterpolation = { |
93 name: 'CSS Transitions', | 101 name: 'CSS Transitions', |
94 supportsProperty: function() {return true;}, | 102 supportsProperty: function() {return true;}, |
95 supportsValue: function() {return true;}, | 103 supportsValue: function() {return true;}, |
96 setup: function(property, from, target) { | 104 setup: function(property, from, target) { |
97 target.style[property] = from; | 105 target.style[property] = isNeutralKeyframe(from) ? '' : from; |
98 }, | 106 }, |
99 nonInterpolationExpectations: function(from, to) { | 107 nonInterpolationExpectations: function(from, to) { |
100 return expectFlip(from, to, -Infinity); | 108 return expectFlip(from, to, -Infinity); |
101 }, | 109 }, |
102 interpolate: function(property, from, to, at, target) { | 110 interpolate: function(property, from, to, at, target) { |
103 target.style.transitionDuration = '2e10s'; | 111 target.style.transitionDuration = '2e10s'; |
104 target.style.transitionDelay = '-1e10s'; | 112 target.style.transitionDelay = '-1e10s'; |
105 target.style.transitionTimingFunction = createEasing(at); | 113 target.style.transitionTimingFunction = createEasing(at); |
106 target.style.transitionProperty = property; | 114 target.style.transitionProperty = property; |
107 target.style[property] = to; | 115 target.style[property] = isNeutralKeyframe(to) ? '' : to; |
108 }, | 116 }, |
109 rebaseline: false, | 117 rebaseline: false, |
110 }; | 118 }; |
111 | 119 |
112 var webAnimationsInterpolation = { | 120 var webAnimationsInterpolation = { |
113 name: 'Web Animations', | 121 name: 'Web Animations', |
114 supportsProperty: function(property) {return property.indexOf('-webkit-') !=
= 0;}, | 122 supportsProperty: function(property) {return property.indexOf('-webkit-') !=
= 0;}, |
115 supportsValue: function(value) {return value !== '';}, | 123 supportsValue: function(value) {return value !== '';}, |
116 setup: function() {}, | 124 setup: function() {}, |
117 nonInterpolationExpectations: function(from, to) { | 125 nonInterpolationExpectations: function(from, to) { |
118 return expectFlip(from, to, 0.5); | 126 return expectFlip(from, to, 0.5); |
119 }, | 127 }, |
120 interpolate: function(property, from, to, at, target) { | 128 interpolate: function(property, from, to, at, target) { |
| 129 this.interpolateComposite(property, from, 'replace', to, 'replace', at, ta
rget); |
| 130 }, |
| 131 interpolateComposite: function(property, from, fromComposite, to, toComposit
e, at, target) { |
121 // Convert to camelCase | 132 // Convert to camelCase |
122 for (var i = property.length - 2; i > 0; --i) { | 133 for (var i = property.length - 2; i > 0; --i) { |
123 if (property[i] === '-') { | 134 if (property[i] === '-') { |
124 property = property.substring(0, i) + property[i + 1].toUpperCase() +
property.substring(i + 2); | 135 property = property.substring(0, i) + property[i + 1].toUpperCase() +
property.substring(i + 2); |
125 } | 136 } |
126 } | 137 } |
127 this.interpolateKeyframes([ | 138 var keyframes = []; |
128 {offset: 0, [property]: from}, | 139 if (!isNeutralKeyframe(from)) { |
129 {offset: 1, [property]: to}, | 140 keyframes.push({ |
130 ], at, target); | 141 offset: 0, |
131 }, | 142 composite: fromComposite, |
132 interpolateKeyframes: function(keyframes, at, target) { | 143 [property]: from, |
| 144 }); |
| 145 } |
| 146 if (!isNeutralKeyframe(to)) { |
| 147 keyframes.push({ |
| 148 offset: 1, |
| 149 composite: toComposite, |
| 150 [property]: to, |
| 151 }); |
| 152 } |
133 target.animate(keyframes, { | 153 target.animate(keyframes, { |
134 fill: 'forwards', | 154 fill: 'forwards', |
135 duration: 1, | 155 duration: 1, |
136 easing: createEasing(at), | 156 easing: createEasing(at), |
137 delay: -0.5, | 157 delay: -0.5, |
138 iterations: 0.5, | 158 iterations: 0.5, |
139 }); | 159 }); |
140 }, | 160 }, |
141 rebaseline: false, | 161 rebaseline: false, |
142 }; | 162 }; |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
251 if (options.to) { | 271 if (options.to) { |
252 console.assert(CSS.supports(options.property, options.to)); | 272 console.assert(CSS.supports(options.property, options.to)); |
253 } | 273 } |
254 interpolationTests.push({options, expectations}); | 274 interpolationTests.push({options, expectations}); |
255 } | 275 } |
256 | 276 |
257 function assertComposition(options, expectations) { | 277 function assertComposition(options, expectations) { |
258 compositionTests.push({options, expectations}); | 278 compositionTests.push({options, expectations}); |
259 } | 279 } |
260 | 280 |
| 281 function keyframeText(keyframe) { |
| 282 return isNeutralKeyframe(keyframe) ? 'neutral' : `[${keyframe}]`; |
| 283 } |
| 284 |
| 285 function keyframeCode(keyframe) { |
| 286 return isNeutralKeyframe(keyframe) ? 'neutralKeyframe' : `'${keyframe}'`; |
| 287 } |
| 288 |
261 function createInterpolationTestTargets(interpolationMethod, interpolationMeth
odContainer, interpolationTest, rebaselineContainer) { | 289 function createInterpolationTestTargets(interpolationMethod, interpolationMeth
odContainer, interpolationTest, rebaselineContainer) { |
262 var property = interpolationTest.options.property; | 290 var property = interpolationTest.options.property; |
263 var from = interpolationTest.options.from; | 291 var from = interpolationTest.options.from; |
264 var to = interpolationTest.options.to; | 292 var to = interpolationTest.options.to; |
265 if ((interpolationTest.options.method && interpolationTest.options.method !=
interpolationMethod.name) | 293 if ((interpolationTest.options.method && interpolationTest.options.method !=
interpolationMethod.name) |
266 || !interpolationMethod.supportsProperty(property) | 294 || !interpolationMethod.supportsProperty(property) |
267 || !interpolationMethod.supportsValue(from) | 295 || !interpolationMethod.supportsValue(from) |
268 || !interpolationMethod.supportsValue(to)) { | 296 || !interpolationMethod.supportsValue(to)) { |
269 return; | 297 return; |
270 } | 298 } |
271 if (interpolationMethod.rebaseline) { | 299 if (interpolationMethod.rebaseline) { |
272 var rebaseline = createElement(rebaselineContainer, 'pre'); | 300 var rebaseline = createElement(rebaselineContainer, 'pre'); |
273 rebaseline.appendChild(document.createTextNode(`\ | 301 rebaseline.appendChild(document.createTextNode(`\ |
274 assertInterpolation({ | 302 assertInterpolation({ |
275 property: '${property}', | 303 property: '${property}', |
276 from: '${from}', | 304 from: ${keyframeCode(from)}, |
277 to: '${to}', | 305 to: ${keyframeCode(to)}, |
278 }, [\n`)); | 306 }, [\n`)); |
279 var rebaselineExpectation; | 307 var rebaselineExpectation; |
280 rebaseline.appendChild(rebaselineExpectation = document.createTextNode('')
); | 308 rebaseline.appendChild(rebaselineExpectation = document.createTextNode('')
); |
281 rebaseline.appendChild(document.createTextNode(']);\n\n')); | 309 rebaseline.appendChild(document.createTextNode(']);\n\n')); |
282 } | 310 } |
283 var testText = `${interpolationMethod.name}: property <${property}> from [${
from}] to [${to}]`; | 311 var testText = `${interpolationMethod.name}: property <${property}> from ${k
eyframeText(from)} to ${keyframeText(to)}`; |
284 var testContainer = createElement(interpolationMethodContainer, 'div', testT
ext); | 312 var testContainer = createElement(interpolationMethodContainer, 'div', testT
ext); |
285 createElement(testContainer, 'br'); | 313 createElement(testContainer, 'br'); |
286 var expectations = interpolationTest.expectations; | 314 var expectations = interpolationTest.expectations; |
287 if (expectations === expectNoInterpolation) { | 315 if (expectations === expectNoInterpolation) { |
288 expectations = interpolationMethod.nonInterpolationExpectations(from, to); | 316 expectations = interpolationMethod.nonInterpolationExpectations(from, to); |
289 } | 317 } |
290 return expectations.map(function(expectation) { | 318 return expectations.map(function(expectation) { |
291 var actualTargetContainer = createTargetContainer(testContainer, 'actual')
; | 319 var actualTargetContainer = createTargetContainer(testContainer, 'actual')
; |
292 var expectedTargetContainer = createTargetContainer(testContainer, 'expect
ed'); | 320 var expectedTargetContainer = createTargetContainer(testContainer, 'expect
ed'); |
293 expectedTargetContainer.target.style[property] = expectation.is; | 321 expectedTargetContainer.target.style[property] = expectation.is; |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
337 var testText = `Compositing: property <${property}> underlying [${underlying
}] from ${fromComposite} [${from}] to ${toComposite} [${to}]`; | 365 var testText = `Compositing: property <${property}> underlying [${underlying
}] from ${fromComposite} [${from}] to ${toComposite} [${to}]`; |
338 var testContainer = createElement(compositionContainer, 'div', testText); | 366 var testContainer = createElement(compositionContainer, 'div', testText); |
339 createElement(testContainer, 'br'); | 367 createElement(testContainer, 'br'); |
340 return compositionTest.expectations.map(function(expectation) { | 368 return compositionTest.expectations.map(function(expectation) { |
341 var actualTargetContainer = createTargetContainer(testContainer, 'actual')
; | 369 var actualTargetContainer = createTargetContainer(testContainer, 'actual')
; |
342 var expectedTargetContainer = createTargetContainer(testContainer, 'expect
ed'); | 370 var expectedTargetContainer = createTargetContainer(testContainer, 'expect
ed'); |
343 expectedTargetContainer.target.style[property] = expectation.is; | 371 expectedTargetContainer.target.style[property] = expectation.is; |
344 var target = actualTargetContainer.target; | 372 var target = actualTargetContainer.target; |
345 target.style[property] = underlying; | 373 target.style[property] = underlying; |
346 target.interpolate = function() { | 374 target.interpolate = function() { |
347 webAnimationsInterpolation.interpolateKeyframes([{ | 375 webAnimationsInterpolation.interpolateComposite(property, from, fromComp
osite, to, toComposite, expectation.at, target); |
348 offset: 0, | |
349 composite: fromComposite, | |
350 [toCamelCase(property)]: from, | |
351 }, { | |
352 offset: 1, | |
353 composite: toComposite, | |
354 [toCamelCase(property)]: to, | |
355 }], expectation.at, target); | |
356 }; | 376 }; |
357 target.measure = function() { | 377 target.measure = function() { |
358 var actualValue = getComputedStyle(target)[property]; | 378 var actualValue = getComputedStyle(target)[property]; |
359 test(function() { | 379 test(function() { |
360 assert_equals( | 380 assert_equals( |
361 normalizeValue(actualValue), | 381 normalizeValue(actualValue), |
362 normalizeValue(getComputedStyle(expectedTargetContainer.target)[prop
erty])); | 382 normalizeValue(getComputedStyle(expectedTargetContainer.target)[prop
erty])); |
363 }, `${testText} at (${expectation.at}) is [${sanitizeUrls(actualValue)}]
`); | 383 }, `${testText} at (${expectation.at}) is [${sanitizeUrls(actualValue)}]
`); |
364 if (rebaselineExpectation) { | 384 if (rebaselineExpectation) { |
365 rebaselineExpectation.textContent += ` {at: ${expectation.at}, is: '$
{actualValue}'},\n`; | 385 rebaselineExpectation.textContent += ` {at: ${expectation.at}, is: '$
{actualValue}'},\n`; |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
406 if (window.testRunner) { | 426 if (window.testRunner) { |
407 container.remove(); | 427 container.remove(); |
408 } | 428 } |
409 afterTestHook(); | 429 afterTestHook(); |
410 } | 430 } |
411 | 431 |
412 function afterTest(f) { | 432 function afterTest(f) { |
413 afterTestHook = f; | 433 afterTestHook = f; |
414 } | 434 } |
415 | 435 |
416 window.assertInterpolation = assertInterpolation; | |
417 window.assertNoInterpolation = assertNoInterpolation; | |
418 window.assertComposition = assertComposition; | |
419 window.afterTest = afterTest; | |
420 | |
421 loadScript('../../resources/testharness.js').then(function() { | 436 loadScript('../../resources/testharness.js').then(function() { |
422 return loadScript('../../resources/testharnessreport.js'); | 437 return loadScript('../../resources/testharnessreport.js'); |
423 }).then(function() { | 438 }).then(function() { |
424 var asyncHandle = async_test('This test uses interpolation-test.js.') | 439 var asyncHandle = async_test('This test uses interpolation-test.js.') |
425 requestAnimationFrame(function() { | 440 requestAnimationFrame(function() { |
426 runTests(); | 441 runTests(); |
427 asyncHandle.done() | 442 asyncHandle.done() |
428 }); | 443 }); |
429 }); | 444 }); |
| 445 |
| 446 window.assertInterpolation = assertInterpolation; |
| 447 window.assertNoInterpolation = assertNoInterpolation; |
| 448 window.assertComposition = assertComposition; |
| 449 window.afterTest = afterTest; |
| 450 window.neutralKeyframe = neutralKeyframe; |
430 })(); | 451 })(); |
OLD | NEW |