taiko
Version:
Taiko is a Node.js library for automating Chromium based browsers
194 lines (169 loc) • 4.95 kB
JavaScript
/**
* Copyright 2018 Thoughtworks Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This module is imported from Puppeteer(https://github.com/GoogleChrome/puppeteer)
* Few modifications are done on the file.
*/
const keyDefinitions = require("../data/USKeyboardLayout");
const { eventHandler } = require("../eventBus");
const { assert } = require("../helper");
let input;
let _modifiers = 0;
const _pressedKeys = new Set();
const createdSessionListener = (client) => {
input = client.Input;
};
eventHandler.on("createdSession", createdSessionListener);
const _down = async (key, options = { text: undefined }) => {
const description = _keyDescriptionForString(key);
const autoRepeat = _pressedKeys.has(description.code);
_pressedKeys.add(description.code);
_modifiers |= _modifierBit(description.key);
const text = options.text === undefined ? description.text : options.text;
await input.dispatchKeyEvent({
type: text ? "keyDown" : "rawKeyDown",
modifiers: _modifiers,
windowsVirtualKeyCode: description.keyCode,
code: description.code,
key: description.key,
text: text,
unmodifiedText: text,
autoRepeat,
location: description.location,
isKeypad: description.location === 3,
});
};
const _up = async (key) => {
const description = _keyDescriptionForString(key);
_modifiers &= ~_modifierBit(description.key);
_pressedKeys.delete(description.code);
await input.dispatchKeyEvent({
type: "keyUp",
modifiers: _modifiers,
key: description.key,
windowsVirtualKeyCode: description.keyCode,
code: description.code,
location: description.location,
});
};
const _keyDescriptionForString = (keyString) => {
const shift = _modifiers & 8;
const description = {
key: "",
keyCode: 0,
code: "",
text: "",
location: 0,
};
const definition = keyDefinitions[keyString];
assert(definition, `Unknown key: "${keyString}"`);
if (definition.key) {
description.key = definition.key;
}
if (shift && definition.shiftKey) {
description.key = definition.shiftKey;
}
if (definition.keyCode) {
description.keyCode = definition.keyCode;
}
if (shift && definition.shiftKeyCode) {
description.keyCode = definition.shiftKeyCode;
}
if (definition.code) {
description.code = definition.code;
}
if (definition.location) {
description.location = definition.location;
}
if (description.key.length === 1) {
description.text = description.key;
}
if (definition.text) {
description.text = definition.text;
}
if (shift && definition.shiftText) {
description.text = definition.shiftText;
}
// if any modifiers besides shift are pressed, no text should be sent
if (_modifiers & ~8) {
description.text = "";
}
return description;
};
const _mouse_move = async (sourcePosition, destinationPosition) => {
const steps = 10;
const fromX = sourcePosition.x;
const fromY = sourcePosition.y;
for (let i = 1; i <= steps; i++) {
await input.dispatchMouseEvent({
type: "mouseMoved",
button: "left",
x: fromX + (destinationPosition.x - fromX) * (i / steps),
y: fromY + (destinationPosition.y - fromY) * (i / steps),
modifiers: _modifiers,
});
}
};
function _modifierBit(key) {
if (key === "Alt") {
return 1;
}
if (key === "Control") {
return 2;
}
if (key === "Meta") {
return 4;
}
if (key === "Shift") {
return 8;
}
return 0;
}
const tap = async (x, y) => {
const touchPoints = [{ x: Math.round(x), y: Math.round(y) }];
await input
.dispatchTouchEvent({ type: "touchStart", touchPoints })
.catch((err) => {
throw new Error(err);
});
await input.dispatchTouchEvent({
type: "touchEnd",
touchPoints: [],
});
};
const simulateMouseClick = async (options) => {
options.type = "mouseMoved";
await input.dispatchMouseEvent(options);
options.type = "mousePressed";
await input.dispatchMouseEvent(options);
options.type = "mouseReleased";
await input.dispatchMouseEvent(options);
};
const _insertText = async (char) => {
await input.insertText({ text: char });
};
const dispatchMouseEvent = async (options) => {
await input.dispatchMouseEvent(options);
};
module.exports = {
up: _up,
down: _down,
mouse_move: _mouse_move,
sendCharacter: _insertText,
tap,
simulateMouseClick,
dispatchMouseEvent,
};