| <!doctype html> |
| <!-- |
| @license |
| Copyright (c) 2015 The Polymer Project Authors. All rights reserved. |
| This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt |
| The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |
| The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt |
| Code distributed by Google as part of the polymer project is also |
| subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt |
| --> |
| <html> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> |
| <title>iron-a11y-keys</title> |
| |
| <script src="../../webcomponentsjs/webcomponents-lite.js"></script> |
| <script src="../../web-component-tester/browser.js"></script> |
| <script src="../../iron-test-helpers/mock-interactions.js"></script> |
| |
| <link rel="import" href="../../polymer/polymer.html"> |
| <link rel="import" href="../iron-a11y-keys-behavior.html"> |
| </head> |
| <body> |
| <test-fixture id="BasicKeys"> |
| <template> |
| <x-a11y-basic-keys></x-a11y-basic-keys> |
| </template> |
| </test-fixture> |
| |
| <test-fixture id="NonPropagatingKeys"> |
| <template> |
| <x-a11y-basic-keys stop-keyboard-event-propagation></x-a11y-basic-keys> |
| </template> |
| </test-fixture> |
| |
| <test-fixture id="ComboKeys"> |
| <template> |
| <x-a11y-combo-keys></x-a11y-combo-keys> |
| </template> |
| </test-fixture> |
| |
| <test-fixture id="AlternativeEventKeys"> |
| <template> |
| <x-a11y-alternate-event-keys></x-a11y-alternate-event-keys> |
| </template> |
| </test-fixture> |
| |
| <test-fixture id="BehaviorKeys"> |
| <template> |
| <x-a11y-behavior-keys></x-a11y-behavior-keys> |
| </template> |
| </test-fixture> |
| |
| <test-fixture id="PreventKeys"> |
| <template> |
| <x-a11y-prevent-keys></x-a11y-prevent-keys> |
| </template> |
| </test-fixture> |
| |
| <script> |
| suite('Polymer.IronA11yKeysBehavior', function() { |
| var keys; |
| |
| suiteSetup(function() { |
| var KeysTestBehavior = [Polymer.IronA11yKeysBehavior, { |
| properties: { |
| keyCount: { |
| type: Number, |
| value: 0 |
| } |
| }, |
| |
| _keyHandler: function(event) { |
| this.keyCount++; |
| this.lastEvent = event; |
| }, |
| |
| // Same as _keyHandler, used to distinguish who's called before who. |
| _keyHandler2: function(event) { |
| this.keyCount++; |
| this.lastEvent = event; |
| }, |
| |
| _preventDefaultHandler: function(event) { |
| event.preventDefault(); |
| this.keyCount++; |
| this.lastEvent = event; |
| } |
| }]; |
| |
| Polymer({ |
| is: 'x-a11y-basic-keys', |
| |
| behaviors: [ |
| KeysTestBehavior |
| ], |
| |
| keyBindings: { |
| 'space': '_keyHandler', |
| '@': '_keyHandler', |
| 'esc': '_keyHandler' |
| } |
| }); |
| |
| Polymer({ |
| is: 'x-a11y-combo-keys', |
| |
| behaviors: [ |
| KeysTestBehavior |
| ], |
| |
| keyBindings: { |
| 'enter': '_keyHandler2', |
| 'ctrl+shift+a shift+enter': '_keyHandler' |
| } |
| }); |
| |
| Polymer({ |
| is: 'x-a11y-alternate-event-keys', |
| |
| behaviors: [ |
| KeysTestBehavior |
| ], |
| |
| keyBindings: { |
| 'space:keyup': '_keyHandler' |
| } |
| }); |
| |
| var XA11yBehavior = { |
| keyBindings: { |
| 'enter': '_keyHandler' |
| } |
| }; |
| |
| Polymer({ |
| is: 'x-a11y-behavior-keys', |
| |
| behaviors: [ |
| KeysTestBehavior, |
| XA11yBehavior |
| ], |
| |
| keyBindings: { |
| 'enter': '_keyHandler' |
| } |
| }); |
| |
| Polymer({ |
| is: 'x-a11y-prevent-keys', |
| |
| behaviors: [ |
| KeysTestBehavior, |
| XA11yBehavior |
| ], |
| |
| keyBindings: { |
| 'space a': '_keyHandler', |
| 'enter shift+a': '_preventDefaultHandler' |
| } |
| }); |
| }); |
| |
| suite('basic keys', function() { |
| setup(function() { |
| keys = fixture('BasicKeys'); |
| }); |
| |
| test('trigger the handler when the specified key is pressed', function() { |
| MockInteractions.pressSpace(keys); |
| |
| expect(keys.keyCount).to.be.equal(1); |
| }); |
| |
| test('keyEventTarget can be null, and disables listeners', function() { |
| keys.keyEventTarget = null; |
| MockInteractions.pressSpace(keys); |
| |
| expect(keys.keyCount).to.be.equal(0); |
| }); |
| |
| test('trigger the handler when the specified key is pressed together with a modifier', function() { |
| var event = new CustomEvent('keydown'); |
| event.ctrlKey = true; |
| event.keyCode = event.code = 32; |
| keys.dispatchEvent(event); |
| expect(keys.keyCount).to.be.equal(1); |
| }); |
| |
| test('handles special character @', function() { |
| MockInteractions.pressAndReleaseKeyOn(keys, undefined, [], '@'); |
| |
| expect(keys.keyCount).to.be.equal(1); |
| }); |
| |
| test('handles variations of Esc key', function() { |
| MockInteractions.pressAndReleaseKeyOn(keys, undefined, [], 'Esc'); |
| expect(keys.keyCount).to.be.equal(1); |
| |
| MockInteractions.pressAndReleaseKeyOn(keys, undefined, [], 'Escape'); |
| expect(keys.keyCount).to.be.equal(2); |
| |
| MockInteractions.pressAndReleaseKeyOn(keys, 27, [], ''); |
| expect(keys.keyCount).to.be.equal(3); |
| }); |
| |
| test('do not trigger the handler for non-specified keys', function() { |
| MockInteractions.pressEnter(keys); |
| |
| expect(keys.keyCount).to.be.equal(0); |
| }); |
| |
| test('can have bindings added imperatively', function() { |
| keys.addOwnKeyBinding('enter', '_keyHandler'); |
| |
| MockInteractions.pressEnter(keys); |
| expect(keys.keyCount).to.be.equal(1); |
| |
| MockInteractions.pressSpace(keys); |
| expect(keys.keyCount).to.be.equal(2); |
| }); |
| |
| test('can remove imperatively added bindings', function() { |
| keys.addOwnKeyBinding('enter', '_keyHandler'); |
| keys.removeOwnKeyBindings(); |
| |
| MockInteractions.pressEnter(keys); |
| expect(keys.keyCount).to.be.equal(0); |
| |
| MockInteractions.pressSpace(keys); |
| expect(keys.keyCount).to.be.equal(1); |
| }); |
| |
| test('allows propagation beyond the key combo handler', function() { |
| var keySpy = sinon.spy(); |
| document.addEventListener('keydown', keySpy); |
| |
| MockInteractions.pressEnter(keys); |
| |
| expect(keySpy.callCount).to.be.equal(1); |
| }); |
| |
| suite('edge cases', function() { |
| test('knows that `spacebar` is the same as `space`', function() { |
| var event = new CustomEvent('keydown'); |
| event.key = 'spacebar'; |
| expect(keys.keyboardEventMatchesKeys(event, 'space')).to.be.equal(true); |
| }); |
| |
| test('handles `+`', function() { |
| var event = new CustomEvent('keydown'); |
| event.key = '+'; |
| expect(keys.keyboardEventMatchesKeys(event, '+')).to.be.equal(true); |
| }); |
| |
| test('handles `:`', function() { |
| var event = new CustomEvent('keydown'); |
| event.key = ':'; |
| expect(keys.keyboardEventMatchesKeys(event, ':')).to.be.equal(true); |
| }); |
| |
| test('handles ` ` (space)', function() { |
| var event = new CustomEvent('keydown'); |
| event.key = ' '; |
| expect(keys.keyboardEventMatchesKeys(event, 'space')).to.be.equal(true); |
| }); |
| }); |
| |
| suite('matching keyboard events to keys', function() { |
| test('can be done imperatively', function() { |
| var event = new CustomEvent('keydown'); |
| event.keyCode = 65; |
| expect(keys.keyboardEventMatchesKeys(event, 'a')).to.be.equal(true); |
| }); |
| |
| test('can be done with a provided keyboardEvent', function() { |
| var event; |
| MockInteractions.pressSpace(keys); |
| event = keys.lastEvent; |
| |
| expect(event.detail.keyboardEvent).to.be.okay; |
| expect(keys.keyboardEventMatchesKeys(event, 'space')).to.be.equal(true); |
| }); |
| |
| test('can handle variations in arrow key names', function() { |
| var event = new CustomEvent('keydown'); |
| event.key = 'up'; |
| expect(keys.keyboardEventMatchesKeys(event, 'up')).to.be.equal(true); |
| event.key = 'ArrowUp'; |
| expect(keys.keyboardEventMatchesKeys(event, 'up')).to.be.equal(true); |
| }); |
| }); |
| |
| suite('matching keyboard events to top row and number pad digit keys', function() { |
| test('top row can be done imperatively', function() { |
| var event = new CustomEvent('keydown'); |
| event.keyCode = 49; |
| expect(keys.keyboardEventMatchesKeys(event, '1')).to.be.equal(true); |
| }); |
| |
| test('number pad digits can be done imperatively', function() { |
| var event = new CustomEvent('keydown'); |
| event.keyCode = 97; |
| expect(keys.keyboardEventMatchesKeys(event, '1')).to.be.equal(true); |
| }); |
| }); |
| }); |
| |
| suite('combo keys', function() { |
| setup(function() { |
| keys = fixture('ComboKeys'); |
| }); |
| |
| test('trigger the handler when the combo is pressed', function() { |
| var event = new CustomEvent('keydown'); |
| |
| event.ctrlKey = true; |
| event.shiftKey = true; |
| event.keyCode = event.code = 65; |
| |
| keys.dispatchEvent(event); |
| |
| expect(keys.keyCount).to.be.equal(1); |
| }); |
| |
| test('check if KeyBoardEvent.key is alpha-numberic', function() { |
| var event = new CustomEvent('keydown'); |
| |
| event.ctrlKey = true; |
| event.shiftKey = true; |
| event.key = 'A'; |
| |
| keys.dispatchEvent(event); |
| |
| expect(keys.keyCount).to.be.equal(1); |
| }); |
| |
| test('trigger also bindings without modifiers', function() { |
| var event = new CustomEvent('keydown'); |
| // Combo `shift+enter`. |
| event.shiftKey = true; |
| event.keyCode = event.code = 13; |
| keys.dispatchEvent(event); |
| expect(keys.keyCount).to.be.equal(2); |
| }); |
| |
| test('give precendence to combos with modifiers', function() { |
| var enterSpy = sinon.spy(keys, '_keyHandler2'); |
| var shiftEnterSpy = sinon.spy(keys, '_keyHandler'); |
| var event = new CustomEvent('keydown'); |
| // Combo `shift+enter`. |
| event.shiftKey = true; |
| event.keyCode = event.code = 13; |
| keys.dispatchEvent(event); |
| expect(enterSpy.called).to.be.true; |
| expect(shiftEnterSpy.called).to.be.true; |
| expect(enterSpy.calledAfter(shiftEnterSpy)).to.be.true; |
| }); |
| |
| }); |
| |
| suite('alternative event keys', function() { |
| setup(function() { |
| keys = fixture('AlternativeEventKeys'); |
| }); |
| |
| test('trigger on the specified alternative keyboard event', function() { |
| MockInteractions.keyDownOn(keys, 32); |
| |
| expect(keys.keyCount).to.be.equal(0); |
| |
| MockInteractions.keyUpOn(keys, 32); |
| |
| expect(keys.keyCount).to.be.equal(1); |
| }); |
| }); |
| |
| suite('behavior keys', function() { |
| setup(function() { |
| keys = fixture('BehaviorKeys'); |
| }); |
| |
| test('bindings in other behaviors are transitive', function() { |
| MockInteractions.pressEnter(keys); |
| expect(keys.keyCount).to.be.equal(2); |
| }); |
| }); |
| |
| suite('stopping propagation automatically', function() { |
| setup(function() { |
| keys = fixture('NonPropagatingKeys'); |
| }); |
| |
| test('does not propagate key events beyond the combo handler', function() { |
| var keySpy = sinon.spy(); |
| |
| document.addEventListener('keydown', keySpy); |
| |
| MockInteractions.pressEnter(keys); |
| |
| expect(keySpy.callCount).to.be.equal(0); |
| }); |
| }); |
| |
| suite('prevent default behavior of event', function() { |
| setup(function() { |
| keys = fixture('PreventKeys'); |
| }); |
| |
| test('`defaultPrevented` is correctly set', function() { |
| MockInteractions.pressEnter(keys); |
| expect(keys.lastEvent.defaultPrevented).to.be.equal(true); |
| }); |
| |
| test('only 1 handler is invoked', function() { |
| var aSpy = sinon.spy(keys, '_keyHandler'); |
| var shiftASpy = sinon.spy(keys, '_preventDefaultHandler'); |
| var event = new CustomEvent('keydown', { |
| cancelable: true |
| }); |
| // Combo `shift+a`. |
| event.shiftKey = true; |
| event.keyCode = event.code = 65; |
| keys.dispatchEvent(event); |
| |
| expect(keys.keyCount).to.be.equal(1); |
| expect(shiftASpy.called).to.be.true; |
| expect(aSpy.called).to.be.false; |
| }); |
| }); |
| |
| suite('remove key behavior with null target', function () { |
| test('add and remove a iron-a11y-keys-behavior', function () { |
| var element = document.createElement('x-a11y-basic-keys'); |
| element.keyEventTarget = null; |
| document.body.appendChild(element); |
| document.body.removeChild(element); |
| }); |
| }); |
| |
| }); |
| </script> |
| </body> |
| </html> |