strong-mock
Version:
Type safe mocking library for TypeScript
1,192 lines (1,139 loc) • 35 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
It: () => it_exports,
UnexpectedProperty: () => UnexpectedProperty,
mock: () => mock,
reset: () => reset,
resetAll: () => resetAll,
setDefaults: () => setDefaults,
verify: () => verify,
verifyAll: () => verifyAll,
when: () => when
});
module.exports = __toCommonJS(index_exports);
// src/errors/unexpected-access.ts
var import_jest_matcher_utils2 = require("jest-matcher-utils");
// src/print.ts
var import_jest_matcher_utils = require("jest-matcher-utils");
var import_cloneDeepWith = __toESM(require("lodash/cloneDeepWith.js"), 1);
// src/expectation/expectation.ts
var ApplyProp = Symbol("apply");
// src/matchers/matcher.ts
var MATCHER_SYMBOL = Symbol("matcher");
function isMatcher(f) {
return !!(f && f[MATCHER_SYMBOL]);
}
var getMatcherDiffs = (matchers, args) => {
const matcherDiffs = matchers.map((matcher, i) => matcher.getDiff(args[i]));
const actual = matcherDiffs.map((d) => d.actual);
const expected = matcherDiffs.map((d) => d.expected);
return { actual, expected };
};
var matches = (predicate, options) => {
var _a, _b;
const toString = (_a = options == null ? void 0 : options.toString) != null ? _a : () => `Matcher(${predicate.toString()})`;
const getDiff = (_b = options == null ? void 0 : options.getDiff) != null ? _b : (actual) => ({
actual,
expected: toString()
});
const matcher = {
[MATCHER_SYMBOL]: true,
matches: (actual) => predicate(actual),
toString,
getDiff: (actual) => {
if (predicate(actual)) {
return {
actual,
expected: actual
};
}
return getDiff(actual);
}
};
return matcher;
};
// src/print.ts
var printProperty = (property) => {
if (property === ApplyProp) {
return "";
}
if (typeof property === "symbol") {
return `[${property.toString()}]`;
}
return `.${property}`;
};
var printValue = (arg) => {
if (isMatcher(arg)) {
return arg.toString();
}
return (0, import_jest_matcher_utils.stringify)(arg);
};
var deepPrint = (value) => (0, import_cloneDeepWith.default)(value, (value2) => {
if (isMatcher(value2)) {
return value2.toString();
}
return void 0;
});
var printArgs = (args) => args.map((arg) => printValue(arg)).join(", ");
var printCall = (mockName, property, args) => {
const prettyProperty = printProperty(property);
if (args) {
const prettyArgs = printArgs(args);
return `${mockName}${(0, import_jest_matcher_utils.RECEIVED_COLOR)(`${prettyProperty}(${prettyArgs})`)}`;
}
return `${mockName}${(0, import_jest_matcher_utils.RECEIVED_COLOR)(`${prettyProperty}`)}`;
};
var printReturns = ({ isError, isPromise, value }, min, max) => {
let thenPrefix = "";
if (isPromise) {
if (isError) {
thenPrefix += "thenReject";
} else {
thenPrefix += "thenResolve";
}
} else if (isError) {
thenPrefix += "thenThrow";
} else {
thenPrefix += "thenReturn";
}
return `.${thenPrefix}(${(0, import_jest_matcher_utils.RECEIVED_COLOR)(
printValue(value)
)}).between(${min}, ${max})`;
};
var printWhen = (mockName, property, args) => {
const prettyProperty = printProperty(property);
if (args) {
return `when(() => ${mockName}${(0, import_jest_matcher_utils.EXPECTED_COLOR)(
`${prettyProperty}(${printArgs(args)})`
)})`;
}
return `when(() => ${mockName}${(0, import_jest_matcher_utils.EXPECTED_COLOR)(`${printProperty(property)}`)})`;
};
var printExpectation = (mockName, property, args, returnValue, min, max) => `${printWhen(mockName, property, args)}${printReturns(returnValue, min, max)}`;
var printRemainingExpectations = (expectations) => expectations.length ? `Remaining unmet expectations:
- ${expectations.map((e) => e.toString()).join("\n - ")}` : "There are no remaining unmet expectations.";
// src/errors/unexpected-access.ts
var UnexpectedAccess = class extends Error {
constructor(mockName, property, expectations) {
super(
(0, import_jest_matcher_utils2.DIM_COLOR)(`Didn't expect ${printCall(mockName, property)} to be accessed.
If you expect this property to be accessed then you should
set an expectation for it with when().
${printRemainingExpectations(expectations)}`)
);
}
};
// src/errors/unexpected-call.ts
var import_jest_matcher_utils4 = require("jest-matcher-utils");
// src/errors/diff.ts
var import_jest_diff = require("jest-diff");
var import_jest_matcher_utils3 = require("jest-matcher-utils");
var noColor = (s) => s;
var printArgsDiff = (expected, actual) => {
const diff = (0, import_jest_diff.diff)(expected, actual, {
omitAnnotationLines: true,
aColor: noColor,
bColor: noColor,
changeColor: noColor,
commonColor: noColor,
patchColor: noColor
});
if (!diff) {
return "";
}
const diffLines = diff.split("\n");
let relevantDiffLines;
if (!expected.length) {
relevantDiffLines = diffLines.slice(2, -1);
} else if (!actual.length) {
relevantDiffLines = diffLines.slice(1, -2);
} else {
relevantDiffLines = diffLines.slice(1, -1);
}
const lastLine = relevantDiffLines[relevantDiffLines.length - 1].slice(0, -1);
const coloredDiffLines = [...relevantDiffLines.slice(0, -1), lastLine].map(
(line) => {
const first = line.charAt(0);
switch (first) {
case "-":
return (0, import_jest_matcher_utils3.EXPECTED_COLOR)(line);
case "+":
return (0, import_jest_matcher_utils3.RECEIVED_COLOR)(line);
default:
return line;
}
}
);
return coloredDiffLines.join("\n");
};
var printExpectationDiff = (e, args) => {
var _a;
if (!((_a = e.args) == null ? void 0 : _a.length)) {
return "";
}
const { actual, expected } = getMatcherDiffs(e.args, args);
return printArgsDiff(expected, actual);
};
var printDiffForAllExpectations = (expectations, actual) => expectations.map((e) => {
const diff = printExpectationDiff(e, actual);
if (diff) {
return `${e.toString()}
${(0, import_jest_matcher_utils3.EXPECTED_COLOR)("- Expected")}
${(0, import_jest_matcher_utils3.RECEIVED_COLOR)("+ Received")}
${diff}`;
}
return void 0;
}).filter((x) => x).join("\n\n");
// src/errors/unexpected-call.ts
var UnexpectedCall = class extends Error {
constructor(mockName, property, args, expectations) {
var _a;
const header = `Didn't expect ${printCall(mockName, property, args)} to be called.`;
const propertyExpectations = expectations.filter(
(e) => e.property === property
);
if (propertyExpectations.length) {
super(
(0, import_jest_matcher_utils4.DIM_COLOR)(`${header}
Remaining expectations:
${printDiffForAllExpectations(propertyExpectations, args)}`)
);
if (propertyExpectations.length === 1 && ((_a = propertyExpectations[0].args) == null ? void 0 : _a.length)) {
const { actual, expected } = getMatcherDiffs(
propertyExpectations[0].args,
args
);
this.actual = actual;
this.expected = expected;
this.matcherResult = {
actual,
expected
};
}
} else {
super(
(0, import_jest_matcher_utils4.DIM_COLOR)(`${header}
No remaining expectations.`)
);
}
}
};
// src/mock/options.ts
var UnexpectedProperty = /* @__PURE__ */ ((UnexpectedProperty2) => {
UnexpectedProperty2[UnexpectedProperty2["THROW"] = 0] = "THROW";
UnexpectedProperty2[UnexpectedProperty2["CALL_THROW"] = 1] = "CALL_THROW";
return UnexpectedProperty2;
})(UnexpectedProperty || {});
// src/expectation/repository/return-value.ts
var unboxReturnValue = ({
isError,
isPromise,
value
}) => {
if (isError) {
if (value instanceof Error) {
if (isPromise) {
return Promise.reject(value);
}
throw value;
}
if (isPromise) {
return Promise.reject(new Error(value));
}
throw new Error(value);
}
if (isPromise) {
return Promise.resolve(value);
}
return value;
};
// src/expectation/repository/flexible-repository.ts
var FlexibleRepository = class {
constructor(mockName, unexpectedProperty = 0 /* THROW */) {
this.mockName = mockName;
this.unexpectedProperty = unexpectedProperty;
this.expectations = /* @__PURE__ */ new Map();
this.expectedCallStats = /* @__PURE__ */ new Map();
this.unexpectedCallStats = /* @__PURE__ */ new Map();
this.apply = (args) => this.get(ApplyProp)(...args);
this.handlePropertyWithMatchingExpectations = (property, expectations) => {
if (property !== ApplyProp) {
this.recordExpected(property, void 0);
}
const propertyExpectation = expectations.find(
(e) => e.expectation.matches(void 0)
);
if (propertyExpectation) {
this.countAndConsume(propertyExpectation);
return unboxReturnValue(propertyExpectation.expectation.returnValue);
}
return (...args) => {
const callExpectation = expectations.find(
(e) => e.expectation.matches(args)
);
if (callExpectation) {
this.recordExpected(property, args);
this.countAndConsume(callExpectation);
return unboxReturnValue(callExpectation.expectation.returnValue);
}
return this.getValueForUnexpectedCall(property, args);
};
};
this.handlePropertyWithNoExpectations = (property) => {
switch (property) {
case "toString":
return () => this.mockName;
case "@@toStringTag":
case Symbol.toStringTag:
case "name":
return this.mockName;
// Promise.resolve() tries to see if it's a "thenable".
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#thenables
case "then":
return void 0;
// pretty-format
case "$$typeof":
case "constructor":
case "@@__IMMUTABLE_ITERABLE__@@":
case "@@__IMMUTABLE_RECORD__@@":
return null;
case MATCHER_SYMBOL:
return false;
case ApplyProp:
return (...args) => this.getValueForUnexpectedCall(property, args);
default:
return this.getValueForUnexpectedAccess(property);
}
};
}
add(expectation) {
const { property } = expectation;
const expectations = this.expectations.get(property) || [];
this.expectations.set(property, [
...expectations,
{
expectation,
matchCount: 0
}
]);
}
clear() {
this.expectations.clear();
this.expectedCallStats.clear();
this.unexpectedCallStats.clear();
}
// TODO: this returns any, but the interface returns unknown
// unknown causes errors in apply tests, and any causes bugs in bootstrapped SM
get(property) {
const expectations = this.expectations.get(property);
if (expectations && expectations.length) {
return this.handlePropertyWithMatchingExpectations(
property,
expectations
);
}
return this.handlePropertyWithNoExpectations(property);
}
getAllProperties() {
return Array.from(this.expectations.keys());
}
getCallStats() {
return {
expected: this.expectedCallStats,
unexpected: this.unexpectedCallStats
};
}
getUnmet() {
return [].concat(
...Array.from(this.expectations.values()).map(
(expectations) => expectations.filter((e) => e.expectation.min > e.matchCount).map((e) => e.expectation)
)
);
}
recordExpected(property, args) {
const calls = this.expectedCallStats.get(property) || [];
this.expectedCallStats.set(property, [...calls, { arguments: args }]);
}
recordUnexpected(property, args) {
const calls = this.unexpectedCallStats.get(property) || [];
this.unexpectedCallStats.set(property, [...calls, { arguments: args }]);
}
countAndConsume(expectation) {
expectation.matchCount++;
this.consumeExpectation(expectation);
}
consumeExpectation(expectation) {
const { property, max } = expectation.expectation;
const expectations = this.expectations.get(property);
if (expectation.matchCount === max) {
this.expectations.set(
property,
expectations.filter((e) => e !== expectation)
);
}
}
getValueForUnexpectedCall(property, args) {
this.recordUnexpected(property, args);
throw new UnexpectedCall(this.mockName, property, args, this.getUnmet());
}
getValueForUnexpectedAccess(property) {
if (this.unexpectedProperty === 0 /* THROW */) {
this.recordUnexpected(property, void 0);
throw new UnexpectedAccess(this.mockName, property, this.getUnmet());
}
return (...args) => {
this.recordUnexpected(property, args);
throw new UnexpectedCall(this.mockName, property, args, this.getUnmet());
};
}
};
// src/expectation/strong-expectation.ts
var StrongExpectation = class {
constructor(mockName, property, args, returnValue, exactParams = false) {
this.mockName = mockName;
this.property = property;
this.args = args;
this.returnValue = returnValue;
this.exactParams = exactParams;
this.matched = 0;
this.min = 1;
this.max = 1;
}
setInvocationCount(min, max = 1) {
this.min = min;
this.max = max;
}
matches(args) {
if (!this.matchesArgs(args)) {
return false;
}
this.matched++;
return this.max === 0 || this.matched <= this.max;
}
isUnmet() {
return this.matched < this.min;
}
matchesArgs(received) {
if (this.args === void 0) {
return !received;
}
if (!received) {
return false;
}
if (this.exactParams) {
if (this.args.length !== received.length) {
return false;
}
}
return this.args.every((arg, i) => arg.matches(received[i]));
}
toString() {
return printExpectation(
this.mockName,
this.property,
this.args,
this.returnValue,
this.min,
this.max
);
}
};
// src/errors/api.ts
var UnfinishedExpectation = class extends Error {
constructor(mockName, property, args) {
super(`There is an unfinished pending expectation:
${printWhen(mockName, property, args)}
You should finish it by setting a return value with e.g. thenReturns(),
even if that value is undefined.`);
}
};
var MissingWhen = class extends Error {
constructor() {
super(`You tried setting a return value without an expectation.
Every call to set a return value must be preceded by an expectation.`);
}
};
var NotAMock = class extends Error {
constructor() {
super(`We couldn't find the mock.
Make sure you're passing in an actual mock.`);
}
};
var NestedWhen = class extends Error {
constructor(parentProp, childProp) {
const snippet = `
const parentMock = mock<T1>();
const childMock = mock<T2>();
when(() => childMock${printProperty(childProp)}).thenReturn(...);
when(() => parentMock${printProperty(parentProp)}).thenReturn(childMock)
`;
super(
`Setting an expectation on a nested property is not supported.
You can return an object directly when the first property is accessed,
or you can even return a separate mock:
${snippet}`
);
}
};
// src/when/expectation-builder.ts
var ExpectationBuilderWithFactory = class {
constructor(createExpectation, concreteMatcher, mockName, exactParams) {
this.createExpectation = createExpectation;
this.concreteMatcher = concreteMatcher;
this.mockName = mockName;
this.exactParams = exactParams;
}
setProperty(value) {
if (this.property) {
throw new UnfinishedExpectation(this.mockName, this.property, this.args);
}
this.property = value;
}
setArgs(value) {
this.args = value;
}
finish(returnValue) {
if (!this.property) {
throw new MissingWhen();
}
const expectation = this.createExpectation(
this.mockName,
this.property,
this.args,
returnValue,
this.concreteMatcher,
this.exactParams
);
this.property = void 0;
this.args = void 0;
return expectation;
}
};
// src/matchers/deep-equals.ts
var import_cloneDeep = __toESM(require("lodash/cloneDeep.js"), 1);
var import_cloneDeepWith2 = __toESM(require("lodash/cloneDeepWith.js"), 1);
var import_isEqualWith = __toESM(require("lodash/isEqualWith.js"), 1);
var import_isMap = __toESM(require("lodash/isMap.js"), 1);
var import_isObjectLike = __toESM(require("lodash/isObjectLike.js"), 1);
var import_omitBy = __toESM(require("lodash/omitBy.js"), 1);
var removeUndefined = (object) => {
if (Array.isArray(object)) {
return object.map((x) => removeUndefined(x));
}
if (!(0, import_isObjectLike.default)(object)) {
return object;
}
return (0, import_omitBy.default)(object, (value) => value === void 0);
};
var getKey = (key, object) => {
if (key === void 0) {
return object;
}
return (0, import_isMap.default)(object) ? object.get(key) : object[key];
};
var deepEquals = (expected, {
strict = true
} = {}) => matches(
(actual) => (0, import_isEqualWith.default)(
strict ? actual : removeUndefined(actual),
strict ? expected : removeUndefined(expected),
(actualValue, expectedValue) => {
if (isMatcher(expectedValue)) {
return expectedValue.matches(actualValue);
}
return void 0;
}
),
{
toString: () => printValue(deepPrint(expected)),
getDiff: (actual) => {
let actualResult = (0, import_cloneDeep.default)(actual);
const expectedResult = (0, import_cloneDeepWith2.default)(expected, (expectedValue, key) => {
const actualValue = getKey(key, actualResult);
if (isMatcher(expectedValue)) {
if (expectedValue.matches(actualValue)) {
return actualValue;
}
const result = expectedValue.getDiff(actualValue);
if (key !== void 0) {
if ((0, import_isMap.default)(actualResult)) {
actualResult.set(key, result.actual);
} else {
actualResult[key] = result.actual;
}
} else {
actualResult = result.actual;
}
return result.expected;
}
return void 0;
});
return {
actual: actualResult,
expected: expectedResult
};
}
}
);
// src/mock/defaults.ts
var defaults = {
name: "mock",
concreteMatcher: deepEquals,
unexpectedProperty: 1 /* CALL_THROW */,
exactParams: false
};
var currentDefaults = defaults;
var setDefaults = (newDefaults) => {
currentDefaults = __spreadValues(__spreadValues({}, defaults), newDefaults);
};
// src/mock/map.ts
var activeMock;
var setActiveMock = (mock2) => {
activeMock = mock2;
};
var getActiveMock = () => activeMock;
var mockMap = /* @__PURE__ */ new Map();
var getMockState = (mock2) => {
if (mockMap.has(mock2)) {
return mockMap.get(mock2);
}
throw new NotAMock();
};
var setMockState = (mock2, state) => {
mockMap.set(mock2, state);
};
var getAllMocks = () => Array.from(mockMap.entries());
// src/mock/mode.ts
var currentMode = 1 /* CALL */;
var setMode = (mode) => {
currentMode = mode;
};
var getMode = () => currentMode;
// src/proxy.ts
var createProxy = (traps) => (
// The Proxy target MUST be a function, otherwise we can't use the `apply` trap:
// https://262.ecma-international.org/6.0/#sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist
new Proxy(
/* c8 ignore next */
() => {
},
{
get: (target, prop) => {
if (prop === "bind") {
return (thisArg, ...args) => (...moreArgs) => traps.apply([...args, ...moreArgs]);
}
if (prop === "apply") {
return (thisArg, args) => traps.apply(args || []);
}
if (prop === "call") {
return (thisArg, ...args) => traps.apply(args);
}
return traps.property(prop);
},
apply: (target, thisArg, args) => traps.apply(args),
ownKeys: () => traps.ownKeys(),
getOwnPropertyDescriptor(target, prop) {
const keys = traps.ownKeys();
if (keys.includes(prop)) {
return {
configurable: true,
enumerable: true
};
}
return void 0;
}
}
)
);
// src/mock/stub.ts
var createStub = (repo, builder, getCurrentMode) => {
const stub = createProxy({
property: (property) => {
if (getCurrentMode() === 1 /* CALL */) {
return repo.get(property);
}
setActiveMock(stub);
builder.setProperty(property);
return createProxy({
property: (childProp) => {
throw new NestedWhen(property, childProp);
},
apply: (args) => {
builder.setArgs(args);
},
ownKeys: () => {
throw new Error("Spreading during an expectation is not supported.");
}
});
},
apply: (args) => {
if (getCurrentMode() === 1 /* CALL */) {
return repo.apply(args);
}
setActiveMock(stub);
builder.setProperty(ApplyProp);
builder.setArgs(args);
return void 0;
},
ownKeys: () => {
if (getCurrentMode() === 1 /* CALL */) {
return repo.getAllProperties();
}
throw new Error("Spreading during an expectation is not supported.");
}
});
return stub;
};
// src/mock/mock.ts
var strongExpectationFactory = (name, property, args, returnValue, concreteMatcher, exactParams) => new StrongExpectation(
name,
property,
args == null ? void 0 : args.map((arg) => isMatcher(arg) ? arg : concreteMatcher(arg)),
returnValue,
exactParams
);
var mock = ({
name,
unexpectedProperty,
concreteMatcher,
exactParams
} = {}) => {
const options = {
name: name != null ? name : currentDefaults.name,
unexpectedProperty: unexpectedProperty != null ? unexpectedProperty : currentDefaults.unexpectedProperty,
concreteMatcher: concreteMatcher != null ? concreteMatcher : currentDefaults.concreteMatcher,
exactParams: exactParams != null ? exactParams : currentDefaults.exactParams
};
const repository = new FlexibleRepository(
options.name,
options.unexpectedProperty
);
const builder = new ExpectationBuilderWithFactory(
strongExpectationFactory,
options.concreteMatcher,
options.name,
options.exactParams
);
const stub = createStub(repository, builder, getMode);
setMockState(stub, {
repository,
builder,
options
});
return stub;
};
// src/return/invocation-count.ts
var createInvocationCount = (expectation) => ({
between(min, max) {
expectation.setInvocationCount(min, max);
},
/* c8 ignore next 3 */
times(exact) {
expectation.setInvocationCount(exact, exact);
},
/* c8 ignore next 3 */
anyTimes() {
expectation.setInvocationCount(0, 0);
},
/* c8 ignore next 3 */
atLeast(min) {
expectation.setInvocationCount(min, Infinity);
},
/* c8 ignore next 3 */
atMost(max) {
expectation.setInvocationCount(0, max);
},
/* c8 ignore next 3 */
once() {
expectation.setInvocationCount(1, 1);
},
/* c8 ignore next 3 */
twice() {
expectation.setInvocationCount(2, 2);
}
});
// src/return/returns.ts
var finishExpectation = (returnValue, builder, repo) => {
const finishedExpectation = builder.finish(returnValue);
repo.add(finishedExpectation);
return createInvocationCount(finishedExpectation);
};
var getError = (errorOrMessage) => {
if (typeof errorOrMessage === "string") {
return new Error(errorOrMessage);
}
if (errorOrMessage instanceof Error) {
return errorOrMessage;
}
return new Error();
};
var createReturns = (builder, repository) => ({
thenReturn: (returnValue) => finishExpectation(
// This will handle both thenReturn(23) and thenReturn(Promise.resolve(3)).
{ value: returnValue, isError: false, isPromise: false },
builder,
repository
),
thenThrow: (errorOrMessage) => finishExpectation(
{ value: getError(errorOrMessage), isError: true, isPromise: false },
builder,
repository
),
thenResolve: (promiseValue) => finishExpectation(
{
value: promiseValue,
isError: false,
isPromise: true
},
builder,
repository
),
thenReject: (errorOrMessage) => finishExpectation(
{
value: getError(errorOrMessage),
isError: true,
isPromise: true
},
builder,
repository
)
});
// src/when/when.ts
var when = (expectation) => {
setMode(0 /* EXPECT */);
expectation();
setMode(1 /* CALL */);
const { builder, repository } = getMockState(getActiveMock());
return createReturns(builder, repository);
};
// src/verify/reset.ts
var reset = (mock2) => {
getMockState(mock2).repository.clear();
};
var resetAll = () => {
getAllMocks().forEach(([mock2]) => {
reset(mock2);
});
};
// src/errors/verify.ts
var import_jest_matcher_utils5 = require("jest-matcher-utils");
var UnmetExpectations = class extends Error {
constructor(expectations) {
super(
(0, import_jest_matcher_utils5.DIM_COLOR)(`There are unmet expectations:
- ${expectations.map((e) => e.toString()).join("\n - ")}`)
);
}
};
var mergeCalls = (callMap) => new Map(
Array.from(callMap.entries()).map(([property, calls]) => {
const hasMethodCalls = calls.some((call) => call.arguments);
const hasPropertyAccesses = calls.some((call) => !call.arguments);
if (hasMethodCalls && hasPropertyAccesses) {
return [property, calls.filter((call) => call.arguments)];
}
return [property, calls];
})
);
var UnexpectedCalls = class extends Error {
constructor(mockName, unexpectedCalls, expectations) {
const printedCalls = Array.from(mergeCalls(unexpectedCalls).entries()).map(
([property, calls]) => calls.map((call) => printCall(mockName, property, call.arguments)).join("\n - ")
).join("\n - ");
super(
(0, import_jest_matcher_utils5.DIM_COLOR)(`The following calls were unexpected:
- ${printedCalls}
${printRemainingExpectations(expectations)}`)
);
}
};
// src/verify/verify.ts
var verifyRepo = (repository) => {
const unmetExpectations = repository.getUnmet();
if (unmetExpectations.length) {
throw new UnmetExpectations(unmetExpectations);
}
const callStats = repository.getCallStats();
if (callStats.unexpected.size) {
throw new UnexpectedCalls(
repository.mockName,
callStats.unexpected,
unmetExpectations
);
}
};
var verify = (mock2) => {
const { repository } = getMockState(mock2);
verifyRepo(repository);
};
var verifyAll = () => {
getAllMocks().forEach(([mock2]) => {
verify(mock2);
});
};
// src/matchers/it.ts
var it_exports = {};
__export(it_exports, {
containsObject: () => containsObject,
deepEquals: () => deepEquals,
is: () => is,
isAny: () => isAny,
isArray: () => isArray,
isNumber: () => isNumber,
isPlainObject: () => isPlainObject,
isString: () => isString,
matches: () => matches,
willCapture: () => willCapture
});
// src/matchers/is.ts
var is = (expected) => matches((actual) => Object.is(actual, expected), {
toString: () => `${printValue(expected)}`,
getDiff: (actual) => ({ actual, expected })
});
// src/matchers/is-any.ts
var isAny = () => matches(() => true, {
toString: () => "Matcher<any>"
});
// src/matchers/is-array.ts
var isArray = (containing) => matches(
(actual) => {
if (!Array.isArray(actual)) {
return false;
}
if (!containing) {
return true;
}
return containing.every(
(x) => actual.find((y) => {
if (isMatcher(x)) {
return x.matches(y);
}
return deepEquals(x).matches(y);
}) !== void 0
);
},
{
toString: () => containing ? `array([${containing.map((v) => printValue(v)).join(", ")}])` : "array",
getDiff: (actual) => {
if (containing) {
return {
actual,
expected: `Matcher<array>([${containing.map((value) => {
if (isMatcher(value)) {
return value.toString();
}
return value;
}).join(", ")}])`
};
}
return {
actual: `${printValue(actual)} (${typeof actual})`,
expected: "Matcher<array>"
};
}
}
);
// src/matchers/is-number.ts
var isNumber = () => matches((actual) => typeof actual === "number" && !Number.isNaN(actual), {
toString: () => "Matcher<number>",
getDiff: (actual) => ({
actual: `${printValue(actual)} (${typeof actual})`,
expected: "Matcher<number>"
})
});
// src/matchers/is-plain-object.ts
var import_isObjectLike2 = __toESM(require("lodash/isObjectLike.js"), 1);
var import_isPlainObject = __toESM(require("lodash/isPlainObject.js"), 1);
var isPlainObject = () => matches((actual) => (0, import_isPlainObject.default)(actual), {
toString: () => "Matcher<object>",
getDiff: (actual) => {
const type = (0, import_isObjectLike2.default)(actual) ? "object-like" : typeof actual;
return {
actual: `${printValue(actual)} (${type})`,
expected: "Matcher<object>"
};
}
});
// src/matchers/contains-object.ts
var import_isPlainObject2 = __toESM(require("lodash/isPlainObject.js"), 1);
var looksLikeObject = (value) => (0, import_isPlainObject2.default)(value);
var getExpectedObjectDiff = (actual, expected) => Object.fromEntries(
getKeys(expected).map((key) => {
const expectedValue = getKey2(expected, key);
const actualValue = getKey2(actual, key);
if (isMatcher(expectedValue)) {
return [key, expectedValue.getDiff(actualValue).expected];
}
if (looksLikeObject(expectedValue)) {
return [key, getExpectedObjectDiff(actualValue, expectedValue)];
}
return [key, expectedValue];
})
);
var getActualObjectDiff = (actual, expected) => {
const actualKeys = getKeys(actual);
const expectedKeys = new Set(getKeys(expected));
const commonKeys = actualKeys.filter((key) => expectedKeys.has(key));
if (!commonKeys.length) {
return actual;
}
return Object.fromEntries(
commonKeys.map((key) => {
const expectedValue = getKey2(expected, key);
const actualValue = getKey2(actual, key);
if (isMatcher(expectedValue)) {
return [key, expectedValue.getDiff(actualValue).actual];
}
if (looksLikeObject(expectedValue)) {
return [key, getActualObjectDiff(actualValue, expectedValue)];
}
return [key, actualValue];
})
);
};
var getKeys = (value) => {
if (typeof value === "object" && value !== null) {
return Reflect.ownKeys(value);
}
return [];
};
var getKey2 = (value, key) => (
// @ts-expect-error because we're fine with a runtime undefined value
value == null ? void 0 : value[key]
);
var isMatch = (actual, expected) => {
const actualKeys = getKeys(actual);
const expectedKeys = getKeys(expected);
if (!isArray(expectedKeys).matches(actualKeys)) {
return false;
}
return expectedKeys.every((key) => {
const expectedValue = getKey2(expected, key);
const actualValue = getKey2(actual, key);
if (isMatcher(expectedValue)) {
return expectedValue.matches(actualValue);
}
if (looksLikeObject(expectedValue)) {
return isMatch(actualValue, expectedValue);
}
return deepEquals(expectedValue).matches(actualValue);
});
};
var containsObject = (partial) => matches((actual) => isMatch(actual, partial), {
toString: () => `Matcher<object>(${printValue(deepPrint(partial))})`,
getDiff: (actual) => ({
actual: getActualObjectDiff(actual, partial),
expected: getExpectedObjectDiff(actual, partial)
})
});
// src/matchers/is-string.ts
var isString = (matching) => matches(
(actual) => {
if (typeof actual !== "string") {
return false;
}
if (!matching) {
return true;
}
if (typeof matching === "string") {
return actual.indexOf(matching) !== -1;
}
return matching.test(actual);
},
{
toString: () => {
if (matching) {
return `Matcher<string>(${matching})`;
}
return "Matcher<string>";
},
getDiff: (actual) => {
if (matching) {
return {
expected: `Matcher<string>(${matching})`,
actual
};
}
return {
expected: "Matcher<string>",
actual: `${actual} (${typeof actual})`
};
}
}
);
// src/matchers/will-capture.ts
var willCapture = (name) => {
let capturedValue;
const matcher = {
[MATCHER_SYMBOL]: true,
matches: (actual) => {
capturedValue = actual;
return true;
},
toString: () => name ? `Capture(${name})` : "Capture",
getDiff: (actual) => ({
actual,
expected: actual
}),
get value() {
return capturedValue;
}
};
return matcher;
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
It,
UnexpectedProperty,
mock,
reset,
resetAll,
setDefaults,
verify,
verifyAll,
when
});
/* c8 ignore next 3 this is not expected in practice -- @preserve */