• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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