Skip to content

Commit

Permalink
feat: use object config
Browse files Browse the repository at this point in the history
  • Loading branch information
Pierre Haller committed Feb 26, 2020
1 parent 7d57872 commit 7876644
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 158 deletions.
53 changes: 21 additions & 32 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,50 +16,39 @@ import distribution from './strategies/distribution';

import executeInSeries from './utils/executeInSeries';

export const species = {
clicker,
toucher,
formFiller,
scroller,
typer,
};
const species = [clicker()];

export const mogwais = {
alert,
fps,
gizmo,
};
const mogwais = [fps()];

export const strategies = {
allTogether,
bySpecies,
distribution,
};
const strategies = [bySpecies()];

const defaultConfig = {
gremlins: Object.values(species),
mogwais: Object.values(mogwais),
strategies: [strategies.bySpecies],
species,
mogwais,
strategies,
logger: console,
randomizer: new Chance(),
};

// do not export anything else here to keep window.gremlins as a function
export default userConfig => {
const config = { ...defaultConfig, ...userConfig };

const unleash = async params => {
const gremlinsAndMogwais = [...config.gremlins, ...config.mogwais];
const beforeHorde = [...config.mogwais];
const afterHorde = gremlinsAndMogwais
.map(beast => beast.cleanUp)
.filter(cleanUp => typeof cleanUp === 'function');

const horde = config.strategies.map(strat => strat.apply(null, [config.gremlins].concat(params)));

const { logger, randomizer } = config;
const species = config.species.map(specie => specie(logger, randomizer));
const mogwais = config.mogwais.map(mogwai => mogwai(logger));
const strategies = config.strategies.map(strat => strat(species));

const unleash = async () => {
const gremlinsAndMogwais = [...species, ...mogwais];
const beforeHorde = [...mogwais];
// const afterHorde = gremlinsAndMogwais
// .map(beast => beast.cleanUp)
// .filter(cleanUp => typeof cleanUp === 'function');

// const horde = config.strategies.map(strat => strat.apply(null, [species].concat(params)));
await executeInSeries(beforeHorde, []);
await Promise.all(horde);
await executeInSeries(afterHorde, []);
await Promise.all(strategies);
// await executeInSeries(afterHorde, []);
};

const stop = () => config.strategies.forEach(strat => strat.stop());
Expand Down
66 changes: 15 additions & 51 deletions src/mogwais/fps.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,18 @@
import configurable from '../utils/configurable';
import LoggerRequiredException from '../exceptions/loggerRequiredException';

/**
* The fps mogwai logs the number of frames per seconds (FPS) of the browser
*
* The normal (and maximal) FPS rate is 60. It decreases when the browser is
* busy refreshing the layout, or executing JavaScript.
*
* This mogwai logs with the error level once the FPS rate drops below 10.
*
* const fpsMogwai = gremlins.mogwais.fps();
* horde.mogwai(fpsMogwai);
*
* The fps mogwai can be customized as follows:
*
* fpsMogwai.delay(500); // the interval for FPS measurements
* fpsMogwai.levelSelector(function(fps) { // select logging level according to fps value });
* fpsMogwai.logger(loggerObject); // inject a logger
*
* Example usage:
*
* horde.mogwai(gremlins.mogwais.fps()
* .delay(250)
* .levelSelector(function(fps) {
* if (fps < 5) return 'error';
* if (fps < 10) return 'warn';
* return 'log';
* })
* );
*/

const NEXT_FRAME_MS = 16;

export default () => {
const getDefaultConfig = () => {
const defaultLevelSelector = fps => {
if (fps < 10) return 'error';
if (fps < 20) return 'warn';
return 'log';
};
return {
delay: 500, // how often should the fps be measured
levelSelector: defaultLevelSelector,
};
};

export default userConfig => logger => {
if (!window.requestAnimationFrame) {
// shim layer with setTimeout fallback
window.requestAnimationFrame =
Expand All @@ -44,17 +24,7 @@ export default () => {
});
}

const defaultLevelSelector = fps => {
if (fps < 10) return 'error';
if (fps < 20) return 'warn';
return 'log';
};

const config = {
delay: 500, // how often should the fps be measured
levelSelector: defaultLevelSelector,
logger: console,
};
const config = { ...getDefaultConfig(), ...userConfig };

let initialTime = -Infinity; // force initial measure
let enabled;
Expand All @@ -77,17 +47,12 @@ export default () => {
const measure = time => {
const fps = time - lastTime < NEXT_FRAME_MS ? 60 : 1000 / (time - lastTime);
const level = config.levelSelector(fps);
config.logger[level]('mogwai ', 'fps ', fps);
logger[level]('mogwai ', 'fps ', fps);
};
window.requestAnimationFrame(init);
};

const fpsMogwai = () => {
if (!config.logger) {
throw new LoggerRequiredException(
'This mogwai requires a logger to run. Please call logger(loggerObject) before executing the mogwai'
);
}
enabled = true;
window.requestAnimationFrame(loop);
};
Expand All @@ -97,6 +62,5 @@ export default () => {
return fpsMogwai;
};

configurable(fpsMogwai, config);
return fpsMogwai;
};
91 changes: 16 additions & 75 deletions src/species/clicker.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,4 @@
import Chance from 'chance';

import configurable from '../utils/configurable';
import RandomizerRequiredException from '../exceptions/randomizerRequiredException';

/**
* The clicker gremlin clicks anywhere on the visible area of the document
*
* The clicker gremlin triggers mouse events (click, dblclick, mousedown,
* mouseup, mouseover, mouseover, mouseover, mousemove, and mouseout) on
* random targets displayed on the viewport.
*
* By default, the clicker gremlin activity is showed by a red circle.
*
* const clickerGremlin = gremlins.species.clicker();
* horde.gremlin(clickerGremlin);
*
* The clicker gremlin can be customized as follows:
*
* clickerGremlin.clickTypes(['click', 'mouseover']); // the mouse event types to trigger
* clickerGremlin.positionSelector(function() { // find a random pair of coordinates to click });
* clickerGremlin.showAction(function(x, y) { // show the gremlin activity on screen });
* clickerGremlin.canClick(function(element) { return true }); // to limit where the gremlin can click
* clickerGremlin.maxNbTries(5); // How many times the gremlin must look for a clickable element before quitting
* clickerGremlin.logger(loggerObject); // inject a logger
* clickerGremlin.randomizer(randomizerObject); // inject a randomizer
*
* Example usage:
*
* horde.gremlin(gremlins.species.clicker()
* .clickTypes(['click'])
* .positionSelector(function() {
* // only click inside the foo element area
* const $el = $('#foo');
* const offset = $el.offset();
* return [
* parseInt(Math.random() * $el.outerWidth() + offset.left),
* parseInt(Math.random() * $el.outerHeight() + offset.top)
* ];
* })
* .canClick(function(element) {
* // only click elements in bar
* return $(element).parents('#bar').length;
* // when canClick returns false, the gremlin will look for another
* // element to click on until maxNbTries is reached
* })
* . showAction(function(x, y) {
* // do nothing (hide the gremlin action on screen)
* })
* );
*/

export default () => {
const document = window.document;
const body = document.body;

const getDefaultConfig = randomizer => {
const defaultClickTypes = [
'click',
'click',
Expand All @@ -74,16 +19,18 @@ export default () => {

const defaultPositionSelector = () => {
return [
config.randomizer.natural({
randomizer.natural({
max: document.documentElement.clientWidth - 1,
}),
config.randomizer.natural({
randomizer.natural({
max: document.documentElement.clientHeight - 1,
}),
];
};

const defaultShowAction = (x, y) => {
const document = window.document;
const body = document.body;
const clickSignal = document.createElement('div');
clickSignal.style.zIndex = 2000;
clickSignal.style.border = '3px solid red';
Expand All @@ -110,24 +57,22 @@ export default () => {
const defaultCanClick = () => {
return true;
};

const config = {
return {
clickTypes: defaultClickTypes,
positionSelector: defaultPositionSelector,
showAction: defaultShowAction,
canClick: defaultCanClick,
maxNbTries: 10,
logger: null,
randomizer: new Chance(),
};
};

const clickerGremlin = () => {
if (!config.randomizer) {
throw new RandomizerRequiredException(
'This gremlin requires a randomizer to run. Please call randomizer(randomizerObject) before executing the gremlin.'
);
}
export default userConfig => (logger, randomizer) => {
const config = {
...getDefaultConfig(randomizer),
userConfig,
};

return () => {
let position;
let posX;
let posY;
Expand All @@ -144,20 +89,16 @@ export default () => {
} while (!targetElement || !config.canClick(targetElement));

const evt = document.createEvent('MouseEvents');
const clickType = config.randomizer.pick(config.clickTypes);
const clickType = randomizer.pick(config.clickTypes);
evt.initMouseEvent(clickType, true, true, window, 0, 0, 0, posX, posY, false, false, false, false, 0, null);
targetElement.dispatchEvent(evt);

if (typeof config.showAction === 'function') {
config.showAction(posX, posY, clickType);
}

if (config.logger && typeof config.logger.log === 'function') {
config.logger.log('gremlin', 'clicker ', clickType, 'at', posX, posY);
if (logger && typeof logger.log === 'function') {
logger.log('gremlin', 'clicker ', clickType, 'at', posX, posY);
}
};

configurable(clickerGremlin, config);

return clickerGremlin;
};
1 change: 1 addition & 0 deletions src/strategies/bySpecies.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default () => {
let stopped;

const bySpeciesStrategy = async (newGremlins, params) => {
console.log('by species called');
const nb = params && params.nb ? params.nb : config.nb;
const delay = params && params.delay ? params.delay : config.delay;

Expand Down

0 comments on commit 7876644

Please sign in to comment.