| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 /* |
| 6 Exported function: |
| 7 assertResponsive |
| 8 |
| 9 Call signature: |
| 10 assertResponsive({ |
| 11 property: <CSS Property>, |
| 12 from: ?<CSS Value>, |
| 13 to: ?<CSS Value>, |
| 14 configurations: [{ |
| 15 state: { |
| 16 ?underlying: <CSS Value>, |
| 17 ?inherited: <CSS Value>, |
| 18 }, |
| 19 expect: [ |
| 20 { at: <Float>, is: <CSS Value> } |
| 21 ], |
| 22 }], |
| 23 }) |
| 24 |
| 25 Description: |
| 26 assertResponsive takes a property specific interpolation and a list of style |
| 27 configurations with interpolation expectations that apply to each |
| 28 configuration. |
| 29 It starts the interpolation in every configuration, changes the |
| 30 state to every other configuration (n * (n - 1) complexity) and asserts that |
| 31 each destination configuration's expectations are met. |
| 32 This test is designed to catch stale interpolation caches. |
| 33 */ |
| 34 |
| 35 (function() { |
| 36 'use strict'; |
| 37 var sharedStyle = null; |
| 38 var animationCount = 0; |
| 39 var pendingResponsiveTests = []; |
| 40 |
| 41 function assertResponsive(options) { |
| 42 pendingResponsiveTests.push(options); |
| 43 } |
| 44 |
| 45 function createStateTransitions(configurations) { |
| 46 var stateTransitions = []; |
| 47 for (var i = 0; i < configurations.length; i++) { |
| 48 var beforeConfiguration = configurations[i]; |
| 49 for (var j = 0; j < configurations.length; j++) { |
| 50 var afterConfiguration = configurations[j]; |
| 51 if (j != i) { |
| 52 stateTransitions.push({ |
| 53 before: beforeConfiguration, |
| 54 after: afterConfiguration, |
| 55 }); |
| 56 } |
| 57 } |
| 58 } |
| 59 return stateTransitions; |
| 60 } |
| 61 |
| 62 function createElement(tag, container) { |
| 63 var element = document.createElement(tag); |
| 64 if (container) { |
| 65 container.appendChild(element); |
| 66 } |
| 67 return element; |
| 68 } |
| 69 |
| 70 function createTargets(n, container) { |
| 71 var targets = []; |
| 72 for (var i = 0; i < n; i++) { |
| 73 targets.push(createElement('div', container)); |
| 74 } |
| 75 return targets; |
| 76 } |
| 77 |
| 78 function setState(targets, property, state) { |
| 79 if (state.inherited) { |
| 80 var parent = targets[0].parentElement; |
| 81 console.assert(targets.every(function(target) { return target.parentElement
=== parent; })); |
| 82 parent.style[property] = state.inherited; |
| 83 } |
| 84 if (state.underlying) { |
| 85 for (var target of targets) { |
| 86 target.style[property] = state.underlying; |
| 87 } |
| 88 } |
| 89 } |
| 90 |
| 91 function createAnimationName() { |
| 92 return 'anim' + (animationCount++); |
| 93 } |
| 94 |
| 95 function createKeyframes(animationName, property, from, to) { |
| 96 return ` |
| 97 @keyframes ${animationName} { |
| 98 from { ${property}: ${from}; } |
| 99 to { ${property}: ${to}; } |
| 100 }`; |
| 101 } |
| 102 |
| 103 function addGlobalStyle(styleText) { |
| 104 if (!sharedStyle) { |
| 105 sharedStyle = createElement('style', document.documentElement); |
| 106 } |
| 107 sharedStyle.textContent += styleText; |
| 108 } |
| 109 |
| 110 function startPausedAnimations(targets, animationName, fractions) { |
| 111 console.assert(targets.length == fractions.length); |
| 112 for (var i = 0; i < targets.length; i++) { |
| 113 var target = targets[i]; |
| 114 var fraction = fractions[i]; |
| 115 console.assert(fraction >= 0 && fraction <= 1); |
| 116 target.style.animation = `${animationName} 1s linear both paused`; |
| 117 target.style.animationDelay = `${-fraction}s`; |
| 118 } |
| 119 } |
| 120 |
| 121 function runPendingResponsiveTests() { |
| 122 var stateTransitionTests = []; |
| 123 pendingResponsiveTests.forEach(function(options) { |
| 124 var property = options.property; |
| 125 var from = options.from; |
| 126 var to = options.to; |
| 127 var animationName = createAnimationName(); |
| 128 addGlobalStyle(createKeyframes(animationName, property, from, to)); |
| 129 |
| 130 var stateTransitions = createStateTransitions(options.configurations); |
| 131 stateTransitions.forEach(function(stateTransition) { |
| 132 var before = stateTransition.before; |
| 133 var after = stateTransition.after; |
| 134 var container = createElement('div', document.body); |
| 135 var targets = createTargets(after.expect.length, container); |
| 136 |
| 137 setState(targets, property, before.state); |
| 138 startPausedAnimations(targets, animationName, after.expect.map(function(ex
pectation) { return expectation.at; })); |
| 139 stateTransitionTests.push({ |
| 140 applyStateTransition() { |
| 141 setState(targets, property, after.state); |
| 142 }, |
| 143 assert() { |
| 144 for (var i = 0; i < targets.length; i++) { |
| 145 var target = targets[i]; |
| 146 var expectation = after.expect[i]; |
| 147 var actual = getComputedStyle(target)[property]; |
| 148 test(function() { |
| 149 assert_equals(actual, expectation.is); |
| 150 }, `Animation on property <${property}> from [${from}] to [${to}] wi
th ${JSON.stringify(before.state)} changed to ${JSON.stringify(after.state)} at
(${expectation.at}) is [${expectation.is}]`); |
| 151 } |
| 152 }, |
| 153 }); |
| 154 }); |
| 155 }); |
| 156 |
| 157 // Force style recalc to instantiate animations internally. |
| 158 getComputedStyle(document.body).color; |
| 159 |
| 160 // Separate style modification from measurement as different phases to avoid a
style recalc storm. |
| 161 for (var stateTransitionTest of stateTransitionTests) { |
| 162 stateTransitionTest.applyStateTransition(); |
| 163 } |
| 164 for (var stateTransitionTest of stateTransitionTests) { |
| 165 stateTransitionTest.assert(); |
| 166 } |
| 167 } |
| 168 |
| 169 function loadScript(url) { |
| 170 return new Promise(function(resolve) { |
| 171 var script = document.createElement('script'); |
| 172 script.src = url; |
| 173 script.onload = resolve; |
| 174 document.head.appendChild(script); |
| 175 }); |
| 176 } |
| 177 |
| 178 loadScript('../../resources/testharness.js').then(function() { |
| 179 return loadScript('../../resources/testharnessreport.js'); |
| 180 }).then(function() { |
| 181 var asyncHandle = async_test('This test uses responsive-test.js.') |
| 182 requestAnimationFrame(function() { |
| 183 runPendingResponsiveTests(); |
| 184 asyncHandle.done() |
| 185 }); |
| 186 }); |
| 187 |
| 188 |
| 189 window.assertResponsive = assertResponsive; |
| 190 |
| 191 })(); |
| OLD | NEW |