Try to be more consistent in how we capitalize things. Both the "Title Case" and "Sentence case" styles are popular, so either would work. Google and Mozilla both prefer "Sentence case", so let's follow them.
606 lines
28 KiB
JavaScript
606 lines
28 KiB
JavaScript
import Keyboard from '../core/input/keyboard.js';
|
|
|
|
describe('Key event handling', function () {
|
|
"use strict";
|
|
|
|
// The real KeyboardEvent constructor might not work everywhere we
|
|
// want to run these tests
|
|
function keyevent(typeArg, KeyboardEventInit) {
|
|
const e = { type: typeArg };
|
|
for (let key in KeyboardEventInit) {
|
|
e[key] = KeyboardEventInit[key];
|
|
}
|
|
e.stopPropagation = sinon.spy();
|
|
e.preventDefault = sinon.spy();
|
|
e.getModifierState = function (key) {
|
|
return e[key];
|
|
};
|
|
|
|
return e;
|
|
}
|
|
|
|
describe('Decode keyboard events', function () {
|
|
it('should decode keydown events', function (done) {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = (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) {
|
|
let calls = 0;
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = (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('Fake keyup', function () {
|
|
it('should fake keyup events for virtual keyboards', function (done) {
|
|
let count = 0;
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = (keysym, code, down) => {
|
|
switch (count++) {
|
|
case 0:
|
|
expect(keysym).to.be.equal(0x61);
|
|
expect(code).to.be.equal('Unidentified');
|
|
expect(down).to.be.equal(true);
|
|
break;
|
|
case 1:
|
|
expect(keysym).to.be.equal(0x61);
|
|
expect(code).to.be.equal('Unidentified');
|
|
expect(down).to.be.equal(false);
|
|
done();
|
|
}
|
|
};
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'Unidentified', key: 'a'}));
|
|
});
|
|
});
|
|
|
|
describe('Track key state', function () {
|
|
it('should send release using the same keysym as the press', function (done) {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = (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 send the same keysym for multiple presses', function () {
|
|
let count = 0;
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = (keysym, code, down) => {
|
|
expect(keysym).to.be.equal(0x61);
|
|
expect(code).to.be.equal('KeyA');
|
|
expect(down).to.be.equal(true);
|
|
count++;
|
|
};
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'b'}));
|
|
expect(count).to.be.equal(2);
|
|
});
|
|
it('should do nothing on keyup events if no keys are down', function () {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'}));
|
|
expect(kbd.onkeyevent).to.not.have.been.called;
|
|
});
|
|
|
|
describe('Legacy events', function () {
|
|
it('should track keys using keyCode if no code', function (done) {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = (keysym, code, down) => {
|
|
expect(keysym).to.be.equal(0x61);
|
|
expect(code).to.be.equal('Platform65');
|
|
if (!down) {
|
|
done();
|
|
}
|
|
};
|
|
kbd._handleKeyDown(keyevent('keydown', {keyCode: 65, key: 'a'}));
|
|
kbd._handleKeyUp(keyevent('keyup', {keyCode: 65, key: 'b'}));
|
|
});
|
|
it('should ignore compositing code', function () {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = (keysym, code, down) => {
|
|
expect(keysym).to.be.equal(0x61);
|
|
expect(code).to.be.equal('Unidentified');
|
|
};
|
|
kbd._handleKeyDown(keyevent('keydown', {keyCode: 229, key: 'a'}));
|
|
});
|
|
it('should track keys using keyIdentifier if no code', function (done) {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = (keysym, code, down) => {
|
|
expect(keysym).to.be.equal(0x61);
|
|
expect(code).to.be.equal('Platform65');
|
|
if (!down) {
|
|
done();
|
|
}
|
|
};
|
|
kbd._handleKeyDown(keyevent('keydown', {keyIdentifier: 'U+0041', key: 'a'}));
|
|
kbd._handleKeyUp(keyevent('keyup', {keyIdentifier: 'U+0041', key: 'b'}));
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Shuffle modifiers on macOS', function () {
|
|
let 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");
|
|
|
|
Object.defineProperty(window, "navigator", {value: {}});
|
|
window.navigator.platform = "Mac x86_64";
|
|
});
|
|
afterEach(function () {
|
|
Object.defineProperty(window, "navigator", origNavigator);
|
|
});
|
|
|
|
it('should change Alt to AltGraph', function () {
|
|
let count = 0;
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = (keysym, code, down) => {
|
|
switch (count++) {
|
|
case 0:
|
|
expect(keysym).to.be.equal(0xFF7E);
|
|
expect(code).to.be.equal('AltLeft');
|
|
break;
|
|
case 1:
|
|
expect(keysym).to.be.equal(0xFE03);
|
|
expect(code).to.be.equal('AltRight');
|
|
break;
|
|
}
|
|
};
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt', location: 1}));
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2}));
|
|
expect(count).to.be.equal(2);
|
|
});
|
|
it('should change left Super to Alt', function (done) {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = (keysym, code, down) => {
|
|
expect(keysym).to.be.equal(0xFFE9);
|
|
expect(code).to.be.equal('MetaLeft');
|
|
done();
|
|
};
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'MetaLeft', key: 'Meta', location: 1}));
|
|
});
|
|
it('should change right Super to left Super', function (done) {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = (keysym, code, down) => {
|
|
expect(keysym).to.be.equal(0xFFEB);
|
|
expect(code).to.be.equal('MetaRight');
|
|
done();
|
|
};
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'MetaRight', key: 'Meta', location: 2}));
|
|
});
|
|
});
|
|
|
|
describe('Meta key combination on iOS and macOS', function () {
|
|
let 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");
|
|
|
|
Object.defineProperty(window, "navigator", {value: {}});
|
|
if (window.navigator.platform !== undefined) {
|
|
// Object.defineProperty() doesn't work properly in old
|
|
// versions of Chrome
|
|
this.skip();
|
|
}
|
|
});
|
|
|
|
afterEach(function () {
|
|
if (origNavigator !== undefined) {
|
|
Object.defineProperty(window, "navigator", origNavigator);
|
|
}
|
|
});
|
|
|
|
it('should send keyup when meta key is pressed on iOS', function () {
|
|
window.navigator.platform = "iPad";
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'MetaRight', key: 'Meta', location: 2, metaKey: true}));
|
|
expect(kbd.onkeyevent).to.have.been.calledOnce;
|
|
kbd.onkeyevent.resetHistory();
|
|
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a', metaKey: true}));
|
|
expect(kbd.onkeyevent).to.have.been.calledTwice;
|
|
expect(kbd.onkeyevent).to.have.been.calledWith(0x61, "KeyA", true);
|
|
expect(kbd.onkeyevent).to.have.been.calledWith(0x61, "KeyA", false);
|
|
kbd.onkeyevent.resetHistory();
|
|
|
|
kbd._handleKeyUp(keyevent('keyup', {code: 'MetaRight', key: 'Meta', location: 2, metaKey: true}));
|
|
expect(kbd.onkeyevent).to.have.been.calledOnce;
|
|
});
|
|
|
|
it('should send keyup when meta key is pressed on macOS', function () {
|
|
window.navigator.platform = "Mac";
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'MetaRight', key: 'Meta', location: 2, metaKey: true}));
|
|
expect(kbd.onkeyevent).to.have.been.calledOnce;
|
|
kbd.onkeyevent.resetHistory();
|
|
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a', metaKey: true}));
|
|
expect(kbd.onkeyevent).to.have.been.calledTwice;
|
|
expect(kbd.onkeyevent).to.have.been.calledWith(0x61, "KeyA", true);
|
|
expect(kbd.onkeyevent).to.have.been.calledWith(0x61, "KeyA", false);
|
|
kbd.onkeyevent.resetHistory();
|
|
|
|
kbd._handleKeyUp(keyevent('keyup', {code: 'MetaRight', key: 'Meta', location: 2, metaKey: true}));
|
|
expect(kbd.onkeyevent).to.have.been.calledOnce;
|
|
});
|
|
});
|
|
|
|
describe('Caps Lock on iOS and macOS', function () {
|
|
let 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");
|
|
|
|
Object.defineProperty(window, "navigator", {value: {}});
|
|
});
|
|
|
|
afterEach(function () {
|
|
Object.defineProperty(window, "navigator", origNavigator);
|
|
});
|
|
|
|
it('should toggle caps lock on key press on iOS', function () {
|
|
window.navigator.platform = "iPad";
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'CapsLock', key: 'CapsLock'}));
|
|
|
|
expect(kbd.onkeyevent).to.have.been.calledTwice;
|
|
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true);
|
|
expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false);
|
|
});
|
|
|
|
it('should toggle caps lock on key press on mac', function () {
|
|
window.navigator.platform = "Mac";
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'CapsLock', key: 'CapsLock'}));
|
|
|
|
expect(kbd.onkeyevent).to.have.been.calledTwice;
|
|
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true);
|
|
expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false);
|
|
});
|
|
|
|
it('should toggle caps lock on key release on iOS', function () {
|
|
window.navigator.platform = "iPad";
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyUp(keyevent('keyup', {code: 'CapsLock', key: 'CapsLock'}));
|
|
|
|
expect(kbd.onkeyevent).to.have.been.calledTwice;
|
|
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true);
|
|
expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false);
|
|
});
|
|
|
|
it('should toggle caps lock on key release on mac', function () {
|
|
window.navigator.platform = "Mac";
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyUp(keyevent('keyup', {code: 'CapsLock', key: 'CapsLock'}));
|
|
|
|
expect(kbd.onkeyevent).to.have.been.calledTwice;
|
|
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true);
|
|
expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false);
|
|
});
|
|
});
|
|
|
|
describe('Modifier status info', function () {
|
|
let 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");
|
|
|
|
Object.defineProperty(window, "navigator", {value: {}});
|
|
});
|
|
|
|
afterEach(function () {
|
|
Object.defineProperty(window, "navigator", origNavigator);
|
|
});
|
|
|
|
it('should provide caps lock state', function () {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'A', NumLock: false, CapsLock: true}));
|
|
|
|
expect(kbd.onkeyevent).to.have.been.calledOnce;
|
|
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0x41, "KeyA", true, false, true);
|
|
});
|
|
|
|
it('should provide num lock state', function () {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'A', NumLock: true, CapsLock: false}));
|
|
|
|
expect(kbd.onkeyevent).to.have.been.calledOnce;
|
|
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0x41, "KeyA", true, true, false);
|
|
});
|
|
|
|
it('should have no num lock state on mac', function () {
|
|
window.navigator.platform = "Mac";
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'A', NumLock: false, CapsLock: true}));
|
|
|
|
expect(kbd.onkeyevent).to.have.been.calledOnce;
|
|
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0x41, "KeyA", true, null, true);
|
|
});
|
|
});
|
|
|
|
describe('Japanese IM keys on Windows', function () {
|
|
let 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");
|
|
|
|
Object.defineProperty(window, "navigator", {value: {}});
|
|
window.navigator.platform = "Windows";
|
|
});
|
|
|
|
afterEach(function () {
|
|
Object.defineProperty(window, "navigator", origNavigator);
|
|
});
|
|
|
|
const keys = { 'Zenkaku': 0xff2a, 'Hankaku': 0xff2a,
|
|
'Alphanumeric': 0xff30, 'Katakana': 0xff26,
|
|
'Hiragana': 0xff25, 'Romaji': 0xff24,
|
|
'KanaMode': 0xff24 };
|
|
for (let [key, keysym] of Object.entries(keys)) {
|
|
it(`should fake key release for ${key} on Windows`, function () {
|
|
let kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'FakeIM', key: key}));
|
|
|
|
expect(kbd.onkeyevent).to.have.been.calledTwice;
|
|
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(keysym, "FakeIM", true);
|
|
expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(keysym, "FakeIM", false);
|
|
});
|
|
}
|
|
});
|
|
|
|
describe('Escape AltGraph on Windows', function () {
|
|
let 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");
|
|
|
|
Object.defineProperty(window, "navigator", {value: {}});
|
|
window.navigator.platform = "Windows x86_64";
|
|
|
|
this.clock = sinon.useFakeTimers();
|
|
});
|
|
afterEach(function () {
|
|
Object.defineProperty(window, "navigator", origNavigator);
|
|
if (this.clock !== undefined) {
|
|
this.clock.restore();
|
|
}
|
|
});
|
|
|
|
it('should supress ControlLeft until it knows if it is AltGr', function () {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
|
|
expect(kbd.onkeyevent).to.not.have.been.called;
|
|
});
|
|
|
|
it('should not trigger on repeating ControlLeft', function () {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
|
|
expect(kbd.onkeyevent).to.have.been.calledTwice;
|
|
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true);
|
|
expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffe3, "ControlLeft", true);
|
|
});
|
|
|
|
it('should not supress ControlRight', function () {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'ControlRight', key: 'Control', location: 2}));
|
|
expect(kbd.onkeyevent).to.have.been.calledOnce;
|
|
expect(kbd.onkeyevent).to.have.been.calledWith(0xffe4, "ControlRight", true);
|
|
});
|
|
|
|
it('should release ControlLeft after 100 ms', function () {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
|
|
expect(kbd.onkeyevent).to.not.have.been.called;
|
|
this.clock.tick(100);
|
|
expect(kbd.onkeyevent).to.have.been.calledOnce;
|
|
expect(kbd.onkeyevent).to.have.been.calledWith(0xffe3, "ControlLeft", true);
|
|
});
|
|
|
|
it('should release ControlLeft on other key press', function () {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
|
|
expect(kbd.onkeyevent).to.not.have.been.called;
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
|
|
expect(kbd.onkeyevent).to.have.been.calledTwice;
|
|
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true);
|
|
expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0x61, "KeyA", true);
|
|
|
|
// Check that the timer is properly dead
|
|
kbd.onkeyevent.resetHistory();
|
|
this.clock.tick(100);
|
|
expect(kbd.onkeyevent).to.not.have.been.called;
|
|
});
|
|
|
|
it('should release ControlLeft on other key release', function () {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
|
|
expect(kbd.onkeyevent).to.have.been.calledOnce;
|
|
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0x61, "KeyA", true);
|
|
kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'}));
|
|
expect(kbd.onkeyevent).to.have.been.calledThrice;
|
|
expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffe3, "ControlLeft", true);
|
|
expect(kbd.onkeyevent.thirdCall).to.have.been.calledWith(0x61, "KeyA", false);
|
|
|
|
// Check that the timer is properly dead
|
|
kbd.onkeyevent.resetHistory();
|
|
this.clock.tick(100);
|
|
expect(kbd.onkeyevent).to.not.have.been.called;
|
|
});
|
|
|
|
it('should release ControlLeft on blur', function () {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
|
|
expect(kbd.onkeyevent).to.not.have.been.called;
|
|
kbd._allKeysUp();
|
|
expect(kbd.onkeyevent).to.have.been.calledTwice;
|
|
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true);
|
|
expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffe3, "ControlLeft", false);
|
|
|
|
// Check that the timer is properly dead
|
|
kbd.onkeyevent.resetHistory();
|
|
this.clock.tick(100);
|
|
expect(kbd.onkeyevent).to.not.have.been.called;
|
|
});
|
|
|
|
it('should generate AltGraph for quick Ctrl+Alt sequence', function () {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1, timeStamp: Date.now()}));
|
|
this.clock.tick(20);
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2, timeStamp: Date.now()}));
|
|
expect(kbd.onkeyevent).to.have.been.calledOnce;
|
|
expect(kbd.onkeyevent).to.have.been.calledWith(0xfe03, 'AltRight', true);
|
|
|
|
// Check that the timer is properly dead
|
|
kbd.onkeyevent.resetHistory();
|
|
this.clock.tick(100);
|
|
expect(kbd.onkeyevent).to.not.have.been.called;
|
|
});
|
|
|
|
it('should generate Ctrl, Alt for slow Ctrl+Alt sequence', function () {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1, timeStamp: Date.now()}));
|
|
this.clock.tick(60);
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2, timeStamp: Date.now()}));
|
|
expect(kbd.onkeyevent).to.have.been.calledTwice;
|
|
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true);
|
|
expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffea, "AltRight", true);
|
|
|
|
// Check that the timer is properly dead
|
|
kbd.onkeyevent.resetHistory();
|
|
this.clock.tick(100);
|
|
expect(kbd.onkeyevent).to.not.have.been.called;
|
|
});
|
|
|
|
it('should pass through single Alt', function () {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2}));
|
|
expect(kbd.onkeyevent).to.have.been.calledOnce;
|
|
expect(kbd.onkeyevent).to.have.been.calledWith(0xffea, 'AltRight', true);
|
|
});
|
|
|
|
it('should pass through single AltGr', function () {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'AltGraph', location: 2}));
|
|
expect(kbd.onkeyevent).to.have.been.calledOnce;
|
|
expect(kbd.onkeyevent).to.have.been.calledWith(0xfe03, 'AltRight', true);
|
|
});
|
|
});
|
|
|
|
describe('Missing Shift keyup on Windows', function () {
|
|
let 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");
|
|
|
|
Object.defineProperty(window, "navigator", {value: {}});
|
|
window.navigator.platform = "Windows x86_64";
|
|
|
|
this.clock = sinon.useFakeTimers();
|
|
});
|
|
afterEach(function () {
|
|
Object.defineProperty(window, "navigator", origNavigator);
|
|
if (this.clock !== undefined) {
|
|
this.clock.restore();
|
|
}
|
|
});
|
|
|
|
it('should fake a left Shift keyup', function () {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftLeft', key: 'Shift', location: 1}));
|
|
expect(kbd.onkeyevent).to.have.been.calledOnce;
|
|
expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', true);
|
|
kbd.onkeyevent.resetHistory();
|
|
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftRight', key: 'Shift', location: 2}));
|
|
expect(kbd.onkeyevent).to.have.been.calledOnce;
|
|
expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', true);
|
|
kbd.onkeyevent.resetHistory();
|
|
|
|
kbd._handleKeyUp(keyevent('keyup', {code: 'ShiftLeft', key: 'Shift', location: 1}));
|
|
expect(kbd.onkeyevent).to.have.been.calledTwice;
|
|
expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', false);
|
|
expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', false);
|
|
});
|
|
|
|
it('should fake a right Shift keyup', function () {
|
|
const kbd = new Keyboard(document);
|
|
kbd.onkeyevent = sinon.spy();
|
|
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftLeft', key: 'Shift', location: 1}));
|
|
expect(kbd.onkeyevent).to.have.been.calledOnce;
|
|
expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', true);
|
|
kbd.onkeyevent.resetHistory();
|
|
|
|
kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftRight', key: 'Shift', location: 2}));
|
|
expect(kbd.onkeyevent).to.have.been.calledOnce;
|
|
expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', true);
|
|
kbd.onkeyevent.resetHistory();
|
|
|
|
kbd._handleKeyUp(keyevent('keyup', {code: 'ShiftRight', key: 'Shift', location: 2}));
|
|
expect(kbd.onkeyevent).to.have.been.calledTwice;
|
|
expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', false);
|
|
expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', false);
|
|
});
|
|
});
|
|
});
|