694 lines
31 KiB
JavaScript
694 lines
31 KiB
JavaScript
var assert = chai.assert;
|
|
var expect = chai.expect;
|
|
|
|
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() {
|
|
"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 not sync modifiers on a keypress', function() {
|
|
// Firefox provides unreliable modifier state on keypress events
|
|
var count = 0;
|
|
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
|
++count;
|
|
}).keypress({code: 'KeyA', key: 'a', ctrlKey: true});
|
|
expect(count).to.be.equal(1);
|
|
});
|
|
it('should sync modifiers if necessary', function(done) {
|
|
var count = 0;
|
|
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
|
switch (count) {
|
|
case 0: // fake a ctrl keydown
|
|
expect(evt).to.be.deep.equal({keysym: 0xffe3, type: 'keydown'});
|
|
++count;
|
|
break;
|
|
case 1:
|
|
expect(evt).to.be.deep.equal({code: 'KeyA', type: 'keydown', keysym: 0x61});
|
|
done();
|
|
break;
|
|
}
|
|
}).keydown({code: 'KeyA', key: 'a', ctrlKey: true});
|
|
});
|
|
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', 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({keyCode: 0x11}); // press ctrl
|
|
expect(obj.keydown({key: 'A'})).to.be.true;
|
|
expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.true;
|
|
expect(obj.keydown({key: '1'})).to.be.true;
|
|
expect(obj.keydown({key: '<'})).to.be.true;
|
|
expect(obj.keydown({key: 'ø'})).to.be.true;
|
|
});
|
|
it('should suppress non-character keys', function() {
|
|
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
|
|
|
|
expect(obj.keydown({keyCode: 0x08}), 'a').to.be.true;
|
|
expect(obj.keydown({keyCode: 0x09}), 'b').to.be.true;
|
|
expect(obj.keydown({keyCode: 0x11}), 'd').to.be.true;
|
|
expect(obj.keydown({keyCode: 0x12}), 'e').to.be.true;
|
|
});
|
|
it('should not suppress shift', function() {
|
|
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
|
|
|
|
expect(obj.keydown({keyCode: 0x10}), 'd').to.be.false;
|
|
});
|
|
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({keyCode: 0x10});
|
|
expect(called).to.be.true;
|
|
});
|
|
it('should not suppress character keys', function() {
|
|
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
|
|
|
|
expect(obj.keydown({key: 'A'})).to.be.false;
|
|
expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.false;
|
|
expect(obj.keydown({key: '1'})).to.be.false;
|
|
expect(obj.keydown({key: '<'})).to.be.false; // < key on DK Windows
|
|
expect(obj.keydown({key: 'ø'})).to.be.false; // Ø key on DK
|
|
});
|
|
it('should not suppress if a char modifier is down', function() {
|
|
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) {});
|
|
|
|
obj.keydown({keyCode: 0xe1}); // press altgr
|
|
expect(obj.keydown({key: 'A'})).to.be.false;
|
|
expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.false;
|
|
expect(obj.keydown({key: '1'})).to.be.false;
|
|
expect(obj.keydown({key: '<'})).to.be.false;
|
|
expect(obj.keydown({key: 'ø'})).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({key: 'A'})).to.be.true;
|
|
expect(obj.keypress({key: '<'})).to.be.true;
|
|
expect(obj.keypress({keyCode: 0x11})).to.be.true;
|
|
|
|
expect(obj.keyup({key: 'A'})).to.be.true;
|
|
expect(obj.keyup({key: '<'})).to.be.true;
|
|
expect(obj.keyup({keyCode: 0x11})).to.be.true;
|
|
});
|
|
});
|
|
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({keyCode: 0xe1}); // press altgr
|
|
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({keyCode: 0xe1}); // press altgr
|
|
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({keyCode: 0x11}); // press ctrl
|
|
obj.keydown({keyCode: 0x12}); // press alt
|
|
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({keyCode: 0xe1}); // press altgr
|
|
|
|
});
|
|
});
|
|
describe('add/remove keysym', function() {
|
|
it('should remove keysym from keydown if a char key and no modifier', function() {
|
|
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
|
expect(evt).to.be.deep.equal({code: 'KeyA', type: 'keydown'});
|
|
}).keydown({code: 'KeyA', key: 'a'});
|
|
});
|
|
it('should not remove keysym from keydown if a shortcut modifier is down', function() {
|
|
var times_called = 0;
|
|
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
|
switch (times_called++) {
|
|
case 1:
|
|
expect(evt).to.be.deep.equal({code: 'KeyA', keysym: 0x61, type: 'keydown'});
|
|
break;
|
|
}
|
|
}).keydown({code: 'KeyA', key: 'a', ctrlKey: true});
|
|
expect(times_called).to.be.equal(2);
|
|
});
|
|
it('should not remove keysym from keydown if a char modifier is down', function() {
|
|
var times_called = 0;
|
|
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) {
|
|
switch (times_called++) {
|
|
case 1:
|
|
expect(evt).to.be.deep.equal({code: 'KeyA', keysym: 0x61, type: 'keydown'});
|
|
break;
|
|
}
|
|
}).keydown({code: 'KeyA', key: 'a', altGraphKey: true});
|
|
expect(times_called).to.be.equal(2);
|
|
});
|
|
it('should not remove keysym from keydown if key is noncharacter', function() {
|
|
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
|
expect(evt, 'tab').to.be.deep.equal({code: 'Tab', keysym: 0xff09, type: 'keydown'});
|
|
}).keydown({keyCode: 0x09});
|
|
|
|
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
|
expect(evt, 'ctrl').to.be.deep.equal({code: 'ControlLeft', keysym: 0xffe3, type: 'keydown'});
|
|
}).keydown({keyCode: 0x11});
|
|
});
|
|
it('should never remove keysym from keypress', function() {
|
|
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
|
expect(evt).to.be.deep.equal({code: 'KeyA', keysym: 0x61, type: 'keypress'});
|
|
}).keypress({code: 'KeyA', key: 'a'});
|
|
});
|
|
it('should never remove keysym from keyup', function() {
|
|
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
|
expect(evt).to.be.deep.equal({code: 'KeyA', keysym: 0x61, type: 'keyup'});
|
|
}).keyup({code: 'KeyA', key: 'a'});
|
|
});
|
|
});
|
|
// on keypress, keyup(?), always set keysym
|
|
// on keydown, only do it if we don't expect a keypress: if noncharacter OR modifier is down
|
|
});
|
|
|
|
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 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 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
|
|
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 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 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);
|
|
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);
|
|
});
|
|
});
|
|
});
|
|
});
|