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.
11 property: <CSS Property>,
16 ?underlying: <CSS Value>,
17 ?inherited: <CSS Value>,
20 { at: <Float>, is: <CSS Value> }
26 assertResponsive takes a property specific interpolation and a list of style
27 configurations with interpolation expectations that apply to each
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 Each animation target can be assigned custom styles via the ".target" selector.
33 This test is designed to catch stale interpolation caches.
38 var sharedStyle
= null;
39 var animationCount
= 0;
40 var pendingResponsiveTests
= [];
42 function assertResponsive(options
) {
43 pendingResponsiveTests
.push(options
);
46 function createStateTransitions(configurations
) {
47 var stateTransitions
= [];
48 for (var i
= 0; i
< configurations
.length
; i
++) {
49 var beforeConfiguration
= configurations
[i
];
50 for (var j
= 0; j
< configurations
.length
; j
++) {
51 var afterConfiguration
= configurations
[j
];
53 stateTransitions
.push({
54 before
: beforeConfiguration
,
55 after
: afterConfiguration
,
60 return stateTransitions
;
63 function createElement(tag
, container
, className
) {
64 var element
= document
.createElement(tag
);
66 container
.appendChild(element
);
69 element
.classList
.add(className
);
74 function createTargets(n
, container
) {
76 for (var i
= 0; i
< n
; i
++) {
77 targets
.push(createElement('div', container
, 'target'));
82 function setState(targets
, property
, state
) {
83 if (state
.inherited
) {
84 var parent
= targets
[0].parentElement
;
85 console
.assert(targets
.every(function(target
) { return target
.parentElement
=== parent
; }));
86 parent
.style
[property
] = state
.inherited
;
88 if (state
.underlying
) {
89 for (var target
of targets
) {
90 target
.style
[property
] = state
.underlying
;
95 function createAnimationName() {
96 return 'anim' + (animationCount
++);
99 function createKeyframes(animationName
, property
, from, to
) {
101 @keyframes ${animationName} {
102 from { ${property}: ${from}; }
103 to { ${property}: ${to}; }
107 function addGlobalStyle(styleText
) {
109 sharedStyle
= createElement('style', document
.documentElement
);
111 sharedStyle
.textContent
+= styleText
;
114 function startPausedAnimations(targets
, animationName
, fractions
) {
115 console
.assert(targets
.length
== fractions
.length
);
116 for (var i
= 0; i
< targets
.length
; i
++) {
117 var target
= targets
[i
];
118 var fraction
= fractions
[i
];
119 console
.assert(fraction
>= 0 && fraction
<= 1);
120 target
.style
.animation
= `${animationName} 1s linear both paused`;
121 target
.style
.animationDelay
= `${-fraction}s`;
125 function runPendingResponsiveTests() {
126 var stateTransitionTests
= [];
127 pendingResponsiveTests
.forEach(function(options
) {
128 var property
= options
.property
;
129 var from = options
.from;
131 var animationName
= createAnimationName();
132 addGlobalStyle(createKeyframes(animationName
, property
, from, to
));
134 var stateTransitions
= createStateTransitions(options
.configurations
);
135 stateTransitions
.forEach(function(stateTransition
) {
136 var before
= stateTransition
.before
;
137 var after
= stateTransition
.after
;
138 var container
= createElement('div', document
.body
);
139 var targets
= createTargets(after
.expect
.length
, container
);
141 setState(targets
, property
, before
.state
);
142 startPausedAnimations(targets
, animationName
, after
.expect
.map(function(expectation
) { return expectation
.at
; }));
143 stateTransitionTests
.push({
144 applyStateTransition() {
145 setState(targets
, property
, after
.state
);
148 for (var i
= 0; i
< targets
.length
; i
++) {
149 var target
= targets
[i
];
150 var expectation
= after
.expect
[i
];
151 var actual
= getComputedStyle(target
)[property
];
153 assert_equals(actual
, expectation
.is
);
154 }, `Animation on property <${property}> from [${from}] to [${to}] with ${JSON.stringify(before.state)} changed to ${JSON.stringify(after.state)} at (${expectation.at}) is [${expectation.is}]`);
161 // Force style recalc to instantiate animations internally.
162 getComputedStyle(document
.body
).color
;
164 // Separate style modification from measurement as different phases to avoid a style recalc storm.
165 for (var stateTransitionTest
of stateTransitionTests
) {
166 stateTransitionTest
.applyStateTransition();
168 for (var stateTransitionTest
of stateTransitionTests
) {
169 stateTransitionTest
.assert();
173 function loadScript(url
) {
174 return new Promise(function(resolve
) {
175 var script
= document
.createElement('script');
177 script
.onload
= resolve
;
178 document
.head
.appendChild(script
);
182 loadScript('../../resources/testharness.js').then(function() {
183 return loadScript('../../resources/testharnessreport.js');
185 var asyncHandle
= async_test('This test uses responsive-test.js.')
186 requestAnimationFrame(function() {
187 runPendingResponsiveTests();
193 window
.assertResponsive
= assertResponsive
;