UNPKG

gremlins-ts

Version:

A monkey testing library written in JavaScript, for Node.js and the browser. Use it to check the robustness of web applications by unleashing a horde of undisciplined gremlins.

148 lines (125 loc) 4.08 kB
import { configurable } from "../utils"; import { RandomizerRequiredException } from "../exceptions"; /** * The typer gremlin types keys on the keyboard * * Note that keyboard events must be localized somewhere on screen, so this * gremlins picks a random screen location first. * * By default, the typer gremlin activity is showed by a letter surrounded by * a orange circle with a keyname on it. * * var typerGremlin = gremlins.species.typer(); * horde.gremlin(typerGremlin); * * The typerGremlin gremlin can be customized as follows: * * typerGremlin.eventTypes(['keypress', 'keyup', 'keydown']); // types of events to trigger * typerGremlin.showAction(function(element) { // show the gremlin activity on screen }); * typerGremlin.logger(loggerObject); // inject a logger * typerGremlin.randomizer(randomizerObject); // inject a randomizer * */ function defaultTargetElement(x: number, y: number) { const { document } = window; return document.elementFromPoint(x, y); } function defaultShowAction( targetElement: Element, x: number, y: number, key: number ) { const { document } = window; const { body } = document; const typeSignal = document.createElement("div"); typeSignal.style.zIndex = "2000"; typeSignal.style.border = "3px solid orange"; typeSignal.style.borderRadius = "50%"; typeSignal.style.width = "40px"; typeSignal.style.height = "40px"; typeSignal.style.boxSizing = "border-box"; typeSignal.style.position = "absolute"; typeSignal.style.transition = "opacity 1s ease-out"; typeSignal.style.left = x + "px"; typeSignal.style.top = y + "px"; typeSignal.style.textAlign = "center"; typeSignal.style.paddingTop = "7px"; typeSignal.innerHTML = String.fromCharCode(key); const element = body.appendChild(typeSignal); setTimeout(() => body.removeChild(element), 1000); setTimeout(() => (element.style.opacity = "0"), 50); } type Config = { eventTypes: string[]; showAction: ( targetElement: Element, x: number, y: number, key: number ) => void; keyGenerator: () => number; targetElement: (x: number, y: number) => Element | null; logger: typeof console; randomizer: Chance.Chance | null; }; export function typer() { const { document } = window; const { documentElement } = document; const defaultEventTypes = ["keypress", "keyup", "keydown"]; function defaultKeyGenerator() { if (config.randomizer === null) { return 3; } return config.randomizer.natural({ min: 3, max: 254 }); } const config: Config = { eventTypes: defaultEventTypes, showAction: defaultShowAction, keyGenerator: defaultKeyGenerator, targetElement: defaultTargetElement, logger: console, randomizer: null }; function typerGremlin() { if (!config.randomizer) { throw new RandomizerRequiredException(); } const keyboardEvent = document.createEvent("Events") as KeyboardEvent; const eventType = config.randomizer.pick(config.eventTypes); const key = config.keyGenerator(); const posX = config.randomizer.natural({ max: documentElement.clientWidth - 1 }); const posY = config.randomizer.natural({ max: documentElement.clientHeight - 1 }); const targetElement = config.targetElement(posX, posY); if (!targetElement) { throw new Error(`No element found at ${posX}x${posY}`); } keyboardEvent.initEvent(eventType, true, true); // @ts-ignore keyboardEvent.keyCode = key; // @ts-ignore keyboardEvent.which = key; // @ts-ignore keyboardEvent.keyCodeVal = key; targetElement.dispatchEvent(keyboardEvent); if (typeof config.showAction === "function") { config.showAction(targetElement, posX, posY, key); } if (config.logger && typeof config.logger.log === "function") { config.logger.log( "gremlin", "typer type", String.fromCharCode(key), "at", posX, posY ); } } configurable(typerGremlin, config); return typerGremlin; }