<!doctype html> <!– @license Copyright © 2015 The Polymer Project Authors. All rights reserved. This code may only be used under the BSD style license found at polymer.github.io/LICENSE.txt The complete set of authors may be found at polymer.github.io/AUTHORS.txt The complete set of contributors may be found at 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 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>