declare const MATCHER_SYMBOL: unique symbol;
type MatcherDiffer = (actual: any) => {
    actual: any;
    expected: any;
};
type MatcherOptions = {
    /**
     * Will be called when printing the diff between an expectation and the
     * (mismatching) received arguments.
     *
     * With this function you can pretty print the `actual` and `expected` values
     * according to your matcher's logic.
     *
     * @param actual The actual value received by this matcher, same as the one
     *   in `matches`.
     *
     * @example
     * const neverMatcher = It.matches(() => false, {
     *   getDiff: (actual) => ({ actual, expected: 'never' })
     * });
     *
     * when(() => fn(neverMatcher)).thenReturn(42);
     *
     * fn(42);
     * // - Expected
     * // + Received
     * //
     * //   - 'never'
     * //   + 42
     */
    getDiff: MatcherDiffer;
    /**
     * Will be called when printing arguments for an unexpected or unmet expectation.
     *
     * @example
     * const neverMatcher = It.matches(() => false, {
     *   toString: () => 'never'
     * });
     * when(() => fn(neverMatcher)).thenReturn(42);
     *
     * fn(42);
     * // Unmet expectations:
     * // when(() => fn(never)).thenReturn(42)
     */
    toString: () => string;
};
/**
 * You MUST use {@link It.matches} to create this branded type.
 */
interface Matcher extends MatcherOptions {
    [MATCHER_SYMBOL]: boolean;
    /**
     * Will be called with the received value and should return whether it matches
     * the expectation.
     */
    matches: (actual: any) => boolean;
}
/**
 * This takes the shape of T to satisfy call sites, but strong-mock will only
 * care about the matcher type.
 */
type TypeMatcher<T> = T & Matcher;
/**
 * Create a custom matcher.
 *
 * @param predicate Will receive the actual value and return whether it matches the expectation.
 * @param options
 * @param options.toString An optional function that should return a string that will be
 *   used when the matcher needs to be printed in an error message. By default,
 *   it stringifies `predicate`.
 * @param options.getDiff An optional function that will be called when printing the
 *   diff for a failed expectation. It will only be called if there's a mismatch
 *   between the expected and received values i.e. `predicate(actual)` fails.
 *   By default, the `toString` method will be used to format the expected value,
 *   while the received value will be returned as-is.
 *
 * @example
 * // Create a matcher for positive numbers.
 * const fn = mock<(x: number) => number>();
 * when(() => fn(It.matches(x => x >= 0))).thenReturn(42);
 *
 * fn(2) === 42
 * fn(-1) // throws
 */
declare const matches: <T>(predicate: (actual: T) => boolean, options?: Partial<MatcherOptions>) => TypeMatcher<T>;

type ConcreteMatcher = <T>(expected: T) => Matcher;
declare enum UnexpectedProperty {
    /**
     * Throw an error immediately.
     *
     * @example
     * // Will throw "Didn't expect foo to be accessed".
     * const { foo } = service;
     *
     * // Will throw "Didn't expect foo to be accessed",
     * // without printing the arguments.
     * foo(42);
     */
    THROW = 0,
    /**
     * Return a function that will throw if called. This can be useful if your
     * code destructures a function but never calls it.
     *
     * It will also improve error messages for unexpected calls because arguments
     * will be captured instead of throwing immediately on the property access.
     *
     * The function will be returned even if the property is not supposed to be a
     * function. This could cause weird behavior at runtime, when your code expects
     * e.g. a number and gets a function instead.
     *
     * @example
     * // This will NOT throw.
     * const { foo } = service;
     *
     * // This will NOT throw, and might produce unexpected results.
     * foo > 0
     *
     * // Will throw "Didn't expect foo(42) to be called".
     * foo(42);
     */
    CALL_THROW = 1
}
interface MockOptions {
    /**
     * The name of the mock that will be used in error messages. Defaults to `mock`.
     *
     * This can be useful when you want to easily identify the mock from the test
     * output, especially if you have multiple mocks in the same test.
     *
     * @example
     * const service = mock<Service>({ name: 'Service' });
     * service.foo() // "Didn't expect Service.foo() to be called"
     */
    name?: string;
    /**
     * Controls what should be returned for a property with no expectations.
     *
     * A property with no expectations is a property that has no `when`
     * expectations set on it. It can also be a property that ran out of `when`
     * expectations.
     *
     * The default is to return a function that will throw when called.
     *
     * @example
     * const foo = mock<{ bar: () => number }>();
     * foo.bar() // unexpected property access
     *
     * @example
     * const foo = mock<{ bar: () => number }>();
     * when(() => foo.bar()).thenReturn(42);
     * foo.bar() === 42
     * foo.bar() // unexpected property access
     */
    unexpectedProperty?: UnexpectedProperty;
    /**
     * If `true`, the number of received arguments in a function/method call has to
     * match the number of arguments set in the expectation.
     *
     * If `false`, extra parameters are considered optional and checked by the
     * TypeScript compiler instead.
     *
     * You may want to set this to `true` if you're not using TypeScript,
     * or if you want to be extra strict.
     *
     * @example
     * const fn = mock<(value?: number) => number>({ exactParams: true });
     * when(() => fn()).thenReturn(42);
     *
     * fn(100) // throws with exactParams, returns 42 without
     */
    exactParams?: boolean;
    /**
     * The matcher that will be used when one isn't specified explicitly.
     *
     * The most common use case is replacing the default {@link It.deepEquals}
     * matcher with {@link It.is}, but you can also use {@link It.matches} to
     * create a custom matcher.
     *
     * @param expected The concrete expected value received from the
     *   {@link when} expectation.
     *
     * @example
     * const fn = mock<(value: number[]) => string>({
     *   concreteMatcher: It.is
     * });
     *
     * const expected = [1, 2, 3];
     * when(() => fn(expected).thenReturn('matched');
     *
     * fn([1, 2, 3]); // throws because different array instance
     * fn(expected); // matched
     */
    concreteMatcher?: ConcreteMatcher;
}

