UNPKG

siesta-lite

Version:

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

320 lines (247 loc) 10.3 kB
import * as types from '../../../generic/simulator/Types' import {ServerEndPoint} from "../../channel/websocket/ServerEndPoint.js"; import {Envelop, Message} from "../../../generic/channel/Types.js"; import {SimulatorCommand} from "../../../generic/simulator/Commands.js"; import * as commands from "../../../generic/simulator/Commands.js"; import {filterPathAccordingToPrecision, getPathBetweenPoints, Point} from "../../../generic/util/Point.js"; import {delay} from "../../../generic/util/helper/Delay.js"; export class SimulatorServerPuppeteer extends ServerEndPoint { page : any mouseMovePrecision : number = 3 // see https://github.com/octalmage/robotjs/issues/347 afterActionDelay : number = 100 currentPosition : Point = [ 0, 0 ] doProcessRawChannelMessage (message : Message, envelop : Envelop) { } doDispatchEnvelop (envelop : Envelop) { const cmd = envelop.payload as SimulatorCommand let action switch (cmd.type) { case 'move_pointer' : action = this.doMovePointer(<commands.MovePointer>cmd); break case 'set_pointer_state' : action = this.doSetPointerState(<commands.SetPointerState>cmd); break case 'pointer_click' : action = this.doPointerClick(<commands.PointerClick>cmd); break case 'pointer_double_click' : action = this.doPointerDoubleClick(<commands.PointerDoubleClick>cmd); break case 'mouse_wheel' : action = this.doMouseWheel(<commands.MouseWheel>cmd); break case 'pointer_drag' : action = this.doPointerDrag(<commands.PointerDrag>cmd); break case 'type' : action = this.doType(<commands.Type>cmd); break case 'reset' : action = this.doSimulationReset(<commands.Reset>cmd); break case 'set_key_state' : action = this.doSetKeyState(<commands.SetKeyState>cmd); break } action.then(result => this.replyWith(envelop, result), reason => { this.info("Simulator command failed: " + reason) this.replyWith(envelop, reason, true) }) } pageMouseMove (x, y, options = undefined) : Promise<any> { return this.page.mouse.move(x, y, options).then(() => { this.currentPosition[ 0 ] = x this.currentPosition[ 1 ] = y }) } siestaKeyToSimulatorKey (key : string) : string { if (key.length == 1) return key return siestaToPuppeteerKeys[ key ] } toggleModifierKeys (modifierKeys : types.ModifierKey[] = [], state : types.KeyState) : Promise<any> { let cont = Promise.resolve() modifierKeys.forEach(modKey => { cont = cont.then(() => this.page.keyboard[ state ](this.siestaKeyToSimulatorKey(modKey))) }) return cont } doWithModifierKeys (action : Function, modifierKeys : types.ModifierKey[] = []) : Promise<any> { return this.toggleModifierKeys(modifierKeys, types.KeyState.Down).then( () => action() ).then( () => this.toggleModifierKeys(modifierKeys, types.KeyState.Up) ) } doMovePointer (command : commands.MovePointer) : Promise<any> { return this.doWithModifierKeys(() => { if (command.moveKind == 'instant') return this.page.mouse.move(command.x, command.y) else if (command.moveKind == 'smooth') { return this.moveMouseTo(command.x, command.y, command.mouseMovePrecision) } }, command.modifierKey) } doSetPointerState (command : commands.SetPointerState) : Promise<any> { return this.doWithModifierKeys( () => this.page.mouse[ command.state ]({ button : command.modifier }), command.modifierKey ).then( () => delay(this.afterActionDelay) ) } doPointerClick (command : commands.PointerClick) : Promise<any> { const mouse = this.page.mouse return this.doWithModifierKeys( () => { return mouse.down({ button : command.modifier, clickCount : 1 }).then( () => mouse.up({ button : command.modifier, clickCount : 1 }) ) }, command.modifierKey ).then( () => delay(this.afterActionDelay) ) } doPointerDoubleClick (command : commands.PointerDoubleClick) : Promise<any> { const mouse = this.page.mouse return this.doWithModifierKeys( () => { return mouse.down({ button : command.modifier, clickCount : 1 }).then( () => mouse.up({ button : command.modifier, clickCount : 1 }) ).then( () => mouse.down({ button : command.modifier, clickCount : 2 }) ).then( () => mouse.up({ button : command.modifier, clickCount : 2 }) ) }, command.modifierKey ).then( () => delay(this.afterActionDelay) ) } doSimulationReset (command : commands.Reset) : Promise<any> { let cont = Promise.resolve() Object.keys(siestaToPuppeteerKeys).forEach(siestaKey => { cont = cont.then( () => this.page.keyboard.up(this.siestaKeyToSimulatorKey(siestaKey)) ) }) Object.keys(types.PointerModifier).forEach(button => { if (isNaN(Number(button))) { cont = cont.then( () => this.page.mouse.up({ button : types.PointerModifier[ button ] }) ) } }) cont = cont.then( () => this.pageMouseMove(0, 0) ).then( () => delay(this.afterActionDelay) ) return cont } doMouseWheel (command : commands.MouseWheel) : Promise<any> { return this.doWithModifierKeys( () => { }, command.modifierKey ).then( () => delay(this.afterActionDelay) ) } doPointerDrag (command : commands.PointerDrag) : Promise<any> { const mouse = this.page.mouse return this.doWithModifierKeys( () => { return this.moveMouseTo(command.fromX, command.fromY).then( () => mouse.down({ button : command.modifier }) ).then( () => this.moveMouseTo(command.toX, command.toY, 1) ).then( () => command.dragOnly ? Promise.resolve() : mouse.up({ button : command.modifier }) ) }, command.modifierKey ).then( () => delay(this.afterActionDelay) ) } doSetKeyState (command : commands.SetKeyState) : Promise<any> { const keyboard = this.page.keyboard return keyboard[ command.state ](this.siestaKeyToSimulatorKey(command.key)) } doType (command : commands.Type) : Promise<any> { const text = command.text let cont = Promise.resolve() for (let i = 0; i < text.length; i++) { cont = cont.then(() => this.typeSingleChar(text[ i ], command.modifierKey)) } return cont.then(() => delay(this.afterActionDelay)) } typeSingleChar (key : string, modifierKey : types.ModifierKey[] = []) : Promise<any> { const keyboard = this.page.keyboard if (key.length > 1) return this.doWithModifierKeys( () => keyboard.press(this.siestaKeyToSimulatorKey(key)), modifierKey ) else { // seems no special processing is needed for Puppeteer, see analogous RobotJS method however return this.doWithModifierKeys( () => keyboard.press(this.siestaKeyToSimulatorKey(key)), modifierKey ) } } moveMouseTo (x : number, y : number, mouseMovePrecision : number = null) : Promise<any> { var path = getPathBetweenPoints(this.currentPosition, [ x, y ]) path = filterPathAccordingToPrecision(path, mouseMovePrecision || this.mouseMovePrecision) let cont = Promise.resolve() path.forEach(step => { cont = cont.then(() => this.pageMouseMove(step[ 0 ], step[ 1 ])) }) return cont } } const siestaToPuppeteerKeys = { 'BACKSPACE' : 'Backspace', 'TAB' : 'Tab', 'RETURN' : 'Enter', 'ENTER' : 'Enter', //special 'SHIFT' : 'Shift', 'CTRL' : 'Control', 'ALT' : 'Alt', 'CMD' : 'Meta', // Mac // //weird 'PAUSE-BREAK' : 'Pause', 'CAPS' : 'CapsLock', 'ESCAPE' : 'Escape', 'ESC' : 'Escape', 'NUM-LOCK' : 'NumLock', 'SCROLL-LOCK' : 'ScrollLock', 'PRINT' : 'Print', //navigation 'PAGE-UP' : 'PageUp', 'PAGE-DOWN' : 'PageDown', 'END' : 'End', 'HOME' : 'Home', 'LEFT' : 'ArrowLeft', 'ARROWLEFT' : 'ArrowLeft', 'UP' : 'ArrowUp', 'ARROWUP' : 'ArrowUp', 'RIGHT' : 'ArrowRight', 'ARROWRIGHT' : 'ArrowRight', 'DOWN' : 'ArrowDown', 'ARROWDOWN' : 'ArrowDown', 'INSERT' : 'Insert', 'DELETE' : 'Delete', //NORMAL-CHARACTERS, NUMPAD 'NUM0' : 'Numpad0', 'NUM1' : 'Numpad1', 'NUM2' : 'Numpad2', 'NUM3' : 'Numpad3', 'NUM4' : 'Numpad4', 'NUM5' : 'Numpad5', 'NUM6' : 'Numpad6', 'NUM7' : 'Numpad7', 'NUM8' : 'Numpad8', 'NUM9' : 'Numpad9', 'F1' : 'F1', 'F2' : 'F2', 'F3' : 'F3', 'F4' : 'F4', 'F5' : 'F5', 'F6' : 'F6', 'F7' : 'F7', 'F8' : 'F8', 'F9' : 'F9', 'F10' : 'F10', 'F11' : 'F11', 'F12' : 'F12' } export const SimulatorServer = SimulatorServerPuppeteer