UNPKG

siesta-lite

Version:

Stress-free JavaScript unit testing and functional testing tool, works in NodeJS and browsers

463 lines (462 loc) 19 kB
"use strict"; 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;