type Mock<T> = T;
/**
 * Create a type safe mock.
 *
 * @see {@link when} Set expectations on the mock using `when`.
 *
 * @param options Configure the options for this specific mock, overriding any
 *   defaults that were set with {@link setDefaults}.
 * @param options.name The name of the mock that appears in error messages.
 * @param options.unexpectedProperty Controls what happens when an unexpected
 *   property is accessed.
 * @param options.concreteMatcher The matcher that will be used when one isn't
 *   specified explicitly.
 * @param options.exactParams Controls whether the number of received arguments
 *   has to match the expectation.
 *
 * @example
 * const fn = mock<() => number>();
 *
 * when(() => fn()).thenReturn(23);
 *
 * fn() === 23;
 */
declare const mock: <T>({ name, unexpectedProperty, concreteMatcher, exactParams, }?: MockOptions) => Mock<T>;

type Property = string | symbol;

interface InvocationCount {
    /**
     * Expect a call to be made at least `min` times and at most `max` times.
     */
    between: (min: number, max: number) => void;
    /**
     * Expect a call to be made exactly `exact` times.
     *
     * Shortcut for `between(exact, exact)`.
     */
    times: (exact: number) => void;
    /**
     * Expect a call to be made any number of times, including never.
     *
     * Shortcut for `between(0, Infinity)`.
     */
    anyTimes: () => void;
    /**
     * Expect a call to be made at least `min` times.
     *
     * Shortcut for `between(min, Infinity)`.
     */
    atLeast: (min: number) => void;
    /**
     * Expect a call to be made at most `max` times.
     *
     * Shortcut for `between(0, max)`.
     */
    atMost: (max: number) => void;
    /**
     * Expect a call to be made exactly once.
     *
     * Shortcut for `times(1)`.
     */
    once: () => void;
    /**
     * Expect a call to be made exactly twice.
     *
     * Shortcut for `times(2)`.
     */
    twice: () => void;
}

type PromiseStub<R, P> = {
    /**
     * Set the return value for the current call.
     *
     * @param value This needs to be of the same type as the value returned
     *   by the call inside `when`.
     *
     * @example
     * when(() => fn()).thenReturn(Promise.resolve(23));
     *
     * @example
     * when(() => fn()).thenReturn(Promise.reject({ foo: 'bar' });
     */
    thenReturn: (value: P) => InvocationCount;
    /**
     * Set the return value for the current call.
     *
     * @param promiseValue This needs to be of the same type as the value inside
     *   the promise returned by the `when` callback.
     *
     * @example
     * when(() => fn()).thenResolve('foo');
     */
    thenResolve: (promiseValue: R) => InvocationCount;
    /**
     * Make the current call reject with the given error.
     *
     * @param error An `Error` instance. You can pass just a message, and
     *   it will be wrapped in an `Error` instance. If you want to reject with
     *   a non error then use the {@link thenReturn} method.
     *
     * @example
     * when(() => fn()).thenReject(new Error('oops'));
     */
    thenReject: ((error: Error) => InvocationCount) & ((message: string) => InvocationCount) & (() => InvocationCount);
};
type NonPromiseStub<R> = {
    /**
     * Set the return value for the current call.
     *
     * @param returnValue This needs to be of the same type as the value returned
     *   by the `when` callback.
     */
    thenReturn: (returnValue: R) => InvocationCount;
    /**
     * Make the current call throw the given error.
     *
     * @param error The error instance. If you want to throw a simple `Error`
     *   you can pass just the message.
     */
    thenThrow: ((error: Error) => InvocationCount) & ((message: string) => InvocationCount) & (() => InvocationCount);
};

