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 24 matching lines...) Expand all Loading... |
35 * The following functions are exported: | 35 * The following functions are exported: |
36 * - assertInterpolation({property, from, to, [method]}, [{at: fraction, is: va
lue}]) | 36 * - assertInterpolation({property, from, to, [method]}, [{at: fraction, is: va
lue}]) |
37 * Constructs a test case for each fraction that asserts the expected val
ue | 37 * Constructs a test case for each fraction that asserts the expected val
ue |
38 * equals the value produced by interpolation between from and to using | 38 * equals the value produced by interpolation between from and to using |
39 * CSS Animations, CSS Transitions and Web Animations. If the method opti
on is | 39 * CSS Animations, CSS Transitions and Web Animations. If the method opti
on is |
40 * specified then only that interpolation method will be used. | 40 * specified then only that interpolation method will be used. |
41 * - assertNoInterpolation({property, from, to, [method]}) | 41 * - assertNoInterpolation({property, from, to, [method]}) |
42 * This works in the same way as assertInterpolation with expectations au
to | 42 * This works in the same way as assertInterpolation with expectations au
to |
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}]) |
| 46 * Similar to assertInterpolation() instead using only the Web Animations
API |
| 47 * to animate composite specified keyframes (add or replace) on top of |
| 48 * an underlying value. |
| 49 * Exactly one of (addFrom, replaceFrom) must be specified. |
| 50 * Exactly one of (addTo, replaceTo) must be specified. |
45 * - afterTest(callback) | 51 * - afterTest(callback) |
46 * Calls callback after all the tests have executed. | 52 * Calls callback after all the tests have executed. |
47 */ | 53 */ |
48 'use strict'; | 54 'use strict'; |
49 (function() { | 55 (function() { |
50 var interpolationTests = []; | 56 var interpolationTests = []; |
| 57 var compositionTests = []; |
51 var cssAnimationsData = { | 58 var cssAnimationsData = { |
52 sharedStyle: null, | 59 sharedStyle: null, |
53 nextID: 0, | 60 nextID: 0, |
54 }; | 61 }; |
55 var webAnimationsEnabled = typeof Element.prototype.animate === 'function'; | 62 var webAnimationsEnabled = typeof Element.prototype.animate === 'function'; |
56 var expectNoInterpolation = {}; | 63 var expectNoInterpolation = {}; |
57 var afterTestHook = function() {}; | 64 var afterTestHook = function() {}; |
58 | 65 |
59 var cssAnimationsInterpolation = { | 66 var cssAnimationsInterpolation = { |
60 name: 'CSS Animations', | 67 name: 'CSS Animations', |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
104 | 111 |
105 var webAnimationsInterpolation = { | 112 var webAnimationsInterpolation = { |
106 name: 'Web Animations', | 113 name: 'Web Animations', |
107 supportsProperty: function(property) {return property.indexOf('-webkit-') !=
= 0;}, | 114 supportsProperty: function(property) {return property.indexOf('-webkit-') !=
= 0;}, |
108 supportsValue: function(value) {return value !== '';}, | 115 supportsValue: function(value) {return value !== '';}, |
109 setup: function() {}, | 116 setup: function() {}, |
110 nonInterpolationExpectations: function(from, to) { | 117 nonInterpolationExpectations: function(from, to) { |
111 return expectFlip(from, to, 0.5); | 118 return expectFlip(from, to, 0.5); |
112 }, | 119 }, |
113 interpolate: function(property, from, to, at, target) { | 120 interpolate: function(property, from, to, at, target) { |
114 var keyframes = [{}, {}]; | 121 this.interpolateKeyframes([ |
115 keyframes[0][property] = from; | 122 {offset: 0, [property]: from}, |
116 keyframes[1][property] = to; | 123 {offset: 1, [property]: to}, |
117 var player = target.animate(keyframes, { | 124 ], at, target); |
| 125 }, |
| 126 interpolateKeyframes: function(keyframes, at, target) { |
| 127 target.animate(keyframes, { |
118 fill: 'forwards', | 128 fill: 'forwards', |
119 duration: 1, | 129 duration: 1, |
120 easing: createEasing(at), | 130 easing: createEasing(at), |
121 delay: -0.5, | 131 delay: -0.5, |
122 iterations: 0.5, | 132 iterations: 0.5, |
123 }); | 133 }); |
124 }, | 134 }, |
125 rebaseline: false, | 135 rebaseline: false, |
126 }; | 136 }; |
127 | 137 |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
161 | 171 |
162 function loadScript(url) { | 172 function loadScript(url) { |
163 return new Promise(function(resolve) { | 173 return new Promise(function(resolve) { |
164 var script = document.createElement('script'); | 174 var script = document.createElement('script'); |
165 script.src = url; | 175 script.src = url; |
166 script.onload = resolve; | 176 script.onload = resolve; |
167 document.head.appendChild(script); | 177 document.head.appendChild(script); |
168 }); | 178 }); |
169 } | 179 } |
170 | 180 |
171 function createTargetContainer(parent) { | 181 function createTargetContainer(parent, className) { |
172 var targetContainer = createElement(parent); | 182 var targetContainer = createElement(parent); |
173 targetContainer.classList.add('container'); | 183 targetContainer.classList.add('container'); |
174 var template = document.querySelector('#target-template'); | 184 var template = document.querySelector('#target-template'); |
175 if (template) { | 185 if (template) { |
176 targetContainer.appendChild(template.content.cloneNode(true)); | 186 targetContainer.appendChild(template.content.cloneNode(true)); |
177 } | 187 } |
178 var target = targetContainer.querySelector('.target') || targetContainer; | 188 var target = targetContainer.querySelector('.target') || targetContainer; |
179 target.classList.add('target'); | 189 target.classList.add('target', className); |
180 target.parentElement.classList.add('parent'); | 190 target.parentElement.classList.add('parent'); |
181 targetContainer.target = target; | 191 targetContainer.target = target; |
182 return targetContainer; | 192 return targetContainer; |
183 } | 193 } |
184 | 194 |
185 function roundNumbers(value) { | 195 function roundNumbers(value) { |
186 return value. | 196 return value. |
187 // Round numbers to two decimal places. | 197 // Round numbers to two decimal places. |
188 replace(/-?\d*\.\d+(e-?\d+)?/g, function(n) { | 198 replace(/-?\d*\.\d+(e-?\d+)?/g, function(n) { |
189 return (parseFloat(n).toFixed(2)). | 199 return (parseFloat(n).toFixed(2)). |
(...skipping 30 matching lines...) Expand all Loading... |
220 assertInterpolation(options, expectNoInterpolation); | 230 assertInterpolation(options, expectNoInterpolation); |
221 } | 231 } |
222 | 232 |
223 function assertInterpolation(options, expectations) { | 233 function assertInterpolation(options, expectations) { |
224 if (options.from) { | 234 if (options.from) { |
225 console.assert(CSS.supports(options.property, options.from)); | 235 console.assert(CSS.supports(options.property, options.from)); |
226 } | 236 } |
227 if (options.to) { | 237 if (options.to) { |
228 console.assert(CSS.supports(options.property, options.to)); | 238 console.assert(CSS.supports(options.property, options.to)); |
229 } | 239 } |
230 interpolationTests.push({ | 240 interpolationTests.push({options, expectations}); |
231 options: options, | |
232 expectations: expectations, | |
233 }); | |
234 } | 241 } |
235 | 242 |
236 function createTestTargets(interpolationMethods, interpolationTests, container
, rebaselineContainer) { | 243 function assertComposition(options, expectations) { |
237 var targets = []; | 244 compositionTests.push({options, expectations}); |
238 interpolationMethods.forEach(function(interpolationMethod, interpolationMeth
odIndex) { | 245 } |
239 var methodContainer = createElement(container); | 246 |
240 interpolationTests.forEach(function(interpolationTest) { | 247 function createInterpolationTestTargets(interpolationMethod, interpolationMeth
odContainer, interpolationTest, rebaselineContainer) { |
241 var property = interpolationTest.options.property; | 248 var property = interpolationTest.options.property; |
242 var from = interpolationTest.options.from; | 249 var from = interpolationTest.options.from; |
243 var to = interpolationTest.options.to; | 250 var to = interpolationTest.options.to; |
244 if ((interpolationTest.options.method && interpolationTest.options.metho
d != interpolationMethod.name) | 251 if ((interpolationTest.options.method && interpolationTest.options.method !=
interpolationMethod.name) |
245 || !interpolationMethod.supportsProperty(property) | 252 || !interpolationMethod.supportsProperty(property) |
246 || !interpolationMethod.supportsValue(from) | 253 || !interpolationMethod.supportsValue(from) |
247 || !interpolationMethod.supportsValue(to)) { | 254 || !interpolationMethod.supportsValue(to)) { |
248 return; | 255 return; |
249 } | 256 } |
250 if (interpolationMethod.rebaseline) { | 257 if (interpolationMethod.rebaseline) { |
251 var rebaseline = createElement(rebaselineContainer, 'pre'); | 258 var rebaseline = createElement(rebaselineContainer, 'pre'); |
252 rebaseline.appendChild(document.createTextNode(`\ | 259 rebaseline.appendChild(document.createTextNode(`\ |
253 assertInterpolation({ | 260 assertInterpolation({ |
254 property: '${property}', | 261 property: '${property}', |
255 from: '${from}', | 262 from: '${from}', |
256 to: '${to}', | 263 to: '${to}', |
257 }, [\n`)); | 264 }, [\n`)); |
258 var rebaselineExpectation; | 265 var rebaselineExpectation; |
259 rebaseline.appendChild(rebaselineExpectation = document.createTextNode
('')); | 266 rebaseline.appendChild(rebaselineExpectation = document.createTextNode('')
); |
260 rebaseline.appendChild(document.createTextNode(']);\n\n')); | 267 rebaseline.appendChild(document.createTextNode(']);\n\n')); |
| 268 } |
| 269 var testText = `${interpolationMethod.name}: property <${property}> from [${
from}] to [${to}]`; |
| 270 var testContainer = createElement(interpolationMethodContainer, 'div', testT
ext); |
| 271 createElement(testContainer, 'br'); |
| 272 var expectations = interpolationTest.expectations; |
| 273 if (expectations === expectNoInterpolation) { |
| 274 expectations = interpolationMethod.nonInterpolationExpectations(from, to); |
| 275 } |
| 276 return expectations.map(function(expectation) { |
| 277 var actualTargetContainer = createTargetContainer(testContainer, 'actual')
; |
| 278 var expectedTargetContainer = createTargetContainer(testContainer, 'expect
ed'); |
| 279 expectedTargetContainer.target.style[property] = expectation.is; |
| 280 var target = actualTargetContainer.target; |
| 281 interpolationMethod.setup(property, from, target); |
| 282 target.interpolate = function() { |
| 283 interpolationMethod.interpolate(property, from, to, expectation.at, targ
et); |
| 284 }; |
| 285 target.measure = function() { |
| 286 var actualValue = getComputedStyle(target)[property]; |
| 287 test(function() { |
| 288 assert_equals( |
| 289 normalizeValue(actualValue), |
| 290 normalizeValue(getComputedStyle(expectedTargetContainer.target)[prop
erty])); |
| 291 }, `${testText} at (${expectation.at}) is [${sanitizeUrls(actualValue)}]
`); |
| 292 if (rebaselineExpectation) { |
| 293 rebaselineExpectation.textContent += ` {at: ${expectation.at}, is: '$
{actualValue}'},\n`; |
261 } | 294 } |
262 var testText = interpolationMethod.name + ': property <' + property + '>
from [' + from + '] to [' + to + ']'; | 295 }; |
263 var testContainer = createElement(methodContainer, 'div', testText); | 296 return target; |
264 createElement(testContainer, 'br'); | 297 }); |
265 var expectations = interpolationTest.expectations; | 298 } |
266 if (expectations === expectNoInterpolation) { | 299 |
267 expectations = interpolationMethod.nonInterpolationExpectations(from,
to); | 300 function createCompositionTestTargets(compositionContainer, compositionTest, r
ebaselineContainer) { |
| 301 var options = compositionTest.options; |
| 302 console.assert('addFrom' in options !== 'replaceFrom' in options); |
| 303 console.assert('addTo' in options !== 'replaceTo' in options); |
| 304 var property = options.property; |
| 305 var underlying = options.underlying; |
| 306 var from = options.addFrom || options.replaceFrom; |
| 307 var to = options.addTo || options.replaceTo; |
| 308 var fromComposite = 'addFrom' in options ? 'add' : 'replace'; |
| 309 var toComposite = 'addTo' in options ? 'add' : 'replace'; |
| 310 if (webAnimationsInterpolation.rebaseline) { |
| 311 var rebaseline = createElement(rebaselineContainer, 'pre'); |
| 312 rebaseline.appendChild(document.createTextNode(`\ |
| 313 assertComposition({ |
| 314 property: '${property}', |
| 315 underlying: '${underlying}', |
| 316 ${fromComposite}From: '${from}', |
| 317 ${toComposite}To: '${to}', |
| 318 }, [\n`)); |
| 319 var rebaselineExpectation; |
| 320 rebaseline.appendChild(rebaselineExpectation = document.createTextNode('')
); |
| 321 rebaseline.appendChild(document.createTextNode(']);\n\n')); |
| 322 } |
| 323 var testText = `Compositing: property <${property}> underlying [${underlying
}] from ${fromComposite} [${from}] to ${toComposite} [${to}]`; |
| 324 var testContainer = createElement(compositionContainer, 'div', testText); |
| 325 createElement(testContainer, 'br'); |
| 326 return compositionTest.expectations.map(function(expectation) { |
| 327 var actualTargetContainer = createTargetContainer(testContainer, 'actual')
; |
| 328 var expectedTargetContainer = createTargetContainer(testContainer, 'expect
ed'); |
| 329 expectedTargetContainer.target.style[property] = expectation.is; |
| 330 var target = actualTargetContainer.target; |
| 331 target.style[property] = underlying; |
| 332 target.interpolate = function() { |
| 333 webAnimationsInterpolation.interpolateKeyframes([{ |
| 334 offset: 0, |
| 335 composite: fromComposite, |
| 336 [property]: from, |
| 337 }, { |
| 338 offset: 1, |
| 339 composite: toComposite, |
| 340 [property]: to, |
| 341 }], expectation.at, target); |
| 342 }; |
| 343 target.measure = function() { |
| 344 var actualValue = getComputedStyle(target)[property]; |
| 345 test(function() { |
| 346 assert_equals( |
| 347 normalizeValue(actualValue), |
| 348 normalizeValue(getComputedStyle(expectedTargetContainer.target)[prop
erty])); |
| 349 }, `${testText} at (${expectation.at}) is [${sanitizeUrls(actualValue)}]
`); |
| 350 if (rebaselineExpectation) { |
| 351 rebaselineExpectation.textContent += ` {at: ${expectation.at}, is: '$
{actualValue}'},\n`; |
268 } | 352 } |
269 expectations.forEach(function(expectation) { | 353 }; |
270 var actualTargetContainer = createTargetContainer(testContainer); | 354 return target; |
271 var expectedTargetContainer = createTargetContainer(testContainer); | |
272 expectedTargetContainer.target.classList.add('expected'); | |
273 expectedTargetContainer.target.style[property] = expectation.is; | |
274 var target = actualTargetContainer.target; | |
275 target.classList.add('actual'); | |
276 interpolationMethod.setup(property, from, target); | |
277 target.interpolate = function() { | |
278 interpolationMethod.interpolate(property, from, to, expectation.at,
target); | |
279 }; | |
280 target.measure = function() { | |
281 var actualValue = getComputedStyle(target)[property]; | |
282 test(function() { | |
283 assert_equals( | |
284 normalizeValue(actualValue), | |
285 normalizeValue(getComputedStyle(expectedTargetContainer.target)[
property])); | |
286 }, testText + ' at (' + expectation.at + ') is [' + sanitizeUrls(act
ualValue) + ']'); | |
287 if (interpolationMethod.rebaseline) { | |
288 rebaselineExpectation.textContent += ` {at: ${expectation.at}, is
: '${actualValue}'},\n`; | |
289 } | |
290 }; | |
291 targets.push(target); | |
292 }); | |
293 }); | |
294 }); | 355 }); |
| 356 } |
| 357 |
| 358 function createTestTargets(interpolationMethods, interpolationTests, compositi
onTests, container, rebaselineContainer) { |
| 359 var targets = []; |
| 360 for (var interpolationMethod of interpolationMethods) { |
| 361 var interpolationMethodContainer = createElement(container); |
| 362 for (var interpolationTest of interpolationTests) { |
| 363 [].push.apply(targets, createInterpolationTestTargets(interpolationMetho
d, interpolationMethodContainer, interpolationTest, rebaselineContainer)); |
| 364 } |
| 365 } |
| 366 var compositionContainer = createElement(container); |
| 367 for (var compositionTest of compositionTests) { |
| 368 [].push.apply(targets, createCompositionTestTargets(compositionContainer,
compositionTest, rebaselineContainer)); |
| 369 } |
295 return targets; | 370 return targets; |
296 } | 371 } |
297 | 372 |
298 function runInterpolationTests() { | 373 function runTests() { |
299 var interpolationMethods = [ | 374 var interpolationMethods = [ |
300 cssTransitionsInterpolation, | 375 cssTransitionsInterpolation, |
301 cssAnimationsInterpolation, | 376 cssAnimationsInterpolation, |
302 ]; | 377 ]; |
303 if (webAnimationsEnabled) { | 378 if (webAnimationsEnabled) { |
304 interpolationMethods.push(webAnimationsInterpolation); | 379 interpolationMethods.push(webAnimationsInterpolation); |
305 } | 380 } |
306 var rebaselineContainer = createElement(document.body); | 381 var rebaselineContainer = createElement(document.body); |
307 var container = createElement(document.body); | 382 var container = createElement(document.body); |
308 var targets = createTestTargets(interpolationMethods, interpolationTests, co
ntainer, rebaselineContainer); | 383 var targets = createTestTargets(interpolationMethods, interpolationTests, co
mpositionTests, container, rebaselineContainer); |
309 getComputedStyle(document.documentElement).left; // Force a style recalc for
transitions. | 384 getComputedStyle(document.documentElement).left; // Force a style recalc for
transitions. |
310 // Separate interpolation and measurement into different phases to avoid O(n
^2) of the number of targets. | 385 // Separate interpolation and measurement into different phases to avoid O(n
^2) of the number of targets. |
311 for (var target of targets) { | 386 for (var target of targets) { |
312 target.interpolate(); | 387 target.interpolate(); |
313 } | 388 } |
314 for (var target of targets) { | 389 for (var target of targets) { |
315 target.measure(); | 390 target.measure(); |
316 } | 391 } |
317 if (window.testRunner) { | 392 if (window.testRunner) { |
318 container.remove(); | 393 container.remove(); |
319 } | 394 } |
320 afterTestHook(); | 395 afterTestHook(); |
321 } | 396 } |
322 | 397 |
323 function afterTest(f) { | 398 function afterTest(f) { |
324 afterTestHook = f; | 399 afterTestHook = f; |
325 } | 400 } |
326 | 401 |
327 window.assertInterpolation = assertInterpolation; | 402 window.assertInterpolation = assertInterpolation; |
328 window.assertNoInterpolation = assertNoInterpolation; | 403 window.assertNoInterpolation = assertNoInterpolation; |
| 404 window.assertComposition = assertComposition; |
329 window.afterTest = afterTest; | 405 window.afterTest = afterTest; |
330 | 406 |
331 loadScript('../../resources/testharness.js').then(function() { | 407 loadScript('../../resources/testharness.js').then(function() { |
332 return loadScript('../../resources/testharnessreport.js'); | 408 return loadScript('../../resources/testharnessreport.js'); |
333 }).then(function() { | 409 }).then(function() { |
334 var asyncHandle = async_test('This test uses interpolation-test.js.') | 410 var asyncHandle = async_test('This test uses interpolation-test.js.') |
335 requestAnimationFrame(function() { | 411 requestAnimationFrame(function() { |
336 runInterpolationTests(); | 412 runTests(); |
337 asyncHandle.done() | 413 asyncHandle.done() |
338 }); | 414 }); |
339 }); | 415 }); |
340 })(); | 416 })(); |
OLD | NEW |