Common base for my other typescript projects
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

121 lines
2.5 KiB

/**
* Testing tools, trying to stick to jasmine/jest syntax for easy porting
*/
import { expect as _expect } from "./expect/expect.ts";
import * as _mock from "./expect/mock.ts";
import { Sys } from "./system.ts";
export const expect = _expect;
export const mockfn = _mock.fn;
var _last_title = "";
/**
* Test block, containing one or more it() calls.
*/
export function describe(
title: string | { new (...args: any[]): any } | Function,
body: Function,
) {
switch (typeof title) {
case "string": {
_last_title = title;
break;
}
case "function": {
_last_title = title.name;
break;
}
}
body();
}
/**
* Single test
*/
export function it(title: string, body: () => void | Promise<void>) {
Sys.test(_last_title ? `${_last_title} ${title}` : title, body);
}
type MockManipulator<F extends Function> = {
_toExpected: () => Parameters<typeof expect>[0];
reset: () => void;
stub: (impl?: F, loop?: boolean) => void;
};
/**
* 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;
}