2008-12-10 Pierre-Olivier Latour <pol@apple.com>
[WebKit-https.git] / LayoutTests / animations / animation-test-helpers.js
1 /* This is the helper function to run animation tests:
2
3 Test page requirements:
4 - The body must contain an empty div with id "result"
5 - Call this function directly from the <script> inside the test page
6
7 Function parameters:
8     expected [required]: an array of arrays defining a set of CSS properties that must have given values at specific times (see below)
9     callback [optional]: a function to be executed just before the test starts (none by default)
10     event [optional]: which DOM event to wait for before starting the test ("webkitAnimationStart" by default)
11
12     Each sub-array must contain these items in this order:
13     - the name of the CSS animation (may be null) [1]
14     - the time in seconds at which to snapshot the CSS property
15     - the id of the element on which to get the CSS property value
16     - the name of the CSS property to get [2]
17     - the expected value for the CSS property
18     - the tolerance to use when comparing the effective CSS property value with its expected value
19
20     [1] If null is passed, a regular setTimeout() will be used instead to snapshot the animated property in the future,
21     instead of fast forwarding using the pauseAnimationAtTimeOnElementWithId() JS API from DRT 
22
23     [2] If the CSS property name is "webkitTransform", expected value must be an array of 1 or more numbers corresponding to the matrix elements,
24     or a string which will be compared directly (useful if the expected value is "none")
25     If the CSS property name is "webkitTransform.N", expected value must be a number corresponding to the Nth element of the matrix
26
27 */
28 function runAnimationTest(expected, callback, event)
29 {
30     var result = "";
31     var hasPauseAnimationAPI = window.layoutTestController && layoutTestController.pauseAnimationAtTimeOnElementWithId;
32
33     function isCloseEnough(actual, desired, tolerance)
34     {
35         var diff = Math.abs(actual - desired);
36         return diff <= tolerance;
37     }
38
39     function checkExpectedValue(expected, index)
40     {
41         var animationName = expected[index][0];
42         var time = expected[index][1];
43         var elementId = expected[index][2];
44         var property = expected[index][3];
45         var expectedValue = expected[index][4];
46         var tolerance = expected[index][5];
47
48         if (animationName && hasPauseAnimationAPI && !layoutTestController.pauseAnimationAtTimeOnElementWithId(animationName, time, elementId)) {
49             result += "FAIL - animation \"" + animationName + "\" is not running" + "<br>";
50             return;
51         }
52
53         var computedValue;
54         var pass;
55         if (!property.indexOf("webkitTransform")) {
56             computedValue = window.getComputedStyle(document.getElementById(elementId)).webkitTransform;
57
58             if (typeof expectedValue == "string")
59                 pass = (computedValue == expectedValue);
60             else if (typeof expectedValue == "number") {
61                 var m = computedValue.split("(");
62                 var m = m[1].split(",");
63                 pass = isCloseEnough(parseFloat(m[parseInt(property.substring(16))]), expectedValue, tolerance);
64             } else {
65                 var m = computedValue.split("(");
66                 var m = m[1].split(",");
67                 for (i = 0; i < expectedValue.length; ++i) {
68                     pass = isCloseEnough(parseFloat(m[i]), expectedValue[i], tolerance);
69                     if (!pass)
70                         break;
71                 }
72             }
73         } else if (property == "lineHeight") {
74             computedValue = parseInt(window.getComputedStyle(document.getElementById(elementId)).lineHeight);
75             pass = isCloseEnough(computedValue, expectedValue, tolerance);
76         } else {    
77             var computedStyle = window.getComputedStyle(document.getElementById(elementId)).getPropertyCSSValue(property);
78             computedValue = computedStyle.getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
79             pass = isCloseEnough(computedValue, expectedValue, tolerance);
80         }
81
82         if (pass)
83             result += "PASS - \"" + property + "\" property for \"" + elementId + "\" element at " + time + "s saw something close to: " + expectedValue + "<br>";
84         else
85             result += "FAIL - \"" + property + "\" property for \"" + elementId + "\" element at " + time + "s expected: " + expectedValue + " but saw: " + computedValue + "<br>";
86     }
87
88     function endTest()
89     {
90         document.getElementById('result').innerHTML = result;
91
92         if (window.layoutTestController)
93             layoutTestController.notifyDone();
94     }
95
96     function checkExpectedValueCallback(expected, index)
97     {
98         return function() { checkExpectedValue(expected, index); };
99     }
100
101     var testStarted = false;
102     function startTest(expected, callback)
103     {
104         if (testStarted) return;
105         testStarted = true;
106
107         if (callback)
108             callback();
109
110         var maxTime = 0;
111
112         for (var i = 0; i < expected.length; ++i) {
113             var animationName = expected[i][0];
114             var time = expected[i][1];
115
116             // We can only use the animation fast-forward mechanism if there's an animation name
117             // and DRT implements pauseAnimationAtTimeOnElementWithId()
118             if (animationName && hasPauseAnimationAPI)
119                 checkExpectedValue(expected, i);
120             else {
121                 if (time > maxTime)
122                     maxTime = time;
123
124                 window.setTimeout(checkExpectedValueCallback(expected, i), time * 1000);
125             }
126         }
127
128         if (maxTime > 0)
129             window.setTimeout(endTest, maxTime * 1000 + 50);
130         else
131             endTest();
132     }
133     
134     if (window.layoutTestController) {
135         layoutTestController.dumpAsText();
136         layoutTestController.waitUntilDone();
137     }
138     
139     if (!expected)
140         throw("Expected results are missing!");
141     
142     var target = document;
143     if (event == undefined)
144         event = "webkitAnimationStart";
145     else if (event == "load")
146         target = window;
147     target.addEventListener(event, function() { startTest(expected, callback); }, false);
148 }