87 lines
1.8 KiB
TypeScript
87 lines
1.8 KiB
TypeScript
import { expect } from "./assertions.ts";
|
|
import * as _mock from "./expect/mock.ts";
|
|
|
|
export const mockfn = _mock.fn;
|
|
|
|
/**
|
|
* Patch an object's method
|
|
*/
|
|
export function patch<
|
|
O,
|
|
K extends keyof O,
|
|
F extends Function & O[K],
|
|
>(
|
|
obj: O,
|
|
method: K,
|
|
lifetime: (mock: MockManipulator<F>) => void,
|
|
): F {
|
|
const orig = obj[method] as F;
|
|
let stubs: { impl: F; loop: boolean }[] = [];
|
|
const createMock = () =>
|
|
mockfn((...args: any[]) => {
|
|
if (stubs.length) {
|
|
const result = stubs[0].impl(...args);
|
|
if (!stubs[0].loop) {
|
|
stubs.shift();
|
|
}
|
|
return result;
|
|
} else {
|
|
return orig.call(obj, ...args);
|
|
}
|
|
});
|
|
let mock = createMock();
|
|
|
|
Object.assign(obj, { [method]: mock });
|
|
try {
|
|
lifetime({
|
|
_toExpected() {
|
|
return mock;
|
|
},
|
|
stub(impl?: F, loop = false) {
|
|
if (impl) {
|
|
stubs.push({ impl, loop });
|
|
} else {
|
|
stubs.push({ impl: function () {} as any, loop: true });
|
|
}
|
|
},
|
|
reset() {
|
|
mock = createMock();
|
|
Object.assign(obj, { [method]: mock });
|
|
stubs = [];
|
|
},
|
|
});
|
|
} finally {
|
|
Object.assign(obj, { [method]: orig });
|
|
}
|
|
|
|
return mock as unknown as F;
|
|
}
|
|
|
|
/**
|
|
* Similar to *patch*, but with stub as enforced default
|
|
*/
|
|
export function mock<
|
|
O,
|
|
K extends keyof O,
|
|
F extends (...args: any) => any & O[K],
|
|
>(
|
|
obj: O,
|
|
method: K,
|
|
stub: F | ReturnType<F> | undefined,
|
|
lifetime: (mock: MockManipulator<F>) => void,
|
|
): F {
|
|
return patch(obj, method, (mock) => {
|
|
mock.stub(
|
|
typeof stub == "function" ? stub as any : (() => stub) as F,
|
|
true,
|
|
);
|
|
lifetime(mock as any);
|
|
}) as unknown as F;
|
|
}
|
|
|
|
type MockManipulator<F extends Function> = {
|
|
_toExpected: () => Parameters<typeof expect>[0];
|
|
reset: () => void;
|
|
stub: (impl?: F, loop?: boolean) => void;
|
|
};
|