interface When {
    <R>(expectation: () => Promise<R>): PromiseStub<R, Promise<R>>;
    <R>(expectation: () => R): NonPromiseStub<R>;
}
/**
 * Set an expectation on a mock.
 *
 * The expectation must be finished by setting a return value, even if the value
 * is `undefined`.
 *
 * If a call happens that was not expected, then the mock will throw an error.
 * By default, the call is expected only once. Use the invocation count helpers
 * to expect a call multiple times.
 *
 * @param expectation A callback to set the expectation on your mock. The
 *   callback must return the value from the mock to properly infer types.
 *
 * @see {@link It.deepEquals} All values are wrapped in the default matcher.
 * @see {@link It} for more matchers.
 *
 * @example
 * const fn = mock<() => void>();
 * when(() => fn()).thenReturn(undefined);
 *
 * @example
 * const fn = mock<() => number>();
 * when(() => fn()).thenReturn(42).atMost(3);
 *
 * @example
 * const fn = mock<(x: number) => Promise<number>();
 * when(() => fn(23)).thenResolve(42);
 */
declare const when: When;

/**
 * Remove any remaining expectations on the given mock.
 *
 * @example
 * const fn = mock<() => number>();
 *
 * when(() => fn()).thenReturn(23);
 *
 * reset(fn);
 *
 * fn(); // throws
 */
declare const reset: (mock: Mock<any>) => void;
/**
 * Reset all existing mocks.
 *
 * @see reset
 */
declare const resetAll: () => void;

/**
 * Verify that all expectations on the given mock have been met.
 *
 * @throws Will throw if there are remaining expectations that were set
 * using `when` and that weren't met.
 *
 * @throws Will throw if any unexpected calls happened. Normally those
 * calls throw on their own, but the error might be caught by the code
 * being tested.
 *
 * @example
 * const fn = mock<() => number>();
 *
 * when(() => fn()).thenReturn(23);
 *
 * verify(fn); // throws
 */
declare const verify: <T>(mock: Mock<T>) => void;
/**
 * Verify all existing mocks.
 *
 * @see verify
 */
declare const verifyAll: () => void;

/**
 * Override strong-mock's defaults.
 *
 * @param newDefaults These will be applied to the library defaults. Multiple
 *   calls don't stack e.g. calling this with `{}` will clear any previously
 *   applied defaults.
 */
declare const setDefaults: (newDefaults: MockOptions) => void;

/**
 * Compare values using deep equality.
 *
 * This is the default matcher that's automatically used when no other matcher
 * is specified. You can change it with the `concreteMatcher` option when creating
 * a {@link mock}, or set a new default with {@link setDefaults}.
 *
 * @param expected Supports nested matchers for objects, arrays, and Maps.
 * @param strict By default, this matcher will treat a missing key in an object
 *   and a key with the value `undefined` as not equal. It will also consider
 *   non `Object` instances with different constructors as not equal. Setting
 *   this to `false` will consider the objects in both cases as equal.
 *
 * @see {@link It.containsObject} or {@link It.isArray} for partially matching
 *   objects or arrays respectively.
 * @see {@link It.is} for strict equality.
 *
 * @example
 * const fn = mock<(x: { foo: { bar: number } }) => number>();
 *
 * // deepEquals is the default matcher.
 * when(() => fn({ foo: { bar: 42 } })).thenReturn(1);
 *
 * // With nested matchers:
 * when(() => fn({ foo: { bar: It.isNumber() } })).thenReturn(2);
 */
declare const deepEquals: <T>(expected: T, { strict, }?: {
    strict?: boolean;
}) => TypeMatcher<T>;

/**
 * Compare values using `Object.is`.
 *
 * @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
 *
 * @see It.deepEquals A matcher that uses deep equality.
 */
declare const is: <T = unknown>(expected: T) => TypeMatcher<T>;

/**
 * Match any value, including `undefined` and `null`.
 *
 * @example
 * const fn = mock<(x: number, y: string) => number>();
 * when(() => fn(It.isAny(), It.isAny())).thenReturn(1);
 *
 * fn(23, 'foobar') === 1
 */
declare const isAny: () => TypeMatcher<any>;

