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 functions: | |
7 assertCSSResponsive | |
8 assertSVGResponsive | |
9 | |
10 Exported objects: | |
11 neutralKeyframe | |
12 | |
13 Options format: { | |
14 ?targetTag: <Target element tag name>, | |
15 property: <Property/Attribute name>, | |
16 ?getter(target): <Reads animated value from target>, | |
17 from: <Value>, | |
18 to: <Value>, | |
19 configurations: [{ | |
20 state: { | |
21 ?underlying: <Value>, | |
22 ?inherited: <CSS Value>, | |
23 }, | |
24 expect: [ | |
25 { at: <Float>, is: <Value> }, | |
26 ], | |
27 }], | |
28 } | |
29 | |
30 Description: | |
31 assertCSSResponsive() and assertSVGResponsive() take a property | |
32 specific interpolation and a list of style configurations with interpolation | |
33 expectations that apply to each configuration. | |
34 It starts the interpolation in every configuration, changes the | |
35 state to every other configuration (n * (n - 1) complexity) and asserts that | |
36 each destination configuration's expectations are met. | |
37 Each animation target can be assigned custom styles via the ".target" selector. | |
38 This test is designed to catch stale interpolation caches. | |
39 Set from/to to the exported neutralKeyframe object to use neutral keyframes. | |
40 */ | |
41 | |
42 (function() { | |
43 'use strict'; | |
44 var pendingResponsiveTests = []; | |
45 var htmlNamespace = 'http://www.w3.org/1999/xhtml'; | |
46 var svgNamespace = 'http://www.w3.org/2000/svg'; | |
47 var neutralKeyframe = {}; | |
48 | |
49 function assertCSSResponsive(options) { | |
50 pendingResponsiveTests.push({ | |
51 options, | |
52 bindings: { | |
53 prefixProperty(property) { | |
54 return toCamelCase(property); | |
55 }, | |
56 createTargetContainer(container) { | |
57 if (options.targetTag) { | |
58 var svgRoot = createElement('svg', container, 'svg-root', svgNamespace
); | |
59 svgRoot.setAttribute('width', 0); | |
60 svgRoot.setAttribute('height', 0); | |
61 return svgRoot; | |
62 } | |
63 | |
64 return createElement('div', container); | |
65 }, | |
66 createTarget(container) { | |
67 if (options.targetTag) | |
68 return createElement(options.targetTag, container, 'target', svgNamesp
ace); | |
69 | |
70 return createElement('div', container, 'target'); | |
71 }, | |
72 setValue(target, property, value) { | |
73 test(function() { | |
74 assert_true(CSS.supports(property, value), 'CSS.supports ' + property
+ ' ' + value); | |
75 }); | |
76 target.style[property] = value; | |
77 }, | |
78 getAnimatedValue(target, property) { | |
79 return getComputedStyle(target)[property]; | |
80 }, | |
81 }, | |
82 }); | |
83 } | |
84 | |
85 function assertSVGResponsive(options) { | |
86 pendingResponsiveTests.push({ | |
87 options, | |
88 bindings: { | |
89 prefixProperty(property) { | |
90 return 'svg-' + property; | |
91 }, | |
92 createTargetContainer(container) { | |
93 var svgRoot = createElement('svg', container, 'svg-root', svgNamespace); | |
94 svgRoot.setAttribute('width', 0); | |
95 svgRoot.setAttribute('height', 0); | |
96 return svgRoot; | |
97 }, | |
98 createTarget(targetContainer) { | |
99 console.assert(options.targetTag); | |
100 return createElement(options.targetTag, targetContainer, 'target', svgNa
mespace); | |
101 }, | |
102 setValue(target, property, value) { | |
103 target.setAttribute(property, value); | |
104 }, | |
105 getAnimatedValue(target, property) { | |
106 return options.getter ? options.getter(target) : target[property].animVa
l; | |
107 }, | |
108 }, | |
109 }); | |
110 } | |
111 | |
112 function createStateTransitions(configurations) { | |
113 var stateTransitions = []; | |
114 for (var i = 0; i < configurations.length; i++) { | |
115 var beforeConfiguration = configurations[i]; | |
116 for (var j = 0; j < configurations.length; j++) { | |
117 var afterConfiguration = configurations[j]; | |
118 if (j != i) { | |
119 stateTransitions.push({ | |
120 before: beforeConfiguration, | |
121 after: afterConfiguration, | |
122 }); | |
123 } | |
124 } | |
125 } | |
126 return stateTransitions; | |
127 } | |
128 | |
129 function createElement(tag, container, className, namespace) { | |
130 var element = document.createElementNS(namespace || htmlNamespace, tag); | |
131 if (container) { | |
132 container.appendChild(element); | |
133 } | |
134 if (className) { | |
135 element.classList.add(className); | |
136 } | |
137 return element; | |
138 } | |
139 | |
140 function createTargets(bindings, n, container) { | |
141 var targets = []; | |
142 for (var i = 0; i < n; i++) { | |
143 targets.push(bindings.createTarget(container)); | |
144 } | |
145 return targets; | |
146 } | |
147 | |
148 function setState(bindings, targets, property, state) { | |
149 for (var item in state) { | |
150 switch (item) { | |
151 case 'inherited': | |
152 var parent = targets[0].parentElement; | |
153 console.assert(targets.every(target => target.parentElement === parent)); | |
154 bindings.setValue(parent, property, state.inherited); | |
155 break; | |
156 case 'underlying': | |
157 for (var target of targets) { | |
158 bindings.setValue(target, property, state.underlying); | |
159 } | |
160 break; | |
161 default: | |
162 for (var target of targets) { | |
163 bindings.setValue(target, item, state[item]); | |
164 } | |
165 break; | |
166 } | |
167 } | |
168 } | |
169 | |
170 function isNeutralKeyframe(keyframe) { | |
171 return keyframe === neutralKeyframe; | |
172 } | |
173 | |
174 function keyframeText(keyframe) { | |
175 return isNeutralKeyframe(keyframe) ? 'neutral' : `[${keyframe}]`; | |
176 } | |
177 | |
178 function toCamelCase(property) { | |
179 for (var i = property.length - 2; i > 0; --i) { | |
180 if (property[i] === '-') { | |
181 property = property.substring(0, i) + property[i + 1].toUpperCase() + prop
erty.substring(i + 2); | |
182 } | |
183 } | |
184 return property; | |
185 } | |
186 | |
187 function createKeyframes(prefixedProperty, from, to) { | |
188 var keyframes = []; | |
189 if (!isNeutralKeyframe(from)) { | |
190 keyframes.push({ | |
191 offset: 0, | |
192 [prefixedProperty]: from, | |
193 }); | |
194 } | |
195 if (!isNeutralKeyframe(to)) { | |
196 keyframes.push({ | |
197 offset: 1, | |
198 [prefixedProperty]: to, | |
199 }); | |
200 } | |
201 return keyframes; | |
202 } | |
203 | |
204 function createPausedAnimations(targets, keyframes, fractions) { | |
205 console.assert(targets.length == fractions.length); | |
206 return targets.map((target, i) => { | |
207 var fraction = fractions[i]; | |
208 console.assert(fraction >= 0 && fraction < 1); | |
209 var animation = target.animate(keyframes, 1); | |
210 animation.pause(); | |
211 animation.currentTime = fraction; | |
212 return animation; | |
213 }); | |
214 } | |
215 | |
216 function runPendingResponsiveTests() { | |
217 return new Promise(resolve => { | |
218 var stateTransitionTests = []; | |
219 pendingResponsiveTests.forEach(responsiveTest => { | |
220 var options = responsiveTest.options; | |
221 var bindings = responsiveTest.bindings; | |
222 var property = options.property; | |
223 var prefixedProperty = bindings.prefixProperty(property); | |
224 assert_true('from' in options); | |
225 assert_true('to' in options); | |
226 var from = options.from; | |
227 var to = options.to; | |
228 var keyframes = createKeyframes(prefixedProperty, from, to); | |
229 | |
230 var stateTransitions = createStateTransitions(options.configurations); | |
231 stateTransitions.forEach(stateTransition => { | |
232 var before = stateTransition.before; | |
233 var after = stateTransition.after; | |
234 var container = bindings.createTargetContainer(document.body); | |
235 var targets = createTargets(bindings, after.expect.length, container); | |
236 var expectationTargets = createTargets(bindings, after.expect.length, co
ntainer); | |
237 | |
238 setState(bindings, targets, property, before.state); | |
239 var animations = createPausedAnimations(targets, keyframes, after.expect
.map(expectation => expectation.at)); | |
240 stateTransitionTests.push({ | |
241 applyStateTransition() { | |
242 setState(bindings, targets, property, after.state); | |
243 }, | |
244 assert() { | |
245 for (var i = 0; i < targets.length; i++) { | |
246 var target = targets[i]; | |
247 var expectation = after.expect[i]; | |
248 var expectationTarget = expectationTargets[i]; | |
249 bindings.setValue(expectationTarget, property, expectation.is); | |
250 var actual = bindings.getAnimatedValue(target, property); | |
251 test(() => { | |
252 assert_equals(actual, bindings.getAnimatedValue(expectationTarge
t, property)); | |
253 }, `Animation on property <${prefixedProperty}> from ${keyframeTex
t(from)} to ${keyframeText(to)} with ${JSON.stringify(before.state)} changed to
${JSON.stringify(after.state)} at (${expectation.at}) is [${expectation.is}]`); | |
254 } | |
255 }, | |
256 }); | |
257 }); | |
258 }); | |
259 | |
260 requestAnimationFrame(() => { | |
261 for (var stateTransitionTest of stateTransitionTests) { | |
262 stateTransitionTest.applyStateTransition(); | |
263 } | |
264 | |
265 requestAnimationFrame(() => { | |
266 for (var stateTransitionTest of stateTransitionTests) { | |
267 stateTransitionTest.assert(); | |
268 } | |
269 resolve(); | |
270 }); | |
271 }); | |
272 }); | |
273 } | |
274 | |
275 function loadScript(url) { | |
276 return new Promise(resolve => { | |
277 var script = document.createElement('script'); | |
278 script.src = url; | |
279 script.onload = resolve; | |
280 document.head.appendChild(script); | |
281 }); | |
282 } | |
283 | |
284 loadScript('../../resources/testharness.js').then(() => { | |
285 return loadScript('../../resources/testharnessreport.js'); | |
286 }).then(() => { | |
287 var asyncHandle = async_test('This test uses responsive-test.js.') | |
288 runPendingResponsiveTests().then(() => { | |
289 asyncHandle.done(); | |
290 }); | |
291 }); | |
292 | |
293 | |
294 window.assertCSSResponsive = assertCSSResponsive; | |
295 window.assertSVGResponsive = assertSVGResponsive; | |
296 window.neutralKeyframe = neutralKeyframe; | |
297 | |
298 })(); | |
OLD | NEW |