multi-touch-interfaces.html (11774B)
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 Mozilla-Nokia-Google's single-touch 8 tests and to some extent Olli Pettay's multi-touch tests. 9 10 The primary purpose of the tests in this document is checking that the 11 various interfaces of the Touch Events APIs are correctly implemented. 12 Other interactions are covered in other test files. 13 14 This document references Test Assertions (abbrev TA below) written by Cathy Chan 15 http://www.w3.org/2010/webevents/wiki/TestAssertions 16 --> 17 18 <head> 19 <title>Touch Events Multi-Touch Interface Tests</title> 20 <meta name="viewport" content="width=device-width"> 21 <script src="/resources/testharness.js"></script> 22 <script src="/resources/testharnessreport.js"></script> 23 <script src="/resources/testdriver.js"></script> 24 <script src="/resources/testdriver-actions.js"></script> 25 <script src="/resources/testdriver-vendor.js"></script> 26 <script> 27 setup({explicit_done: true}); 28 29 // Check a Touch object's atttributes for existence and correct type 30 // TA: 1.1.2, 1.1.3 31 function check_Touch_object (t, element) { 32 test(function() { 33 assert_equals(Object.prototype.toString.call(t), "[object Touch]", "touch is of type Touch"); 34 }, element + "'s touch point is a Touch object"); 35 [ 36 ["long", "identifier"], 37 ["EventTarget", "target"], 38 ["long", "screenX"], 39 ["long", "screenY"], 40 ["long", "clientX"], 41 ["long", "clientY"], 42 ["long", "pageX"], 43 ["long", "pageY"], 44 ].forEach(function(attr) { 45 var type = attr[0]; 46 var name = attr[1]; 47 48 // existence check 49 test(function() { 50 assert_true(name in t, name + " attribute in Touch object"); 51 }, element + ".Touch." + name + " attribute exists"); 52 53 // type check 54 switch(type) { 55 case "long": 56 test(function() { 57 assert_equals(typeof t[name], "number", name + " attribute of type long"); 58 }, element + ".Touch." + name + " attribute is of type number (long)"); 59 break; 60 case "EventTarget": 61 // An event target is some type of Element 62 test(function() { 63 assert_true(t[name] instanceof Element, "EventTarget must be an Element."); 64 }, element + ".Touch." + name + " attribute is of type Element"); 65 break; 66 default: 67 break; 68 } 69 }); 70 } 71 72 // Check a TouchList object's attributes and methods for existence and proper type 73 // Also make sure all of the members of the list are Touch objects 74 // TA: 1.2.1, 1.2.2, 1.2.5, 1.2.6 75 function check_TouchList_object (tl, element) { 76 test(function() { 77 assert_equals(Object.prototype.toString.call(tl), "[object TouchList]", "touch list is of type TouchList"); 78 }, element + "'s touch list is a TouchList object"); 79 [ 80 ["unsigned long", "length"], 81 ["function", "item"], 82 ].forEach(function(attr) { 83 var type = attr[0]; 84 var name = attr[1]; 85 86 // existence check 87 test(function() { 88 assert_true(name in tl, name + " attribute in TouchList"); 89 }, element + ".TouchList." + name + " attribute exists"); 90 91 // type check 92 switch(type) { 93 case "unsigned long": 94 test(function() { 95 assert_equals(typeof tl[name], "number", name + " attribute of type long"); 96 }, element + ".TouchList." + name + " attribute is of type number (unsigned long)"); 97 break; 98 case "function": 99 test(function() { 100 assert_equals(typeof tl[name], "function", name + " attribute of type function"); 101 }, element + ".TouchList." + name + " attribute is of type function"); 102 break; 103 default: 104 break; 105 } 106 }); 107 // Each member of tl should be a proper Touch object 108 for (var i=0; i < tl.length; i++) { 109 check_Touch_object(tl.item(i), element + "[" + i + "]"); 110 } 111 } 112 113 // Check a TouchEvent event's attributes for existence and proper type 114 // Also check that each of the event's TouchList objects are valid 115 // TA: 1.{3,4,5}.1.1, 1.{3,4,5}.1.2 116 function check_TouchEvent(ev, touchstart_count) { 117 test(function() { 118 assert_true(ev instanceof TouchEvent, "event is a TouchEvent event"); 119 }, ev.type + touchstart_count + " event is a TouchEvent event"); 120 [ 121 ["TouchList", "touches"], 122 ["TouchList", "targetTouches"], 123 ["TouchList", "changedTouches"], 124 ["boolean", "altKey"], 125 ["boolean", "metaKey"], 126 ["boolean", "ctrlKey"], 127 ["boolean", "shiftKey"], 128 ].forEach(function(attr) { 129 var type = attr[0]; 130 var name = attr[1]; 131 132 // existence check 133 test(function() { 134 assert_true(name in ev, name + " attribute in " + ev.type + " event"); 135 }, ev.type + touchstart_count + "." + name + " attribute exists"); 136 137 // type check 138 switch(type) { 139 case "boolean": 140 test(function() { 141 assert_equals(typeof ev[name], "boolean", name + " attribute of type boolean"); 142 }, ev.type + touchstart_count + "." + name + " attribute is of type boolean"); 143 break; 144 case "TouchList": 145 test(function() { 146 assert_equals(Object.prototype.toString.call(ev[name]), "[object TouchList]", name + " attribute of type TouchList"); 147 }, ev.type + touchstart_count + "." + name + " attribute is of type TouchList"); 148 // Now check the validity of the TouchList 149 check_TouchList_object(ev[name], ev.type + touchstart_count + "." + name); 150 break; 151 default: 152 break; 153 } 154 }); 155 } 156 157 function is_touch_over_element(touch, element) { 158 var bounds = element.getBoundingClientRect(); 159 return touch.pageX >= bounds.left && touch.pageX <= bounds.right && 160 touch.pageY >= bounds.top && touch.pageY <= bounds.bottom; 161 } 162 163 function check_touch_clientXY(touch) { 164 assert_equals(touch.clientX, touch.pageX - window.pageXOffset, "touch.clientX is touch.pageX - window.pageXOffset."); 165 assert_equals(touch.clientY, touch.pageY - window.pageYOffset, "touch.clientY is touch.pageY - window.pageYOffset."); 166 } 167 168 async function run() { 169 var target0 = document.getElementById("target0"); 170 var target1 = document.getElementById("target1"); 171 172 var test_touchstart = async_test("touchstart event received"); 173 var test_touchmove = async_test("touchmove event received"); 174 var test_touchend = async_test("touchend event received"); 175 var test_mousedown = async_test("Interaction with mouse events"); 176 177 var touchstart_received = 0; 178 var touchmove_received = 0; 179 var touchend_received = false; 180 var invalid_touchmove_received = false; 181 182 on_event(target0, "touchstart", function onTouchStart(ev) { 183 ev.preventDefault(); 184 185 if(!touchstart_received) { 186 // Check event ordering TA: 1.6.2 187 test_touchstart.step(function() { 188 assert_true(touchmove_received==0, "touchstart precedes touchmove"); 189 assert_false(touchend_received, "touchstart precedes touchend"); 190 }); 191 test_touchstart.done(); 192 test_mousedown.done(); // If we got here, then the mouse event test is not needed. 193 } 194 195 if(++touchstart_received <= 2) 196 check_TouchEvent(ev, touchstart_received); 197 }); 198 199 on_event(target0, "touchmove", function onTouchMove(ev) { 200 ev.preventDefault(); 201 202 if(!touchmove_received) { 203 test_touchmove.step(function() { 204 assert_true(touchstart_received>0, "touchmove follows touchstart"); 205 assert_false(touchend_received, "touchmove precedes touchend"); 206 }); 207 test_touchmove.done(); 208 } 209 210 if(++touchmove_received <= 2) 211 check_TouchEvent(ev, touchmove_received); 212 }); 213 214 on_event(target1, "touchmove", function onTouchMove(ev) { 215 invalid_touchmove_received = true; 216 }); 217 218 on_event(window, "touchend", function onTouchEnd(ev) { 219 touchend_received = true; 220 221 test_touchend.step(function() { 222 assert_true(touchstart_received>0, "touchend follows touchstart"); 223 assert_true(touchmove_received>0, "touchend follows touchmove"); 224 assert_false(invalid_touchmove_received, "touchmove dispatched to correct target"); 225 }); 226 test_touchend.done(); 227 228 check_TouchEvent(ev, touchstart_received); 229 done(); 230 }); 231 232 on_event(target0, "mousedown", function onMouseDown(ev) { 233 test_mousedown.step(function() { 234 assert_true(touchstart_received, 235 "The touchstart event must be dispatched before any mouse " + 236 "events. (If this fails, it might mean that the user agent does " + 237 "not implement W3C touch events at all.)" 238 ); 239 }); 240 test_mousedown.done(); 241 242 if (!touchstart_received) { 243 // Abort the tests. If touch events are not supported, then most of 244 // the other event handlers will never be called, and the test will 245 // time out with misleading results. 246 done(); 247 } 248 }); 249 250 await new test_driver.Actions() 251 .addPointer("touchPointer1", "touch") 252 .addPointer("touchPointer2", "touch") 253 .pointerMove(0, 0, {origin: target0, sourceName: "touchPointer1"}) 254 .pointerMove(3, 0, {origin: target0, sourceName: "touchPointer2"}) 255 .pointerDown({sourceName: "touchPointer1"}) 256 .pointerDown({sourceName: "touchPointer2"}) 257 .pointerMove(0, 10, {origin: target0, sourceName: "touchPointer1"}) 258 .pause(10, "pointer", {sourceName: "touchPointer2"}) 259 .pause(10, "pointer", {sourceName: "touchPointer1"}) 260 .pointerMove(3, 10, {origin: target0, sourceName: "touchPointer2"}) 261 .pointerMove(0, 0, {origin: target1, sourceName: "touchPointer1"}) 262 .pause(10, "pointer", {sourceName: "touchPointer2"}) 263 .pause(10, "pointer", {sourceName: "touchPointer1"}) 264 .pointerMove(3, 0, {origin: target1, sourceName: "touchPointer2"}) 265 .pointerUp({sourceName: "touchPointer1"}) 266 .pointerUp({sourceName: "touchPointer2"}) 267 .send(); 268 } 269 </script> 270 <style> 271 div { 272 margin: 0em; 273 padding: 2em; 274 } 275 #target0 { 276 background: yellow; 277 border: 1px solid orange; 278 } 279 #target1 { 280 background: lightblue; 281 border: 1px solid blue; 282 } 283 </style> 284 </head> 285 <body onload="run()"> 286 <h1>Touch Events: multi-touch interface tests</h1> 287 <div id="target0"> 288 Touch this box with one finger, then another one... 289 </div> 290 <div id="target1"> 291 ...then drag to this box and lift your fingers. 292 </div> 293 <div id="log"></div> 294 </body> 295 </html>