1<!doctype html> 2<!-- 3@license 4Copyright (c) 2015 The Polymer Project Authors. All rights reserved. 5This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt 6The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 7The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt 8Code distributed by Google as part of the polymer project is also 9subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt 10--> 11<html> 12<head> 13 <meta charset="UTF-8"> 14 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> 15 <title>iron-a11y-keys</title> 16 17 <script src="../../webcomponentsjs/webcomponents-lite.js"></script> 18 <script src="../../web-component-tester/browser.js"></script> 19 <script src="../../iron-test-helpers/mock-interactions.js"></script> 20 21 <link rel="import" href="../../polymer/polymer.html"> 22 <link rel="import" href="../iron-a11y-keys-behavior.html"> 23</head> 24<body> 25 <test-fixture id="BasicKeys"> 26 <template> 27 <x-a11y-basic-keys></x-a11y-basic-keys> 28 </template> 29 </test-fixture> 30 31 <test-fixture id="NonPropagatingKeys"> 32 <template> 33 <x-a11y-basic-keys stop-keyboard-event-propagation></x-a11y-basic-keys> 34 </template> 35 </test-fixture> 36 37 <test-fixture id="ComboKeys"> 38 <template> 39 <x-a11y-combo-keys></x-a11y-combo-keys> 40 </template> 41 </test-fixture> 42 43 <test-fixture id="AlternativeEventKeys"> 44 <template> 45 <x-a11y-alternate-event-keys></x-a11y-alternate-event-keys> 46 </template> 47 </test-fixture> 48 49 <test-fixture id="BehaviorKeys"> 50 <template> 51 <x-a11y-behavior-keys></x-a11y-behavior-keys> 52 </template> 53 </test-fixture> 54 55 <test-fixture id="PreventKeys"> 56 <template> 57 <x-a11y-prevent-keys></x-a11y-prevent-keys> 58 </template> 59 </test-fixture> 60 61 <script> 62suite('Polymer.IronA11yKeysBehavior', function() { 63 var keys; 64 65 suiteSetup(function() { 66 var KeysTestBehavior = [Polymer.IronA11yKeysBehavior, { 67 properties: { 68 keyCount: { 69 type: Number, 70 value: 0 71 } 72 }, 73 74 _keyHandler: function(event) { 75 this.keyCount++; 76 this.lastEvent = event; 77 }, 78 79 // Same as _keyHandler, used to distinguish who's called before who. 80 _keyHandler2: function(event) { 81 this.keyCount++; 82 this.lastEvent = event; 83 }, 84 85 _preventDefaultHandler: function(event) { 86 event.preventDefault(); 87 this.keyCount++; 88 this.lastEvent = event; 89 } 90 }]; 91 92 Polymer({ 93 is: 'x-a11y-basic-keys', 94 95 behaviors: [ 96 KeysTestBehavior 97 ], 98 99 keyBindings: { 100 'space': '_keyHandler', 101 '@': '_keyHandler', 102 'esc': '_keyHandler' 103 } 104 }); 105 106 Polymer({ 107 is: 'x-a11y-combo-keys', 108 109 behaviors: [ 110 KeysTestBehavior 111 ], 112 113 keyBindings: { 114 'enter': '_keyHandler2', 115 'ctrl+shift+a shift+enter': '_keyHandler' 116 } 117 }); 118 119 Polymer({ 120 is: 'x-a11y-alternate-event-keys', 121 122 behaviors: [ 123 KeysTestBehavior 124 ], 125 126 keyBindings: { 127 'space:keyup': '_keyHandler' 128 } 129 }); 130 131 var XA11yBehavior = { 132 keyBindings: { 133 'enter': '_keyHandler' 134 } 135 }; 136 137 Polymer({ 138 is: 'x-a11y-behavior-keys', 139 140 behaviors: [ 141 KeysTestBehavior, 142 XA11yBehavior 143 ], 144 145 keyBindings: { 146 'enter': '_keyHandler' 147 } 148 }); 149 150 Polymer({ 151 is: 'x-a11y-prevent-keys', 152 153 behaviors: [ 154 KeysTestBehavior, 155 XA11yBehavior 156 ], 157 158 keyBindings: { 159 'space a': '_keyHandler', 160 'enter shift+a': '_preventDefaultHandler' 161 } 162 }); 163 }); 164 165 suite('basic keys', function() { 166 setup(function() { 167 keys = fixture('BasicKeys'); 168 }); 169 170 test('trigger the handler when the specified key is pressed', function() { 171 MockInteractions.pressSpace(keys); 172 173 expect(keys.keyCount).to.be.equal(1); 174 }); 175 176 test('keyEventTarget can be null, and disables listeners', function() { 177 keys.keyEventTarget = null; 178 MockInteractions.pressSpace(keys); 179 180 expect(keys.keyCount).to.be.equal(0); 181 }); 182 183 test('trigger the handler when the specified key is pressed together with a modifier', function() { 184 var event = new CustomEvent('keydown'); 185 event.ctrlKey = true; 186 event.keyCode = event.code = 32; 187 keys.dispatchEvent(event); 188 expect(keys.keyCount).to.be.equal(1); 189 }); 190 191 test('handles special character @', function() { 192 MockInteractions.pressAndReleaseKeyOn(keys, undefined, [], '@'); 193 194 expect(keys.keyCount).to.be.equal(1); 195 }); 196 197 test('handles variations of Esc key', function() { 198 MockInteractions.pressAndReleaseKeyOn(keys, undefined, [], 'Esc'); 199 expect(keys.keyCount).to.be.equal(1); 200 201 MockInteractions.pressAndReleaseKeyOn(keys, undefined, [], 'Escape'); 202 expect(keys.keyCount).to.be.equal(2); 203 204 MockInteractions.pressAndReleaseKeyOn(keys, 27, [], ''); 205 expect(keys.keyCount).to.be.equal(3); 206 }); 207 208 test('do not trigger the handler for non-specified keys', function() { 209 MockInteractions.pressEnter(keys); 210 211 expect(keys.keyCount).to.be.equal(0); 212 }); 213 214 test('can have bindings added imperatively', function() { 215 keys.addOwnKeyBinding('enter', '_keyHandler'); 216 217 MockInteractions.pressEnter(keys); 218 expect(keys.keyCount).to.be.equal(1); 219 220 MockInteractions.pressSpace(keys); 221 expect(keys.keyCount).to.be.equal(2); 222 }); 223 224 test('can remove imperatively added bindings', function() { 225 keys.addOwnKeyBinding('enter', '_keyHandler'); 226 keys.removeOwnKeyBindings(); 227 228 MockInteractions.pressEnter(keys); 229 expect(keys.keyCount).to.be.equal(0); 230 231 MockInteractions.pressSpace(keys); 232 expect(keys.keyCount).to.be.equal(1); 233 }); 234 235 test('allows propagation beyond the key combo handler', function() { 236 var keySpy = sinon.spy(); 237 document.addEventListener('keydown', keySpy); 238 239 MockInteractions.pressEnter(keys); 240 241 expect(keySpy.callCount).to.be.equal(1); 242 }); 243 244 suite('edge cases', function() { 245 test('knows that `spacebar` is the same as `space`', function() { 246 var event = new CustomEvent('keydown'); 247 event.key = 'spacebar'; 248 expect(keys.keyboardEventMatchesKeys(event, 'space')).to.be.equal(true); 249 }); 250 251 test('handles `+`', function() { 252 var event = new CustomEvent('keydown'); 253 event.key = '+'; 254 expect(keys.keyboardEventMatchesKeys(event, '+')).to.be.equal(true); 255 }); 256 257 test('handles `:`', function() { 258 var event = new CustomEvent('keydown'); 259 event.key = ':'; 260 expect(keys.keyboardEventMatchesKeys(event, ':')).to.be.equal(true); 261 }); 262 263 test('handles ` ` (space)', function() { 264 var event = new CustomEvent('keydown'); 265 event.key = ' '; 266 expect(keys.keyboardEventMatchesKeys(event, 'space')).to.be.equal(true); 267 }); 268 }); 269 270 suite('matching keyboard events to keys', function() { 271 test('can be done imperatively', function() { 272 var event = new CustomEvent('keydown'); 273 event.keyCode = 65; 274 expect(keys.keyboardEventMatchesKeys(event, 'a')).to.be.equal(true); 275 }); 276 277 test('can be done with a provided keyboardEvent', function() { 278 var event; 279 MockInteractions.pressSpace(keys); 280 event = keys.lastEvent; 281 282 expect(event.detail.keyboardEvent).to.be.okay; 283 expect(keys.keyboardEventMatchesKeys(event, 'space')).to.be.equal(true); 284 }); 285 286 test('can handle variations in arrow key names', function() { 287 var event = new CustomEvent('keydown'); 288 event.key = 'up'; 289 expect(keys.keyboardEventMatchesKeys(event, 'up')).to.be.equal(true); 290 event.key = 'ArrowUp'; 291 expect(keys.keyboardEventMatchesKeys(event, 'up')).to.be.equal(true); 292 }); 293 }); 294 295 suite('matching keyboard events to top row and number pad digit keys', function() { 296 test('top row can be done imperatively', function() { 297 var event = new CustomEvent('keydown'); 298 event.keyCode = 49; 299 expect(keys.keyboardEventMatchesKeys(event, '1')).to.be.equal(true); 300 }); 301 302 test('number pad digits can be done imperatively', function() { 303 var event = new CustomEvent('keydown'); 304 event.keyCode = 97; 305 expect(keys.keyboardEventMatchesKeys(event, '1')).to.be.equal(true); 306 }); 307 }); 308 }); 309 310 suite('combo keys', function() { 311 setup(function() { 312 keys = fixture('ComboKeys'); 313 }); 314 315 test('trigger the handler when the combo is pressed', function() { 316 var event = new CustomEvent('keydown'); 317 318 event.ctrlKey = true; 319 event.shiftKey = true; 320 event.keyCode = event.code = 65; 321 322 keys.dispatchEvent(event); 323 324 expect(keys.keyCount).to.be.equal(1); 325 }); 326 327 test('check if KeyBoardEvent.key is alpha-numberic', function() { 328 var event = new CustomEvent('keydown'); 329 330 event.ctrlKey = true; 331 event.shiftKey = true; 332 event.key = 'A'; 333 334 keys.dispatchEvent(event); 335 336 expect(keys.keyCount).to.be.equal(1); 337 }); 338 339 test('trigger also bindings without modifiers', function() { 340 var event = new CustomEvent('keydown'); 341 // Combo `shift+enter`. 342 event.shiftKey = true; 343 event.keyCode = event.code = 13; 344 keys.dispatchEvent(event); 345 expect(keys.keyCount).to.be.equal(2); 346 }); 347 348 test('give precendence to combos with modifiers', function() { 349 var enterSpy = sinon.spy(keys, '_keyHandler2'); 350 var shiftEnterSpy = sinon.spy(keys, '_keyHandler'); 351 var event = new CustomEvent('keydown'); 352 // Combo `shift+enter`. 353 event.shiftKey = true; 354 event.keyCode = event.code = 13; 355 keys.dispatchEvent(event); 356 expect(enterSpy.called).to.be.true; 357 expect(shiftEnterSpy.called).to.be.true; 358 expect(enterSpy.calledAfter(shiftEnterSpy)).to.be.true; 359 }); 360 361 }); 362 363 suite('alternative event keys', function() { 364 setup(function() { 365 keys = fixture('AlternativeEventKeys'); 366 }); 367 368 test('trigger on the specified alternative keyboard event', function() { 369 MockInteractions.keyDownOn(keys, 32); 370 371 expect(keys.keyCount).to.be.equal(0); 372 373 MockInteractions.keyUpOn(keys, 32); 374 375 expect(keys.keyCount).to.be.equal(1); 376 }); 377 }); 378 379 suite('behavior keys', function() { 380 setup(function() { 381 keys = fixture('BehaviorKeys'); 382 }); 383 384 test('bindings in other behaviors are transitive', function() { 385 MockInteractions.pressEnter(keys); 386 expect(keys.keyCount).to.be.equal(2); 387 }); 388 }); 389 390 suite('stopping propagation automatically', function() { 391 setup(function() { 392 keys = fixture('NonPropagatingKeys'); 393 }); 394 395 test('does not propagate key events beyond the combo handler', function() { 396 var keySpy = sinon.spy(); 397 398 document.addEventListener('keydown', keySpy); 399 400 MockInteractions.pressEnter(keys); 401 402 expect(keySpy.callCount).to.be.equal(0); 403 }); 404 }); 405 406 suite('prevent default behavior of event', function() { 407 setup(function() { 408 keys = fixture('PreventKeys'); 409 }); 410 411 test('`defaultPrevented` is correctly set', function() { 412 MockInteractions.pressEnter(keys); 413 expect(keys.lastEvent.defaultPrevented).to.be.equal(true); 414 }); 415 416 test('only 1 handler is invoked', function() { 417 var aSpy = sinon.spy(keys, '_keyHandler'); 418 var shiftASpy = sinon.spy(keys, '_preventDefaultHandler'); 419 var event = new CustomEvent('keydown', { 420 cancelable: true 421 }); 422 // Combo `shift+a`. 423 event.shiftKey = true; 424 event.keyCode = event.code = 65; 425 keys.dispatchEvent(event); 426 427 expect(keys.keyCount).to.be.equal(1); 428 expect(shiftASpy.called).to.be.true; 429 expect(aSpy.called).to.be.false; 430 }); 431 }); 432 433 suite('remove key behavior with null target', function () { 434 test('add and remove a iron-a11y-keys-behavior', function () { 435 var element = document.createElement('x-a11y-basic-keys'); 436 element.keyEventTarget = null; 437 document.body.appendChild(element); 438 document.body.removeChild(element); 439 }); 440 }); 441 442}); 443 </script> 444</body> 445</html> 446