| Sangwhan Moon | 70f1aa2 | 2014-04-08 11:10:47 | [diff] [blame] | 1 | <!DOCTYPE HTML> |
| 2 | <html> |
| 3 | <!-- |
| 4 | Test cases for Touch Events v1 Recommendation |
| 5 | http://www.w3.org/TR/touch-events/ |
| 6 | |
| 7 | These tests are based on Matt Bruebeck's single-touch tests. |
| 8 | There are NO multi-touch tests in this document. |
| Sangwhan Moon | 70f1aa2 | 2014-04-08 11:10:47 | [diff] [blame] | 9 | |
| 10 | This document references Test Assertions (abbrev TA below) written by Cathy Chan |
| 11 | http://www.w3.org/2010/webevents/wiki/TestAssertions |
| 12 | --> |
| 13 | |
| 14 | <head> |
| 15 | <title>Touch Events Single Touch Tests</title> |
| 16 | <meta name="viewport" content="width=device-width"> |
| 17 | <script src="/resources/testharness.js"></script> |
| 18 | <script> |
| 19 | setup({explicit_done: true}); |
| 20 | |
| 21 | // Check a Touch object's atttributes for existence and correct type |
| 22 | // TA: 1.1.2, 1.1.3 |
| 23 | function check_Touch_object (t) { |
| 24 | test(function() { |
| 25 | assert_equals(Object.prototype.toString.call(t), "[object Touch]", name + " attribute of type TouchList"); |
| 26 | }, "touch point is a Touch object"); |
| 27 | [ |
| 28 | ["long", "identifier"], |
| 29 | ["EventTarget", "target"], |
| 30 | ["long", "screenX"], |
| 31 | ["long", "screenY"], |
| 32 | ["long", "clientX"], |
| 33 | ["long", "clientY"], |
| 34 | ["long", "pageX"], |
| 35 | ["long", "pageY"], |
| 36 | ].forEach(function(attr) { |
| 37 | var type = attr[0]; |
| 38 | var name = attr[1]; |
| 39 | |
| 40 | // existence check |
| 41 | test(function() { |
| 42 | assert_true(name in t, name + " attribute in Touch object"); |
| 43 | }, "Touch." + name + " attribute exists"); |
| 44 | |
| 45 | // type check |
| 46 | switch(type) { |
| 47 | case "long": |
| 48 | test(function() { |
| 49 | assert_equals(typeof t[name], "number", name + " attribute of type long"); |
| 50 | }, "Touch." + name + " attribute is of type number (long)"); |
| 51 | break; |
| 52 | case "EventTarget": |
| 53 | // An event target is some type of Element |
| 54 | test(function() { |
| 55 | assert_true(t[name] instanceof Element, "EventTarget must be an Element."); |
| 56 | }, "Touch." + name + " attribute is of type Element"); |
| 57 | break; |
| 58 | default: |
| 59 | break; |
| 60 | } |
| 61 | }); |
| 62 | } |
| 63 | |
| 64 | // Check a TouchList object's attributes and methods for existence and proper type |
| 65 | // Also make sure all of the members of the list are Touch objects |
| 66 | // TA: 1.2.1, 1.2.2, 1.2.5 |
| 67 | function check_TouchList_object (tl) { |
| 68 | test(function() { |
| 69 | assert_equals(Object.prototype.toString.call(tl), "[object TouchList]", name + " attribute of type TouchList"); |
| 70 | }, "touch list is a TouchList object"); |
| 71 | [ |
| 72 | ["unsigned long", "length"], |
| 73 | ["function", "item"], |
| 74 | ].forEach(function(attr) { |
| 75 | var type = attr[0]; |
| 76 | var name = attr[1]; |
| 77 | |
| 78 | // existence check |
| 79 | test(function() { |
| 80 | assert_true(name in tl, name + " attribute in TouchList"); |
| 81 | }, "TouchList." + name + " attribute exists"); |
| 82 | |
| 83 | // type check |
| 84 | switch(type) { |
| 85 | case "unsigned long": |
| 86 | test(function() { |
| 87 | assert_equals(typeof tl[name], "number", name + " attribute of type long"); |
| 88 | }, "TouchList." + name + " attribute is of type number (unsigned long)"); |
| 89 | break; |
| 90 | case "function": |
| 91 | test(function() { |
| 92 | assert_equals(typeof tl[name], "function", name + " attribute of type function"); |
| 93 | }, "TouchList." + name + " attribute is of type function"); |
| 94 | break; |
| 95 | default: |
| 96 | break; |
| 97 | } |
| 98 | }); |
| 99 | // Each member of tl should be a proper Touch object |
| 100 | for (var i=0; i < tl.length; i++) { |
| 101 | check_Touch_object(tl.item(i)); |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | // Check a TouchEvent event's attributes for existence and proper type |
| 106 | // Also check that each of the event's TouchList objects are valid |
| 107 | // TA: 1.{3,4,5}.1.1, 1.{3,4,5}.1.2 |
| 108 | function check_TouchEvent(ev) { |
| 109 | test(function() { |
| 110 | assert_true(ev instanceof TouchEvent, "event is a TouchEvent event"); |
| 111 | }, ev.type + " event is a TouchEvent event"); |
| 112 | [ |
| 113 | ["TouchList", "touches"], |
| 114 | ["TouchList", "targetTouches"], |
| 115 | ["TouchList", "changedTouches"], |
| 116 | ["boolean", "altKey"], |
| 117 | ["boolean", "metaKey"], |
| 118 | ["boolean", "ctrlKey"], |
| 119 | ["boolean", "shiftKey"], |
| 120 | ].forEach(function(attr) { |
| 121 | var type = attr[0]; |
| 122 | var name = attr[1]; |
| 123 | |
| 124 | // existence check |
| 125 | test(function() { |
| 126 | assert_true(name in ev, name + " attribute in " + ev.type + " event"); |
| 127 | }, ev.type + "." + name + " attribute exists"); |
| 128 | |
| 129 | // type check |
| 130 | switch(type) { |
| 131 | case "boolean": |
| 132 | test(function() { |
| 133 | assert_equals(typeof ev[name], "boolean", name + " attribute of type boolean"); |
| 134 | }, ev.type + "." + name + " attribute is of type boolean"); |
| 135 | break; |
| 136 | case "TouchList": |
| 137 | test(function() { |
| 138 | assert_equals(Object.prototype.toString.call(ev[name]), "[object TouchList]", name + " attribute of type TouchList"); |
| 139 | }, ev.type + "." + name + " attribute is of type TouchList"); |
| 140 | // Now check the validity of the TouchList |
| 141 | check_TouchList_object(ev[name]); |
| 142 | break; |
| 143 | default: |
| 144 | break; |
| 145 | } |
| 146 | }); |
| 147 | } |
| 148 | |
| 149 | function is_touch_over_element(touch, element) { |
| 150 | var bounds = element.getBoundingClientRect(); |
| 151 | return touch.pageX >= bounds.left && touch.pageX <= bounds.right && |
| 152 | touch.pageY >= bounds.top && touch.pageY <= bounds.bottom; |
| 153 | } |
| 154 | |
| 155 | function check_touch_clientXY(touch) { |
| 156 | assert_equals(touch.clientX, touch.pageX - window.pageXOffset, "touch.clientX is touch.pageX - window.pageXOffset."); |
| 157 | assert_equals(touch.clientY, touch.pageY - window.pageYOffset, "touch.clientY is touch.pageY - window.pageYOffset."); |
| 158 | } |
| 159 | |
| 160 | function run() { |
| 161 | var target0 = document.getElementById("target0"); |
| 162 | var target1 = document.getElementById("target1"); |
| 163 | |
| 164 | var test_touchstart = async_test("touchstart event received"); |
| 165 | var test_touchmove = async_test("touchmove event received"); |
| 166 | var test_touchend = async_test("touchend event received"); |
| 167 | var test_mousedown = async_test("Interaction with mouse events"); |
| 168 | |
| 169 | var touchstart_received = false; |
| 170 | var touchmove_received = false; |
| 171 | var touchend_received = false; |
| 172 | var invalid_touchmove_received = false; |
| 173 | var touchstart_identifier; |
| 174 | |
| 175 | on_event(target0, "touchstart", function onTouchStart(ev) { |
| 176 | ev.preventDefault(); |
| 177 | |
| 178 | // Check event ordering TA: 1.6.2 |
| 179 | test_touchstart.step(function() { |
| 180 | assert_false(touchstart_received, "duplicate touchstart event"); |
| 181 | assert_false(touchmove_received, "touchstart precedes touchmove"); |
| 182 | assert_false(touchend_received, "touchstart precedes touchend"); |
| 183 | }); |
| 184 | test_touchstart.done(); |
| 185 | if (touchstart_received) |
| 186 | return; |
| 187 | touchstart_received = true; |
| 188 | test_mousedown.done(); // If we got here, then the mouse event test is not needed. |
| 189 | |
| 190 | check_TouchEvent(ev); |
| 191 | |
| 192 | // TA: 1.3.2.1, 1.3.3.1, 1.3.4.1 |
| 193 | test(function() { |
| 194 | assert_equals(ev.touches.length, 1, "One touch point."); |
| 195 | assert_equals(ev.changedTouches.length, 1, "One changed touch point."); |
| 196 | assert_equals(ev.targetTouches.length, 1, "One target touch point."); |
| 197 | }, "touchstart: all TouchList lengths are correct"); |
| 198 | |
| 199 | var t = ev.touches[0]; |
| 200 | var ct = ev.changedTouches[0]; |
| 201 | var tt = ev.targetTouches[0]; |
| 202 | |
| 203 | touchstart_identifier = t.identifier; |
| 204 | // TA: 1.3.3.3, 1.3.2.3, 1.3.3.4 (indirect (transitive)) |
| 205 | test(function() { |
| 206 | assert_equals(ct.identifier, touchstart_identifier, "changedTouches identifier matches."); |
| 207 | assert_equals(tt.identifier, touchstart_identifier, "targetTouches identifier matches."); |
| 208 | }, "touchstart: all TouchList identifiers are consistent"); |
| 209 | |
| 210 | // TA: 1.3.3.9 |
| 211 | test(function() { |
| 212 | assert_equals(tt.target, ev.target, "event target same as targetTouches target."); |
| 213 | }, "touchstart: event target same as targetTouches target"); |
| 214 | |
| 215 | test(function() { |
| 216 | assert_true(is_touch_over_element(t, target0), "touch.pageX/pageY is over target0."); |
| 217 | }, "touchstart: touch pageX/pageY inside of target element"); |
| 218 | test(function() { |
| 219 | check_touch_clientXY(t); |
| 220 | }, "touchstart: touch clientX/clientY is consistent with pageX/pageY"); |
| 221 | // Note we don't bother testing screenX/screenY values - there's no reliable way to |
| 222 | // verify they are consistent with clientX/clientY (due to unknown amount of window |
| 223 | // chrome), and also various forms of scaling mean they are in different units. |
| 224 | }); |
| 225 | |
| 226 | on_event(target0, "touchmove", function onTouchMove(ev) { |
| 227 | ev.preventDefault(); |
| 228 | |
| 229 | if (touchmove_received) |
| 230 | return; |
| 231 | touchmove_received = true; |
| 232 | |
| 233 | test_touchmove.step(function() { |
| 234 | assert_true(touchstart_received, "touchmove follows touchstart"); |
| 235 | assert_false(touchend_received, "touchmove precedes touchend"); |
| 236 | }); |
| 237 | test_touchmove.done(); |
| 238 | |
| 239 | check_TouchEvent(ev); |
| 240 | |
| 241 | // TA: 1.4.2.1, 1.4.3.1 |
| 242 | test(function() { |
| 243 | assert_equals(ev.touches.length, 1, "One touch point."); |
| 244 | assert_equals(ev.changedTouches.length, 1, "One changed touch point."); |
| 245 | assert_equals(ev.targetTouches.length, 1, "One target touch point."); |
| 246 | }, "touchmove: all TouchList lengths are correct"); |
| 247 | |
| 248 | // 1.4.2.3, 1.4.3.3, 1.4.3.5, 1.4.4.3 |
| 249 | test(function() { |
| 250 | assert_equals(ev.touches[0].identifier, touchstart_identifier, "Touch identifier matches."); |
| 251 | assert_equals(ev.changedTouches[0].identifier, touchstart_identifier, "Changed touch identifier matches."); |
| 252 | assert_equals(ev.targetTouches[0].identifier, touchstart_identifier, "Target touch identifier matches."); |
| 253 | }, "touchmove: all TouchList identifiers matches touchstart identifier"); |
| 254 | |
| 255 | // TA: 1.4.3.8 |
| 256 | var tt = ev.targetTouches[0]; |
| 257 | test(function() { |
| 258 | assert_equals(tt.target, ev.target, "event target same as targetTouches target."); |
| 259 | }, "touchmove: event target same as targetTouches target"); |
| 260 | |
| 261 | test(function() { |
| 262 | assert_true(is_touch_over_element(tt, target0) || is_touch_over_element(tt, target1), |
| 263 | "touch.pageX/pageY is over one of the targets."); |
| 264 | }, "touchmove: touch pageX/pageY inside of one of the target elements"); |
| 265 | test(function() { |
| 266 | check_touch_clientXY(tt); |
| 267 | }, "touchmove: touch clientX/clientY is consistent with pageX/pageY"); |
| 268 | |
| 269 | }); |
| 270 | |
| 271 | on_event(target1, "touchmove", function onTouchMove(ev) { |
| 272 | invalid_touchmove_received = true; |
| 273 | }); |
| 274 | |
| 275 | on_event(window, "touchend", function onTouchEnd(ev) { |
| 276 | touchend_received = true; |
| 277 | |
| 278 | test_touchend.step(function() { |
| 279 | assert_equals(ev.target, target0, "touchend is dispatched to the original target"); |
| 280 | assert_true(touchstart_received, "touchend follows touchstart"); |
| 281 | assert_true(touchmove_received, "touchend follows touchmove"); |
| 282 | assert_false(invalid_touchmove_received, "touchmove dispatched to correct target"); |
| 283 | }); |
| 284 | test_touchend.done(); |
| 285 | |
| 286 | check_TouchEvent(ev); |
| 287 | |
| 288 | // TA: 1.5.1.2, 1.5.3.1, 1.5.4.1 |
| 289 | test(function() { |
| 290 | assert_equals(ev.touches.length, 0, "Zero touch points."); |
| 291 | assert_equals(ev.changedTouches.length, 1, "One changed touch point."); |
| 292 | assert_equals(ev.targetTouches.length, 0, "Zero target touch points."); |
| 293 | }, "touchend: all TouchList lengths are correct"); |
| 294 | |
| 295 | var t = ev.changedTouches[0]; |
| 296 | |
| 297 | // TA: 1.5.2.6, 1.5.2.3 |
| 298 | test(function() { |
| 299 | assert_equals(t.identifier, touchstart_identifier, "changedTouches identifier matches."); |
| 300 | }, "touchend: touches identifier matches changedTouches identifier"); |
| 301 | |
| 302 | test(function() { |
| 303 | assert_true(is_touch_over_element(t, target1), |
| 304 | "touch.pageX/pageY is over target1."); |
| 305 | }, "touchend: touch pageX/pageY inside expected element"); |
| 306 | test(function() { |
| 307 | check_touch_clientXY(t); |
| 308 | }, "touchend: touch clientX/clientY is consistent with pageX/pageY"); |
| 309 | |
| 310 | done(); |
| 311 | }); |
| 312 | |
| 313 | on_event(target0, "mousedown", function onMouseDown(ev) { |
| 314 | test_mousedown.step(function() { |
| 315 | assert_true(touchstart_received, |
| 316 | "The touchstart event must be dispatched before any mouse " + |
| 317 | "events. (If this fails, it might mean that the user agent does " + |
| 318 | "not implement W3C touch events at all.)" |
| 319 | ); |
| 320 | }); |
| 321 | test_mousedown.done(); |
| 322 | |
| 323 | if (!touchstart_received) { |
| 324 | // Abort the tests. If touch events are not supported, then most of |
| 325 | // the other event handlers will never be called, and the test will |
| 326 | // time out with misleading results. |
| 327 | done(); |
| 328 | } |
| 329 | }); |
| 330 | } |
| 331 | </script> |
| 332 | <style> |
| 333 | div { |
| 334 | margin: 0em; |
| 335 | padding: 2em; |
| 336 | } |
| 337 | #target0 { |
| 338 | background: yellow; |
| 339 | border: 1px solid orange; |
| 340 | } |
| 341 | #target1 { |
| 342 | background: lightblue; |
| 343 | border: 1px solid blue; |
| 344 | } |
| 345 | </style> |
| 346 | </head> |
| 347 | <body onload="run()"> |
| 348 | <h1>Touch Events: single-touch tests</h1> |
| 349 | <div id="target0"> |
| 350 | Touch this box with one finger (or other pointing device)... |
| 351 | </div> |
| 352 | <div id="target1"> |
| 353 | ...then drag to this box and lift your finger. |
| 354 | </div> |
| 355 | <div id="log"></div> |
| 356 | </body> |
| 357 | </html> |