stand-in
Version:
Method replacement and testing utility
95 lines (76 loc) • 2.21 kB
JavaScript
;
// Load modules
const Assert = require('assert');
const Get = require('lodash.get');
const Set = require('lodash.set');
const STORE = [];
STORE.lookUp = function (obj, path) {
let i = this.length;
let lookup;
while (!lookup && i--) {
const item = this[i];
if (item.obj === obj && item.path === path) {
lookup = item;
}
}
return {
result: lookup,
index: i
};
};
module.exports.replace = function (obj, path, fn, options) {
Assert.ok(obj, 'obj must be defined');
Assert.ok(typeof obj === 'object' || typeof obj === 'function', 'obj must be an object or a constructor');
const func = Get(obj, path);
Assert.strictEqual(typeof func, 'function', 'path must be a valid function of obj');
Assert.strictEqual(typeof fn, 'function', 'fn must be a function object');
const find = STORE.lookUp(obj, path);
Assert.strictEqual(find.result, undefined, 'there is already a replace for "obj"[' + path + ']');
options = Object.assign({}, options);
const startOn = options.startOn | 0; // Force to an integer
let invocations = 0;
const stand = {
obj: obj,
path: path,
restore: restore
};
STORE.push(stand);
const result = {
original: func,
restore,
get invocations () { return invocations; },
_stand: stand
};
function restore () {
const find = STORE.lookUp(obj, path);
STORE.splice(find.index, 1);
Set(obj, path, func);
}
function wrapper () {
invocations++;
if (invocations < startOn) {
return func.apply(this, arguments);
}
if (invocations === options.stopAfter) {
result.restore();
}
const args = new Array(arguments.length + 1);
args[0] = result;
for (let i = 0; i < arguments.length; ++i) {
args[i + 1] = arguments[i];
}
return fn.apply(this, args);
}
Set(obj, path, wrapper);
return result;
};
module.exports.replaceOnce = function (obj, path, fn, options) {
options = Object.assign({}, options);
options.stopAfter = (options.startOn | 0) || 1;
return module.exports.replace(obj, path, fn, options);
};
module.exports.restoreAll = function () {
while (STORE.length > 0) {
STORE[0].restore();
}
};