siesta-lite
Version:
Stress-free JavaScript unit testing and functional testing tool, works in NodeJS and browsers
463 lines (462 loc) • 19 kB
JavaScript
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var point = require("../../../generic/util/Point");
var types = require("../../../generic/simulator/Types");
var ServerEndPoint_js_1 = require("../../channel/websocket/ServerEndPoint.js");
var Point_js_1 = require("../../../generic/util/Point.js");
var Delay_js_1 = require("../../../generic/util/helper/Delay.js");
var robotjs;
var noRobot = false;
try {
robotjs = require('robotjs');
// this can probably be reduced on Linux (or even set to 0)
// once https://github.com/octalmage/robotjs/issues/347 gets fixed
robotjs.setMouseDelay(process.platform == 'win32' ? 10 : 10);
robotjs.setKeyboardDelay(process.platform == 'win32' ? 20 : 20);
}
catch (e) {
noRobot = true;
}
var SimulatorServerRobotJs = /** @class */ (function (_super) {
__extends(SimulatorServerRobotJs, _super);
function SimulatorServerRobotJs() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.mouseMovePrecision = 3;
// see https://github.com/octalmage/robotjs/issues/347
_this.afterActionDelay = 100;
return _this;
}
SimulatorServerRobotJs.prototype.setXDisplayName = function (displayNumber) {
this.displayNumber = displayNumber;
robotjs.setXDisplayName(':' + displayNumber + '.0');
};
SimulatorServerRobotJs.prototype.connect = function (channel) {
if ((process.platform == 'linux' || process.platform == 'freebsd')) {
// running on Linux w/o X11 server?
// no point to even setup the WebSocket server - trying to call any RobotJS method, like `moveMouse` below
// will segfault
if (!process.env.DISPLAY || noRobot)
return Promise.resolve();
if (this.displayNumber)
this.setXDisplayName(this.displayNumber);
}
// move mouse left-top corner
robotjs.moveMouse(0, 0);
return _super.prototype.connect.call(this, channel);
};
SimulatorServerRobotJs.prototype.doDispatchEnvelop = function (envelop) {
var _this = this;
var cmd = envelop.payload;
var action;
switch (cmd.type) {
case 'calibrate':
action = this.doCalibrate(cmd);
break;
case 'move_pointer':
action = this.doMovePointer(cmd);
break;
case 'set_pointer_state':
action = this.doSetPointerState(cmd);
break;
case 'pointer_click':
action = this.doPointerClick(cmd);
break;
case 'pointer_double_click':
action = this.doPointerDoubleClick(cmd);
break;
case 'mouse_wheel':
action = this.doMouseWheel(cmd);
break;
case 'pointer_drag':
action = this.doPointerDrag(cmd);
break;
case 'type':
action = this.doType(cmd);
break;
case 'reset':
action = this.doSimulationReset(cmd);
break;
case 'set_key_state':
action = this.doSetKeyState(cmd);
break;
}
action.then(function (result) { return _this.replyWith(envelop, result); }, function (reason) {
_this.info("Simulator command failed: " + reason);
_this.replyWith(envelop, reason, true);
});
};
SimulatorServerRobotJs.prototype.doCalibrate = function (command) {
var testPointX = Math.round(command.left + command.width / 2);
var testPointY = Math.round(command.top + command.height / 2);
robotjs.moveMouse(0, 0);
return Delay_js_1.delay(100).then(function () {
robotjs.moveMouse(testPointX, testPointY);
return Delay_js_1.delay(100);
}).then(function () {
return Promise.resolve({ x: testPointX, y: testPointY });
});
};
SimulatorServerRobotJs.prototype.doMovePointer = function (command) {
var _this = this;
this.doWithModifierKeys(function () {
if (command.moveKind == 'instant')
robotjs.moveMouse(command.x, command.y);
else if (command.moveKind == 'smooth') {
_this.moveMouseTo(command.x, command.y, command.mouseMovePrecision);
}
}, command.modifierKey);
return Promise.resolve();
};
SimulatorServerRobotJs.prototype.doSetKeyState = function (command) {
robotjs.keyToggle(command.key, command.state);
return Delay_js_1.delay(this.afterActionDelay);
};
SimulatorServerRobotJs.prototype.doSetPointerState = function (command) {
this.doWithModifierKeys(function () {
robotjs.mouseToggle(command.state, command.modifier);
}, command.modifierKey);
return Delay_js_1.delay(this.afterActionDelay);
};
SimulatorServerRobotJs.prototype.doPointerClick = function (command) {
this.doWithModifierKeys(function () {
robotjs.mouseClick(command.modifier, false);
}, command.modifierKey);
return Delay_js_1.delay(this.afterActionDelay);
};
SimulatorServerRobotJs.prototype.doPointerDoubleClick = function (command) {
this.doWithModifierKeys(function () {
robotjs.mouseClick(command.modifier, true);
}, command.modifierKey);
return Delay_js_1.delay(this.afterActionDelay);
};
// not used currently but might be useful
SimulatorServerRobotJs.prototype.doInBatches = function (processor, input, batchSize, delayAfterBatch) {
var _this = this;
if (input.length === 0)
return Promise.resolve();
for (var i = 0; i < batchSize; i++) {
if (input.length === 0)
break;
processor(input.shift());
}
return Delay_js_1.delay(delayAfterBatch).then(function () { return _this.doInBatches(processor, input, batchSize, delayAfterBatch); });
};
SimulatorServerRobotJs.prototype.doSimulationReset = function (command) {
'`1234567890-=qwertyuiop[]\\asdfghjkl;\'zxcvbnm,./ '.split('').concat(Object.keys(SpecialKey).filter(function (key) { return isNaN(Number(key))
&& !key.match(/^lights_/) // fails on linux
&& !key.match(/^numpad_/) // fails on linux
&& !key.match(/^audio_/) // fails on linux
&& !(process.platform == 'darwin' && (key == 'printscreen' || key == 'insert')); })).forEach(function (key) {
// console.log("Key up: " + key)
robotjs.keyToggle(key, types.KeyState.Up);
});
if (process.platform != 'win32')
for (var button in types.PointerModifier) {
if (isNaN(Number(button))) {
robotjs.mouseToggle(types.PointerState.Up, types.PointerModifier[button]);
}
}
robotjs.moveMouse(0, 0);
return Delay_js_1.delay(this.afterActionDelay);
};
SimulatorServerRobotJs.prototype.doMouseWheel = function (command) {
this.doWithModifierKeys(function () {
// https://github.com/octalmage/robotjs/issues/318
// https://github.com/octalmage/robotjs/issues/303 ??
robotjs.scrollMouse(command.deltaX, 0);
}, command.modifierKey);
return Delay_js_1.delay(this.afterActionDelay);
};
SimulatorServerRobotJs.prototype.doPointerDrag = function (command) {
var _this = this;
this.doWithModifierKeys(function () {
robotjs.moveMouse(command.fromX, command.fromY);
robotjs.mouseToggle(types.PointerState.Down, command.modifier);
_this.moveMouseTo(command.toX, command.toY, 1);
if (!command.dragOnly)
robotjs.mouseToggle(types.PointerState.Up, command.modifier);
}, command.modifierKey);
return Promise.resolve();
};
SimulatorServerRobotJs.prototype.doWithModifierKeys = function (action, modifierKeys) {
if (modifierKeys === void 0) { modifierKeys = []; }
this.toggleModifierKeys(modifierKeys, types.KeyState.Down);
action();
this.toggleModifierKeys(modifierKeys, types.KeyState.Up);
};
SimulatorServerRobotJs.prototype.toggleModifierKeys = function (modifierKeys, state) {
var _this = this;
if (modifierKeys === void 0) { modifierKeys = []; }
modifierKeys.forEach(function (modKey) { return robotjs.keyToggle(_this.siestaKeyToSimulatorKey(modKey), state); });
};
SimulatorServerRobotJs.prototype.siestaKeyToSimulatorKey = function (key) {
if (key.length == 1)
return key;
return siestaToRobotJsKeys[key];
};
SimulatorServerRobotJs.prototype.doType = function (command) {
var text = command.text;
for (var i = 0; i < text.length; i++) {
this.typeSingleChar(this.siestaKeyToSimulatorKey(text[i]), command.modifierKey);
}
// this setTimeout is required, because for some reason when typing just "delete" in the input field
// the callback is called, but the delete has not been processed yet
// (the it=`keyPress` method should support special chars like [SMTH]` in keyevents/043_special_keys.t.js
// created a ticket for robotjs: https://github.com/octalmage/robotjs/issues/347 but project is very poorly maintained..
return Delay_js_1.delay(2 * this.afterActionDelay);
};
// `key` is a robotjs-recognizable key
SimulatorServerRobotJs.prototype.typeSingleChar = function (key, modifierKey) {
if (modifierKey === void 0) { modifierKey = []; }
if (key.length > 1)
// special key - we just tap it
// see also comment below
this.doWithModifierKeys(function () {
robotjs.keyTap(key);
}, modifierKey);
else {
// a single char
var needShift = false;
if (key == '!') {
key = "1";
needShift = true;
}
if (key == '@') {
key = "2";
needShift = true;
}
if (key == '#') {
key = "3";
needShift = true;
}
if (key == '$') {
key = "4";
needShift = true;
}
if (key == '%') {
key = "5";
needShift = true;
}
if (key == '^') {
key = "6";
needShift = true;
}
if (key == '&') {
key = "7";
needShift = true;
}
if (key == '*') {
key = "8";
needShift = true;
}
if (key == '(') {
key = "9";
needShift = true;
}
if (key == ')') {
key = "0";
needShift = true;
}
if (key == '_') {
key = "-";
needShift = true;
}
if (key == '+') {
key = "=";
needShift = true;
}
if (key == '{') {
key = "[";
needShift = true;
}
if (key == '}') {
key = "]";
needShift = true;
}
if (key == '|') {
key = "\\";
needShift = true;
}
if (key == ':') {
key = ";";
needShift = true;
}
if (key == '"') {
key = "'";
needShift = true;
}
if (key == '<') {
key = ",";
needShift = true;
}
if (key == '>') {
key = ".";
needShift = true;
}
if (key == '?') {
key = "/";
needShift = true;
}
// the `keyTap` method of robotjs support modifiers array, but we can't use it, because
// lets say for CTRL + A (keyTap('a', [ 'control' ]), the order of events will be:
// keydown CTRL / keydown a / keyup CTRL / keyup a
// which is obviously wrong, at least for us, we want:
// keydown CTRL / keydown a / keyup a / keyup CTRL
// so we use our "doWithModifierKeys" wrapper
this.doWithModifierKeys(function () {
robotjs.keyTap(key);
}, needShift || key != key.toLowerCase() ? ['SHIFT'].concat(modifierKey) : modifierKey);
}
};
// Of course, the "moveMouseSmooth" method in RobotJS is a total mess:
// https://github.com/octalmage/robotjs/issues/238
// we have to introduce our own
SimulatorServerRobotJs.prototype.moveMouseTo = function (x, y, mouseMovePrecision) {
var currentPos = robotjs.getMousePos();
var path = point.getPathBetweenPoints([currentPos.x, currentPos.y], [x, y]);
path = Point_js_1.filterPathAccordingToPrecision(path, mouseMovePrecision || this.mouseMovePrecision);
for (var i = 0; i < path.length; i++) {
var step = path[i];
robotjs.moveMouse(step[0], step[1]);
}
};
return SimulatorServerRobotJs;
}(ServerEndPoint_js_1.ServerEndPoint));
exports.SimulatorServerRobotJs = SimulatorServerRobotJs;
var SpecialKey;
(function (SpecialKey) {
SpecialKey[SpecialKey["backspace"] = 0] = "backspace";
SpecialKey[SpecialKey["delete"] = 1] = "delete";
SpecialKey[SpecialKey["enter"] = 2] = "enter";
SpecialKey[SpecialKey["tab"] = 3] = "tab";
SpecialKey[SpecialKey["escape"] = 4] = "escape";
SpecialKey[SpecialKey["up"] = 5] = "up";
SpecialKey[SpecialKey["down"] = 6] = "down";
SpecialKey[SpecialKey["right"] = 7] = "right";
SpecialKey[SpecialKey["left"] = 8] = "left";
SpecialKey[SpecialKey["home"] = 9] = "home";
SpecialKey[SpecialKey["end"] = 10] = "end";
SpecialKey[SpecialKey["pageup"] = 11] = "pageup";
SpecialKey[SpecialKey["pagedown"] = 12] = "pagedown";
SpecialKey[SpecialKey["f1"] = 13] = "f1";
SpecialKey[SpecialKey["f2"] = 14] = "f2";
SpecialKey[SpecialKey["f3"] = 15] = "f3";
SpecialKey[SpecialKey["f4"] = 16] = "f4";
SpecialKey[SpecialKey["f5"] = 17] = "f5";
SpecialKey[SpecialKey["f6"] = 18] = "f6";
SpecialKey[SpecialKey["f7"] = 19] = "f7";
SpecialKey[SpecialKey["f8"] = 20] = "f8";
SpecialKey[SpecialKey["f9"] = 21] = "f9";
SpecialKey[SpecialKey["f10"] = 22] = "f10";
SpecialKey[SpecialKey["f11"] = 23] = "f11";
SpecialKey[SpecialKey["f12"] = 24] = "f12";
SpecialKey[SpecialKey["command"] = 25] = "command";
SpecialKey[SpecialKey["alt"] = 26] = "alt";
SpecialKey[SpecialKey["control"] = 27] = "control";
SpecialKey[SpecialKey["shift"] = 28] = "shift";
SpecialKey[SpecialKey["right_shift"] = 29] = "right_shift";
SpecialKey[SpecialKey["space"] = 30] = "space";
SpecialKey[SpecialKey["printscreen"] = 31] = "printscreen";
SpecialKey[SpecialKey["insert"] = 32] = "insert";
SpecialKey[SpecialKey["audio_mute"] = 33] = "audio_mute";
SpecialKey[SpecialKey["audio_vol_down"] = 34] = "audio_vol_down";
SpecialKey[SpecialKey["audio_vol_up"] = 35] = "audio_vol_up";
SpecialKey[SpecialKey["audio_play"] = 36] = "audio_play";
SpecialKey[SpecialKey["audio_stop"] = 37] = "audio_stop";
SpecialKey[SpecialKey["audio_pause"] = 38] = "audio_pause";
SpecialKey[SpecialKey["audio_prev"] = 39] = "audio_prev";
SpecialKey[SpecialKey["audio_next"] = 40] = "audio_next";
SpecialKey[SpecialKey["audio_rewind"] = 41] = "audio_rewind";
SpecialKey[SpecialKey["audio_forward"] = 42] = "audio_forward";
SpecialKey[SpecialKey["audio_repeat"] = 43] = "audio_repeat";
SpecialKey[SpecialKey["audio_random"] = 44] = "audio_random";
SpecialKey[SpecialKey["numpad_0"] = 45] = "numpad_0";
SpecialKey[SpecialKey["numpad_1"] = 46] = "numpad_1";
SpecialKey[SpecialKey["numpad_2"] = 47] = "numpad_2";
SpecialKey[SpecialKey["numpad_3"] = 48] = "numpad_3";
SpecialKey[SpecialKey["numpad_4"] = 49] = "numpad_4";
SpecialKey[SpecialKey["numpad_5"] = 50] = "numpad_5";
SpecialKey[SpecialKey["numpad_6"] = 51] = "numpad_6";
SpecialKey[SpecialKey["numpad_7"] = 52] = "numpad_7";
SpecialKey[SpecialKey["numpad_8"] = 53] = "numpad_8";
SpecialKey[SpecialKey["numpad_9"] = 54] = "numpad_9";
SpecialKey[SpecialKey["lights_mon_up"] = 55] = "lights_mon_up";
SpecialKey[SpecialKey["lights_mon_down"] = 56] = "lights_mon_down";
SpecialKey[SpecialKey["lights_kbd_toggle"] = 57] = "lights_kbd_toggle";
SpecialKey[SpecialKey["lights_kbd_up"] = 58] = "lights_kbd_up";
SpecialKey[SpecialKey["lights_kbd_down"] = 59] = "lights_kbd_down";
})(SpecialKey = exports.SpecialKey || (exports.SpecialKey = {}));
var siestaToRobotJsKeys = {
'BACKSPACE': 'backspace',
'TAB': 'tab',
'RETURN': 'enter',
'ENTER': 'enter',
//special
'SHIFT': 'shift',
'CTRL': 'control',
'ALT': 'alt',
'CMD': 'command',
//weird
'PAUSE-BREAK': null,
'CAPS': null,
'ESCAPE': 'escape',
'ESC': 'escape',
'NUM-LOCK': null,
'SCROLL-LOCK': null,
'PRINT': 'printscreen',
//navigation
'PAGE-UP': 'pageup',
'PAGE-DOWN': 'pagedown',
'PAGEUP': 'pageup',
'PAGEDOWN': 'pagedown',
'END': 'end',
'HOME': 'home',
'LEFT': 'left',
'ARROWLEFT': 'left',
'UP': 'up',
'ARROWUP': 'up',
'RIGHT': 'right',
'ARROWRIGHT': 'right',
'DOWN': 'down',
'ARROWDOWN': 'down',
'INSERT': 'insert',
'DELETE': 'delete',
//NORMAL-CHARACTERS, NUMPAD
'NUM0': 'numpad_0',
'NUM1': 'numpad_1',
'NUM2': 'numpad_2',
'NUM3': 'numpad_3',
'NUM4': 'numpad_4',
'NUM5': 'numpad_5',
'NUM6': 'numpad_6',
'NUM7': 'numpad_7',
'NUM8': 'numpad_8',
'NUM9': 'numpad_9',
'F1': 'f1',
'F2': 'f2',
'F3': 'f3',
'F4': 'f4',
'F5': 'f5',
'F6': 'f6',
'F7': 'f7',
'F8': 'f8',
'F9': 'f9',
'F10': 'f10',
'F11': 'f11',
'F12': 'f12'
};
// eof robotJSKeys
exports.SimulatorServer = SimulatorServerRobotJs;
;