std/expect/expect.ts
David Sherret 4a7f38339e
chore(expect): fix future no-slow-type lint errors with expando properties (#4711)
* chore: fix future fast check errors with expando properties

* spelling

---------

Co-authored-by: Asher Gomez <ashersaupingomez@gmail.com>
2024-05-13 08:29:05 +10:00

208 lines
5.4 KiB
TypeScript

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
// Copyright 2019 Allain Lalonde. All rights reserved. ISC License.
import type {
Expected,
ExtendMatchResult,
Matcher,
MatcherContext,
MatcherKey,
Matchers,
} from "./_types.ts";
import { AssertionError } from "@std/assert/assertion-error";
import {
addCustomEqualityTesters,
getCustomEqualityTesters,
} from "./_custom_equality_tester.ts";
import { equal } from "./_equal.ts";
import { getExtendMatchers, setExtendMatchers } from "./_extend.ts";
import {
toBe,
toBeCloseTo,
toBeDefined,
toBeFalsy,
toBeGreaterThan,
toBeGreaterThanOrEqual,
toBeInstanceOf,
toBeLessThan,
toBeLessThanOrEqual,
toBeNaN,
toBeNull,
toBeTruthy,
toBeUndefined,
toContain,
toContainEqual,
toEqual,
toHaveBeenCalled,
toHaveBeenCalledTimes,
toHaveBeenCalledWith,
toHaveBeenLastCalledWith,
toHaveBeenNthCalledWith,
toHaveLastReturnedWith,
toHaveLength,
toHaveNthReturnedWith,
toHaveProperty,
toHaveReturned,
toHaveReturnedTimes,
toHaveReturnedWith,
toMatch,
toMatchObject,
toStrictEqual,
toThrow,
} from "./_matchers.ts";
import { addSerializer } from "./_snapshot_serializer.ts";
import { isPromiseLike } from "./_utils.ts";
import * as asymmetricMatchers from "./_asymmetric_matchers.ts";
const matchers: Record<MatcherKey, Matcher> = {
lastCalledWith: toHaveBeenLastCalledWith,
lastReturnedWith: toHaveLastReturnedWith,
nthCalledWith: toHaveBeenNthCalledWith,
nthReturnedWith: toHaveNthReturnedWith,
toBeCalled: toHaveBeenCalled,
toBeCalledTimes: toHaveBeenCalledTimes,
toBeCalledWith: toHaveBeenCalledWith,
toBeCloseTo,
toBeDefined,
toBeFalsy,
toBeGreaterThanOrEqual,
toBeGreaterThan,
toBeInstanceOf,
toBeLessThanOrEqual,
toBeLessThan,
toBeNaN,
toBeNull,
toBeTruthy,
toBeUndefined,
toBe,
toContainEqual,
toContain,
toEqual,
toHaveBeenCalledTimes,
toHaveBeenCalledWith,
toHaveBeenCalled,
toHaveBeenLastCalledWith,
toHaveBeenNthCalledWith,
toHaveLength,
toHaveLastReturnedWith,
toHaveNthReturnedWith,
toHaveProperty,
toHaveReturnedTimes,
toHaveReturnedWith,
toHaveReturned,
toMatchObject,
toMatch,
toReturn: toHaveReturned,
toReturnTimes: toHaveReturnedTimes,
toReturnWith: toHaveReturnedWith,
toStrictEqual,
toThrow,
};
export function expect(value: unknown, customMessage?: string): Expected {
let isNot = false;
let isPromised = false;
const self: Expected = new Proxy<Expected>(
<Expected> {},
{
get(_, name) {
if (name === "not") {
isNot = !isNot;
return self;
}
if (name === "resolves") {
if (!isPromiseLike(value)) {
throw new AssertionError("expected value must be Promiselike");
}
isPromised = true;
return self;
}
if (name === "rejects") {
if (!isPromiseLike(value)) {
throw new AssertionError("expected value must be a PromiseLike");
}
value = value.then(
(value) => {
throw new AssertionError(
`Promise did not reject. resolved to ${value}`,
);
},
(err) => err,
);
isPromised = true;
return self;
}
const extendMatchers: Matchers = getExtendMatchers();
const allMatchers = {
...extendMatchers,
...matchers,
};
const matcher = allMatchers[name as MatcherKey] as Matcher;
if (!matcher) {
throw new TypeError(
typeof name === "string"
? `matcher not found: ${name}`
: "matcher not found",
);
}
return (...args: unknown[]) => {
function applyMatcher(value: unknown, args: unknown[]) {
const context: MatcherContext = {
value,
equal,
isNot: false,
customMessage,
customTesters: getCustomEqualityTesters(),
};
if (isNot) {
context.isNot = true;
}
if (name in extendMatchers) {
const result = matcher(context, ...args) as ExtendMatchResult;
if (context.isNot) {
if (result.pass) {
throw new AssertionError(result.message());
}
} else if (!result.pass) {
throw new AssertionError(result.message());
}
} else {
matcher(context, ...args);
}
}
return isPromised
? (value as Promise<unknown>).then((value: unknown) =>
applyMatcher(value, args)
)
: applyMatcher(value, args);
};
},
},
);
return self;
}
expect.addEqualityTesters = addCustomEqualityTesters;
/**
* @deprecated (will be removed in 0.226.0) Use {@linkcode expect.addSnapshotSerializer} instead.
*/
expect.addSnapshotSerializers = addSerializer;
expect.addSnapshotSerializer = addSerializer;
expect.extend = setExtendMatchers;
expect.anything = asymmetricMatchers.anything;
expect.any = asymmetricMatchers.any;
expect.arrayContaining = asymmetricMatchers.arrayContaining;
expect.closeTo = asymmetricMatchers.closeTo;
expect.stringContaining = asymmetricMatchers.stringContaining;
expect.stringMatching = asymmetricMatchers.stringMatching;