diff options
| author | Adam Mathes <adam@adammathes.com> | 2026-02-13 21:34:48 -0800 |
|---|---|---|
| committer | Adam Mathes <adam@adammathes.com> | 2026-02-13 21:34:48 -0800 |
| commit | 76cb9c2a39d477a64824a985ade40507e3bbade1 (patch) | |
| tree | 41e997aa9c6f538d3a136af61dae9424db2005a9 /vanilla/node_modules/@vitest/expect | |
| parent | 819a39a21ac992b1393244a4c283bbb125208c69 (diff) | |
| download | neko-76cb9c2a39d477a64824a985ade40507e3bbade1.tar.gz neko-76cb9c2a39d477a64824a985ade40507e3bbade1.tar.bz2 neko-76cb9c2a39d477a64824a985ade40507e3bbade1.zip | |
feat(vanilla): add testing infrastructure and tests (NK-wjnczv)
Diffstat (limited to 'vanilla/node_modules/@vitest/expect')
| -rw-r--r-- | vanilla/node_modules/@vitest/expect/LICENSE | 21 | ||||
| -rw-r--r-- | vanilla/node_modules/@vitest/expect/README.md | 21 | ||||
| -rw-r--r-- | vanilla/node_modules/@vitest/expect/dist/index.d.ts | 806 | ||||
| -rw-r--r-- | vanilla/node_modules/@vitest/expect/dist/index.js | 1875 | ||||
| -rw-r--r-- | vanilla/node_modules/@vitest/expect/package.json | 46 |
5 files changed, 2769 insertions, 0 deletions
diff --git a/vanilla/node_modules/@vitest/expect/LICENSE b/vanilla/node_modules/@vitest/expect/LICENSE new file mode 100644 index 0000000..0e5771d --- /dev/null +++ b/vanilla/node_modules/@vitest/expect/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021-Present VoidZero Inc. and Vitest contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vanilla/node_modules/@vitest/expect/README.md b/vanilla/node_modules/@vitest/expect/README.md new file mode 100644 index 0000000..4d7143b --- /dev/null +++ b/vanilla/node_modules/@vitest/expect/README.md @@ -0,0 +1,21 @@ +# @vitest/expect + +Jest's expect matchers as a Chai plugin. + +## Usage + +```js +import { + JestAsymmetricMatchers, + JestChaiExpect, + JestExtend, +} from '@vitest/expect' +import * as chai from 'chai' + +// allows using expect.extend instead of chai.use to extend plugins +chai.use(JestExtend) +// adds all jest matchers to expect +chai.use(JestChaiExpect) +// adds asymmetric matchers like stringContaining, objectContaining +chai.use(JestAsymmetricMatchers) +``` diff --git a/vanilla/node_modules/@vitest/expect/dist/index.d.ts b/vanilla/node_modules/@vitest/expect/dist/index.d.ts new file mode 100644 index 0000000..a296d58 --- /dev/null +++ b/vanilla/node_modules/@vitest/expect/dist/index.d.ts @@ -0,0 +1,806 @@ +import { Test } from '@vitest/runner'; +import { MockInstance } from '@vitest/spy'; +import { Constructable } from '@vitest/utils'; +import { Formatter } from 'tinyrainbow'; +import { StandardSchemaV1 } from '@standard-schema/spec'; +import { diff, printDiffOrStringify } from '@vitest/utils/diff'; +export { DiffOptions } from '@vitest/utils/diff'; +import { stringify } from '@vitest/utils/display'; +import * as chai from 'chai'; +export { chai }; + +declare const MATCHERS_OBJECT: unique symbol; +declare const JEST_MATCHERS_OBJECT: unique symbol; +declare const GLOBAL_EXPECT: unique symbol; +declare const ASYMMETRIC_MATCHERS_OBJECT: unique symbol; + +interface AsymmetricMatcherInterface { + asymmetricMatch: (other: unknown, customTesters?: Array<Tester>) => boolean; + toString: () => string; + getExpectedType?: () => string; + toAsymmetricMatcher?: () => string; +} +declare abstract class AsymmetricMatcher< + T, + State extends MatcherState = MatcherState +> implements AsymmetricMatcherInterface { + protected sample: T; + protected inverse: boolean; + $$typeof: symbol; + constructor(sample: T, inverse?: boolean); + protected getMatcherContext(expect?: Chai.ExpectStatic): State; + abstract asymmetricMatch(other: unknown, customTesters?: Array<Tester>): boolean; + abstract toString(): string; + getExpectedType?(): string; + toAsymmetricMatcher?(): string; +} +declare class StringContaining extends AsymmetricMatcher<string> { + constructor(sample: string, inverse?: boolean); + asymmetricMatch(other: string): boolean; + toString(): string; + getExpectedType(): string; +} +declare class Anything extends AsymmetricMatcher<void> { + asymmetricMatch(other: unknown): boolean; + toString(): string; + toAsymmetricMatcher(): string; +} +declare class ObjectContaining extends AsymmetricMatcher<Record<string | symbol | number, unknown>> { + constructor(sample: Record<string, unknown>, inverse?: boolean); + getPrototype(obj: object): any; + hasProperty(obj: object | null, property: string | symbol): boolean; + getProperties(obj: object): (string | symbol)[]; + asymmetricMatch(other: any, customTesters?: Array<Tester>): boolean; + toString(): string; + getExpectedType(): string; +} +declare class ArrayContaining<T = unknown> extends AsymmetricMatcher<Array<T>> { + constructor(sample: Array<T>, inverse?: boolean); + asymmetricMatch(other: Array<T>, customTesters?: Array<Tester>): boolean; + toString(): string; + getExpectedType(): string; +} +declare class Any extends AsymmetricMatcher<any> { + constructor(sample: unknown); + fnNameFor(func: Function): string; + asymmetricMatch(other: unknown): boolean; + toString(): string; + getExpectedType(): string; + toAsymmetricMatcher(): string; +} +declare class StringMatching extends AsymmetricMatcher<RegExp> { + constructor(sample: string | RegExp, inverse?: boolean); + asymmetricMatch(other: string): boolean; + toString(): string; + getExpectedType(): string; +} +declare class SchemaMatching extends AsymmetricMatcher<StandardSchemaV1<unknown, unknown>> { + private result; + constructor(sample: StandardSchemaV1<unknown, unknown>, inverse?: boolean); + asymmetricMatch(other: unknown): boolean; + toString(): string; + getExpectedType(): string; + toAsymmetricMatcher(): string; +} +declare const JestAsymmetricMatchers: ChaiPlugin; + +declare function matcherHint(matcherName: string, received?: string, expected?: string, options?: MatcherHintOptions): string; +declare function printReceived(object: unknown): string; +declare function printExpected(value: unknown): string; +declare function getMatcherUtils(): { + EXPECTED_COLOR: Formatter; + RECEIVED_COLOR: Formatter; + INVERTED_COLOR: Formatter; + BOLD_WEIGHT: Formatter; + DIM_COLOR: Formatter; + diff: typeof diff; + matcherHint: typeof matcherHint; + printReceived: typeof printReceived; + printExpected: typeof printExpected; + printDiffOrStringify: typeof printDiffOrStringify; + printWithType: typeof printWithType; +}; +declare function printWithType<T>(name: string, value: T, print: (value: T) => string): string; +declare function addCustomEqualityTesters(newTesters: Array<Tester>): void; + +/** +* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +* +*/ + +type ChaiPlugin = Chai.ChaiPlugin; +type Tester = (this: TesterContext, a: any, b: any, customTesters: Array<Tester>) => boolean | undefined; +interface TesterContext { + equals: (a: unknown, b: unknown, customTesters?: Array<Tester>, strictCheck?: boolean) => boolean; +} + +interface MatcherHintOptions { + comment?: string; + expectedColor?: Formatter; + isDirectExpectCall?: boolean; + isNot?: boolean; + promise?: string; + receivedColor?: Formatter; + secondArgument?: string; + secondArgumentColor?: Formatter; +} +interface MatcherState { + customTesters: Array<Tester>; + assertionCalls: number; + currentTestName?: string; + dontThrow?: () => void; + error?: Error; + equals: (a: unknown, b: unknown, customTesters?: Array<Tester>, strictCheck?: boolean) => boolean; + expand?: boolean; + expectedAssertionsNumber?: number | null; + expectedAssertionsNumberErrorGen?: (() => Error) | null; + isExpectingAssertions?: boolean; + isExpectingAssertionsError?: Error | null; + isNot: boolean; + promise: string; + suppressedErrors: Array<Error>; + testPath?: string; + utils: ReturnType<typeof getMatcherUtils> & { + diff: typeof diff; + stringify: typeof stringify; + iterableEquality: Tester; + subsetEquality: Tester; + }; + soft?: boolean; + poll?: boolean; + task?: Readonly<Test>; +} +interface SyncExpectationResult { + pass: boolean; + message: () => string; + actual?: any; + expected?: any; +} +type AsyncExpectationResult = Promise<SyncExpectationResult>; +type ExpectationResult = SyncExpectationResult | AsyncExpectationResult; +interface RawMatcherFn< + T extends MatcherState = MatcherState, + E extends Array<any> = Array<any> +> { + (this: T, received: any, ...expected: E): ExpectationResult; +} +interface Matchers<T = any> {} +type MatchersObject<T extends MatcherState = MatcherState> = Record<string, RawMatcherFn<T>> & ThisType<T> & { [K in keyof Matchers<T>]? : RawMatcherFn<T, Parameters<Matchers<T>[K]>> }; +interface ExpectStatic extends Chai.ExpectStatic, Matchers, AsymmetricMatchersContaining { + <T>(actual: T, message?: string): Assertion<T>; + extend: (expects: MatchersObject) => void; + anything: () => any; + any: (constructor: unknown) => any; + getState: () => MatcherState; + setState: (state: Partial<MatcherState>) => void; + not: AsymmetricMatchersContaining; +} +interface CustomMatcher { + /** + * Checks that a value satisfies a custom matcher function. + * + * @param matcher - A function returning a boolean based on the custom condition + * @param message - Optional custom error message on failure + * + * @example + * expect(age).toSatisfy(val => val >= 18, 'Age must be at least 18'); + * expect(age).toEqual(expect.toSatisfy(val => val >= 18, 'Age must be at least 18')); + */ + toSatisfy: (matcher: (value: any) => boolean, message?: string) => any; + /** + * Matches if the received value is one of the values in the expected array or set. + * + * @example + * expect(1).toBeOneOf([1, 2, 3]) + * expect('foo').toBeOneOf([expect.any(String)]) + * expect({ a: 1 }).toEqual({ a: expect.toBeOneOf(['1', '2', '3']) }) + */ + toBeOneOf: <T>(sample: Array<T> | Set<T>) => any; +} +interface AsymmetricMatchersContaining extends CustomMatcher { + /** + * Matches if the received string contains the expected substring. + * + * @example + * expect('I have an apple').toEqual(expect.stringContaining('apple')); + * expect({ a: 'test string' }).toEqual({ a: expect.stringContaining('test') }); + */ + stringContaining: (expected: string) => any; + /** + * Matches if the received object contains all properties of the expected object. + * + * @example + * expect({ a: '1', b: 2 }).toEqual(expect.objectContaining({ a: '1' })) + */ + objectContaining: <T = any>(expected: DeeplyAllowMatchers<T>) => any; + /** + * Matches if the received array contains all elements in the expected array. + * + * @example + * expect(['a', 'b', 'c']).toEqual(expect.arrayContaining(['b', 'a'])); + */ + arrayContaining: <T = unknown>(expected: Array<DeeplyAllowMatchers<T>>) => any; + /** + * Matches if the received string or regex matches the expected pattern. + * + * @example + * expect('hello world').toEqual(expect.stringMatching(/^hello/)); + * expect('hello world').toEqual(expect.stringMatching('hello')); + */ + stringMatching: (expected: string | RegExp) => any; + /** + * Matches if the received number is within a certain precision of the expected number. + * + * @param precision - Optional decimal precision for comparison. Default is 2. + * + * @example + * expect(10.45).toEqual(expect.closeTo(10.5, 1)); + * expect(5.11).toEqual(expect.closeTo(5.12)); // with default precision + */ + closeTo: (expected: number, precision?: number) => any; + /** + * Matches if the received value validates against a Standard Schema. + * + * @param schema - A Standard Schema V1 compatible schema object + * + * @example + * expect(user).toEqual(expect.schemaMatching(z.object({ name: z.string() }))) + * expect(['hello', 'world']).toEqual([expect.schemaMatching(z.string()), expect.schemaMatching(z.string())]) + */ + schemaMatching: (schema: unknown) => any; +} +type WithAsymmetricMatcher<T> = T | AsymmetricMatcher<unknown>; +type DeeplyAllowMatchers<T> = T extends Array<infer Element> ? WithAsymmetricMatcher<T> | DeeplyAllowMatchers<Element>[] : T extends object ? WithAsymmetricMatcher<T> | { [K in keyof T] : DeeplyAllowMatchers<T[K]> } : WithAsymmetricMatcher<T>; +interface JestAssertion<T = any> extends jest.Matchers<void, T>, CustomMatcher { + /** + * Used when you want to check that two objects have the same value. + * This matcher recursively checks the equality of all fields, rather than checking for object identity. + * + * @example + * expect(user).toEqual({ name: 'Alice', age: 30 }); + */ + toEqual: <E>(expected: E) => void; + /** + * Use to test that objects have the same types as well as structure. + * + * @example + * expect(user).toStrictEqual({ name: 'Alice', age: 30 }); + */ + toStrictEqual: <E>(expected: E) => void; + /** + * Checks that a value is what you expect. It calls `Object.is` to compare values. + * Don't use `toBe` with floating-point numbers. + * + * @example + * expect(result).toBe(42); + * expect(status).toBe(true); + */ + toBe: <E>(expected: E) => void; + /** + * Check that a string matches a regular expression. + * + * @example + * expect(message).toMatch(/hello/); + * expect(greeting).toMatch('world'); + */ + toMatch: (expected: string | RegExp) => void; + /** + * Used to check that a JavaScript object matches a subset of the properties of an object + * + * @example + * expect(user).toMatchObject({ + * name: 'Alice', + * address: { city: 'Wonderland' } + * }); + */ + toMatchObject: <E extends object | any[]>(expected: E) => void; + /** + * Used when you want to check that an item is in a list. + * For testing the items in the list, this uses `===`, a strict equality check. + * + * @example + * expect(items).toContain('apple'); + * expect(numbers).toContain(5); + */ + toContain: <E>(item: E) => void; + /** + * Used when you want to check that an item is in a list. + * For testing the items in the list, this matcher recursively checks the + * equality of all fields, rather than checking for object identity. + * + * @example + * expect(items).toContainEqual({ name: 'apple', quantity: 1 }); + */ + toContainEqual: <E>(item: E) => void; + /** + * Use when you don't care what a value is, you just want to ensure a value + * is true in a boolean context. In JavaScript, there are six falsy values: + * `false`, `0`, `''`, `null`, `undefined`, and `NaN`. Everything else is truthy. + * + * @example + * expect(user.isActive).toBeTruthy(); + */ + toBeTruthy: () => void; + /** + * When you don't care what a value is, you just want to + * ensure a value is false in a boolean context. + * + * @example + * expect(user.isActive).toBeFalsy(); + */ + toBeFalsy: () => void; + /** + * For comparing floating point numbers. + * + * @example + * expect(score).toBeGreaterThan(10); + */ + toBeGreaterThan: (num: number | bigint) => void; + /** + * For comparing floating point numbers. + * + * @example + * expect(score).toBeGreaterThanOrEqual(10); + */ + toBeGreaterThanOrEqual: (num: number | bigint) => void; + /** + * For comparing floating point numbers. + * + * @example + * expect(score).toBeLessThan(10); + */ + toBeLessThan: (num: number | bigint) => void; + /** + * For comparing floating point numbers. + * + * @example + * expect(score).toBeLessThanOrEqual(10); + */ + toBeLessThanOrEqual: (num: number | bigint) => void; + /** + * Used to check that a variable is NaN. + * + * @example + * expect(value).toBeNaN(); + */ + toBeNaN: () => void; + /** + * Used to check that a variable is undefined. + * + * @example + * expect(value).toBeUndefined(); + */ + toBeUndefined: () => void; + /** + * This is the same as `.toBe(null)` but the error messages are a bit nicer. + * So use `.toBeNull()` when you want to check that something is null. + * + * @example + * expect(value).toBeNull(); + */ + toBeNull: () => void; + /** + * Used to check that a variable is nullable (null or undefined). + * + * @example + * expect(value).toBeNullable(); + */ + toBeNullable: () => void; + /** + * Ensure that a variable is not undefined. + * + * @example + * expect(value).toBeDefined(); + */ + toBeDefined: () => void; + /** + * Ensure that an object is an instance of a class. + * This matcher uses `instanceof` underneath. + * + * @example + * expect(new Date()).toBeInstanceOf(Date); + */ + toBeInstanceOf: <E>(expected: E) => void; + /** + * Used to check that an object has a `.length` property + * and it is set to a certain numeric value. + * + * @example + * expect([1, 2, 3]).toHaveLength(3); + * expect('hello').toHaveLength(5); + */ + toHaveLength: (length: number) => void; + /** + * Use to check if a property at the specified path exists on an object. + * For checking deeply nested properties, you may use dot notation or an array containing + * the path segments for deep references. + * + * Optionally, you can provide a value to check if it matches the value present at the path + * on the target object. This matcher uses 'deep equality' (like `toEqual()`) and recursively checks + * the equality of all fields. + * + * @example + * expect(user).toHaveProperty('address.city', 'New York'); + * expect(config).toHaveProperty(['settings', 'theme'], 'dark'); + */ + toHaveProperty: <E>(property: string | (string | number)[], value?: E) => void; + /** + * Using exact equality with floating point numbers is a bad idea. + * Rounding means that intuitive things fail. + * The default for `numDigits` is 2. + * + * @example + * expect(price).toBeCloseTo(9.99, 2); + */ + toBeCloseTo: (number: number, numDigits?: number) => void; + /** + * Ensures that a mock function is called an exact number of times. + * + * Also under the alias `expect.toBeCalledTimes`. + * + * @example + * expect(mockFunc).toHaveBeenCalledTimes(2); + */ + toHaveBeenCalledTimes: (times: number) => void; + /** + * Ensures that a mock function is called an exact number of times. + * + * Alias for `expect.toHaveBeenCalledTimes`. + * + * @example + * expect(mockFunc).toBeCalledTimes(2); + */ + toBeCalledTimes: (times: number) => void; + /** + * Ensures that a mock function is called. + * + * Also under the alias `expect.toBeCalled`. + * + * @example + * expect(mockFunc).toHaveBeenCalled(); + */ + toHaveBeenCalled: () => void; + /** + * Ensures that a mock function is called. + * + * Alias for `expect.toHaveBeenCalled`. + * + * @example + * expect(mockFunc).toBeCalled(); + */ + toBeCalled: () => void; + /** + * Ensure that a mock function is called with specific arguments. + * + * Also under the alias `expect.toBeCalledWith`. + * + * @example + * expect(mockFunc).toHaveBeenCalledWith('arg1', 42); + */ + toHaveBeenCalledWith: <E extends any[]>(...args: E) => void; + /** + * Ensure that a mock function is called with specific arguments. + * + * Alias for `expect.toHaveBeenCalledWith`. + * + * @example + * expect(mockFunc).toBeCalledWith('arg1', 42); + */ + toBeCalledWith: <E extends any[]>(...args: E) => void; + /** + * Ensure that a mock function is called with specific arguments on an Nth call. + * + * Also under the alias `expect.nthCalledWith`. + * + * @example + * expect(mockFunc).toHaveBeenNthCalledWith(2, 'secondArg'); + */ + toHaveBeenNthCalledWith: <E extends any[]>(n: number, ...args: E) => void; + /** + * Ensure that a mock function is called with specific arguments on an Nth call. + * + * Alias for `expect.toHaveBeenNthCalledWith`. + * + * @example + * expect(mockFunc).nthCalledWith(2, 'secondArg'); + */ + nthCalledWith: <E extends any[]>(nthCall: number, ...args: E) => void; + /** + * If you have a mock function, you can use `.toHaveBeenLastCalledWith` + * to test what arguments it was last called with. + * + * Also under the alias `expect.lastCalledWith`. + * + * @example + * expect(mockFunc).toHaveBeenLastCalledWith('lastArg'); + */ + toHaveBeenLastCalledWith: <E extends any[]>(...args: E) => void; + /** + * If you have a mock function, you can use `.lastCalledWith` + * to test what arguments it was last called with. + * + * Alias for `expect.toHaveBeenLastCalledWith`. + * + * @example + * expect(mockFunc).lastCalledWith('lastArg'); + */ + lastCalledWith: <E extends any[]>(...args: E) => void; + /** + * Used to test that a function throws when it is called. + * + * Also under the alias `expect.toThrowError`. + * + * @example + * expect(() => functionWithError()).toThrow('Error message'); + * expect(() => parseJSON('invalid')).toThrow(SyntaxError); + */ + toThrow: (expected?: string | Constructable | RegExp | Error) => void; + /** + * Used to test that a function throws when it is called. + * + * Alias for `expect.toThrow`. + * + * @example + * expect(() => functionWithError()).toThrowError('Error message'); + * expect(() => parseJSON('invalid')).toThrowError(SyntaxError); + */ + toThrowError: (expected?: string | Constructable | RegExp | Error) => void; + /** + * Use to test that the mock function successfully returned (i.e., did not throw an error) at least one time + * + * Alias for `expect.toHaveReturned`. + * + * @example + * expect(mockFunc).toReturn(); + */ + toReturn: () => void; + /** + * Use to test that the mock function successfully returned (i.e., did not throw an error) at least one time + * + * Also under the alias `expect.toReturn`. + * + * @example + * expect(mockFunc).toHaveReturned(); + */ + toHaveReturned: () => void; + /** + * Use to ensure that a mock function returned successfully (i.e., did not throw an error) an exact number of times. + * Any calls to the mock function that throw an error are not counted toward the number of times the function returned. + * + * Alias for `expect.toHaveReturnedTimes`. + * + * @example + * expect(mockFunc).toReturnTimes(3); + */ + toReturnTimes: (times: number) => void; + /** + * Use to ensure that a mock function returned successfully (i.e., did not throw an error) an exact number of times. + * Any calls to the mock function that throw an error are not counted toward the number of times the function returned. + * + * Also under the alias `expect.toReturnTimes`. + * + * @example + * expect(mockFunc).toHaveReturnedTimes(3); + */ + toHaveReturnedTimes: (times: number) => void; + /** + * Use to ensure that a mock function returned a specific value. + * + * Alias for `expect.toHaveReturnedWith`. + * + * @example + * expect(mockFunc).toReturnWith('returnValue'); + */ + toReturnWith: <E>(value: E) => void; + /** + * Use to ensure that a mock function returned a specific value. + * + * Also under the alias `expect.toReturnWith`. + * + * @example + * expect(mockFunc).toHaveReturnedWith('returnValue'); + */ + toHaveReturnedWith: <E>(value: E) => void; + /** + * Use to test the specific value that a mock function last returned. + * If the last call to the mock function threw an error, then this matcher will fail + * no matter what value you provided as the expected return value. + * + * Also under the alias `expect.lastReturnedWith`. + * + * @example + * expect(mockFunc).toHaveLastReturnedWith('lastValue'); + */ + toHaveLastReturnedWith: <E>(value: E) => void; + /** + * Use to test the specific value that a mock function last returned. + * If the last call to the mock function threw an error, then this matcher will fail + * no matter what value you provided as the expected return value. + * + * Alias for `expect.toHaveLastReturnedWith`. + * + * @example + * expect(mockFunc).lastReturnedWith('lastValue'); + */ + lastReturnedWith: <E>(value: E) => void; + /** + * Use to test the specific value that a mock function returned for the nth call. + * If the nth call to the mock function threw an error, then this matcher will fail + * no matter what value you provided as the expected return value. + * + * Also under the alias `expect.nthReturnedWith`. + * + * @example + * expect(mockFunc).toHaveNthReturnedWith(2, 'nthValue'); + */ + toHaveNthReturnedWith: <E>(nthCall: number, value: E) => void; + /** + * Use to test the specific value that a mock function returned for the nth call. + * If the nth call to the mock function threw an error, then this matcher will fail + * no matter what value you provided as the expected return value. + * + * Alias for `expect.toHaveNthReturnedWith`. + * + * @example + * expect(mockFunc).nthReturnedWith(2, 'nthValue'); + */ + nthReturnedWith: <E>(nthCall: number, value: E) => void; +} +type VitestAssertion< + A, + T +> = { [K in keyof A] : A[K] extends Chai.Assertion ? Assertion<T> : A[K] extends (...args: any[]) => any ? A[K] : VitestAssertion<A[K], T> } & ((type: string, message?: string) => Assertion); +type Promisify<O> = { [K in keyof O] : O[K] extends (...args: infer A) => infer R ? Promisify<O[K]> & ((...args: A) => Promise<R>) : O[K] }; +type PromisifyAssertion<T> = Promisify<Assertion<T>>; +interface Assertion<T = any> extends VitestAssertion<Chai.Assertion, T>, JestAssertion<T>, Matchers<T> { + /** + * Ensures a value is of a specific type. + * + * @example + * expect(value).toBeTypeOf('string'); + * expect(number).toBeTypeOf('number'); + */ + toBeTypeOf: (expected: "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined") => void; + /** + * Asserts that a mock function was called exactly once. + * + * @example + * expect(mockFunc).toHaveBeenCalledOnce(); + */ + toHaveBeenCalledOnce: () => void; + /** + * Ensure that a mock function is called with specific arguments and called + * exactly once. + * + * @example + * expect(mockFunc).toHaveBeenCalledExactlyOnceWith('arg1', 42); + */ + toHaveBeenCalledExactlyOnceWith: <E extends any[]>(...args: E) => void; + /** + * This assertion checks if a `Mock` was called before another `Mock`. + * @param mock - A mock function created by `vi.spyOn` or `vi.fn` + * @param failIfNoFirstInvocation - Fail if the first mock was never called + * @example + * const mock1 = vi.fn() + * const mock2 = vi.fn() + * + * mock1() + * mock2() + * mock1() + * + * expect(mock1).toHaveBeenCalledBefore(mock2) + */ + toHaveBeenCalledBefore: (mock: MockInstance, failIfNoFirstInvocation?: boolean) => void; + /** + * This assertion checks if a `Mock` was called after another `Mock`. + * @param mock - A mock function created by `vi.spyOn` or `vi.fn` + * @param failIfNoFirstInvocation - Fail if the first mock was never called + * @example + * const mock1 = vi.fn() + * const mock2 = vi.fn() + * + * mock2() + * mock1() + * mock2() + * + * expect(mock1).toHaveBeenCalledAfter(mock2) + */ + toHaveBeenCalledAfter: (mock: MockInstance, failIfNoFirstInvocation?: boolean) => void; + /** + * Checks that a promise resolves successfully at least once. + * + * @example + * await expect(promise).toHaveResolved(); + */ + toHaveResolved: () => void; + /** + * Checks that a promise resolves to a specific value. + * + * @example + * await expect(promise).toHaveResolvedWith('success'); + */ + toHaveResolvedWith: <E>(value: E) => void; + /** + * Ensures a promise resolves a specific number of times. + * + * @example + * expect(mockAsyncFunc).toHaveResolvedTimes(3); + */ + toHaveResolvedTimes: (times: number) => void; + /** + * Asserts that the last resolved value of a promise matches an expected value. + * + * @example + * await expect(mockAsyncFunc).toHaveLastResolvedWith('finalResult'); + */ + toHaveLastResolvedWith: <E>(value: E) => void; + /** + * Ensures a specific value was returned by a promise on the nth resolution. + * + * @example + * await expect(mockAsyncFunc).toHaveNthResolvedWith(2, 'secondResult'); + */ + toHaveNthResolvedWith: <E>(nthCall: number, value: E) => void; + /** + * Verifies that a promise resolves. + * + * @example + * await expect(someAsyncFunc).resolves.toBe(42); + */ + resolves: PromisifyAssertion<T>; + /** + * Verifies that a promise rejects. + * + * @example + * await expect(someAsyncFunc).rejects.toThrow('error'); + */ + rejects: PromisifyAssertion<T>; +} +declare global { + namespace jest { + interface Matchers< + R, + T = {} + > {} + } +} + +declare const customMatchers: MatchersObject; + +declare const JestChaiExpect: ChaiPlugin; + +declare const JestExtend: ChaiPlugin; + +declare function equals(a: unknown, b: unknown, customTesters?: Array<Tester>, strictCheck?: boolean): boolean; +declare function isAsymmetric(obj: any): obj is AsymmetricMatcher<any>; +declare function hasAsymmetric(obj: any, seen?: Set<any>): boolean; +declare function isA(typeName: string, value: unknown): boolean; +declare function fnNameFor(func: Function): string; +declare function hasProperty(obj: object | null, property: string): boolean; +declare function isImmutableUnorderedKeyed(maybeKeyed: any): boolean; +declare function isImmutableUnorderedSet(maybeSet: any): boolean; +declare function iterableEquality(a: any, b: any, customTesters?: Array<Tester>, aStack?: Array<any>, bStack?: Array<any>): boolean | undefined; +declare function subsetEquality(object: unknown, subset: unknown, customTesters?: Array<Tester>): boolean | undefined; +declare function typeEquality(a: any, b: any): boolean | undefined; +declare function arrayBufferEquality(a: unknown, b: unknown): boolean | undefined; +declare function sparseArrayEquality(a: unknown, b: unknown, customTesters?: Array<Tester>): boolean | undefined; +declare function generateToBeMessage(deepEqualityName: string, expected?: string, actual?: string): string; +declare function pluralize(word: string, count: number): string; +declare function getObjectKeys(object: object): Array<string | symbol>; +declare function getObjectSubset(object: any, subset: any, customTesters: Array<Tester>): { + subset: any; + stripped: number; +}; +/** +* Detects if an object is a Standard Schema V1 compatible schema +*/ +declare function isStandardSchema(obj: any): obj is StandardSchemaV1; + +declare function getState<State extends MatcherState = MatcherState>(expect: ExpectStatic): State; +declare function setState<State extends MatcherState = MatcherState>(state: Partial<State>, expect: ExpectStatic): void; + +export { ASYMMETRIC_MATCHERS_OBJECT, Any, Anything, ArrayContaining, AsymmetricMatcher, GLOBAL_EXPECT, JEST_MATCHERS_OBJECT, JestAsymmetricMatchers, JestChaiExpect, JestExtend, MATCHERS_OBJECT, ObjectContaining, SchemaMatching, StringContaining, StringMatching, addCustomEqualityTesters, arrayBufferEquality, customMatchers, equals, fnNameFor, generateToBeMessage, getObjectKeys, getObjectSubset, getState, hasAsymmetric, hasProperty, isA, isAsymmetric, isImmutableUnorderedKeyed, isImmutableUnorderedSet, isStandardSchema, iterableEquality, pluralize, setState, sparseArrayEquality, subsetEquality, typeEquality }; +export type { Assertion, AsymmetricMatcherInterface, AsymmetricMatchersContaining, AsyncExpectationResult, ChaiPlugin, DeeplyAllowMatchers, ExpectStatic, ExpectationResult, JestAssertion, MatcherHintOptions, MatcherState, Matchers, MatchersObject, PromisifyAssertion, RawMatcherFn, SyncExpectationResult, Tester, TesterContext }; diff --git a/vanilla/node_modules/@vitest/expect/dist/index.js b/vanilla/node_modules/@vitest/expect/dist/index.js new file mode 100644 index 0000000..ec7e5f9 --- /dev/null +++ b/vanilla/node_modules/@vitest/expect/dist/index.js @@ -0,0 +1,1875 @@ +import { printDiffOrStringify, diff } from '@vitest/utils/diff'; +import { stringify } from '@vitest/utils/display'; +import { getType, isObject, noop, assertTypes } from '@vitest/utils/helpers'; +import c from 'tinyrainbow'; +import { isMockFunction } from '@vitest/spy'; +import { processError } from '@vitest/utils/error'; +import { use, util } from 'chai'; +import * as chai from 'chai'; +export { chai }; + +const MATCHERS_OBJECT = Symbol.for("matchers-object"); +const JEST_MATCHERS_OBJECT = Symbol.for("$$jest-matchers-object"); +const GLOBAL_EXPECT = Symbol.for("expect-global"); +const ASYMMETRIC_MATCHERS_OBJECT = Symbol.for("asymmetric-matchers-object"); + +// selectively ported from https://github.com/jest-community/jest-extended +const customMatchers = { + toSatisfy(actual, expected, message) { + const { printReceived, printExpected, matcherHint } = this.utils; + const pass = expected(actual); + return { + pass, + message: () => pass ? `\ +${matcherHint(".not.toSatisfy", "received", "")} + +Expected value to not satisfy: +${message || printExpected(expected)} +Received: +${printReceived(actual)}` : `\ +${matcherHint(".toSatisfy", "received", "")} + +Expected value to satisfy: +${message || printExpected(expected)} + +Received: +${printReceived(actual)}` + }; + }, + toBeOneOf(actual, expected) { + const { equals, customTesters } = this; + const { printReceived, printExpected, matcherHint } = this.utils; + let pass; + if (Array.isArray(expected)) { + pass = expected.length === 0 || expected.some((item) => equals(item, actual, customTesters)); + } else if (expected instanceof Set) { + pass = expected.size === 0 || expected.has(actual) || [...expected].some((item) => equals(item, actual, customTesters)); + } else { + throw new TypeError(`You must provide an array or set to ${matcherHint(".toBeOneOf")}, not '${typeof expected}'.`); + } + return { + pass, + message: () => pass ? `\ +${matcherHint(".not.toBeOneOf", "received", "")} + +Expected value to not be one of: +${printExpected(expected)} +Received: +${printReceived(actual)}` : `\ +${matcherHint(".toBeOneOf", "received", "")} + +Expected value to be one of: +${printExpected(expected)} + +Received: +${printReceived(actual)}` + }; + } +}; + +const EXPECTED_COLOR = c.green; +const RECEIVED_COLOR = c.red; +const INVERTED_COLOR = c.inverse; +const BOLD_WEIGHT = c.bold; +const DIM_COLOR = c.dim; +function matcherHint(matcherName, received = "received", expected = "expected", options = {}) { + const { comment = "", isDirectExpectCall = false, isNot = false, promise = "", secondArgument = "", expectedColor = EXPECTED_COLOR, receivedColor = RECEIVED_COLOR, secondArgumentColor = EXPECTED_COLOR } = options; + let hint = ""; + let dimString = "expect"; + if (!isDirectExpectCall && received !== "") { + hint += DIM_COLOR(`${dimString}(`) + receivedColor(received); + dimString = ")"; + } + if (promise !== "") { + hint += DIM_COLOR(`${dimString}.`) + promise; + dimString = ""; + } + if (isNot) { + hint += `${DIM_COLOR(`${dimString}.`)}not`; + dimString = ""; + } + if (matcherName.includes(".")) { + // Old format: for backward compatibility, + // especially without promise or isNot options + dimString += matcherName; + } else { + // New format: omit period from matcherName arg + hint += DIM_COLOR(`${dimString}.`) + matcherName; + dimString = ""; + } + if (expected === "") { + dimString += "()"; + } else { + hint += DIM_COLOR(`${dimString}(`) + expectedColor(expected); + if (secondArgument) { + hint += DIM_COLOR(", ") + secondArgumentColor(secondArgument); + } + dimString = ")"; + } + if (comment !== "") { + dimString += ` // ${comment}`; + } + if (dimString !== "") { + hint += DIM_COLOR(dimString); + } + return hint; +} +const SPACE_SYMBOL = "·"; +// Instead of inverse highlight which now implies a change, +// replace common spaces with middle dot at the end of any line. +function replaceTrailingSpaces(text) { + return text.replace(/\s+$/gm, (spaces) => SPACE_SYMBOL.repeat(spaces.length)); +} +function printReceived(object) { + return RECEIVED_COLOR(replaceTrailingSpaces(stringify(object))); +} +function printExpected(value) { + return EXPECTED_COLOR(replaceTrailingSpaces(stringify(value))); +} +function getMatcherUtils() { + return { + EXPECTED_COLOR, + RECEIVED_COLOR, + INVERTED_COLOR, + BOLD_WEIGHT, + DIM_COLOR, + diff, + matcherHint, + printReceived, + printExpected, + printDiffOrStringify, + printWithType + }; +} +function printWithType(name, value, print) { + const type = getType(value); + const hasType = type !== "null" && type !== "undefined" ? `${name} has type: ${type}\n` : ""; + const hasValue = `${name} has value: ${print(value)}`; + return hasType + hasValue; +} +function addCustomEqualityTesters(newTesters) { + if (!Array.isArray(newTesters)) { + throw new TypeError(`expect.customEqualityTesters: Must be set to an array of Testers. Was given "${getType(newTesters)}"`); + } + globalThis[JEST_MATCHERS_OBJECT].customEqualityTesters.push(...newTesters); +} +function getCustomEqualityTesters() { + return globalThis[JEST_MATCHERS_OBJECT].customEqualityTesters; +} + +// Extracted out of jasmine 2.5.2 +function equals(a, b, customTesters, strictCheck) { + customTesters = customTesters || []; + return eq(a, b, [], [], customTesters, strictCheck ? hasKey : hasDefinedKey); +} +const functionToString = Function.prototype.toString; +function isAsymmetric(obj) { + return !!obj && typeof obj === "object" && "asymmetricMatch" in obj && isA("Function", obj.asymmetricMatch); +} +function hasAsymmetric(obj, seen = new Set()) { + if (seen.has(obj)) { + return false; + } + seen.add(obj); + if (isAsymmetric(obj)) { + return true; + } + if (Array.isArray(obj)) { + return obj.some((i) => hasAsymmetric(i, seen)); + } + if (obj instanceof Set) { + return Array.from(obj).some((i) => hasAsymmetric(i, seen)); + } + if (isObject(obj)) { + return Object.values(obj).some((v) => hasAsymmetric(v, seen)); + } + return false; +} +function asymmetricMatch(a, b, customTesters) { + const asymmetricA = isAsymmetric(a); + const asymmetricB = isAsymmetric(b); + if (asymmetricA && asymmetricB) { + return undefined; + } + if (asymmetricA) { + return a.asymmetricMatch(b, customTesters); + } + if (asymmetricB) { + return b.asymmetricMatch(a, customTesters); + } +} +// Equality function lovingly adapted from isEqual in +// [Underscore](http://underscorejs.org) +function eq(a, b, aStack, bStack, customTesters, hasKey) { + let result = true; + const asymmetricResult = asymmetricMatch(a, b, customTesters); + if (asymmetricResult !== undefined) { + return asymmetricResult; + } + const testerContext = { equals }; + for (let i = 0; i < customTesters.length; i++) { + const customTesterResult = customTesters[i].call(testerContext, a, b, customTesters); + if (customTesterResult !== undefined) { + return customTesterResult; + } + } + if (typeof URL === "function" && a instanceof URL && b instanceof URL) { + return a.href === b.href; + } + if (Object.is(a, b)) { + return true; + } + // A strict comparison is necessary because `null == undefined`. + if (a === null || b === null) { + return a === b; + } + const className = Object.prototype.toString.call(a); + if (className !== Object.prototype.toString.call(b)) { + return false; + } + switch (className) { + case "[object Boolean]": + case "[object String]": + case "[object Number]": if (typeof a !== typeof b) { + // One is a primitive, one a `new Primitive()` + return false; + } else if (typeof a !== "object" && typeof b !== "object") { + // both are proper primitives + return Object.is(a, b); + } else { + // both are `new Primitive()`s + return Object.is(a.valueOf(), b.valueOf()); + } + case "[object Date]": { + const numA = +a; + const numB = +b; + // Coerce dates to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are equivalent. + return numA === numB || Number.isNaN(numA) && Number.isNaN(numB); + } + case "[object RegExp]": return a.source === b.source && a.flags === b.flags; + case "[object Temporal.Instant]": + case "[object Temporal.ZonedDateTime]": + case "[object Temporal.PlainDateTime]": + case "[object Temporal.PlainDate]": + case "[object Temporal.PlainTime]": + case "[object Temporal.PlainYearMonth]": + case "[object Temporal.PlainMonthDay]": return a.equals(b); + case "[object Temporal.Duration]": return a.toString() === b.toString(); + } + if (typeof a !== "object" || typeof b !== "object") { + return false; + } + // Use DOM3 method isEqualNode (IE>=9) + if (isDomNode(a) && isDomNode(b)) { + return a.isEqualNode(b); + } + // Used to detect circular references. + let length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + // circular references at same depth are equal + // circular reference is not equal to non-circular one + if (aStack[length] === a) { + return bStack[length] === b; + } else if (bStack[length] === b) { + return false; + } + } + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + // Recursively compare objects and arrays. + // Compare array lengths to determine if a deep comparison is necessary. + if (className === "[object Array]" && a.length !== b.length) { + return false; + } + if (a instanceof Error && b instanceof Error) { + try { + return isErrorEqual(a, b, aStack, bStack, customTesters, hasKey); + } finally { + aStack.pop(); + bStack.pop(); + } + } + // Deep compare objects. + const aKeys = keys(a, hasKey); + let key; + let size = aKeys.length; + // Ensure that both objects contain the same number of properties before comparing deep equality. + if (keys(b, hasKey).length !== size) { + return false; + } + while (size--) { + key = aKeys[size]; + // Deep compare each member + result = hasKey(b, key) && eq(a[key], b[key], aStack, bStack, customTesters, hasKey); + if (!result) { + return false; + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + return result; +} +function isErrorEqual(a, b, aStack, bStack, customTesters, hasKey) { + // https://nodejs.org/docs/latest-v22.x/api/assert.html#comparison-details + // - [[Prototype]] of objects are compared using the === operator. + // - Only enumerable "own" properties are considered. + // - Error names, messages, causes, and errors are always compared, even if these are not enumerable properties. errors is also compared. + let result = Object.getPrototypeOf(a) === Object.getPrototypeOf(b) && a.name === b.name && a.message === b.message; + // check Error.cause asymmetrically + if (typeof b.cause !== "undefined") { + result && (result = eq(a.cause, b.cause, aStack, bStack, customTesters, hasKey)); + } + // AggregateError.errors + if (a instanceof AggregateError && b instanceof AggregateError) { + result && (result = eq(a.errors, b.errors, aStack, bStack, customTesters, hasKey)); + } + // spread to compare enumerable properties + result && (result = eq({ ...a }, { ...b }, aStack, bStack, customTesters, hasKey)); + return result; +} +function keys(obj, hasKey) { + const keys = []; + for (const key in obj) { + if (hasKey(obj, key)) { + keys.push(key); + } + } + return keys.concat(Object.getOwnPropertySymbols(obj).filter((symbol) => Object.getOwnPropertyDescriptor(obj, symbol).enumerable)); +} +function hasDefinedKey(obj, key) { + return hasKey(obj, key) && obj[key] !== undefined; +} +function hasKey(obj, key) { + return Object.hasOwn(obj, key); +} +function isA(typeName, value) { + return Object.prototype.toString.apply(value) === `[object ${typeName}]`; +} +function isDomNode(obj) { + return obj !== null && typeof obj === "object" && "nodeType" in obj && typeof obj.nodeType === "number" && "nodeName" in obj && typeof obj.nodeName === "string" && "isEqualNode" in obj && typeof obj.isEqualNode === "function"; +} +function fnNameFor(func) { + if (func.name) { + return func.name; + } + const matches = functionToString.call(func).match(/^(?:async)?\s*function\s*(?:\*\s*)?([\w$]+)\s*\(/); + return matches ? matches[1] : "<anonymous>"; +} +function getPrototype(obj) { + if (Object.getPrototypeOf) { + return Object.getPrototypeOf(obj); + } + if (obj.constructor.prototype === obj) { + return null; + } + return obj.constructor.prototype; +} +function hasProperty(obj, property) { + if (!obj) { + return false; + } + if (Object.hasOwn(obj, property)) { + return true; + } + return hasProperty(getPrototype(obj), property); +} +// SENTINEL constants are from https://github.com/facebook/immutable-js +const IS_KEYED_SENTINEL = "@@__IMMUTABLE_KEYED__@@"; +const IS_SET_SENTINEL = "@@__IMMUTABLE_SET__@@"; +const IS_LIST_SENTINEL = "@@__IMMUTABLE_LIST__@@"; +const IS_ORDERED_SENTINEL = "@@__IMMUTABLE_ORDERED__@@"; +const IS_RECORD_SYMBOL = "@@__IMMUTABLE_RECORD__@@"; +function isImmutableUnorderedKeyed(maybeKeyed) { + return !!(maybeKeyed && maybeKeyed[IS_KEYED_SENTINEL] && !maybeKeyed[IS_ORDERED_SENTINEL]); +} +function isImmutableUnorderedSet(maybeSet) { + return !!(maybeSet && maybeSet[IS_SET_SENTINEL] && !maybeSet[IS_ORDERED_SENTINEL]); +} +function isObjectLiteral(source) { + return source != null && typeof source === "object" && !Array.isArray(source); +} +function isImmutableList(source) { + return Boolean(source && isObjectLiteral(source) && source[IS_LIST_SENTINEL]); +} +function isImmutableOrderedKeyed(source) { + return Boolean(source && isObjectLiteral(source) && source[IS_KEYED_SENTINEL] && source[IS_ORDERED_SENTINEL]); +} +function isImmutableOrderedSet(source) { + return Boolean(source && isObjectLiteral(source) && source[IS_SET_SENTINEL] && source[IS_ORDERED_SENTINEL]); +} +function isImmutableRecord(source) { + return Boolean(source && isObjectLiteral(source) && source[IS_RECORD_SYMBOL]); +} +/** +* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +* +*/ +const IteratorSymbol = Symbol.iterator; +function hasIterator(object) { + return !!(object != null && object[IteratorSymbol]); +} +function iterableEquality(a, b, customTesters = [], aStack = [], bStack = []) { + if (typeof a !== "object" || typeof b !== "object" || Array.isArray(a) || Array.isArray(b) || !hasIterator(a) || !hasIterator(b)) { + return undefined; + } + if (a.constructor !== b.constructor) { + return false; + } + let length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + // circular references at same depth are equal + // circular reference is not equal to non-circular one + if (aStack[length] === a) { + return bStack[length] === b; + } + } + aStack.push(a); + bStack.push(b); + const filteredCustomTesters = [...customTesters.filter((t) => t !== iterableEquality), iterableEqualityWithStack]; + function iterableEqualityWithStack(a, b) { + return iterableEquality(a, b, [...customTesters], [...aStack], [...bStack]); + } + if (a.size !== undefined) { + if (a.size !== b.size) { + return false; + } else if (isA("Set", a) || isImmutableUnorderedSet(a)) { + let allFound = true; + for (const aValue of a) { + if (!b.has(aValue)) { + let has = false; + for (const bValue of b) { + const isEqual = equals(aValue, bValue, filteredCustomTesters); + if (isEqual === true) { + has = true; + } + } + if (has === false) { + allFound = false; + break; + } + } + } + // Remove the first value from the stack of traversed values. + aStack.pop(); + bStack.pop(); + return allFound; + } else if (isA("Map", a) || isImmutableUnorderedKeyed(a)) { + let allFound = true; + for (const aEntry of a) { + if (!b.has(aEntry[0]) || !equals(aEntry[1], b.get(aEntry[0]), filteredCustomTesters)) { + let has = false; + for (const bEntry of b) { + const matchedKey = equals(aEntry[0], bEntry[0], filteredCustomTesters); + let matchedValue = false; + if (matchedKey === true) { + matchedValue = equals(aEntry[1], bEntry[1], filteredCustomTesters); + } + if (matchedValue === true) { + has = true; + } + } + if (has === false) { + allFound = false; + break; + } + } + } + // Remove the first value from the stack of traversed values. + aStack.pop(); + bStack.pop(); + return allFound; + } + } + const bIterator = b[IteratorSymbol](); + for (const aValue of a) { + const nextB = bIterator.next(); + if (nextB.done || !equals(aValue, nextB.value, filteredCustomTesters)) { + return false; + } + } + if (!bIterator.next().done) { + return false; + } + if (!isImmutableList(a) && !isImmutableOrderedKeyed(a) && !isImmutableOrderedSet(a) && !isImmutableRecord(a)) { + const aEntries = Object.entries(a); + const bEntries = Object.entries(b); + if (!equals(aEntries, bEntries, filteredCustomTesters)) { + return false; + } + } + // Remove the first value from the stack of traversed values. + aStack.pop(); + bStack.pop(); + return true; +} +/** +* Checks if `hasOwnProperty(object, key)` up the prototype chain, stopping at `Object.prototype`. +*/ +function hasPropertyInObject(object, key) { + const shouldTerminate = !object || typeof object !== "object" || object === Object.prototype; + if (shouldTerminate) { + return false; + } + return Object.hasOwn(object, key) || hasPropertyInObject(Object.getPrototypeOf(object), key); +} +function isObjectWithKeys(a) { + return isObject(a) && !(a instanceof Error) && !Array.isArray(a) && !(a instanceof Date); +} +function subsetEquality(object, subset, customTesters = []) { + const filteredCustomTesters = customTesters.filter((t) => t !== subsetEquality); + // subsetEquality needs to keep track of the references + // it has already visited to avoid infinite loops in case + // there are circular references in the subset passed to it. + const subsetEqualityWithContext = (seenReferences = new WeakMap()) => (object, subset) => { + if (!isObjectWithKeys(subset)) { + return undefined; + } + return Object.keys(subset).every((key) => { + if (subset[key] != null && typeof subset[key] === "object") { + if (seenReferences.has(subset[key])) { + return equals(object[key], subset[key], filteredCustomTesters); + } + seenReferences.set(subset[key], true); + } + const result = object != null && hasPropertyInObject(object, key) && equals(object[key], subset[key], [...filteredCustomTesters, subsetEqualityWithContext(seenReferences)]); + // The main goal of using seenReference is to avoid circular node on tree. + // It will only happen within a parent and its child, not a node and nodes next to it (same level) + // We should keep the reference for a parent and its child only + // Thus we should delete the reference immediately so that it doesn't interfere + // other nodes within the same level on tree. + seenReferences.delete(subset[key]); + return result; + }); + }; + return subsetEqualityWithContext()(object, subset); +} +function typeEquality(a, b) { + if (a == null || b == null || a.constructor === b.constructor) { + return undefined; + } + return false; +} +function arrayBufferEquality(a, b) { + let dataViewA = a; + let dataViewB = b; + if (!(a instanceof DataView && b instanceof DataView)) { + if (!(a instanceof ArrayBuffer) || !(b instanceof ArrayBuffer)) { + return undefined; + } + try { + dataViewA = new DataView(a); + dataViewB = new DataView(b); + } catch { + return undefined; + } + } + // Buffers are not equal when they do not have the same byte length + if (dataViewA.byteLength !== dataViewB.byteLength) { + return false; + } + // Check if every byte value is equal to each other + for (let i = 0; i < dataViewA.byteLength; i++) { + if (dataViewA.getUint8(i) !== dataViewB.getUint8(i)) { + return false; + } + } + return true; +} +function sparseArrayEquality(a, b, customTesters = []) { + if (!Array.isArray(a) || !Array.isArray(b)) { + return undefined; + } + // A sparse array [, , 1] will have keys ["2"] whereas [undefined, undefined, 1] will have keys ["0", "1", "2"] + const aKeys = Object.keys(a); + const bKeys = Object.keys(b); + const filteredCustomTesters = customTesters.filter((t) => t !== sparseArrayEquality); + return equals(a, b, filteredCustomTesters, true) && equals(aKeys, bKeys); +} +function generateToBeMessage(deepEqualityName, expected = "#{this}", actual = "#{exp}") { + const toBeMessage = `expected ${expected} to be ${actual} // Object.is equality`; + if (["toStrictEqual", "toEqual"].includes(deepEqualityName)) { + return `${toBeMessage}\n\nIf it should pass with deep equality, replace "toBe" with "${deepEqualityName}"\n\nExpected: ${expected}\nReceived: serializes to the same string\n`; + } + return toBeMessage; +} +function pluralize(word, count) { + return `${count} ${word}${count === 1 ? "" : "s"}`; +} +function getObjectKeys(object) { + return [...Object.keys(object), ...Object.getOwnPropertySymbols(object).filter((s) => { + var _Object$getOwnPropert; + return (_Object$getOwnPropert = Object.getOwnPropertyDescriptor(object, s)) === null || _Object$getOwnPropert === void 0 ? void 0 : _Object$getOwnPropert.enumerable; + })]; +} +function getObjectSubset(object, subset, customTesters) { + let stripped = 0; + const getObjectSubsetWithContext = (seenReferences = new WeakMap()) => (object, subset) => { + if (Array.isArray(object)) { + if (Array.isArray(subset) && subset.length === object.length) { + // The map method returns correct subclass of subset. + return subset.map((sub, i) => getObjectSubsetWithContext(seenReferences)(object[i], sub)); + } + } else if (object instanceof Date) { + return object; + } else if (isObject(object) && isObject(subset)) { + if (equals(object, subset, [ + ...customTesters, + iterableEquality, + subsetEquality + ])) { + // return "expected" subset to avoid showing irrelevant toMatchObject diff + return subset; + } + const trimmed = {}; + seenReferences.set(object, trimmed); + // preserve constructor for toMatchObject diff + if (typeof object.constructor === "function" && typeof object.constructor.name === "string") { + Object.defineProperty(trimmed, "constructor", { + enumerable: false, + value: object.constructor + }); + } + for (const key of getObjectKeys(object)) { + if (hasPropertyInObject(subset, key)) { + trimmed[key] = seenReferences.has(object[key]) ? seenReferences.get(object[key]) : getObjectSubsetWithContext(seenReferences)(object[key], subset[key]); + } else { + if (!seenReferences.has(object[key])) { + stripped += 1; + if (isObject(object[key])) { + stripped += getObjectKeys(object[key]).length; + } + getObjectSubsetWithContext(seenReferences)(object[key], subset[key]); + } + } + } + if (getObjectKeys(trimmed).length > 0) { + return trimmed; + } + } + return object; + }; + return { + subset: getObjectSubsetWithContext()(object, subset), + stripped + }; +} +/** +* Detects if an object is a Standard Schema V1 compatible schema +*/ +function isStandardSchema(obj) { + return !!obj && (typeof obj === "object" || typeof obj === "function") && obj["~standard"] && typeof obj["~standard"].validate === "function"; +} + +if (!Object.hasOwn(globalThis, MATCHERS_OBJECT)) { + const globalState = new WeakMap(); + const matchers = Object.create(null); + const customEqualityTesters = []; + const asymmetricMatchers = Object.create(null); + Object.defineProperty(globalThis, MATCHERS_OBJECT, { get: () => globalState }); + Object.defineProperty(globalThis, JEST_MATCHERS_OBJECT, { + configurable: true, + get: () => ({ + state: globalState.get(globalThis[GLOBAL_EXPECT]), + matchers, + customEqualityTesters + }) + }); + Object.defineProperty(globalThis, ASYMMETRIC_MATCHERS_OBJECT, { get: () => asymmetricMatchers }); +} +function getState(expect) { + return globalThis[MATCHERS_OBJECT].get(expect); +} +function setState(state, expect) { + const map = globalThis[MATCHERS_OBJECT]; + const current = map.get(expect) || {}; + // so it keeps getters from `testPath` + const results = Object.defineProperties(current, { + ...Object.getOwnPropertyDescriptors(current), + ...Object.getOwnPropertyDescriptors(state) + }); + map.set(expect, results); +} + +class AsymmetricMatcher { + // should have "jest" to be compatible with its ecosystem + $$typeof = Symbol.for("jest.asymmetricMatcher"); + constructor(sample, inverse = false) { + this.sample = sample; + this.inverse = inverse; + } + getMatcherContext(expect) { + return { + ...getState(expect || globalThis[GLOBAL_EXPECT]), + equals, + isNot: this.inverse, + customTesters: getCustomEqualityTesters(), + utils: { + ...getMatcherUtils(), + diff, + stringify, + iterableEquality, + subsetEquality + } + }; + } +} +// implement custom chai/loupe inspect for better AssertionError.message formatting +// https://github.com/chaijs/loupe/blob/9b8a6deabcd50adc056a64fb705896194710c5c6/src/index.ts#L29 +// @ts-expect-error computed properties is not supported when isolatedDeclarations is enabled +// FIXME: https://github.com/microsoft/TypeScript/issues/61068 +AsymmetricMatcher.prototype[Symbol.for("chai/inspect")] = function(options) { + // minimal pretty-format with simple manual truncation + const result = stringify(this, options.depth, { min: true }); + if (result.length <= options.truncate) { + return result; + } + return `${this.toString()}{…}`; +}; +class StringContaining extends AsymmetricMatcher { + constructor(sample, inverse = false) { + if (!isA("String", sample)) { + throw new Error("Expected is not a string"); + } + super(sample, inverse); + } + asymmetricMatch(other) { + const result = isA("String", other) && other.includes(this.sample); + return this.inverse ? !result : result; + } + toString() { + return `String${this.inverse ? "Not" : ""}Containing`; + } + getExpectedType() { + return "string"; + } +} +class Anything extends AsymmetricMatcher { + asymmetricMatch(other) { + return other != null; + } + toString() { + return "Anything"; + } + toAsymmetricMatcher() { + return "Anything"; + } +} +class ObjectContaining extends AsymmetricMatcher { + constructor(sample, inverse = false) { + super(sample, inverse); + } + getPrototype(obj) { + if (Object.getPrototypeOf) { + return Object.getPrototypeOf(obj); + } + if (obj.constructor.prototype === obj) { + return null; + } + return obj.constructor.prototype; + } + hasProperty(obj, property) { + if (!obj) { + return false; + } + if (Object.hasOwn(obj, property)) { + return true; + } + return this.hasProperty(this.getPrototype(obj), property); + } + getProperties(obj) { + return [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj).filter((s) => { + var _Object$getOwnPropert; + return (_Object$getOwnPropert = Object.getOwnPropertyDescriptor(obj, s)) === null || _Object$getOwnPropert === void 0 ? void 0 : _Object$getOwnPropert.enumerable; + })]; + } + asymmetricMatch(other, customTesters) { + if (typeof this.sample !== "object") { + throw new TypeError(`You must provide an object to ${this.toString()}, not '${typeof this.sample}'.`); + } + let result = true; + const properties = this.getProperties(this.sample); + for (const property of properties) { + var _Object$getOwnPropert2, _Object$getOwnPropert3; + if (!this.hasProperty(other, property)) { + result = false; + break; + } + const value = ((_Object$getOwnPropert2 = Object.getOwnPropertyDescriptor(this.sample, property)) === null || _Object$getOwnPropert2 === void 0 ? void 0 : _Object$getOwnPropert2.value) ?? this.sample[property]; + const otherValue = ((_Object$getOwnPropert3 = Object.getOwnPropertyDescriptor(other, property)) === null || _Object$getOwnPropert3 === void 0 ? void 0 : _Object$getOwnPropert3.value) ?? other[property]; + if (!equals(value, otherValue, customTesters)) { + result = false; + break; + } + } + return this.inverse ? !result : result; + } + toString() { + return `Object${this.inverse ? "Not" : ""}Containing`; + } + getExpectedType() { + return "object"; + } +} +class ArrayContaining extends AsymmetricMatcher { + constructor(sample, inverse = false) { + super(sample, inverse); + } + asymmetricMatch(other, customTesters) { + if (!Array.isArray(this.sample)) { + throw new TypeError(`You must provide an array to ${this.toString()}, not '${typeof this.sample}'.`); + } + const result = this.sample.length === 0 || Array.isArray(other) && this.sample.every((item) => other.some((another) => equals(item, another, customTesters))); + return this.inverse ? !result : result; + } + toString() { + return `Array${this.inverse ? "Not" : ""}Containing`; + } + getExpectedType() { + return "array"; + } +} +class Any extends AsymmetricMatcher { + constructor(sample) { + if (typeof sample === "undefined") { + throw new TypeError("any() expects to be passed a constructor function. " + "Please pass one or use anything() to match any object."); + } + super(sample); + } + fnNameFor(func) { + if (func.name) { + return func.name; + } + const functionToString = Function.prototype.toString; + const matches = functionToString.call(func).match(/^(?:async)?\s*function\s*(?:\*\s*)?([\w$]+)\s*\(/); + return matches ? matches[1] : "<anonymous>"; + } + asymmetricMatch(other) { + if (this.sample === String) { + return typeof other == "string" || other instanceof String; + } + if (this.sample === Number) { + return typeof other == "number" || other instanceof Number; + } + if (this.sample === Function) { + return typeof other == "function" || typeof other === "function"; + } + if (this.sample === Boolean) { + return typeof other == "boolean" || other instanceof Boolean; + } + if (this.sample === BigInt) { + return typeof other == "bigint" || other instanceof BigInt; + } + if (this.sample === Symbol) { + return typeof other == "symbol" || other instanceof Symbol; + } + if (this.sample === Object) { + return typeof other == "object"; + } + return other instanceof this.sample; + } + toString() { + return "Any"; + } + getExpectedType() { + if (this.sample === String) { + return "string"; + } + if (this.sample === Number) { + return "number"; + } + if (this.sample === Function) { + return "function"; + } + if (this.sample === Object) { + return "object"; + } + if (this.sample === Boolean) { + return "boolean"; + } + return this.fnNameFor(this.sample); + } + toAsymmetricMatcher() { + return `Any<${this.fnNameFor(this.sample)}>`; + } +} +class StringMatching extends AsymmetricMatcher { + constructor(sample, inverse = false) { + if (!isA("String", sample) && !isA("RegExp", sample)) { + throw new Error("Expected is not a String or a RegExp"); + } + super(new RegExp(sample), inverse); + } + asymmetricMatch(other) { + const result = isA("String", other) && this.sample.test(other); + return this.inverse ? !result : result; + } + toString() { + return `String${this.inverse ? "Not" : ""}Matching`; + } + getExpectedType() { + return "string"; + } +} +class CloseTo extends AsymmetricMatcher { + precision; + constructor(sample, precision = 2, inverse = false) { + if (!isA("Number", sample)) { + throw new Error("Expected is not a Number"); + } + if (!isA("Number", precision)) { + throw new Error("Precision is not a Number"); + } + super(sample); + this.inverse = inverse; + this.precision = precision; + } + asymmetricMatch(other) { + if (!isA("Number", other)) { + return false; + } + let result = false; + if (other === Number.POSITIVE_INFINITY && this.sample === Number.POSITIVE_INFINITY) { + result = true; + } else if (other === Number.NEGATIVE_INFINITY && this.sample === Number.NEGATIVE_INFINITY) { + result = true; + } else { + result = Math.abs(this.sample - other) < 10 ** -this.precision / 2; + } + return this.inverse ? !result : result; + } + toString() { + return `Number${this.inverse ? "Not" : ""}CloseTo`; + } + getExpectedType() { + return "number"; + } + toAsymmetricMatcher() { + return [ + this.toString(), + this.sample, + `(${pluralize("digit", this.precision)})` + ].join(" "); + } +} +class SchemaMatching extends AsymmetricMatcher { + result; + constructor(sample, inverse = false) { + if (!isStandardSchema(sample)) { + throw new TypeError("SchemaMatching expected to receive a Standard Schema."); + } + super(sample, inverse); + } + asymmetricMatch(other) { + const result = this.sample["~standard"].validate(other); + // Check if the result is a Promise (async validation) + if (result instanceof Promise) { + throw new TypeError("Async schema validation is not supported in asymmetric matchers."); + } + this.result = result; + const pass = !this.result.issues || this.result.issues.length === 0; + return this.inverse ? !pass : pass; + } + toString() { + return `Schema${this.inverse ? "Not" : ""}Matching`; + } + getExpectedType() { + return "object"; + } + toAsymmetricMatcher() { + var _this$result; + const { utils } = this.getMatcherContext(); + const issues = ((_this$result = this.result) === null || _this$result === void 0 ? void 0 : _this$result.issues) || []; + if (issues.length > 0) { + return `${this.toString()} ${utils.stringify(this.result, undefined, { printBasicPrototype: false })}`; + } + return this.toString(); + } +} +const JestAsymmetricMatchers = (chai, utils) => { + utils.addMethod(chai.expect, "anything", () => new Anything()); + utils.addMethod(chai.expect, "any", (expected) => new Any(expected)); + utils.addMethod(chai.expect, "stringContaining", (expected) => new StringContaining(expected)); + utils.addMethod(chai.expect, "objectContaining", (expected) => new ObjectContaining(expected)); + utils.addMethod(chai.expect, "arrayContaining", (expected) => new ArrayContaining(expected)); + utils.addMethod(chai.expect, "stringMatching", (expected) => new StringMatching(expected)); + utils.addMethod(chai.expect, "closeTo", (expected, precision) => new CloseTo(expected, precision)); + utils.addMethod(chai.expect, "schemaMatching", (expected) => new SchemaMatching(expected)); + // defineProperty does not work + chai.expect.not = { + stringContaining: (expected) => new StringContaining(expected, true), + objectContaining: (expected) => new ObjectContaining(expected, true), + arrayContaining: (expected) => new ArrayContaining(expected, true), + stringMatching: (expected) => new StringMatching(expected, true), + closeTo: (expected, precision) => new CloseTo(expected, precision, true), + schemaMatching: (expected) => new SchemaMatching(expected, true) + }; +}; + +function createAssertionMessage(util, assertion, hasArgs) { + const not = util.flag(assertion, "negate") ? "not." : ""; + const name = `${util.flag(assertion, "_name")}(${hasArgs ? "expected" : ""})`; + const promiseName = util.flag(assertion, "promise"); + const promise = promiseName ? `.${promiseName}` : ""; + return `expect(actual)${promise}.${not}${name}`; +} +function recordAsyncExpect(_test, promise, assertion, error) { + const test = _test; + // record promise for test, that resolves before test ends + if (test && promise instanceof Promise) { + // if promise is explicitly awaited, remove it from the list + promise = promise.finally(() => { + if (!test.promises) { + return; + } + const index = test.promises.indexOf(promise); + if (index !== -1) { + test.promises.splice(index, 1); + } + }); + // record promise + if (!test.promises) { + test.promises = []; + } + test.promises.push(promise); + let resolved = false; + test.onFinished ?? (test.onFinished = []); + test.onFinished.push(() => { + if (!resolved) { + var _vitest_worker__; + const processor = ((_vitest_worker__ = globalThis.__vitest_worker__) === null || _vitest_worker__ === void 0 ? void 0 : _vitest_worker__.onFilterStackTrace) || ((s) => s || ""); + const stack = processor(error.stack); + console.warn([ + `Promise returned by \`${assertion}\` was not awaited. `, + "Vitest currently auto-awaits hanging assertions at the end of the test, but this will cause the test to fail in Vitest 3. ", + "Please remember to await the assertion.\n", + stack + ].join("")); + } + }); + return { + then(onFulfilled, onRejected) { + resolved = true; + return promise.then(onFulfilled, onRejected); + }, + catch(onRejected) { + return promise.catch(onRejected); + }, + finally(onFinally) { + return promise.finally(onFinally); + }, + [Symbol.toStringTag]: "Promise" + }; + } + return promise; +} +function handleTestError(test, err) { + var _test$result; + test.result || (test.result = { state: "fail" }); + test.result.state = "fail"; + (_test$result = test.result).errors || (_test$result.errors = []); + test.result.errors.push(processError(err)); +} +function wrapAssertion(utils, name, fn) { + return function(...args) { + // private + if (name !== "withTest") { + utils.flag(this, "_name", name); + } + if (!utils.flag(this, "soft")) { + return fn.apply(this, args); + } + const test = utils.flag(this, "vitest-test"); + if (!test) { + throw new Error("expect.soft() can only be used inside a test"); + } + try { + const result = fn.apply(this, args); + if (result && typeof result === "object" && typeof result.then === "function") { + return result.then(noop, (err) => { + handleTestError(test, err); + }); + } + return result; + } catch (err) { + handleTestError(test, err); + } + }; +} + +// Jest Expect Compact +const JestChaiExpect = (chai, utils) => { + const { AssertionError } = chai; + const customTesters = getCustomEqualityTesters(); + function def(name, fn) { + const addMethod = (n) => { + const softWrapper = wrapAssertion(utils, n, fn); + utils.addMethod(chai.Assertion.prototype, n, softWrapper); + utils.addMethod(globalThis[JEST_MATCHERS_OBJECT].matchers, n, softWrapper); + }; + if (Array.isArray(name)) { + name.forEach((n) => addMethod(n)); + } else { + addMethod(name); + } + } + [ + "throw", + "throws", + "Throw" + ].forEach((m) => { + utils.overwriteMethod(chai.Assertion.prototype, m, (_super) => { + return function(...args) { + const promise = utils.flag(this, "promise"); + const object = utils.flag(this, "object"); + const isNot = utils.flag(this, "negate"); + if (promise === "rejects") { + utils.flag(this, "object", () => { + throw object; + }); + } else if (promise === "resolves" && typeof object !== "function") { + if (!isNot) { + const message = utils.flag(this, "message") || "expected promise to throw an error, but it didn't"; + const error = { showDiff: false }; + throw new AssertionError(message, error, utils.flag(this, "ssfi")); + } else { + return; + } + } + _super.apply(this, args); + }; + }); + }); + // @ts-expect-error @internal + def("withTest", function(test) { + utils.flag(this, "vitest-test", test); + return this; + }); + def("toEqual", function(expected) { + const actual = utils.flag(this, "object"); + const equal = equals(actual, expected, [...customTesters, iterableEquality]); + return this.assert(equal, "expected #{this} to deeply equal #{exp}", "expected #{this} to not deeply equal #{exp}", expected, actual); + }); + def("toStrictEqual", function(expected) { + const obj = utils.flag(this, "object"); + const equal = equals(obj, expected, [ + ...customTesters, + iterableEquality, + typeEquality, + sparseArrayEquality, + arrayBufferEquality + ], true); + return this.assert(equal, "expected #{this} to strictly equal #{exp}", "expected #{this} to not strictly equal #{exp}", expected, obj); + }); + def("toBe", function(expected) { + const actual = this._obj; + const pass = Object.is(actual, expected); + let deepEqualityName = ""; + if (!pass) { + const toStrictEqualPass = equals(actual, expected, [ + ...customTesters, + iterableEquality, + typeEquality, + sparseArrayEquality, + arrayBufferEquality + ], true); + if (toStrictEqualPass) { + deepEqualityName = "toStrictEqual"; + } else { + const toEqualPass = equals(actual, expected, [...customTesters, iterableEquality]); + if (toEqualPass) { + deepEqualityName = "toEqual"; + } + } + } + return this.assert(pass, generateToBeMessage(deepEqualityName), "expected #{this} not to be #{exp} // Object.is equality", expected, actual); + }); + def("toMatchObject", function(expected) { + const actual = this._obj; + const pass = equals(actual, expected, [ + ...customTesters, + iterableEquality, + subsetEquality + ]); + const isNot = utils.flag(this, "negate"); + const { subset: actualSubset, stripped } = getObjectSubset(actual, expected, customTesters); + if (pass && isNot || !pass && !isNot) { + const msg = utils.getMessage(this, [ + pass, + "expected #{this} to match object #{exp}", + "expected #{this} to not match object #{exp}", + expected, + actualSubset, + false + ]); + const message = stripped === 0 ? msg : `${msg}\n(${stripped} matching ${stripped === 1 ? "property" : "properties"} omitted from actual)`; + throw new AssertionError(message, { + showDiff: true, + expected, + actual: actualSubset + }); + } + }); + def("toMatch", function(expected) { + const actual = this._obj; + if (typeof actual !== "string") { + throw new TypeError(`.toMatch() expects to receive a string, but got ${typeof actual}`); + } + return this.assert(typeof expected === "string" ? actual.includes(expected) : actual.match(expected), `expected #{this} to match #{exp}`, `expected #{this} not to match #{exp}`, expected, actual); + }); + def("toContain", function(item) { + const actual = this._obj; + if (typeof Node !== "undefined" && actual instanceof Node) { + if (!(item instanceof Node)) { + throw new TypeError(`toContain() expected a DOM node as the argument, but got ${typeof item}`); + } + return this.assert(actual.contains(item), "expected #{this} to contain element #{exp}", "expected #{this} not to contain element #{exp}", item, actual); + } + if (typeof DOMTokenList !== "undefined" && actual instanceof DOMTokenList) { + assertTypes(item, "class name", ["string"]); + const isNot = utils.flag(this, "negate"); + const expectedClassList = isNot ? actual.value.replace(item, "").trim() : `${actual.value} ${item}`; + return this.assert(actual.contains(item), `expected "${actual.value}" to contain "${item}"`, `expected "${actual.value}" not to contain "${item}"`, expectedClassList, actual.value); + } + // handle simple case on our own using `this.assert` to include diff in error message + if (typeof actual === "string" && typeof item === "string") { + return this.assert(actual.includes(item), `expected #{this} to contain #{exp}`, `expected #{this} not to contain #{exp}`, item, actual); + } + // make "actual" indexable to have compatibility with jest + if (actual != null && typeof actual !== "string") { + utils.flag(this, "object", Array.from(actual)); + } + return this.contain(item); + }); + def("toContainEqual", function(expected) { + const obj = utils.flag(this, "object"); + const index = Array.from(obj).findIndex((item) => { + return equals(item, expected, customTesters); + }); + this.assert(index !== -1, "expected #{this} to deep equally contain #{exp}", "expected #{this} to not deep equally contain #{exp}", expected); + }); + def("toBeTruthy", function() { + const obj = utils.flag(this, "object"); + this.assert(Boolean(obj), "expected #{this} to be truthy", "expected #{this} to not be truthy", true, obj); + }); + def("toBeFalsy", function() { + const obj = utils.flag(this, "object"); + this.assert(!obj, "expected #{this} to be falsy", "expected #{this} to not be falsy", false, obj); + }); + def("toBeGreaterThan", function(expected) { + const actual = this._obj; + assertTypes(actual, "actual", ["number", "bigint"]); + assertTypes(expected, "expected", ["number", "bigint"]); + return this.assert(actual > expected, `expected ${actual} to be greater than ${expected}`, `expected ${actual} to be not greater than ${expected}`, expected, actual, false); + }); + def("toBeGreaterThanOrEqual", function(expected) { + const actual = this._obj; + assertTypes(actual, "actual", ["number", "bigint"]); + assertTypes(expected, "expected", ["number", "bigint"]); + return this.assert(actual >= expected, `expected ${actual} to be greater than or equal to ${expected}`, `expected ${actual} to be not greater than or equal to ${expected}`, expected, actual, false); + }); + def("toBeLessThan", function(expected) { + const actual = this._obj; + assertTypes(actual, "actual", ["number", "bigint"]); + assertTypes(expected, "expected", ["number", "bigint"]); + return this.assert(actual < expected, `expected ${actual} to be less than ${expected}`, `expected ${actual} to be not less than ${expected}`, expected, actual, false); + }); + def("toBeLessThanOrEqual", function(expected) { + const actual = this._obj; + assertTypes(actual, "actual", ["number", "bigint"]); + assertTypes(expected, "expected", ["number", "bigint"]); + return this.assert(actual <= expected, `expected ${actual} to be less than or equal to ${expected}`, `expected ${actual} to be not less than or equal to ${expected}`, expected, actual, false); + }); + def("toBeNaN", function() { + const obj = utils.flag(this, "object"); + this.assert(Number.isNaN(obj), "expected #{this} to be NaN", "expected #{this} not to be NaN", Number.NaN, obj); + }); + def("toBeUndefined", function() { + const obj = utils.flag(this, "object"); + this.assert(undefined === obj, "expected #{this} to be undefined", "expected #{this} not to be undefined", undefined, obj); + }); + def("toBeNull", function() { + const obj = utils.flag(this, "object"); + this.assert(obj === null, "expected #{this} to be null", "expected #{this} not to be null", null, obj); + }); + def("toBeNullable", function() { + const obj = utils.flag(this, "object"); + this.assert(obj == null, "expected #{this} to be nullish", "expected #{this} not to be nullish", null, obj); + }); + def("toBeDefined", function() { + const obj = utils.flag(this, "object"); + this.assert(typeof obj !== "undefined", "expected #{this} to be defined", "expected #{this} to be undefined", obj); + }); + def("toBeTypeOf", function(expected) { + const actual = typeof this._obj; + const equal = expected === actual; + return this.assert(equal, "expected #{this} to be type of #{exp}", "expected #{this} not to be type of #{exp}", expected, actual); + }); + def("toBeInstanceOf", function(obj) { + return this.instanceOf(obj); + }); + def("toHaveLength", function(length) { + return this.have.length(length); + }); + // destructuring, because it checks `arguments` inside, and value is passing as `undefined` + def("toHaveProperty", function(...args) { + if (Array.isArray(args[0])) { + args[0] = args[0].map((key) => String(key).replace(/([.[\]])/g, "\\$1")).join("."); + } + const actual = this._obj; + const [propertyName, expected] = args; + const getValue = () => { + const hasOwn = Object.hasOwn(actual, propertyName); + if (hasOwn) { + return { + value: actual[propertyName], + exists: true + }; + } + return utils.getPathInfo(actual, propertyName); + }; + const { value, exists } = getValue(); + const pass = exists && (args.length === 1 || equals(expected, value, customTesters)); + const valueString = args.length === 1 ? "" : ` with value ${utils.objDisplay(expected)}`; + return this.assert(pass, `expected #{this} to have property "${propertyName}"${valueString}`, `expected #{this} to not have property "${propertyName}"${valueString}`, expected, exists ? value : undefined); + }); + def("toBeCloseTo", function(received, precision = 2) { + const expected = this._obj; + let pass = false; + let expectedDiff = 0; + let receivedDiff = 0; + if (received === Number.POSITIVE_INFINITY && expected === Number.POSITIVE_INFINITY) { + pass = true; + } else if (received === Number.NEGATIVE_INFINITY && expected === Number.NEGATIVE_INFINITY) { + pass = true; + } else { + expectedDiff = 10 ** -precision / 2; + receivedDiff = Math.abs(expected - received); + pass = receivedDiff < expectedDiff; + } + return this.assert(pass, `expected #{this} to be close to #{exp}, received difference is ${receivedDiff}, but expected ${expectedDiff}`, `expected #{this} to not be close to #{exp}, received difference is ${receivedDiff}, but expected ${expectedDiff}`, received, expected, false); + }); + function assertIsMock(assertion) { + if (!isMockFunction(assertion._obj)) { + throw new TypeError(`${utils.inspect(assertion._obj)} is not a spy or a call to a spy!`); + } + } + function getSpy(assertion) { + assertIsMock(assertion); + return assertion._obj; + } + def(["toHaveBeenCalledTimes", "toBeCalledTimes"], function(number) { + const spy = getSpy(this); + const spyName = spy.getMockName(); + const callCount = spy.mock.calls.length; + return this.assert(callCount === number, `expected "${spyName}" to be called #{exp} times, but got ${callCount} times`, `expected "${spyName}" to not be called #{exp} times`, number, callCount, false); + }); + def("toHaveBeenCalledOnce", function() { + const spy = getSpy(this); + const spyName = spy.getMockName(); + const callCount = spy.mock.calls.length; + return this.assert(callCount === 1, `expected "${spyName}" to be called once, but got ${callCount} times`, `expected "${spyName}" to not be called once`, 1, callCount, false); + }); + def(["toHaveBeenCalled", "toBeCalled"], function() { + const spy = getSpy(this); + const spyName = spy.getMockName(); + const callCount = spy.mock.calls.length; + const called = callCount > 0; + const isNot = utils.flag(this, "negate"); + let msg = utils.getMessage(this, [ + called, + `expected "${spyName}" to be called at least once`, + `expected "${spyName}" to not be called at all, but actually been called ${callCount} times`, + true, + called + ]); + if (called && isNot) { + msg = formatCalls(spy, msg); + } + if (called && isNot || !called && !isNot) { + throw new AssertionError(msg); + } + }); + // manually compare array elements since `jestEquals` cannot + // apply asymmetric matcher to `undefined` array element. + function equalsArgumentArray(a, b) { + return a.length === b.length && a.every((aItem, i) => equals(aItem, b[i], [...customTesters, iterableEquality])); + } + def(["toHaveBeenCalledWith", "toBeCalledWith"], function(...args) { + const spy = getSpy(this); + const spyName = spy.getMockName(); + const pass = spy.mock.calls.some((callArg) => equalsArgumentArray(callArg, args)); + const isNot = utils.flag(this, "negate"); + const msg = utils.getMessage(this, [ + pass, + `expected "${spyName}" to be called with arguments: #{exp}`, + `expected "${spyName}" to not be called with arguments: #{exp}`, + args + ]); + if (pass && isNot || !pass && !isNot) { + throw new AssertionError(formatCalls(spy, msg, args)); + } + }); + def("toHaveBeenCalledExactlyOnceWith", function(...args) { + const spy = getSpy(this); + const spyName = spy.getMockName(); + const callCount = spy.mock.calls.length; + const hasCallWithArgs = spy.mock.calls.some((callArg) => equalsArgumentArray(callArg, args)); + const pass = hasCallWithArgs && callCount === 1; + const isNot = utils.flag(this, "negate"); + const msg = utils.getMessage(this, [ + pass, + `expected "${spyName}" to be called once with arguments: #{exp}`, + `expected "${spyName}" to not be called once with arguments: #{exp}`, + args + ]); + if (pass && isNot || !pass && !isNot) { + throw new AssertionError(formatCalls(spy, msg, args)); + } + }); + def(["toHaveBeenNthCalledWith", "nthCalledWith"], function(times, ...args) { + const spy = getSpy(this); + const spyName = spy.getMockName(); + const nthCall = spy.mock.calls[times - 1]; + const callCount = spy.mock.calls.length; + const isCalled = times <= callCount; + this.assert(nthCall && equalsArgumentArray(nthCall, args), `expected ${ordinalOf(times)} "${spyName}" call to have been called with #{exp}${isCalled ? `` : `, but called only ${callCount} times`}`, `expected ${ordinalOf(times)} "${spyName}" call to not have been called with #{exp}`, args, nthCall, isCalled); + }); + def(["toHaveBeenLastCalledWith", "lastCalledWith"], function(...args) { + const spy = getSpy(this); + const spyName = spy.getMockName(); + const lastCall = spy.mock.calls.at(-1); + this.assert(lastCall && equalsArgumentArray(lastCall, args), `expected last "${spyName}" call to have been called with #{exp}`, `expected last "${spyName}" call to not have been called with #{exp}`, args, lastCall); + }); + /** + * Used for `toHaveBeenCalledBefore` and `toHaveBeenCalledAfter` to determine if the expected spy was called before the result spy. + */ + function isSpyCalledBeforeAnotherSpy(beforeSpy, afterSpy, failIfNoFirstInvocation) { + const beforeInvocationCallOrder = beforeSpy.mock.invocationCallOrder; + const afterInvocationCallOrder = afterSpy.mock.invocationCallOrder; + if (beforeInvocationCallOrder.length === 0) { + return !failIfNoFirstInvocation; + } + if (afterInvocationCallOrder.length === 0) { + return false; + } + return beforeInvocationCallOrder[0] < afterInvocationCallOrder[0]; + } + def(["toHaveBeenCalledBefore"], function(resultSpy, failIfNoFirstInvocation = true) { + const expectSpy = getSpy(this); + if (!isMockFunction(resultSpy)) { + throw new TypeError(`${utils.inspect(resultSpy)} is not a spy or a call to a spy`); + } + this.assert(isSpyCalledBeforeAnotherSpy(expectSpy, resultSpy, failIfNoFirstInvocation), `expected "${expectSpy.getMockName()}" to have been called before "${resultSpy.getMockName()}"`, `expected "${expectSpy.getMockName()}" to not have been called before "${resultSpy.getMockName()}"`, resultSpy, expectSpy); + }); + def(["toHaveBeenCalledAfter"], function(resultSpy, failIfNoFirstInvocation = true) { + const expectSpy = getSpy(this); + if (!isMockFunction(resultSpy)) { + throw new TypeError(`${utils.inspect(resultSpy)} is not a spy or a call to a spy`); + } + this.assert(isSpyCalledBeforeAnotherSpy(resultSpy, expectSpy, failIfNoFirstInvocation), `expected "${expectSpy.getMockName()}" to have been called after "${resultSpy.getMockName()}"`, `expected "${expectSpy.getMockName()}" to not have been called after "${resultSpy.getMockName()}"`, resultSpy, expectSpy); + }); + def(["toThrow", "toThrowError"], function(expected) { + if (typeof expected === "string" || typeof expected === "undefined" || expected instanceof RegExp) { + // Fixes the issue related to `chai` <https://github.com/vitest-dev/vitest/issues/6618> + return this.throws(expected === "" ? /^$/ : expected); + } + const obj = this._obj; + const promise = utils.flag(this, "promise"); + const isNot = utils.flag(this, "negate"); + let thrown = null; + if (promise === "rejects") { + thrown = obj; + } else if (promise === "resolves" && typeof obj !== "function") { + if (!isNot) { + const message = utils.flag(this, "message") || "expected promise to throw an error, but it didn't"; + const error = { showDiff: false }; + throw new AssertionError(message, error, utils.flag(this, "ssfi")); + } else { + return; + } + } else { + let isThrow = false; + try { + obj(); + } catch (err) { + isThrow = true; + thrown = err; + } + if (!isThrow && !isNot) { + const message = utils.flag(this, "message") || "expected function to throw an error, but it didn't"; + const error = { showDiff: false }; + throw new AssertionError(message, error, utils.flag(this, "ssfi")); + } + } + if (typeof expected === "function") { + const name = expected.name || expected.prototype.constructor.name; + return this.assert(thrown && thrown instanceof expected, `expected error to be instance of ${name}`, `expected error not to be instance of ${name}`, expected, thrown); + } + if (expected instanceof Error) { + const equal = equals(thrown, expected, [...customTesters, iterableEquality]); + return this.assert(equal, "expected a thrown error to be #{exp}", "expected a thrown error not to be #{exp}", expected, thrown); + } + if (typeof expected === "object" && "asymmetricMatch" in expected && typeof expected.asymmetricMatch === "function") { + const matcher = expected; + return this.assert(thrown && matcher.asymmetricMatch(thrown), "expected error to match asymmetric matcher", "expected error not to match asymmetric matcher", matcher, thrown); + } + throw new Error(`"toThrow" expects string, RegExp, function, Error instance or asymmetric matcher, got "${typeof expected}"`); + }); + [{ + name: "toHaveResolved", + condition: (spy) => spy.mock.settledResults.length > 0 && spy.mock.settledResults.some(({ type }) => type === "fulfilled"), + action: "resolved" + }, { + name: ["toHaveReturned", "toReturn"], + condition: (spy) => spy.mock.calls.length > 0 && spy.mock.results.some(({ type }) => type !== "throw"), + action: "called" + }].forEach(({ name, condition, action }) => { + def(name, function() { + const spy = getSpy(this); + const spyName = spy.getMockName(); + const pass = condition(spy); + this.assert(pass, `expected "${spyName}" to be successfully ${action} at least once`, `expected "${spyName}" to not be successfully ${action}`, pass, !pass, false); + }); + }); + [{ + name: "toHaveResolvedTimes", + condition: (spy, times) => spy.mock.settledResults.reduce((s, { type }) => type === "fulfilled" ? ++s : s, 0) === times, + action: "resolved" + }, { + name: ["toHaveReturnedTimes", "toReturnTimes"], + condition: (spy, times) => spy.mock.results.reduce((s, { type }) => type === "throw" ? s : ++s, 0) === times, + action: "called" + }].forEach(({ name, condition, action }) => { + def(name, function(times) { + const spy = getSpy(this); + const spyName = spy.getMockName(); + const pass = condition(spy, times); + this.assert(pass, `expected "${spyName}" to be successfully ${action} ${times} times`, `expected "${spyName}" to not be successfully ${action} ${times} times`, `expected resolved times: ${times}`, `received resolved times: ${pass}`, false); + }); + }); + [{ + name: "toHaveResolvedWith", + condition: (spy, value) => spy.mock.settledResults.some(({ type, value: result }) => type === "fulfilled" && equals(value, result)), + action: "resolve" + }, { + name: ["toHaveReturnedWith", "toReturnWith"], + condition: (spy, value) => spy.mock.results.some(({ type, value: result }) => type === "return" && equals(value, result)), + action: "return" + }].forEach(({ name, condition, action }) => { + def(name, function(value) { + const spy = getSpy(this); + const pass = condition(spy, value); + const isNot = utils.flag(this, "negate"); + if (pass && isNot || !pass && !isNot) { + const spyName = spy.getMockName(); + const msg = utils.getMessage(this, [ + pass, + `expected "${spyName}" to ${action} with: #{exp} at least once`, + `expected "${spyName}" to not ${action} with: #{exp}`, + value + ]); + const results = action === "return" ? spy.mock.results : spy.mock.settledResults; + throw new AssertionError(formatReturns(spy, results, msg, value)); + } + }); + }); + [{ + name: "toHaveLastResolvedWith", + condition: (spy, value) => { + const result = spy.mock.settledResults.at(-1); + return Boolean(result && result.type === "fulfilled" && equals(result.value, value)); + }, + action: "resolve" + }, { + name: ["toHaveLastReturnedWith", "lastReturnedWith"], + condition: (spy, value) => { + const result = spy.mock.results.at(-1); + return Boolean(result && result.type === "return" && equals(result.value, value)); + }, + action: "return" + }].forEach(({ name, condition, action }) => { + def(name, function(value) { + const spy = getSpy(this); + const results = action === "return" ? spy.mock.results : spy.mock.settledResults; + const result = results.at(-1); + const spyName = spy.getMockName(); + this.assert(condition(spy, value), `expected last "${spyName}" call to ${action} #{exp}`, `expected last "${spyName}" call to not ${action} #{exp}`, value, result === null || result === void 0 ? void 0 : result.value); + }); + }); + [{ + name: "toHaveNthResolvedWith", + condition: (spy, index, value) => { + const result = spy.mock.settledResults[index - 1]; + return result && result.type === "fulfilled" && equals(result.value, value); + }, + action: "resolve" + }, { + name: ["toHaveNthReturnedWith", "nthReturnedWith"], + condition: (spy, index, value) => { + const result = spy.mock.results[index - 1]; + return result && result.type === "return" && equals(result.value, value); + }, + action: "return" + }].forEach(({ name, condition, action }) => { + def(name, function(nthCall, value) { + const spy = getSpy(this); + const spyName = spy.getMockName(); + const results = action === "return" ? spy.mock.results : spy.mock.settledResults; + const result = results[nthCall - 1]; + const ordinalCall = `${ordinalOf(nthCall)} call`; + this.assert(condition(spy, nthCall, value), `expected ${ordinalCall} "${spyName}" call to ${action} #{exp}`, `expected ${ordinalCall} "${spyName}" call to not ${action} #{exp}`, value, result === null || result === void 0 ? void 0 : result.value); + }); + }); + // @ts-expect-error @internal + def("withContext", function(context) { + for (const key in context) { + utils.flag(this, key, context[key]); + } + return this; + }); + utils.addProperty(chai.Assertion.prototype, "resolves", function __VITEST_RESOLVES__() { + const error = new Error("resolves"); + utils.flag(this, "promise", "resolves"); + utils.flag(this, "error", error); + const test = utils.flag(this, "vitest-test"); + const obj = utils.flag(this, "object"); + if (utils.flag(this, "poll")) { + throw new SyntaxError(`expect.poll() is not supported in combination with .resolves`); + } + if (typeof (obj === null || obj === void 0 ? void 0 : obj.then) !== "function") { + throw new TypeError(`You must provide a Promise to expect() when using .resolves, not '${typeof obj}'.`); + } + const proxy = new Proxy(this, { get: (target, key, receiver) => { + const result = Reflect.get(target, key, receiver); + if (typeof result !== "function") { + return result instanceof chai.Assertion ? proxy : result; + } + return (...args) => { + utils.flag(this, "_name", key); + const promise = obj.then((value) => { + utils.flag(this, "object", value); + return result.call(this, ...args); + }, (err) => { + const _error = new AssertionError(`promise rejected "${utils.inspect(err)}" instead of resolving`, { showDiff: false }); + _error.cause = err; + _error.stack = error.stack.replace(error.message, _error.message); + throw _error; + }); + return recordAsyncExpect(test, promise, createAssertionMessage(utils, this, !!args.length), error); + }; + } }); + return proxy; + }); + utils.addProperty(chai.Assertion.prototype, "rejects", function __VITEST_REJECTS__() { + const error = new Error("rejects"); + utils.flag(this, "promise", "rejects"); + utils.flag(this, "error", error); + const test = utils.flag(this, "vitest-test"); + const obj = utils.flag(this, "object"); + const wrapper = typeof obj === "function" ? obj() : obj; + if (utils.flag(this, "poll")) { + throw new SyntaxError(`expect.poll() is not supported in combination with .rejects`); + } + if (typeof (wrapper === null || wrapper === void 0 ? void 0 : wrapper.then) !== "function") { + throw new TypeError(`You must provide a Promise to expect() when using .rejects, not '${typeof wrapper}'.`); + } + const proxy = new Proxy(this, { get: (target, key, receiver) => { + const result = Reflect.get(target, key, receiver); + if (typeof result !== "function") { + return result instanceof chai.Assertion ? proxy : result; + } + return (...args) => { + utils.flag(this, "_name", key); + const promise = wrapper.then((value) => { + const _error = new AssertionError(`promise resolved "${utils.inspect(value)}" instead of rejecting`, { + showDiff: true, + expected: new Error("rejected promise"), + actual: value + }); + _error.stack = error.stack.replace(error.message, _error.message); + throw _error; + }, (err) => { + utils.flag(this, "object", err); + return result.call(this, ...args); + }); + return recordAsyncExpect(test, promise, createAssertionMessage(utils, this, !!args.length), error); + }; + } }); + return proxy; + }); +}; +function ordinalOf(i) { + const j = i % 10; + const k = i % 100; + if (j === 1 && k !== 11) { + return `${i}st`; + } + if (j === 2 && k !== 12) { + return `${i}nd`; + } + if (j === 3 && k !== 13) { + return `${i}rd`; + } + return `${i}th`; +} +function formatCalls(spy, msg, showActualCall) { + if (spy.mock.calls.length) { + msg += c.gray(`\n\nReceived: \n\n${spy.mock.calls.map((callArg, i) => { + let methodCall = c.bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call:\n\n`); + if (showActualCall) { + methodCall += diff(showActualCall, callArg, { omitAnnotationLines: true }); + } else { + methodCall += stringify(callArg).split("\n").map((line) => ` ${line}`).join("\n"); + } + methodCall += "\n"; + return methodCall; + }).join("\n")}`); + } + msg += c.gray(`\n\nNumber of calls: ${c.bold(spy.mock.calls.length)}\n`); + return msg; +} +function formatReturns(spy, results, msg, showActualReturn) { + if (results.length) { + msg += c.gray(`\n\nReceived: \n\n${results.map((callReturn, i) => { + let methodCall = c.bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call return:\n\n`); + if (showActualReturn) { + methodCall += diff(showActualReturn, callReturn.value, { omitAnnotationLines: true }); + } else { + methodCall += stringify(callReturn).split("\n").map((line) => ` ${line}`).join("\n"); + } + methodCall += "\n"; + return methodCall; + }).join("\n")}`); + } + msg += c.gray(`\n\nNumber of calls: ${c.bold(spy.mock.calls.length)}\n`); + return msg; +} + +function getMatcherState(assertion, expect) { + const obj = assertion._obj; + const isNot = util.flag(assertion, "negate"); + const promise = util.flag(assertion, "promise") || ""; + const customMessage = util.flag(assertion, "message"); + const jestUtils = { + ...getMatcherUtils(), + diff, + stringify, + iterableEquality, + subsetEquality + }; + let task = util.flag(assertion, "vitest-test"); + const currentTestName = (task === null || task === void 0 ? void 0 : task.fullTestName) ?? ""; + if ((task === null || task === void 0 ? void 0 : task.type) !== "test") { + task = undefined; + } + const matcherState = { + ...getState(expect), + task, + currentTestName, + customTesters: getCustomEqualityTesters(), + isNot, + utils: jestUtils, + promise, + equals, + suppressedErrors: [], + soft: util.flag(assertion, "soft"), + poll: util.flag(assertion, "poll") + }; + return { + state: matcherState, + isNot, + obj, + customMessage + }; +} +class JestExtendError extends Error { + constructor(message, actual, expected) { + super(message); + this.actual = actual; + this.expected = expected; + } +} +function JestExtendPlugin(c, expect, matchers) { + return (_, utils) => { + Object.entries(matchers).forEach(([expectAssertionName, expectAssertion]) => { + function expectWrapper(...args) { + const { state, isNot, obj, customMessage } = getMatcherState(this, expect); + const result = expectAssertion.call(state, obj, ...args); + if (result && typeof result === "object" && typeof result.then === "function") { + const thenable = result; + return thenable.then(({ pass, message, actual, expected }) => { + if (pass && isNot || !pass && !isNot) { + const errorMessage = customMessage != null ? customMessage : message(); + throw new JestExtendError(errorMessage, actual, expected); + } + }); + } + const { pass, message, actual, expected } = result; + if (pass && isNot || !pass && !isNot) { + const errorMessage = customMessage != null ? customMessage : message(); + throw new JestExtendError(errorMessage, actual, expected); + } + } + const softWrapper = wrapAssertion(utils, expectAssertionName, expectWrapper); + utils.addMethod(globalThis[JEST_MATCHERS_OBJECT].matchers, expectAssertionName, softWrapper); + utils.addMethod(c.Assertion.prototype, expectAssertionName, softWrapper); + class CustomMatcher extends AsymmetricMatcher { + constructor(inverse = false, ...sample) { + super(sample, inverse); + } + asymmetricMatch(other) { + const { pass } = expectAssertion.call(this.getMatcherContext(expect), other, ...this.sample); + return this.inverse ? !pass : pass; + } + toString() { + return `${this.inverse ? "not." : ""}${expectAssertionName}`; + } + getExpectedType() { + return "any"; + } + toAsymmetricMatcher() { + return `${this.toString()}<${this.sample.map((item) => stringify(item)).join(", ")}>`; + } + } + const customMatcher = (...sample) => new CustomMatcher(false, ...sample); + Object.defineProperty(expect, expectAssertionName, { + configurable: true, + enumerable: true, + value: customMatcher, + writable: true + }); + Object.defineProperty(expect.not, expectAssertionName, { + configurable: true, + enumerable: true, + value: (...sample) => new CustomMatcher(true, ...sample), + writable: true + }); + // keep track of asymmetric matchers on global so that it can be copied over to local context's `expect`. + // note that the negated variant is automatically shared since it's assigned on the single `expect.not` object. + Object.defineProperty(globalThis[ASYMMETRIC_MATCHERS_OBJECT], expectAssertionName, { + configurable: true, + enumerable: true, + value: customMatcher, + writable: true + }); + }); + }; +} +const JestExtend = (chai, utils) => { + utils.addMethod(chai.expect, "extend", (expect, expects) => { + use(JestExtendPlugin(chai, expect, expects)); + }); +}; + +export { ASYMMETRIC_MATCHERS_OBJECT, Any, Anything, ArrayContaining, AsymmetricMatcher, GLOBAL_EXPECT, JEST_MATCHERS_OBJECT, JestAsymmetricMatchers, JestChaiExpect, JestExtend, MATCHERS_OBJECT, ObjectContaining, SchemaMatching, StringContaining, StringMatching, addCustomEqualityTesters, arrayBufferEquality, customMatchers, equals, fnNameFor, generateToBeMessage, getObjectKeys, getObjectSubset, getState, hasAsymmetric, hasProperty, isA, isAsymmetric, isImmutableUnorderedKeyed, isImmutableUnorderedSet, isStandardSchema, iterableEquality, pluralize, setState, sparseArrayEquality, subsetEquality, typeEquality }; diff --git a/vanilla/node_modules/@vitest/expect/package.json b/vanilla/node_modules/@vitest/expect/package.json new file mode 100644 index 0000000..14c0b9e --- /dev/null +++ b/vanilla/node_modules/@vitest/expect/package.json @@ -0,0 +1,46 @@ +{ + "name": "@vitest/expect", + "type": "module", + "version": "4.0.18", + "description": "Jest's expect matchers as a Chai plugin", + "license": "MIT", + "funding": "https://opencollective.com/vitest", + "homepage": "https://github.com/vitest-dev/vitest/tree/main/packages/expect#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/vitest-dev/vitest.git", + "directory": "packages/expect" + }, + "bugs": { + "url": "https://github.com/vitest-dev/vitest/issues" + }, + "sideEffects": false, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "./*": "./*" + }, + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18" + }, + "devDependencies": { + "@vitest/runner": "4.0.18" + }, + "scripts": { + "build": "premove dist && rollup -c", + "dev": "rollup -c --watch" + } +}
\ No newline at end of file |