/**
 * Match an array.
 *
 * Supports nested matchers.
 *
 * @param containing If given, the matched array has to contain ALL of these
 *   elements in ANY order. Use {@link It.deepEquals} or {@link It.matches} if
 *   you want more control.
 *
 * @example
 * const fn = mock<(arr: number[]) => number>();
 * when(() => fn(It.isArray())).thenReturn(1);
 * when(() => fn(It.isArray([2, 3]))).thenReturn(2);
 *
 * fn({ length: 1, 0: 42 }) // throws
 * fn([]) === 1
 * fn([3, 2, 1]) === 2
 *
 * @example
 * It.isArray([It.isString({ containing: 'foobar' })])
 */
declare const isArray: <T extends unknown[]>(containing?: T) => TypeMatcher<T>;

/**
 * Match any number.
 *
 * @example
 * const fn = mock<(x: number) => number>();
 * when(() => fn(It.isNumber())).returns(42);
 *
 * fn(20.5) === 42
 * fn(NaN) // throws
 */
declare const isNumber: () => TypeMatcher<number>;

type ObjectType$1 = Record<Property, unknown>;
/**
 * Matches any plain object e.g. object literals or objects created with `Object.create()`.
 *
 * Classes, arrays, maps, sets etc. are not considered plain objects.
 * You can use {@link containsObject} or {@link matches} to match those.
 *
 * @example
 * const fn = mock<({ foo: string }) => number>();
 * when(() => fn(It.isPlainObject())).thenReturn(42);
 *
 * fn({ foo: 'bar' }) // returns 42
 */
declare const isPlainObject: <T extends ObjectType$1>() => TypeMatcher<T>;

type ObjectType = Record<Property, unknown>;
type NonEmptyObject<T extends ObjectType> = keyof T extends never ? never : T;
type DeepPartial<T> = T extends ObjectType ? {
    [K in keyof T]?: DeepPartial<T[K]>;
} : T;
/**
 * Check if an object recursively contains the expected properties,
 * i.e. the expected object is a subset of the received object.
 *
 * @param partial A subset of the expected object that will be recursively matched.
 *   Supports nested matchers.
 *   Concrete values will be compared with {@link deepEquals}.
 *
 * @see {@link isPlainObject} if you want to match any plain object.
 *
 * @example
 * const fn = mock<(pos: { x: number, y: number }) => number>();
 * when(() => fn(It.containsObject({ x: 23 }))).returns(42);
 *
 * fn({ x: 23, y: 200 }) // returns 42
 *
 * @example
 * It.containsObject({ foo: It.isString() })
 */
declare const containsObject: <T, K extends DeepPartial<T>>(partial: K extends ObjectType ? NonEmptyObject<K> : never) => TypeMatcher<T>;

/**
 * Match any string.
 *
 * @param matching An optional string or RegExp to match the string against.
 *   If it's a string, a case-sensitive search will be performed.
 *
 * @example
 * const fn = mock<(x: string, y: string) => number>();
 * when(() => fn(It.isString(), It.isString('bar'))).returns(42);
 *
 * fn('foo', 'baz') // throws
 * fn('foo', 'bar') === 42
 */
declare const isString: (matching?: string | RegExp) => TypeMatcher<string>;

/**
 * Matches anything and stores the received value.
 *
 * This should not be needed for most cases, but can be useful if you need
 * access to a complex argument outside the expectation e.g. to test a
 * callback.
 *
 * @param name If given, this name will be printed in error messages.
 *
 * @example
 * const fn = mock<(cb: (value: number) => number) => void>();
 * const matcher = It.willCapture();
 * when(() => fn(matcher)).thenReturn();
 *
 * fn(x => x + 1);
 * matcher.value?.(3) === 4
 */
declare const willCapture: <T = unknown>(name?: string) => TypeMatcher<T> & {
    value: T | undefined;
};

declare const it_containsObject: typeof containsObject;
declare const it_deepEquals: typeof deepEquals;
declare const it_is: typeof is;
declare const it_isAny: typeof isAny;
declare const it_isArray: typeof isArray;
declare const it_isNumber: typeof isNumber;
declare const it_isPlainObject: typeof isPlainObject;
declare const it_isString: typeof isString;
declare const it_matches: typeof matches;
declare const it_willCapture: typeof willCapture;
declare namespace it {
  export { it_containsObject as containsObject, it_deepEquals as deepEquals, it_is as is, it_isAny as isAny, it_isArray as isArray, it_isNumber as isNumber, it_isPlainObject as isPlainObject, it_isString as isString, it_matches as matches, it_willCapture as willCapture };
}

export { it as It, type Matcher, type MatcherOptions, type MockOptions, UnexpectedProperty, mock, reset, resetAll, setDefaults, verify, verifyAll, when };
