@chubbyts/chubbyts-function-mock
Version:
A function mock helper.
113 lines (112 loc) • 4.13 kB
JavaScript
/* eslint-disable @typescript-eslint/no-invalid-void-type */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { deepStrictEqual } from 'assert';
const formatContext = (context) => JSON.stringify(context);
export const internalResolveCallerLineFromStack = (stack) => {
if (!stack) {
return undefined;
}
const stackLines = stack.split('\n');
for (const stackLine of stackLines) {
if (-1 === stackLine.search(/at /)) {
continue;
}
if (-1 === stackLine.search(/object-mock\.(cjs|js|mjs|ts)/)) {
return parseInt(stackLine.split(':')[1]);
}
}
return undefined;
};
export const createObjectMock = (mocks) => {
const line = internalResolveCallerLineFromStack(new Error().stack);
// eslint-disable-next-line functional/no-let
let mockIndex = 0;
const object = new Proxy({}, {
get: (_, actualName) => {
// Promise.resolve
if (actualName === 'then') {
return;
}
// eslint-disable-next-line functional/immutable-data
const mock = mocks.shift();
if (!mock) {
throw new Error(`Missing mock: ${formatContext({
line,
mockIndex,
})}`);
}
if (actualName !== mock.name) {
throw new Error(`Method name mismatch: ${formatContext({
line,
mockIndex,
actual: actualName,
expect: mock.name,
})}`);
}
if ('value' in mock) {
mockIndex++;
return mock.value;
}
return (...actualParameters) => {
if ('callback' in mock) {
mockIndex++;
return mock.callback(...actualParameters);
}
if (actualParameters.length !== mock.parameters.length) {
throw new Error(`Parameters count mismatch: ${formatContext({
line,
mockIndex,
name: mock.name,
actual: actualParameters.length,
expect: mock.parameters.length,
})}`);
}
mock.parameters.forEach((expect, parameterIndex) => {
const actual = actualParameters[parameterIndex];
if (mock.strict) {
if (actual !== expect) {
throw new Error(`Parameter mismatch: ${formatContext({
line,
mockIndex,
name: mock.name,
parameterIndex,
actual,
expect,
strict: true,
})}`);
}
}
else {
try {
deepStrictEqual(actual, expect);
}
catch {
throw new Error(`Parameter mismatch: ${formatContext({
line,
mockIndex,
name: mock.name,
parameterIndex,
actual,
expect,
})}`);
}
}
});
mockIndex++;
if ('error' in mock) {
throw mock.error;
}
if ('returnSelf' in mock) {
return object;
}
if ('return' in mock) {
return mock.return;
}
};
},
});
return object;
};
export const useObjectMock = (mocks) => {
return [createObjectMock(mocks), mocks];
};