Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / LayoutTests / animations / responsive / resources / responsive-test.js
blobaacdbb60b1bff99f85951a597f4b465b20ff30af
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.
5 /*
6 Exported function:
7 assertResponsive
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>,
19 expect: [
20 { at: <Float>, is: <CSS Value> }
22 }],
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 Each animation target can be assigned custom styles via the ".target" selector.
33 This test is designed to catch stale interpolation caches.
36 (function() {
37 'use strict';
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];
52 if (j != i) {
53 stateTransitions.push({
54 before: beforeConfiguration,
55 after: afterConfiguration,
56 });
60 return stateTransitions;
63 function createElement(tag, container, className) {
64 var element = document.createElement(tag);
65 if (container) {
66 container.appendChild(element);
68 if (className) {
69 element.classList.add(className);
71 return element;
74 function createTargets(n, container) {
75 var targets = [];
76 for (var i = 0; i < n; i++) {
77 targets.push(createElement('div', container, 'target'));
79 return targets;
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) {
100 return `
101 @keyframes ${animationName} {
102 from { ${property}: ${from}; }
103 to { ${property}: ${to}; }
107 function addGlobalStyle(styleText) {
108 if (!sharedStyle) {
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;
130 var to = options.to;
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);
147 assert() {
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];
152 test(function() {
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');
176 script.src = url;
177 script.onload = resolve;
178 document.head.appendChild(script);
182 loadScript('../../resources/testharness.js').then(function() {
183 return loadScript('../../resources/testharnessreport.js');
184 }).then(function() {
185 var asyncHandle = async_test('This test uses responsive-test.js.')
186 requestAnimationFrame(function() {
187 runPendingResponsiveTests();
188 asyncHandle.done()
193 window.assertResponsive = assertResponsive;
195 })();