Skip to content

Instantly share code, notes, and snippets.

@JosePedroDias
Last active September 27, 2023 15:36
Show Gist options
  • Save JosePedroDias/aa737777251e8c41ecc17c440cc75d9f to your computer and use it in GitHub Desktop.
Save JosePedroDias/aa737777251e8c41ecc17c440cc75d9f to your computer and use it in GitHub Desktop.
spies of method calls and optionally returns prepared results instead
export function spy(fn: Function, prot = fn.prototype) {
function proxyFn() {
const args = Array.prototype.slice.call(arguments);
const call = { args, result: undefined, error: undefined, real: true };
proxyFn.calls.push(call);
let responded = false;
// check if preparedCalls can answer
for (const pc of proxyFn.preparedCalls) {
const { useMe, updateCall, keepAfterUsage } = pc;
if (!useMe || useMe(args)) {
call.real = false;
updateCall(call);
responded = true;
if (!keepAfterUsage) {
const idx = proxyFn.preparedCalls.indexOf(pc);
proxyFn.preparedCalls.splice(idx, 1);
}
break;
}
}
if (!responded) {
try {
const result = fn.apply(this, args);
if (result instanceof Promise) {
result.then(
(res) => { call.result = res; },
(err) => { throw err; },
);
}
call.result = result;
} catch (ex) {
call.error = ex;
}
}
if (call.error) {
throw call.error;
} else {
return call.result;
}
}
proxyFn.prototype = prot;
proxyFn.calls = []; // { args, result, error, real? }
proxyFn.preparedCalls = []; // { useMe, updateCall, keepAfterUsage? }
return proxyFn;
}
// return new object/instance
export function spyOnObject<T>(o: T): T {
const o2 = {};
for (const k in o) {
const v = o[k];
// @ts-ignore
o2[k] = typeof v === 'function' ? spy(v, o.prototype) : v;
}
return o2 as T;
}
// patch the original object/instance
export function spyOnOriginalObject<T>(o: T) {
for (const k in o) {
const v = o[k];
if (typeof v === 'function') {
// @ts-ignore
o[k] = spy(v, o.prototype);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment