diff options
author | Pierre Ossman <ossman@cendio.se> | 2017-01-27 10:36:10 +0100 |
---|---|---|
committer | Pierre Ossman <ossman@cendio.se> | 2017-05-04 12:13:47 +0200 |
commit | f7363fd26dd7d9fced3bce4c629f5119d8b476ae (patch) | |
tree | deceba4bc76ddcb1feb49244e93aa2c33bd310df /tests/test.keyboard.js | |
parent | 9e6f71cb753405507f8977ed8cb64f11189ac4c2 (diff) | |
download | novnc-f7363fd26dd7d9fced3bce4c629f5119d8b476ae.tar.gz |
Move keyboard handling in to Keyboard class
Replace the multi stage pipeline system with something simpler.
That level of abstraction is not needed.
Diffstat (limited to 'tests/test.keyboard.js')
-rw-r--r-- | tests/test.keyboard.js | 807 |
1 files changed, 223 insertions, 584 deletions
diff --git a/tests/test.keyboard.js b/tests/test.keyboard.js index 7ecfcaf..e4ee503 100644 --- a/tests/test.keyboard.js +++ b/tests/test.keyboard.js @@ -1,612 +1,251 @@ var assert = chai.assert; var expect = chai.expect; +import { Keyboard } from '../core/input/devices.js'; import keysyms from '../core/input/keysymdef.js'; import * as KeyboardUtil from '../core/input/util.js'; /* jshint newcap: false, expr: true */ -describe('Key Event Pipeline Stages', function() { +describe('Key Event Handling', function() { "use strict"; - describe('Decode Keyboard Events', function() { - it('should pass events to the next stage', function(done) { - KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { - expect(evt).to.be.an.object; - done(); - }).keydown({code: 'KeyA', key: 'a'}); - }); - it('should pass the right keysym through', function(done) { - KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { - expect(evt.keysym).to.be.deep.equal(0x61); - done(); - }).keypress({code: 'KeyA', key: 'a'}); - }); - it('should pass the right keyid through', function(done) { - KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { - expect(evt).to.have.property('code', 'KeyA'); - done(); - }).keydown({code: 'KeyA', key: 'a'}); - }); - it('should forward keydown events with the right type', function(done) { - KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { - expect(evt).to.be.deep.equal({code: 'KeyA', keysym: 0x61, type: 'keydown'}); - done(); - }).keydown({code: 'KeyA', key: 'a'}); - }); - it('should forward keyup events with the right type', function(done) { - KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { - expect(evt).to.be.deep.equal({code: 'KeyA', keysym: 0x61, type: 'keyup'}); - done(); - }).keyup({code: 'KeyA', key: 'a'}); - }); - it('should forward keypress events with the right type', function(done) { - KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { - expect(evt).to.be.deep.equal({code: 'KeyA', keysym: 0x61, type: 'keypress'}); - done(); - }).keypress({code: 'KeyA', key: 'a'}); - }); - describe('suppress the right events at the right time', function() { - it('should suppress anything while a shortcut modifier is down', function() { - var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {}); - obj.keydown({code: 'ControlLeft'}); - expect(obj.keydown({code: 'KeyA', key: 'a'})).to.be.true; - expect(obj.keydown({code: 'Space', key: ' '})).to.be.true; - expect(obj.keydown({code: 'Digit1', key: '1'})).to.be.true; - expect(obj.keydown({code: 'IntlBackslash', key: '<'})).to.be.true; - expect(obj.keydown({code: 'Semicolon', key: 'ø'})).to.be.true; - }); - it('should suppress non-character keys', function() { - var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {}); + // The real KeyboardEvent constructor might not work everywhere we + // want to run these tests + function keyevent(typeArg, KeyboardEventInit) { + var e = { type: typeArg }; + for (var key in KeyboardEventInit) { + e[key] = KeyboardEventInit[key]; + } + e.stopPropagation = sinon.spy(); + e.preventDefault = sinon.spy(); + return e; + }; - expect(obj.keydown({code: 'Backspace'}), 'a').to.be.true; - expect(obj.keydown({code: 'Tab'}), 'b').to.be.true; - expect(obj.keydown({code: 'ControlLeft'}), 'd').to.be.true; - expect(obj.keydown({code: 'AltLeft'}), 'e').to.be.true; - }); - it('should generate event for shift keydown', function() { - var called = false; - var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { - expect(evt).to.have.property('keysym'); - called = true; - }).keydown({code: 'ShiftLeft'}); - expect(called).to.be.true; - }); - it('should suppress character keys with key', function() { - var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {}); - - expect(obj.keydown({code: 'KeyA', key: 'a'})).to.be.true; - expect(obj.keydown({code: 'Digit1', key: '1'})).to.be.true; - expect(obj.keydown({code: 'IntlBackslash', key: '<'})).to.be.true; - expect(obj.keydown({code: 'Semicolon', key: 'ø'})).to.be.true; - }); - it('should not suppress character keys without key', function() { - var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {}); - - expect(obj.keydown({code: 'KeyA'})).to.be.false; - expect(obj.keydown({code: 'Digit1'})).to.be.false; - expect(obj.keydown({code: 'IntlBackslash'})).to.be.false; - expect(obj.keydown({code: 'Semicolon'})).to.be.false; - }); - }); - describe('Keypress and keyup events', function() { - it('should always suppress event propagation', function() { - var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {}); - - expect(obj.keypress({code: 'KeyA', key: 'a'})).to.be.true; - expect(obj.keypress({code: 'IntlBackslash', key: '<'})).to.be.true; - expect(obj.keypress({code: 'ControlLeft', key: 'Control'})).to.be.true; - - expect(obj.keyup({code: 'KeyA', key: 'a'})).to.be.true; - expect(obj.keyup({code: 'IntlBackslash', key: '<'})).to.be.true; - expect(obj.keyup({code: 'ControlLeft', key: 'Control'})).to.be.true; + describe('Decode Keyboard Events', function() { + it('should decode keydown events', function(done) { + var kbd = new Keyboard({ + onKeyEvent: function(keysym, code, down) { + expect(keysym).to.be.equal(0x61); + expect(code).to.be.equal('KeyA'); + expect(down).to.be.equal(true); + done(); + }}); + kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); + }); + it('should decode keyup events', function(done) { + var calls = 0; + var kbd = new Keyboard({ + onKeyEvent: function(keysym, code, down) { + expect(keysym).to.be.equal(0x61); + expect(code).to.be.equal('KeyA'); + if (calls++ === 1) { + expect(down).to.be.equal(false); + done(); + } + }}); + kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); + kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'})); + }); + + describe('Legacy keypress Events', function() { + it('should wait for keypress when needed', function() { + var callback = sinon.spy(); + var kbd = new Keyboard({onKeyEvent: callback}); + kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41})); + expect(callback).to.not.have.been.called; + }); + it('should decode keypress events', function(done) { + var kbd = new Keyboard({ + onKeyEvent: function(keysym, code, down) { + expect(keysym).to.be.equal(0x61); + expect(code).to.be.equal('KeyA'); + expect(down).to.be.equal(true); + done(); + }}); + kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41})); + kbd._handleKeyPress(keyevent('keypress', {code: 'KeyA', charCode: 0x61})); }); }); - describe('mark events if a char modifier is down', function() { - it('should not mark modifiers on a keydown event', function() { - var times_called = 0; - var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) { - switch (times_called++) { - case 0: //altgr - break; - case 1: // 'a' - expect(evt).to.not.have.property('escape'); - break; - } - }); - - obj.keydown({code: 'AltRight', key: 'AltGraph'}) - obj.keydown({code: 'KeyA', key: 'a'}); - }); - - it('should indicate on events if a single-key char modifier is down', function(done) { - var times_called = 0; - var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) { - switch (times_called++) { - case 0: //altgr - break; - case 1: // 'a' - expect(evt).to.be.deep.equal({ - type: 'keypress', - code: 'KeyA', - keysym: 0x61, - escape: [0xfe03] - }); - done(); - return; - } - }); - - obj.keydown({code: 'AltRight', key: 'AltGraph'}) - obj.keypress({code: 'KeyA', key: 'a'}); - }); - it('should indicate on events if a multi-key char modifier is down', function(done) { - var times_called = 0; - var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xffe9, 0xffe3]), function(evt) { - switch (times_called++) { - case 0: //ctrl - break; - case 1: //alt - break; - case 2: // 'a' - expect(evt).to.be.deep.equal({ - type: 'keypress', - code: 'KeyA', - keysym: 0x61, - escape: [0xffe9, 0xffe3] - }); - done(); - return; - } - }); - - obj.keydown({code: 'ControlLeft'}); - obj.keydown({code: 'AltLeft'}); - obj.keypress({code: 'KeyA', key: 'a'}); - }); - it('should not consider a char modifier to be down on the modifier key itself', function() { - var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) { - expect(evt).to.not.have.property('escape'); - }); - - obj.keydown({code: 'AltRight', key: 'AltGraph'}) + describe('suppress the right events at the right time', function() { + it('should suppress anything with a valid key', function() { + var kbd = new Keyboard({}); + var evt = keyevent('keydown', {code: 'KeyA', key: 'a'}); + kbd._handleKeyDown(evt); + expect(evt.preventDefault).to.have.been.called; + evt = keyevent('keyup', {code: 'KeyA', key: 'a'}); + kbd._handleKeyUp(evt); + expect(evt.preventDefault).to.have.been.called; + }); + it('should not suppress keys without key', function() { + var kbd = new Keyboard({}); + var evt = keyevent('keydown', {code: 'KeyA', keyCode: 0x41}); + kbd._handleKeyDown(evt); + expect(evt.preventDefault).to.not.have.been.called; + }); + it('should suppress the following keypress event', function() { + var kbd = new Keyboard({}); + var evt = keyevent('keydown', {code: 'KeyA', keyCode: 0x41}); + kbd._handleKeyDown(evt); + var evt = keyevent('keypress', {code: 'KeyA', charCode: 0x41}); + kbd._handleKeyPress(evt); + expect(evt.preventDefault).to.have.been.called; }); }); }); describe('Track Key State', function() { - it('should do nothing on keyup events if no keys are down', function() { - var obj = KeyboardUtil.TrackKeyState(function(evt) { - expect(true).to.be.false; - }); - obj({type: 'keyup', code: 'KeyA'}); + it('should send release using the same keysym as the press', function(done) { + var kbd = new Keyboard({ + onKeyEvent: function(keysym, code, down) { + expect(keysym).to.be.equal(0x61); + expect(code).to.be.equal('KeyA'); + if (!down) { + done(); + } + }}); + kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); + kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'b'})); }); - it('should insert into the queue on keydown if no keys are down', function() { - var times_called = 0; - var elem = null; - var keysymsdown = {}; - var obj = KeyboardUtil.TrackKeyState(function(evt) { - ++times_called; - if (elem.type == 'keyup') { - expect(evt).to.have.property('keysym'); - expect (keysymsdown[evt.keysym]).to.not.be.undefined; - delete keysymsdown[evt.keysym]; - } - else { - expect(evt).to.be.deep.equal(elem); - expect (keysymsdown[evt.keysym]).to.not.be.undefined; - } - elem = null; - }); - - expect(elem).to.be.null; - elem = {type: 'keydown', code: 'KeyA', keysym: 0x42}; - keysymsdown[0x42] = true; - obj(elem); - expect(elem).to.be.null; - elem = {type: 'keyup', code: 'KeyA'}; - obj(elem); - expect(elem).to.be.null; - expect(times_called).to.be.equal(2); - }); - it('should insert into the queue on keypress if no keys are down', function() { - var times_called = 0; - var elem = null; - var keysymsdown = {}; - var obj = KeyboardUtil.TrackKeyState(function(evt) { - ++times_called; - if (elem.type == 'keyup') { - expect(evt).to.have.property('keysym'); - expect (keysymsdown[evt.keysym]).to.not.be.undefined; - delete keysymsdown[evt.keysym]; - } - else { - expect(evt).to.be.deep.equal(elem); - expect (keysymsdown[evt.keysym]).to.not.be.undefined; - } - elem = null; - }); - - expect(elem).to.be.null; - elem = {type: 'keypress', code: 'KeyA', keysym: 0x42}; - keysymsdown[0x42] = true; - obj(elem); - expect(elem).to.be.null; - elem = {type: 'keyup', code: 'KeyA'}; - obj(elem); - expect(elem).to.be.null; - expect(times_called).to.be.equal(2); - }); - it('should add keysym to last key entry if code matches', function() { - // this implies that a single keyup will release both keysyms - var times_called = 0; - var elem = null; - var keysymsdown = {}; - var obj = KeyboardUtil.TrackKeyState(function(evt) { - ++times_called; - if (elem.type == 'keyup') { - expect(evt).to.have.property('keysym'); - expect (keysymsdown[evt.keysym]).to.not.be.undefined; - delete keysymsdown[evt.keysym]; - } - else { - expect(evt).to.be.deep.equal(elem); - expect (keysymsdown[evt.keysym]).to.not.be.undefined; - elem = null; - } - }); - - expect(elem).to.be.null; - elem = {type: 'keypress', code: 'KeyA', keysym: 0x42}; - keysymsdown[0x42] = true; - obj(elem); - expect(elem).to.be.null; - elem = {type: 'keypress', code: 'KeyA', keysym: 0x43}; - keysymsdown[0x43] = true; - obj(elem); - expect(elem).to.be.null; - elem = {type: 'keyup', code: 'KeyA'}; - obj(elem); - expect(times_called).to.be.equal(4); - }); - it('should create new key entry if code matches and keysym does not', function() { - // this implies that a single keyup will release both keysyms - var times_called = 0; - var elem = null; - var keysymsdown = {}; - var obj = KeyboardUtil.TrackKeyState(function(evt) { - ++times_called; - if (elem.type == 'keyup') { - expect(evt).to.have.property('keysym'); - expect (keysymsdown[evt.keysym]).to.not.be.undefined; - delete keysymsdown[evt.keysym]; - } - else { - expect(evt).to.be.deep.equal(elem); - expect (keysymsdown[evt.keysym]).to.not.be.undefined; - elem = null; - } - }); - - expect(elem).to.be.null; - elem = {type: 'keydown', code: 'Unidentified', keysym: 0x42}; - keysymsdown[0x42] = true; - obj(elem); - expect(elem).to.be.null; - elem = {type: 'keydown', code: 'Unidentified', keysym: 0x43}; - keysymsdown[0x43] = true; - obj(elem); - expect(times_called).to.be.equal(2); - expect(elem).to.be.null; - elem = {type: 'keyup', code: 'Unidentified'}; - obj(elem); - expect(times_called).to.be.equal(3); - elem = {type: 'keyup', code: 'Unidentified'}; - obj(elem); - expect(times_called).to.be.equal(4); + it('should do nothing on keyup events if no keys are down', function() { + var callback = sinon.spy(); + var kbd = new Keyboard({onKeyEvent: callback}); + kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'})); + expect(callback).to.not.have.been.called; + }); + it('should send a key release for each key press with the same code', function() { + var callback = sinon.spy(); + var kbd = new Keyboard({onKeyEvent: callback}); + kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); + kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'b'})); + kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA'})); + expect(callback.callCount).to.be.equal(4); }); - it('should merge key entry if codes are zero and keysyms match', function() { - // this implies that a single keyup will release both keysyms - var times_called = 0; - var elem = null; - var keysymsdown = {}; - var obj = KeyboardUtil.TrackKeyState(function(evt) { - ++times_called; - if (elem.type == 'keyup') { - expect(evt).to.have.property('keysym'); - expect (keysymsdown[evt.keysym]).to.not.be.undefined; - delete keysymsdown[evt.keysym]; - } - else { - expect(evt).to.be.deep.equal(elem); - expect (keysymsdown[evt.keysym]).to.not.be.undefined; - elem = null; - } - }); + }); - expect(elem).to.be.null; - elem = {type: 'keydown', code: 'Unidentified', keysym: 0x42}; - keysymsdown[0x42] = true; - obj(elem); - expect(elem).to.be.null; - elem = {type: 'keydown', code: 'Unidentified', keysym: 0x42}; - keysymsdown[0x42] = true; - obj(elem); - expect(times_called).to.be.equal(2); - expect(elem).to.be.null; - elem = {type: 'keyup', code: 'Unidentified'}; - obj(elem); - expect(times_called).to.be.equal(3); - }); - it('should add keysym as separate entry if code does not match last event', function() { - // this implies that separate keyups are required + describe('Escape Modifiers', function() { + var origNavigator; + beforeEach(function () { + // window.navigator is a protected read-only property in many + // environments, so we need to redefine it whilst running these + // tests. + origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); + if (origNavigator === undefined) { + // Object.getOwnPropertyDescriptor() doesn't work + // properly in any version of IE + this.skip(); + } + + Object.defineProperty(window, "navigator", {value: {}}); + if (window.navigator.platform !== undefined) { + // Object.defineProperty() doesn't work properly in old + // versions of Chrome + this.skip(); + } + + window.navigator.platform = "Windows x86_64"; + }); + afterEach(function () { + Object.defineProperty(window, "navigator", origNavigator); + }); + + it('should generate fake undo/redo events on press when a char modifier is down', function() { var times_called = 0; - var elem = null; - var keysymsdown = {}; - var obj = KeyboardUtil.TrackKeyState(function(evt) { - ++times_called; - if (elem.type == 'keyup') { - expect(evt).to.have.property('keysym'); - expect (keysymsdown[evt.keysym]).to.not.be.undefined; - delete keysymsdown[evt.keysym]; - } - else { - expect(evt).to.be.deep.equal(elem); - expect (keysymsdown[evt.keysym]).to.not.be.undefined; - elem = null; - } - }); - - expect(elem).to.be.null; - elem = {type: 'keypress', code: 'KeyA', keysym: 0x42}; - keysymsdown[0x42] = true; - obj(elem); - expect(elem).to.be.null; - elem = {type: 'keypress', code: 'KeyB', keysym: 0x43}; - keysymsdown[0x43] = true; - obj(elem); - expect(elem).to.be.null; - elem = {type: 'keyup', code: 'KeyA'}; - obj(elem); - expect(times_called).to.be.equal(4); - elem = {type: 'keyup', code: 'KeyB'}; - obj(elem); - expect(times_called).to.be.equal(4); - }); - it('should add keysym as separate entry if code does not match last event and first is zero', function() { - // this implies that separate keyups are required + var kbd = new Keyboard({ + onKeyEvent: function(keysym, code, down) { + switch(times_called++) { + case 0: + expect(keysym).to.be.equal(0xFFE3); + expect(code).to.be.equal('ControlLeft'); + expect(down).to.be.equal(true); + break; + case 1: + expect(keysym).to.be.equal(0xFFE9); + expect(code).to.be.equal('AltLeft'); + expect(down).to.be.equal(true); + break; + case 2: + expect(keysym).to.be.equal(0xFFE9); + expect(code).to.be.equal('Unidentified'); + expect(down).to.be.equal(false); + break; + case 3: + expect(keysym).to.be.equal(0xFFE3); + expect(code).to.be.equal('Unidentified'); + expect(down).to.be.equal(false); + break; + case 4: + expect(keysym).to.be.equal(0x61); + expect(code).to.be.equal('KeyA'); + expect(down).to.be.equal(true); + break; + case 5: + expect(keysym).to.be.equal(0xFFE9); + expect(code).to.be.equal('Unidentified'); + expect(down).to.be.equal(true); + break; + case 6: + expect(keysym).to.be.equal(0xFFE3); + expect(code).to.be.equal('Unidentified'); + expect(down).to.be.equal(true); + break; + } + }}); + // First the modifier combo + kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control'})); + kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt'})); + // Next a normal character + kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); + expect(times_called).to.be.equal(7); + }); + it('should no do anything on key release', function() { var times_called = 0; - var elem = null; - var keysymsdown = {}; - var obj = KeyboardUtil.TrackKeyState(function(evt) { - ++times_called; - if (elem.type == 'keyup') { - expect(evt).to.have.property('keysym'); - expect (keysymsdown[evt.keysym]).to.not.be.undefined; - delete keysymsdown[evt.keysym]; - } - else { - expect(evt).to.be.deep.equal(elem); - expect (keysymsdown[evt.keysym]).to.not.be.undefined; - elem = null; - } - }); - - expect(elem).to.be.null; - elem = {type: 'keydown', code: 'Unidentified', keysym: 0x42}; - keysymsdown[0x42] = true; - obj(elem); - expect(elem).to.be.null; - elem = {type: 'keydown', code: 'KeyB', keysym: 0x43}; - keysymsdown[0x43] = true; - obj(elem); - expect(elem).to.be.null; - expect(times_called).to.be.equal(2); - elem = {type: 'keyup', code: 'Unidentified'}; - obj(elem); - expect(times_called).to.be.equal(3); - elem = {type: 'keyup', code: 'KeyB'}; - obj(elem); - expect(times_called).to.be.equal(4); - }); - it('should add keysym as separate entry if code does not match last event and second is zero', function() { - // this implies that a separate keyups are required + var kbd = new Keyboard({ + onKeyEvent: function(keysym, code, down) { + switch(times_called++) { + case 7: + expect(keysym).to.be.equal(0x61); + expect(code).to.be.equal('KeyA'); + expect(down).to.be.equal(false); + break; + } + }}); + // First the modifier combo + kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control'})); + kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt'})); + // Next a normal character + kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); + kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'})); + expect(times_called).to.be.equal(8); + }); + it('should not consider a char modifier to be down on the modifier key itself', function() { var times_called = 0; - var elem = null; - var keysymsdown = {}; - var obj = KeyboardUtil.TrackKeyState(function(evt) { - ++times_called; - if (elem.type == 'keyup') { - expect(evt).to.have.property('keysym'); - expect (keysymsdown[evt.keysym]).to.not.be.undefined; - delete keysymsdown[evt.keysym]; - } - else { - expect(evt).to.be.deep.equal(elem); - expect (keysymsdown[evt.keysym]).to.not.be.undefined; - elem = null; - } - }); - - expect(elem).to.be.null; - elem = {type: 'keydown', code: 'KeyA', keysym: 0x42}; - keysymsdown[0x42] = true; - obj(elem); - expect(elem).to.be.null; - elem = {type: 'keydown', code: 'Unidentified', keysym: 0x43}; - keysymsdown[0x43] = true; - obj(elem); - expect(elem).to.be.null; - elem = {type: 'keyup', code: 'KeyA'}; - obj(elem); + var kbd = new Keyboard({ + onKeyEvent: function(keysym, code, down) { + switch(times_called++) { + case 0: + expect(keysym).to.be.equal(0xFFE3); + expect(code).to.be.equal('ControlLeft'); + expect(down).to.be.equal(true); + break; + case 1: + expect(keysym).to.be.equal(0xFFE9); + expect(code).to.be.equal('AltLeft'); + expect(down).to.be.equal(true); + break; + case 2: + expect(keysym).to.be.equal(0xFFE3); + expect(code).to.be.equal('ControlLeft'); + expect(down).to.be.equal(true); + break; + } + }}); + // First the modifier combo + kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control'})); + kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt'})); + // Then one of the keys again + kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control'})); expect(times_called).to.be.equal(3); - elem = {type: 'keyup', code: 'Unidentified'}; - obj(elem); - expect(times_called).to.be.equal(4); - }); - it('should pop matching key event on keyup', function() { - var times_called = 0; - var obj = KeyboardUtil.TrackKeyState(function(evt) { - switch (times_called++) { - case 0: - case 1: - case 2: - expect(evt.type).to.be.equal('keydown'); - break; - case 3: - expect(evt).to.be.deep.equal({type: 'keyup', code: 'KeyB', keysym: 0x62}); - break; - } - }); - - obj({type: 'keydown', code: 'KeyA', keysym: 0x61}); - obj({type: 'keydown', code: 'KeyB', keysym: 0x62}); - obj({type: 'keydown', code: 'KeyC', keysym: 0x63}); - obj({type: 'keyup', code: 'KeyB'}); - expect(times_called).to.equal(4); - }); - it('should pop the first zero keyevent on keyup with zero code', function() { - var times_called = 0; - var obj = KeyboardUtil.TrackKeyState(function(evt) { - switch (times_called++) { - case 0: - case 1: - case 2: - expect(evt.type).to.be.equal('keydown'); - break; - case 3: - expect(evt).to.be.deep.equal({type: 'keyup', code: 'Unidentified', keysym: 0x61}); - break; - } - }); - - obj({type: 'keydown', code: 'Unidentified', keysym: 0x61}); - obj({type: 'keydown', code: 'Unidentified', keysym: 0x62}); - obj({type: 'keydown', code: 'KeyA', keysym: 0x63}); - obj({type: 'keyup', code: 'Unidentified'}); - expect(times_called).to.equal(4); - }); - it('should pop the last keyevents keysym if no match is found for code', function() { - var times_called = 0; - var obj = KeyboardUtil.TrackKeyState(function(evt) { - switch (times_called++) { - case 0: - case 1: - case 2: - expect(evt.type).to.be.equal('keydown'); - break; - case 3: - expect(evt).to.be.deep.equal({type: 'keyup', code: 'KeyD', keysym: 0x63}); - break; - } - }); - - obj({type: 'keydown', code: 'KeyA', keysym: 0x61}); - obj({type: 'keydown', code: 'KeyB', keysym: 0x62}); - obj({type: 'keydown', code: 'KeyC', keysym: 0x63}); - obj({type: 'keyup', code: 'KeyD'}); - expect(times_called).to.equal(4); - }); - describe('Firefox sends keypress even when keydown is suppressed', function() { - it('should discard the keypress', function() { - var times_called = 0; - var obj = KeyboardUtil.TrackKeyState(function(evt) { - expect(times_called).to.be.equal(0); - ++times_called; - }); - - obj({type: 'keydown', code: 'KeyA', keysym: 0x42}); - expect(times_called).to.be.equal(1); - obj({type: 'keypress', code: 'KeyA', keysym: 0x43}); - }); - }); - describe('releaseAll', function() { - it('should do nothing if no keys have been pressed', function() { - var times_called = 0; - var obj = KeyboardUtil.TrackKeyState(function(evt) { - ++times_called; - }); - obj({type: 'releaseall'}); - expect(times_called).to.be.equal(0); - }); - it('should release the keys that have been pressed', function() { - var times_called = 0; - var obj = KeyboardUtil.TrackKeyState(function(evt) { - switch (times_called++) { - case 2: - expect(evt).to.be.deep.equal({type: 'keyup', code: 'Unidentified', keysym: 0x41}); - break; - case 3: - expect(evt).to.be.deep.equal({type: 'keyup', code: 'Unidentified', keysym: 0x42}); - break; - } - }); - obj({type: 'keydown', code: 'KeyA', keysym: 0x41}); - obj({type: 'keydown', code: 'KeyB', keysym: 0x42}); - expect(times_called).to.be.equal(2); - obj({type: 'releaseall'}); - expect(times_called).to.be.equal(4); - obj({type: 'releaseall'}); - expect(times_called).to.be.equal(4); - }); - }); - - }); - - describe('Escape Modifiers', function() { - describe('Keydown', function() { - it('should pass through when a char modifier is not down', function() { - var times_called = 0; - KeyboardUtil.EscapeModifiers(function(evt) { - expect(times_called).to.be.equal(0); - ++times_called; - expect(evt).to.be.deep.equal({type: 'keydown', code: 'KeyA', keysym: 0x42}); - })({type: 'keydown', code: 'KeyA', keysym: 0x42}); - expect(times_called).to.be.equal(1); - }); - it('should generate fake undo/redo events when a char modifier is down', function() { - var times_called = 0; - KeyboardUtil.EscapeModifiers(function(evt) { - switch(times_called++) { - case 0: - expect(evt).to.be.deep.equal({type: 'keyup', code: 'Unidentified', keysym: 0xffe9}); - break; - case 1: - expect(evt).to.be.deep.equal({type: 'keyup', code: 'Unidentified', keysym: 0xffe3}); - break; - case 2: - expect(evt).to.be.deep.equal({type: 'keydown', code: 'KeyA', keysym: 0x42, escape: [0xffe9, 0xffe3]}); - break; - case 3: - expect(evt).to.be.deep.equal({type: 'keydown', code: 'Unidentified', keysym: 0xffe9}); - break; - case 4: - expect(evt).to.be.deep.equal({type: 'keydown', code: 'Unidentified', keysym: 0xffe3}); - break; - } - })({type: 'keydown', code: 'KeyA', keysym: 0x42, escape: [0xffe9, 0xffe3]}); - expect(times_called).to.be.equal(5); - }); - }); - describe('Keyup', function() { - it('should pass through when a char modifier is down', function() { - var times_called = 0; - KeyboardUtil.EscapeModifiers(function(evt) { - expect(times_called).to.be.equal(0); - ++times_called; - expect(evt).to.be.deep.equal({type: 'keyup', code: 'KeyA', keysym: 0x42, escape: [0xfe03]}); - })({type: 'keyup', code: 'KeyA', keysym: 0x42, escape: [0xfe03]}); - expect(times_called).to.be.equal(1); - }); - it('should pass through when a char modifier is not down', function() { - var times_called = 0; - KeyboardUtil.EscapeModifiers(function(evt) { - expect(times_called).to.be.equal(0); - ++times_called; - expect(evt).to.be.deep.equal({type: 'keyup', code: 'KeyA', keysym: 0x42}); - })({type: 'keyup', code: 'KeyA', keysym: 0x42}); - expect(times_called).to.be.equal(1); - }); }); }); }); |