UEA-Prodem

This commit is contained in:
2026-06-10 12:38:42 -03:00
parent 3f33154e16
commit c41625e542
9352 changed files with 1128292 additions and 14752 deletions
+21
View File
@@ -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.
+25
View File
@@ -0,0 +1,25 @@
# @vitest/expect
[![NPM version](https://img.shields.io/npm/v/@vitest/runner?color=a1b858&label=)](https://npmx.dev/package/@vitest/runner)
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)
```
[GitHub](https://github.com/vitest-dev/vitest/tree/main/packages/expect) | [Documentation](https://vitest.dev/api/expect)
+930
View File
@@ -0,0 +1,930 @@
import { MockInstance } from '@vitest/spy';
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 };
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;
/**
* @deprecated exists only in types
*/
dontThrow?: () => void;
/**
* @deprecated exists only in types
*/
error?: Error;
equals: (a: unknown, b: unknown, customTesters?: Array<Tester>, strictCheck?: boolean) => boolean;
/**
* @deprecated exists only in types
*/
expand?: boolean;
expectedAssertionsNumber?: number | null;
expectedAssertionsNumberErrorGen?: (() => Error) | null;
isExpectingAssertions?: boolean;
isExpectingAssertionsError?: Error | null;
isNot: boolean;
promise: string;
/**
* @deprecated exists only in types
*/
suppressedErrors: Array<Error>;
testPath?: string;
utils: ReturnType<typeof getMatcherUtils> & {
diff: typeof diff;
stringify: typeof stringify;
iterableEquality: Tester;
subsetEquality: Tester;
};
soft?: boolean;
poll?: boolean;
/**
* The same assertion instance that chai plugins receive.
* @experimental
* @see {@link https://www.chaijs.com/guide/plugins/} Core Plugin Concepts
*/
readonly assertion: Assertion;
}
interface SyncExpectationResult {
pass: boolean;
message: () => string;
actual?: any;
expected?: any;
meta?: object;
}
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.
*
* @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);
* @deprecated Use `toHaveBeenCalledTimes` instead
*/
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();
* @deprecated Use `toHaveBeenCalled` instead
*/
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);
* @deprecated Use `toHaveBeenCalledWith` instead
*/
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;
/**
* 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;
/**
* 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);
* expect(() => { throw 42 }).toThrow(42);
*/
toThrow: (expected?: any) => 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);
* expect(() => { throw 42 }).toThrowError(42);
* @deprecated Use `toThrow` instead
*/
toThrowError: (expected?: any) => 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();
* @deprecated Use `toHaveReturned` instead
*/
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);
* @deprecated Use `toHaveReturnedTimes` instead
*/
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');
* @deprecated Use `toHaveReturnedWith` instead
*/
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 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;
}
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>, ChaiMockAssertion, 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>;
}
/**
* Chai-style assertions for spy/mock testing.
* These provide sinon-chai compatible assertion names that delegate to Jest-style implementations.
*/
interface ChaiMockAssertion {
/**
* Checks that a spy was called at least once.
* Chai-style equivalent of `toHaveBeenCalled`.
*
* @example
* expect(spy).to.have.been.called
*/
readonly called: Assertion;
/**
* Checks that a spy was called a specific number of times.
* Chai-style equivalent of `toHaveBeenCalledTimes`.
*
* @example
* expect(spy).to.have.callCount(3)
*/
callCount: (count: number) => void;
/**
* Checks that a spy was called with specific arguments at least once.
* Chai-style equivalent of `toHaveBeenCalledWith`.
*
* @example
* expect(spy).to.have.been.calledWith('arg1', 'arg2')
*/
calledWith: <E extends any[]>(...args: E) => void;
/**
* Checks that a spy was called exactly once.
* Chai-style equivalent of `toHaveBeenCalledOnce`.
*
* @example
* expect(spy).to.have.been.calledOnce
*/
readonly calledOnce: Assertion;
/**
* Checks that a spy was called exactly once with specific arguments.
* Chai-style equivalent of `toHaveBeenCalledExactlyOnceWith`.
*
* @example
* expect(spy).to.have.been.calledOnceWith('arg1', 'arg2')
*/
calledOnceWith: <E extends any[]>(...args: E) => void;
/**
* Checks that the last call to a spy was made with specific arguments.
* Chai-style equivalent of `toHaveBeenLastCalledWith`.
*
* @example
* expect(spy).to.have.been.lastCalledWith('arg1', 'arg2')
*/
lastCalledWith: <E extends any[]>(...args: E) => void;
/**
* Checks that the nth call to a spy was made with specific arguments.
* Chai-style equivalent of `toHaveBeenNthCalledWith`.
*
* @example
* expect(spy).to.have.been.nthCalledWith(2, 'arg1', 'arg2')
*/
nthCalledWith: <E extends any[]>(n: number, ...args: E) => void;
/**
* Checks that a spy returned a specific value at least once.
* Chai-style equivalent of `toHaveReturnedWith`.
*
* @example
* expect(spy).to.have.returned('value')
*/
returned: <E>(value: E) => void;
/**
* Checks that a spy returned a specific value at least once.
* Chai-style equivalent of `toHaveReturnedWith`.
*
* @example
* expect(spy).to.have.returnedWith('value')
*/
returnedWith: <E>(value: E) => void;
/**
* Checks that a spy returned successfully a specific number of times.
* Chai-style equivalent of `toHaveReturnedTimes`.
*
* @example
* expect(spy).to.have.returnedTimes(3)
*/
returnedTimes: (count: number) => void;
/**
* Checks that the last return value of a spy matches the expected value.
* Chai-style equivalent of `toHaveLastReturnedWith`.
*
* @example
* expect(spy).to.have.lastReturnedWith('value')
*/
lastReturnedWith: <E>(value: E) => void;
/**
* Checks that the nth return value of a spy matches the expected value.
* Chai-style equivalent of `toHaveNthReturnedWith`.
*
* @example
* expect(spy).to.have.nthReturnedWith(2, 'value')
*/
nthReturnedWith: <E>(n: number, value: E) => void;
/**
* Checks that a spy was called before another spy.
* Chai-style equivalent of `toHaveBeenCalledBefore`.
*
* @example
* expect(spy1).to.have.been.calledBefore(spy2)
*/
calledBefore: (mock: MockInstance, failIfNoFirstInvocation?: boolean) => void;
/**
* Checks that a spy was called after another spy.
* Chai-style equivalent of `toHaveBeenCalledAfter`.
*
* @example
* expect(spy1).to.have.been.calledAfter(spy2)
*/
calledAfter: (mock: MockInstance, failIfNoFirstInvocation?: boolean) => void;
/**
* Checks that a spy was called exactly twice.
* Chai-style equivalent of `toHaveBeenCalledTimes(2)`.
*
* @example
* expect(spy).to.have.been.calledTwice
*/
readonly calledTwice: Assertion;
/**
* Checks that a spy was called exactly three times.
* Chai-style equivalent of `toHaveBeenCalledTimes(3)`.
*
* @example
* expect(spy).to.have.been.calledThrice
*/
readonly calledThrice: Assertion;
}
declare global {
namespace jest {
interface Matchers<
R,
T = {}
> {}
}
}
declare const ChaiStyleAssertions: ChaiPlugin;
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;
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 isError(value: unknown): value is Error;
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;
declare function createAssertionMessage(util: Chai.ChaiUtils, assertion: Chai.Assertion, hasArgs: boolean): string;
declare function recordAsyncExpect(_test: any, promise: Promise<any>, assertion: string, error: Error, isSoft?: boolean): Promise<any>;
/** wrap assertion function to support `expect.soft` and provide assertion name as `_name` */
declare function wrapAssertion(utils: Chai.ChaiUtils, name: string, fn: (this: Chai.AssertionStatic & Assertion, ...args: any[]) => void | PromiseLike<void>): (this: Chai.AssertionStatic & Assertion, ...args: any[]) => void | PromiseLike<void>;
export { ASYMMETRIC_MATCHERS_OBJECT, Any, Anything, ArrayContaining, AsymmetricMatcher, ChaiStyleAssertions, GLOBAL_EXPECT, JEST_MATCHERS_OBJECT, JestAsymmetricMatchers, JestChaiExpect, JestExtend, MATCHERS_OBJECT, ObjectContaining, SchemaMatching, StringContaining, StringMatching, addCustomEqualityTesters, arrayBufferEquality, createAssertionMessage, customMatchers, equals, fnNameFor, generateToBeMessage, getObjectKeys, getObjectSubset, getState, hasAsymmetric, hasProperty, isA, isAsymmetric, isError, isImmutableUnorderedKeyed, isImmutableUnorderedSet, isStandardSchema, iterableEquality, pluralize, recordAsyncExpect, setState, sparseArrayEquality, subsetEquality, typeEquality, wrapAssertion };
export type { Assertion, AsymmetricMatcherInterface, AsymmetricMatchersContaining, AsyncExpectationResult, ChaiMockAssertion, ChaiPlugin, DeeplyAllowMatchers, ExpectStatic, ExpectationResult, JestAssertion, MatcherHintOptions, MatcherState, Matchers, MatchersObject, PromisifyAssertion, RawMatcherFn, SyncExpectationResult, Tester, TesterContext };
+1952
View File
File diff suppressed because it is too large Load Diff
+52
View File
@@ -0,0 +1,52 @@
{
"name": "@vitest/expect",
"type": "module",
"version": "4.1.8",
"description": "Jest's expect matchers as a Chai plugin",
"license": "MIT",
"funding": "https://opencollective.com/vitest",
"homepage": "https://vitest.dev/api/expect",
"repository": {
"type": "git",
"url": "git+https://github.com/vitest-dev/vitest.git",
"directory": "packages/expect"
},
"bugs": {
"url": "https://github.com/vitest-dev/vitest/issues"
},
"keywords": [
"vitest",
"test",
"chai",
"assertion"
],
"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.1.0",
"@types/chai": "^5.2.2",
"chai": "^6.2.2",
"tinyrainbow": "^3.1.0",
"@vitest/utils": "4.1.8",
"@vitest/spy": "4.1.8"
},
"devDependencies": {
"@vitest/runner": "4.1.8"
},
"scripts": {
"build": "premove dist && rollup -c",
"dev": "rollup -c --watch"
}
}
+21
View File
@@ -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.
+7
View File
@@ -0,0 +1,7 @@
# @vitest/mocker
[![NPM version](https://img.shields.io/npm/v/@vitest/mocker?color=a1b858&label=)](https://npmx.dev/package/@vitest/mocker)
Vitest's module mocker implementation.
[GitHub](https://github.com/vitest-dev/vitest/blob/main/packages/mocker/) | [Documentation](https://github.com/vitest-dev/vitest/blob/main/packages/mocker/EXPORTS.md)
+2
View File
@@ -0,0 +1,2 @@
export { };
+10
View File
@@ -0,0 +1,10 @@
import { M as ModuleMockerServerInterceptor } from './chunk-interceptor-native.js';
import { registerModuleMocker } from './register.js';
import './chunk-mocker.js';
import './chunk-helpers.js';
import './index.js';
import './chunk-registry.js';
import './chunk-pathe.M-eThtNZ.js';
import '@vitest/spy';
registerModuleMocker(() => new ModuleMockerServerInterceptor());
+13
View File
@@ -0,0 +1,13 @@
import MagicString from 'magic-string';
interface AutomockOptions {
/**
* @default "__vitest_mocker__"
*/
globalThisAccessor?: string;
id?: string;
}
declare function automockModule(code: string, mockType: "automock" | "autospy", parse: (code: string) => any, options?: AutomockOptions): MagicString;
export { automockModule };
export type { AutomockOptions };
+8
View File
@@ -0,0 +1,8 @@
import 'node:fs';
import 'node:url';
import 'magic-string';
export { a as automockModule } from './chunk-automock.js';
import 'estree-walker';
import 'node:module';
import 'node:path';
import './chunk-helpers.js';
+53
View File
@@ -0,0 +1,53 @@
import { M as ModuleMockerInterceptor } from './mocker.d-QEntlm6J.js';
export { C as CompilerHintsOptions, b as ModuleMocker, a as ModuleMockerCompilerHints, c as ModuleMockerConfig, d as ModuleMockerRPC, R as ResolveIdResult, e as ResolveMockResult, f as createCompilerHints } from './mocker.d-QEntlm6J.js';
import { StartOptions, SetupWorker } from 'msw/browser';
import { M as MockerRegistry, a as MockedModule } from './types.d-BjI5eAwu.js';
import '@vitest/spy';
import './index.d-B41z0AuW.js';
interface ModuleMockerMSWInterceptorOptions {
/**
* The identifier to access the globalThis object in the worker.
* This will be injected into the script as is, so make sure it's a valid JS expression.
* @example
* ```js
* // globalThisAccessor: '__my_variable__' produces:
* globalThis[__my_variable__]
* // globalThisAccessor: 'Symbol.for('secret:mocks')' produces:
* globalThis[Symbol.for('secret:mocks')]
* // globalThisAccessor: '"__vitest_mocker__"' (notice quotes) produces:
* globalThis["__vitest_mocker__"]
* ```
* @default `"__vitest_mocker__"`
*/
globalThisAccessor?: string;
/**
* Options passed down to `msw.setupWorker().start(options)`
*/
mswOptions?: StartOptions;
/**
* A pre-configured `msw.setupWorker` instance.
*/
mswWorker?: SetupWorker;
}
declare class ModuleMockerMSWInterceptor implements ModuleMockerInterceptor {
private readonly options;
protected readonly mocks: MockerRegistry;
private startPromise;
private worker;
constructor(options?: ModuleMockerMSWInterceptorOptions);
register(module: MockedModule): Promise<void>;
delete(url: string): Promise<void>;
invalidate(): Promise<void>;
private resolveManualMock;
protected init(): Promise<SetupWorker>;
}
declare class ModuleMockerServerInterceptor implements ModuleMockerInterceptor {
register(module: MockedModule): Promise<void>;
delete(id: string): Promise<void>;
invalidate(): Promise<void>;
}
export { ModuleMockerInterceptor, ModuleMockerMSWInterceptor, ModuleMockerServerInterceptor };
export type { ModuleMockerMSWInterceptorOptions };
+92
View File
@@ -0,0 +1,92 @@
export { M as ModuleMocker, c as createCompilerHints } from './chunk-mocker.js';
import { M as MockerRegistry } from './chunk-registry.js';
import { c as createManualModuleSource, a as cleanUrl } from './chunk-utils.js';
export { M as ModuleMockerServerInterceptor } from './chunk-interceptor-native.js';
import './chunk-helpers.js';
import './index.js';
import './chunk-pathe.M-eThtNZ.js';
class ModuleMockerMSWInterceptor {
mocks = new MockerRegistry();
startPromise;
worker;
constructor(options = {}) {
this.options = options;
if (!options.globalThisAccessor) {
options.globalThisAccessor = "\"__vitest_mocker__\"";
}
}
async register(module) {
await this.init();
this.mocks.add(module);
}
async delete(url) {
await this.init();
this.mocks.delete(url);
}
async invalidate() {
this.mocks.clear();
}
async resolveManualMock(mock) {
const exports$1 = Object.keys(await mock.resolve());
const text = createManualModuleSource(mock.url, exports$1, this.options.globalThisAccessor);
return new Response(text, { headers: { "Content-Type": "application/javascript" } });
}
async init() {
if (this.worker) {
return this.worker;
}
if (this.startPromise) {
return this.startPromise;
}
const worker = this.options.mswWorker;
this.startPromise = Promise.all([worker ? { setupWorker(handler) {
worker.use(handler);
return worker;
} } : import('msw/browser'), import('msw/core/http')]).then(([{ setupWorker }, { http }]) => {
const worker = setupWorker(http.get(/.+/, async ({ request }) => {
const path = cleanQuery(request.url.slice(location.origin.length));
if (!this.mocks.has(path)) {
return passthrough();
}
const mock = this.mocks.get(path);
switch (mock.type) {
case "manual": return this.resolveManualMock(mock);
case "automock":
case "autospy": return Response.redirect(injectQuery(path, `mock=${mock.type}`));
case "redirect": return Response.redirect(mock.redirect);
default: throw new Error(`Unknown mock type: ${mock.type}`);
}
}));
return worker.start(this.options.mswOptions).then(() => worker);
}).finally(() => {
this.worker = worker;
this.startPromise = undefined;
});
return await this.startPromise;
}
}
const trailingSeparatorRE = /[?&]$/;
const timestampRE = /\bt=\d{13}&?\b/;
const versionRE = /\bv=\w{8}&?\b/;
function cleanQuery(url) {
return url.replace(timestampRE, "").replace(versionRE, "").replace(trailingSeparatorRE, "");
}
function passthrough() {
return new Response(null, {
status: 302,
statusText: "Passthrough",
headers: { "x-msw-intention": "passthrough" }
});
}
const replacePercentageRE = /%/g;
function injectQuery(url, queryToInject) {
// encode percents for consistent behavior with pathToFileURL
// see #2614 for details
const resolvedUrl = new URL(url.replace(replacePercentageRE, "%25"), location.href);
const { search, hash } = resolvedUrl;
const pathname = cleanUrl(url);
return `${pathname}?${queryToInject}${search ? `&${search.slice(1)}` : ""}${hash ?? ""}`;
}
export { ModuleMockerMSWInterceptor };
File diff suppressed because one or more lines are too long
+44
View File
@@ -0,0 +1,44 @@
/**
* Get original stacktrace without source map support the most performant way.
* - Create only 1 stack frame.
* - Rewrite prepareStackTrace to bypass "support-stack-trace" (usually takes ~250ms).
*/
function createSimpleStackTrace(options) {
const { message = "$$stack trace error", stackTraceLimit = 1 } = options || {};
const limit = Error.stackTraceLimit;
const prepareStackTrace = Error.prepareStackTrace;
Error.stackTraceLimit = stackTraceLimit;
Error.prepareStackTrace = (e) => e.stack;
const err = new Error(message);
const stackTrace = err.stack || "";
Error.prepareStackTrace = prepareStackTrace;
Error.stackTraceLimit = limit;
return stackTrace;
}
function filterOutComments(s) {
const result = [];
let commentState = "none";
for (let i = 0; i < s.length; ++i) {
if (commentState === "singleline") {
if (s[i] === "\n") {
commentState = "none";
}
} else if (commentState === "multiline") {
if (s[i - 1] === "*" && s[i] === "/") {
commentState = "none";
}
} else if (commentState === "none") {
if (s[i] === "/" && s[i + 1] === "/") {
commentState = "singleline";
} else if (s[i] === "/" && s[i + 1] === "*") {
commentState = "multiline";
i += 2;
} else {
result.push(s[i]);
}
}
}
return result.join("");
}
export { createSimpleStackTrace as c, filterOutComments as f };
+659
View File
@@ -0,0 +1,659 @@
import MagicString from 'magic-string';
import { e as esmWalker } from './chunk-automock.js';
// AST walker module for ESTree compatible trees
function makeTest(test) {
if (typeof test === "string")
{ return function (type) { return type === test; } }
else if (!test)
{ return function () { return true; } }
else
{ return test }
}
var Found = function Found(node, state) { this.node = node; this.state = state; };
// Find the innermost node of a given type that contains the given
// position. Interface similar to findNodeAt.
function findNodeAround(node, pos, test, baseVisitor, state) {
test = makeTest(test);
if (!baseVisitor) { baseVisitor = base; }
try {
(function c(node, st, override) {
var type = override || node.type;
if (node.start > pos || node.end < pos) { return }
visitNode(baseVisitor, type, node, st, c);
if (test(type, node)) { throw new Found(node, st) }
})(node, state);
} catch (e) {
if (e instanceof Found) { return e }
throw e
}
}
function skipThrough(node, st, c) { c(node, st); }
function ignore(_node, _st, _c) {}
function visitNode(baseVisitor, type, node, st, c) {
if (baseVisitor[type] == null) { throw new Error(("No walker function defined for node type " + type)) }
baseVisitor[type](node, st, c);
}
// Node walkers.
var base = {};
base.Program = base.BlockStatement = base.StaticBlock = function (node, st, c) {
for (var i = 0, list = node.body; i < list.length; i += 1)
{
var stmt = list[i];
c(stmt, st, "Statement");
}
};
base.Statement = skipThrough;
base.EmptyStatement = ignore;
base.ExpressionStatement = base.ParenthesizedExpression = base.ChainExpression =
function (node, st, c) { return c(node.expression, st, "Expression"); };
base.IfStatement = function (node, st, c) {
c(node.test, st, "Expression");
c(node.consequent, st, "Statement");
if (node.alternate) { c(node.alternate, st, "Statement"); }
};
base.LabeledStatement = function (node, st, c) { return c(node.body, st, "Statement"); };
base.BreakStatement = base.ContinueStatement = ignore;
base.WithStatement = function (node, st, c) {
c(node.object, st, "Expression");
c(node.body, st, "Statement");
};
base.SwitchStatement = function (node, st, c) {
c(node.discriminant, st, "Expression");
for (var i = 0, list = node.cases; i < list.length; i += 1) {
var cs = list[i];
c(cs, st);
}
};
base.SwitchCase = function (node, st, c) {
if (node.test) { c(node.test, st, "Expression"); }
for (var i = 0, list = node.consequent; i < list.length; i += 1)
{
var cons = list[i];
c(cons, st, "Statement");
}
};
base.ReturnStatement = base.YieldExpression = base.AwaitExpression = function (node, st, c) {
if (node.argument) { c(node.argument, st, "Expression"); }
};
base.ThrowStatement = base.SpreadElement =
function (node, st, c) { return c(node.argument, st, "Expression"); };
base.TryStatement = function (node, st, c) {
c(node.block, st, "Statement");
if (node.handler) { c(node.handler, st); }
if (node.finalizer) { c(node.finalizer, st, "Statement"); }
};
base.CatchClause = function (node, st, c) {
if (node.param) { c(node.param, st, "Pattern"); }
c(node.body, st, "Statement");
};
base.WhileStatement = base.DoWhileStatement = function (node, st, c) {
c(node.test, st, "Expression");
c(node.body, st, "Statement");
};
base.ForStatement = function (node, st, c) {
if (node.init) { c(node.init, st, "ForInit"); }
if (node.test) { c(node.test, st, "Expression"); }
if (node.update) { c(node.update, st, "Expression"); }
c(node.body, st, "Statement");
};
base.ForInStatement = base.ForOfStatement = function (node, st, c) {
c(node.left, st, "ForInit");
c(node.right, st, "Expression");
c(node.body, st, "Statement");
};
base.ForInit = function (node, st, c) {
if (node.type === "VariableDeclaration") { c(node, st); }
else { c(node, st, "Expression"); }
};
base.DebuggerStatement = ignore;
base.FunctionDeclaration = function (node, st, c) { return c(node, st, "Function"); };
base.VariableDeclaration = function (node, st, c) {
for (var i = 0, list = node.declarations; i < list.length; i += 1)
{
var decl = list[i];
c(decl, st);
}
};
base.VariableDeclarator = function (node, st, c) {
c(node.id, st, "Pattern");
if (node.init) { c(node.init, st, "Expression"); }
};
base.Function = function (node, st, c) {
if (node.id) { c(node.id, st, "Pattern"); }
for (var i = 0, list = node.params; i < list.length; i += 1)
{
var param = list[i];
c(param, st, "Pattern");
}
c(node.body, st, node.expression ? "Expression" : "Statement");
};
base.Pattern = function (node, st, c) {
if (node.type === "Identifier")
{ c(node, st, "VariablePattern"); }
else if (node.type === "MemberExpression")
{ c(node, st, "MemberPattern"); }
else
{ c(node, st); }
};
base.VariablePattern = ignore;
base.MemberPattern = skipThrough;
base.RestElement = function (node, st, c) { return c(node.argument, st, "Pattern"); };
base.ArrayPattern = function (node, st, c) {
for (var i = 0, list = node.elements; i < list.length; i += 1) {
var elt = list[i];
if (elt) { c(elt, st, "Pattern"); }
}
};
base.ObjectPattern = function (node, st, c) {
for (var i = 0, list = node.properties; i < list.length; i += 1) {
var prop = list[i];
if (prop.type === "Property") {
if (prop.computed) { c(prop.key, st, "Expression"); }
c(prop.value, st, "Pattern");
} else if (prop.type === "RestElement") {
c(prop.argument, st, "Pattern");
}
}
};
base.Expression = skipThrough;
base.ThisExpression = base.Super = base.MetaProperty = ignore;
base.ArrayExpression = function (node, st, c) {
for (var i = 0, list = node.elements; i < list.length; i += 1) {
var elt = list[i];
if (elt) { c(elt, st, "Expression"); }
}
};
base.ObjectExpression = function (node, st, c) {
for (var i = 0, list = node.properties; i < list.length; i += 1)
{
var prop = list[i];
c(prop, st);
}
};
base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration;
base.SequenceExpression = function (node, st, c) {
for (var i = 0, list = node.expressions; i < list.length; i += 1)
{
var expr = list[i];
c(expr, st, "Expression");
}
};
base.TemplateLiteral = function (node, st, c) {
for (var i = 0, list = node.quasis; i < list.length; i += 1)
{
var quasi = list[i];
c(quasi, st);
}
for (var i$1 = 0, list$1 = node.expressions; i$1 < list$1.length; i$1 += 1)
{
var expr = list$1[i$1];
c(expr, st, "Expression");
}
};
base.TemplateElement = ignore;
base.UnaryExpression = base.UpdateExpression = function (node, st, c) {
c(node.argument, st, "Expression");
};
base.BinaryExpression = base.LogicalExpression = function (node, st, c) {
c(node.left, st, "Expression");
c(node.right, st, "Expression");
};
base.AssignmentExpression = base.AssignmentPattern = function (node, st, c) {
c(node.left, st, "Pattern");
c(node.right, st, "Expression");
};
base.ConditionalExpression = function (node, st, c) {
c(node.test, st, "Expression");
c(node.consequent, st, "Expression");
c(node.alternate, st, "Expression");
};
base.NewExpression = base.CallExpression = function (node, st, c) {
c(node.callee, st, "Expression");
if (node.arguments)
{ for (var i = 0, list = node.arguments; i < list.length; i += 1)
{
var arg = list[i];
c(arg, st, "Expression");
} }
};
base.MemberExpression = function (node, st, c) {
c(node.object, st, "Expression");
if (node.computed) { c(node.property, st, "Expression"); }
};
base.ExportNamedDeclaration = base.ExportDefaultDeclaration = function (node, st, c) {
if (node.declaration)
{ c(node.declaration, st, node.type === "ExportNamedDeclaration" || node.declaration.id ? "Statement" : "Expression"); }
if (node.source) { c(node.source, st, "Expression"); }
if (node.attributes)
{ for (var i = 0, list = node.attributes; i < list.length; i += 1)
{
var attr = list[i];
c(attr, st);
} }
};
base.ExportAllDeclaration = function (node, st, c) {
if (node.exported)
{ c(node.exported, st); }
c(node.source, st, "Expression");
if (node.attributes)
{ for (var i = 0, list = node.attributes; i < list.length; i += 1)
{
var attr = list[i];
c(attr, st);
} }
};
base.ImportAttribute = function (node, st, c) {
c(node.value, st, "Expression");
};
base.ImportDeclaration = function (node, st, c) {
for (var i = 0, list = node.specifiers; i < list.length; i += 1)
{
var spec = list[i];
c(spec, st);
}
c(node.source, st, "Expression");
if (node.attributes)
{ for (var i$1 = 0, list$1 = node.attributes; i$1 < list$1.length; i$1 += 1)
{
var attr = list$1[i$1];
c(attr, st);
} }
};
base.ImportExpression = function (node, st, c) {
c(node.source, st, "Expression");
if (node.options) { c(node.options, st, "Expression"); }
};
base.ImportSpecifier = base.ImportDefaultSpecifier = base.ImportNamespaceSpecifier = base.Identifier = base.PrivateIdentifier = base.Literal = ignore;
base.TaggedTemplateExpression = function (node, st, c) {
c(node.tag, st, "Expression");
c(node.quasi, st, "Expression");
};
base.ClassDeclaration = base.ClassExpression = function (node, st, c) { return c(node, st, "Class"); };
base.Class = function (node, st, c) {
if (node.id) { c(node.id, st, "Pattern"); }
if (node.superClass) { c(node.superClass, st, "Expression"); }
c(node.body, st);
};
base.ClassBody = function (node, st, c) {
for (var i = 0, list = node.body; i < list.length; i += 1)
{
var elt = list[i];
c(elt, st);
}
};
base.MethodDefinition = base.PropertyDefinition = base.Property = function (node, st, c) {
if (node.computed) { c(node.key, st, "Expression"); }
if (node.value) { c(node.value, st, "Expression"); }
};
const API_NOT_FOUND_ERROR = `There are some problems in resolving the mocks API.
You may encounter this issue when importing the mocks API from another module other than 'vitest'.
To fix this issue you can either:
- import the mocks API directly from 'vitest'
- enable the 'globals' option`;
function API_NOT_FOUND_CHECK(names) {
return `\nif (${names.map((name) => `typeof globalThis["${name}"] === "undefined"`).join(" && ")}) ` + `{ throw new Error(${JSON.stringify(API_NOT_FOUND_ERROR)}) }\n`;
}
function isIdentifier(node) {
return node.type === "Identifier";
}
function getNodeTail(code, node) {
let end = node.end;
if (code[node.end] === ";") {
end += 1;
}
if (code[node.end] === "\n") {
return end + 1;
}
if (code[node.end + 1] === "\n") {
end += 1;
}
return end;
}
const regexpHoistable = /\b(?:vi|vitest)\s*\.\s*(?:mock|unmock|hoisted|doMock|doUnmock)\s*\(/;
const hashbangRE = /^#!.*\n/;
// this is a fork of Vite SSR transform
function hoistMocks(code, id, parse, options = {}) {
const needHoisting = (options.regexpHoistable || regexpHoistable).test(code);
if (!needHoisting) {
return;
}
const s = options.magicString?.() || new MagicString(code);
let ast;
try {
ast = parse(code);
} catch (err) {
console.error(`Cannot parse ${id}:\n${err.message}.`);
return;
}
const { hoistableMockMethodNames = ["mock", "unmock"], dynamicImportMockMethodNames = [
"mock",
"unmock",
"doMock",
"doUnmock"
], hoistedMethodNames = ["hoisted"], utilsObjectNames = ["vi", "vitest"], hoistedModule = "vitest" } = options;
// hoist at the start of the file, after the hashbang
const hashbangEnd = hashbangRE.exec(code)?.[0].length ?? 0;
let hoistIndex = hashbangEnd;
let hoistedModuleImported = false;
let uid = 0;
const idToImportMap = new Map();
const imports = [];
// this will transform import statements into dynamic ones, if there are imports
// it will keep the import as is, if we don't need to mock anything
// in browser environment it will wrap the module value with "vitest_wrap_module" function
// that returns a proxy to the module so that named exports can be mocked
function defineImport(importNode) {
const source = importNode.source.value;
// always hoist vitest import to top of the file, so
// "vi" helpers can access it
if (hoistedModule === source) {
hoistedModuleImported = true;
return;
}
const importId = `__vi_import_${uid++}__`;
imports.push({
id: importId,
node: importNode
});
return importId;
}
// 1. check all import statements and record id -> importName map
for (const node of ast.body) {
// import foo from 'foo' --> foo -> __import_foo__.default
// import { baz } from 'foo' --> baz -> __import_foo__.baz
// import * as ok from 'foo' --> ok -> __import_foo__
if (node.type === "ImportDeclaration") {
const importId = defineImport(node);
if (!importId) {
continue;
}
for (const spec of node.specifiers) {
if (spec.type === "ImportSpecifier") {
if (spec.imported.type === "Identifier") {
idToImportMap.set(spec.local.name, `${importId}.${spec.imported.name}`);
} else {
idToImportMap.set(spec.local.name, `${importId}[${JSON.stringify(spec.imported.value)}]`);
}
} else if (spec.type === "ImportDefaultSpecifier") {
idToImportMap.set(spec.local.name, `${importId}.default`);
} else {
// namespace specifier
idToImportMap.set(spec.local.name, importId);
}
}
}
}
const declaredConst = new Set();
const hoistedNodes = new Set();
function createSyntaxError(node, message) {
const _error = new SyntaxError(message);
Error.captureStackTrace(_error, createSyntaxError);
const serializedError = {
name: "SyntaxError",
message: _error.message,
stack: _error.stack
};
if (options.codeFrameGenerator) {
serializedError.frame = options.codeFrameGenerator(node, id, code);
}
return serializedError;
}
function assertNotDefaultExport(node, error) {
const defaultExport = findNodeAround(ast, node.start, "ExportDefaultDeclaration")?.node;
if (defaultExport?.declaration === node || defaultExport?.declaration.type === "AwaitExpression" && defaultExport.declaration.argument === node) {
throw createSyntaxError(defaultExport, error);
}
}
function assertNotNamedExport(node, error) {
const nodeExported = findNodeAround(ast, node.start, "ExportNamedDeclaration")?.node;
if (nodeExported?.declaration === node) {
throw createSyntaxError(nodeExported, error);
}
}
function getVariableDeclaration(node) {
const declarationNode = findNodeAround(ast, node.start, "VariableDeclaration")?.node;
const init = declarationNode?.declarations[0]?.init;
if (init && (init === node || init.type === "AwaitExpression" && init.argument === node)) {
return declarationNode;
}
}
const usedUtilityExports = new Set();
let hasImportMetaVitest = false;
esmWalker(ast, {
onImportMeta(node) {
const property = code.slice(node.end, node.end + 7);
if (property === ".vitest") {
hasImportMetaVitest = true;
}
},
onIdentifier(id, info, parentStack) {
const binding = idToImportMap.get(id.name);
if (!binding) {
return;
}
if (info.hasBindingShortcut) {
s.appendLeft(id.end, `: ${binding}`);
} else if (info.classDeclaration) {
if (!declaredConst.has(id.name)) {
declaredConst.add(id.name);
// locate the top-most node containing the class declaration
const topNode = parentStack[parentStack.length - 2];
s.prependRight(topNode.start, `const ${id.name} = ${binding};\n`);
}
} else if (!info.classExpression) {
s.update(id.start, id.end, binding);
}
},
onDynamicImport(_node) {
// TODO: vi.mock(import) breaks it, and vi.mock('', () => import) also does,
// only move imports that are outside of vi.mock
// backwards compat, don't do if not passed
// if (!options.globalThisAccessor) {
// return
// }
// const globalThisAccessor = options.globalThisAccessor
// const replaceString = `globalThis[${globalThisAccessor}].wrapDynamicImport(() => import(`
// const importSubstring = code.substring(node.start, node.end)
// const hasIgnore = importSubstring.includes('/* @vite-ignore */')
// s.overwrite(
// node.start,
// (node.source as Positioned<Expression>).start,
// replaceString + (hasIgnore ? '/* @vite-ignore */ ' : ''),
// )
// s.overwrite(node.end - 1, node.end, '))')
},
onCallExpression(node) {
if (node.callee.type === "MemberExpression" && isIdentifier(node.callee.object) && utilsObjectNames.includes(node.callee.object.name) && isIdentifier(node.callee.property)) {
const methodName = node.callee.property.name;
usedUtilityExports.add(node.callee.object.name);
if (hoistableMockMethodNames.includes(methodName)) {
const method = `${node.callee.object.name}.${methodName}`;
assertNotDefaultExport(node, `Cannot export the result of "${method}". Remove export declaration because "${method}" doesn\'t return anything.`);
const declarationNode = getVariableDeclaration(node);
if (declarationNode) {
assertNotNamedExport(declarationNode, `Cannot export the result of "${method}". Remove export declaration because "${method}" doesn\'t return anything.`);
}
// rewrite vi.mock(import('..')) into vi.mock('..')
if (node.type === "CallExpression" && node.callee.type === "MemberExpression" && dynamicImportMockMethodNames.includes(node.callee.property.name)) {
const moduleInfo = node.arguments[0];
// vi.mock(import('./path')) -> vi.mock('./path')
if (moduleInfo.type === "ImportExpression") {
const source = moduleInfo.source;
s.overwrite(moduleInfo.start, moduleInfo.end, s.slice(source.start, source.end));
}
// vi.mock(await import('./path')) -> vi.mock('./path')
if (moduleInfo.type === "AwaitExpression" && moduleInfo.argument.type === "ImportExpression") {
const source = moduleInfo.argument.source;
s.overwrite(moduleInfo.start, moduleInfo.end, s.slice(source.start, source.end));
}
}
hoistedNodes.add(node);
} else if (dynamicImportMockMethodNames.includes(methodName)) {
const moduleInfo = node.arguments[0];
let source = null;
if (moduleInfo.type === "ImportExpression") {
source = moduleInfo.source;
}
if (moduleInfo.type === "AwaitExpression" && moduleInfo.argument.type === "ImportExpression") {
source = moduleInfo.argument.source;
}
if (source) {
s.overwrite(moduleInfo.start, moduleInfo.end, s.slice(source.start, source.end));
}
}
if (hoistedMethodNames.includes(methodName)) {
assertNotDefaultExport(node, "Cannot export hoisted variable. You can control hoisting behavior by placing the import from this file first.");
const declarationNode = getVariableDeclaration(node);
if (declarationNode) {
assertNotNamedExport(declarationNode, "Cannot export hoisted variable. You can control hoisting behavior by placing the import from this file first.");
// hoist "const variable = vi.hoisted(() => {})"
hoistedNodes.add(declarationNode);
} else {
const awaitedExpression = findNodeAround(ast, node.start, "AwaitExpression")?.node;
// hoist "await vi.hoisted(async () => {})" or "vi.hoisted(() => {})"
const moveNode = awaitedExpression?.argument === node ? awaitedExpression : node;
hoistedNodes.add(moveNode);
}
}
}
}
});
function getNodeName(node) {
const callee = node.callee || {};
if (callee.type === "MemberExpression" && isIdentifier(callee.property) && isIdentifier(callee.object)) {
const argument = node.arguments[0];
const argStr = argument.type === "Literal" || argument.type === "ImportExpression" ? code.slice(argument.start, argument.end) : "";
return `${callee.object.name}.${callee.property.name}(${argStr})`;
}
return "\"hoisted method\"";
}
function getNodeCall(node) {
if (node.type === "CallExpression") {
return node;
}
if (node.type === "VariableDeclaration") {
const { declarations } = node;
const init = declarations[0].init;
if (init) {
return getNodeCall(init);
}
}
if (node.type === "AwaitExpression") {
const { argument } = node;
if (argument.type === "CallExpression") {
return getNodeCall(argument);
}
}
return node;
}
function createError(outsideNode, insideNode) {
const outsideCall = getNodeCall(outsideNode);
const insideCall = getNodeCall(insideNode);
throw createSyntaxError(insideCall, `Cannot call ${getNodeName(insideCall)} inside ${getNodeName(outsideCall)}: both methods are hoisted to the top of the file and not actually called inside each other.`);
}
// validate hoistedNodes doesn't have nodes inside other nodes
const arrayNodes = Array.from(hoistedNodes);
for (let i = 0; i < arrayNodes.length; i++) {
const node = arrayNodes[i];
for (let j = i + 1; j < arrayNodes.length; j++) {
const otherNode = arrayNodes[j];
if (node.start >= otherNode.start && node.end <= otherNode.end) {
throw createError(otherNode, node);
}
if (otherNode.start >= node.start && otherNode.end <= node.end) {
throw createError(node, otherNode);
}
}
}
// validate that hoisted nodes are defined on the top level
// ignore `import.meta.vitest` because it needs to be inside an IfStatement
// and it can be used anywhere in the code (inside methods too)
if (!hasImportMetaVitest) {
for (const node of ast.body) {
hoistedNodes.delete(node);
if (node.type === "ExpressionStatement") {
hoistedNodes.delete(node.expression);
}
}
for (const invalidNode of hoistedNodes) {
console.warn(`Warning: A ${getNodeName(getNodeCall(invalidNode))} call in "${id}" is not at the top level of the module. ` + `Although it appears nested, it will be hoisted and executed before any tests run. ` + `Move it to the top level to reflect its actual execution order. This will become an error in a future version.\n` + `See: https://vitest.dev/guide/mocking/modules#how-it-works`);
}
}
// hoist vi.mock/vi.hoisted
for (const node of arrayNodes) {
const end = getNodeTail(code, node);
// don't hoist into itself if it's already at the top
if (hoistIndex === end || hoistIndex === node.start) {
hoistIndex = end;
} else {
s.move(node.start, end, hoistIndex);
}
}
// hoist actual dynamic imports last so they are inserted after all hoisted mocks
for (const { node: importNode, id: importId } of imports) {
const source = importNode.source.value;
const sourceString = JSON.stringify(source);
let importLine = `const ${importId} = await `;
if (options.globalThisAccessor) {
importLine += `globalThis[${options.globalThisAccessor}].wrapDynamicImport(() => import(${sourceString}));\n`;
} else {
importLine += `import(${sourceString});\n`;
}
s.update(importNode.start, importNode.end, importLine);
if (importNode.start === hoistIndex) {
// no need to hoist, but update hoistIndex to keep the order
hoistIndex = importNode.end;
} else {
// There will be an error if the module is called before it is imported,
// so the module import statement is hoisted to the top
s.move(importNode.start, importNode.end, hoistIndex);
}
}
if (!hoistedModuleImported && arrayNodes.length > 0) {
const utilityImports = [...usedUtilityExports];
// "vi" or "vitest" is imported from a module other than "vitest"
if (utilityImports.some((name) => idToImportMap.has(name))) {
s.appendLeft(hashbangEnd, API_NOT_FOUND_CHECK(utilityImports));
} else if (utilityImports.length) {
s.appendLeft(hashbangEnd, `import { ${[...usedUtilityExports].join(", ")} } from ${JSON.stringify(hoistedModule)}\n`);
}
}
return s;
}
export { hoistMocks as h };
+15
View File
@@ -0,0 +1,15 @@
import { r as rpc } from './chunk-mocker.js';
class ModuleMockerServerInterceptor {
async register(module) {
await rpc("vitest:interceptor:register", module.toJSON());
}
async delete(id) {
await rpc("vitest:interceptor:delete", id);
}
async invalidate() {
await rpc("vitest:interceptor:invalidate");
}
}
export { ModuleMockerServerInterceptor as M };
+532
View File
@@ -0,0 +1,532 @@
import { c as createSimpleStackTrace } from './chunk-helpers.js';
import { mockObject } from './index.js';
import { M as MockerRegistry, R as RedirectedModule, A as AutomockedModule } from './chunk-registry.js';
import { e as extname, j as join } from './chunk-pathe.M-eThtNZ.js';
const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
function normalizeWindowsPath(input = "") {
if (!input) {
return input;
}
return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
}
const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
function cwd() {
if (typeof process !== "undefined" && typeof process.cwd === "function") {
return process.cwd().replace(/\\/g, "/");
}
return "/";
}
const resolve = function(...arguments_) {
arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
let resolvedPath = "";
let resolvedAbsolute = false;
for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
const path = index >= 0 ? arguments_[index] : cwd();
if (!path || path.length === 0) {
continue;
}
resolvedPath = `${path}/${resolvedPath}`;
resolvedAbsolute = isAbsolute(path);
}
resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
if (resolvedAbsolute && !isAbsolute(resolvedPath)) {
return `/${resolvedPath}`;
}
return resolvedPath.length > 0 ? resolvedPath : ".";
};
function normalizeString(path, allowAboveRoot) {
let res = "";
let lastSegmentLength = 0;
let lastSlash = -1;
let dots = 0;
let char = null;
for (let index = 0; index <= path.length; ++index) {
if (index < path.length) {
char = path[index];
} else if (char === "/") {
break;
} else {
char = "/";
}
if (char === "/") {
if (lastSlash === index - 1 || dots === 1);
else if (dots === 2) {
if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
if (res.length > 2) {
const lastSlashIndex = res.lastIndexOf("/");
if (lastSlashIndex === -1) {
res = "";
lastSegmentLength = 0;
} else {
res = res.slice(0, lastSlashIndex);
lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
}
lastSlash = index;
dots = 0;
continue;
} else if (res.length > 0) {
res = "";
lastSegmentLength = 0;
lastSlash = index;
dots = 0;
continue;
}
}
if (allowAboveRoot) {
res += res.length > 0 ? "/.." : "..";
lastSegmentLength = 2;
}
} else {
if (res.length > 0) {
res += `/${path.slice(lastSlash + 1, index)}`;
} else {
res = path.slice(lastSlash + 1, index);
}
lastSegmentLength = index - lastSlash - 1;
}
lastSlash = index;
dots = 0;
} else if (char === "." && dots !== -1) {
++dots;
} else {
dots = -1;
}
}
return res;
}
const isAbsolute = function(p) {
return _IS_ABSOLUTE_RE.test(p);
};
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var intToChar = new Uint8Array(64);
var charToInt = new Uint8Array(128);
for (let i = 0; i < chars.length; i++) {
const c = chars.charCodeAt(i);
intToChar[i] = c;
charToInt[c] = i;
}
const CHROME_IE_STACK_REGEXP = /^\s*at .*(?:\S:\d+|\(native\))/m;
const SAFARI_NATIVE_CODE_REGEXP = /^(?:eval@)?(?:\[native code\])?$/;
const NOW_LENGTH = Date.now().toString().length;
const REGEXP_VITEST = new RegExp(`vitest=\\d{${NOW_LENGTH}}`);
function extractLocation(urlLike) {
// Fail-fast but return locations like "(native)"
if (!urlLike.includes(":")) {
return [urlLike];
}
const regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
const parts = regExp.exec(urlLike.replace(/^\(|\)$/g, ""));
if (!parts) {
return [urlLike];
}
let url = parts[1];
if (url.startsWith("async ")) {
url = url.slice(6);
}
if (url.startsWith("http:") || url.startsWith("https:")) {
const urlObj = new URL(url);
urlObj.searchParams.delete("import");
urlObj.searchParams.delete("browserv");
url = urlObj.pathname + urlObj.hash + urlObj.search;
}
if (url.startsWith("/@fs/")) {
const isWindows = /^\/@fs\/[a-zA-Z]:\//.test(url);
url = url.slice(isWindows ? 5 : 4);
}
if (url.includes("vitest=")) {
url = url.replace(REGEXP_VITEST, "").replace(/[?&]$/, "");
}
return [
url,
parts[2] || undefined,
parts[3] || undefined
];
}
function parseSingleFFOrSafariStack(raw) {
let line = raw.trim();
if (SAFARI_NATIVE_CODE_REGEXP.test(line)) {
return null;
}
if (line.includes(" > eval")) {
line = line.replace(/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g, ":$1");
}
// Early return for lines that don't look like Firefox/Safari stack traces
// Firefox/Safari stack traces must contain '@' and should have location info after it
if (!line.includes("@")) {
return null;
}
// Find the correct @ that separates function name from location
// For cases like '@https://@fs/path' or 'functionName@https://@fs/path'
// we need to find the first @ that precedes a valid location (containing :)
let atIndex = -1;
let locationPart = "";
let functionName;
// Try each @ from left to right to find the one that gives us a valid location
for (let i = 0; i < line.length; i++) {
if (line[i] === "@") {
const candidateLocation = line.slice(i + 1);
// Minimum length 3 for valid location: 1 for filename + 1 for colon + 1 for line number (e.g., "a:1")
if (candidateLocation.includes(":") && candidateLocation.length >= 3) {
atIndex = i;
locationPart = candidateLocation;
functionName = i > 0 ? line.slice(0, i) : undefined;
break;
}
}
}
// Validate we found a valid location with minimum length (filename:line format)
if (atIndex === -1 || !locationPart.includes(":") || locationPart.length < 3) {
return null;
}
const [url, lineNumber, columnNumber] = extractLocation(locationPart);
if (!url || !lineNumber || !columnNumber) {
return null;
}
return {
file: url,
method: functionName || "",
line: Number.parseInt(lineNumber),
column: Number.parseInt(columnNumber)
};
}
function parseSingleStack(raw) {
const line = raw.trim();
if (!CHROME_IE_STACK_REGEXP.test(line)) {
return parseSingleFFOrSafariStack(line);
}
return parseSingleV8Stack(line);
}
// Based on https://github.com/stacktracejs/error-stack-parser
// Credit to stacktracejs
function parseSingleV8Stack(raw) {
let line = raw.trim();
if (!CHROME_IE_STACK_REGEXP.test(line)) {
return null;
}
if (line.includes("(eval ")) {
line = line.replace(/eval code/g, "eval").replace(/(\(eval at [^()]*)|(,.*$)/g, "");
}
let sanitizedLine = line.replace(/^\s+/, "").replace(/\(eval code/g, "(").replace(/^.*?\s+/, "");
// capture and preserve the parenthesized location "(/foo/my bar.js:12:87)" in
// case it has spaces in it, as the string is split on \s+ later on
const location = sanitizedLine.match(/ (\(.+\)$)/);
// remove the parenthesized location from the line, if it was matched
sanitizedLine = location ? sanitizedLine.replace(location[0], "") : sanitizedLine;
// if a location was matched, pass it to extractLocation() otherwise pass all sanitizedLine
// because this line doesn't have function name
const [url, lineNumber, columnNumber] = extractLocation(location ? location[1] : sanitizedLine);
let method = location && sanitizedLine || "";
let file = url && ["eval", "<anonymous>"].includes(url) ? undefined : url;
if (!file || !lineNumber || !columnNumber) {
return null;
}
if (method.startsWith("async ")) {
method = method.slice(6);
}
if (file.startsWith("file://")) {
file = file.slice(7);
}
// normalize Windows path (\ -> /)
file = file.startsWith("node:") || file.startsWith("internal:") ? file : resolve(file);
if (method) {
method = method.replace(/\(0\s?,\s?__vite_ssr_import_\d+__.(\w+)\)/g, "$1").replace(/__(vite_ssr_import|vi_import)_\d+__\./g, "").replace(/(Object\.)?__vite_ssr_export_default__\s?/g, "");
}
return {
method,
file,
line: Number.parseInt(lineNumber),
column: Number.parseInt(columnNumber)
};
}
function createCompilerHints(options) {
const globalThisAccessor = options?.globalThisKey || "__vitest_mocker__";
function _mocker() {
// @ts-expect-error injected by the plugin
return typeof globalThis[globalThisAccessor] !== "undefined" ? globalThis[globalThisAccessor] : new Proxy({}, { get(_, name) {
throw new Error("Vitest mocker was not initialized in this environment. " + `vi.${String(name)}() is forbidden.`);
} });
}
return {
hoisted(factory) {
if (typeof factory !== "function") {
throw new TypeError(`vi.hoisted() expects a function, but received a ${typeof factory}`);
}
return factory();
},
mock(path, factory) {
if (typeof path !== "string") {
throw new TypeError(`vi.mock() expects a string path, but received a ${typeof path}`);
}
const importer = getImporter("mock");
_mocker().queueMock(path, importer, typeof factory === "function" ? () => factory(() => _mocker().importActual(path, importer)) : factory);
},
unmock(path) {
if (typeof path !== "string") {
throw new TypeError(`vi.unmock() expects a string path, but received a ${typeof path}`);
}
_mocker().queueUnmock(path, getImporter("unmock"));
},
doMock(path, factory) {
if (typeof path !== "string") {
throw new TypeError(`vi.doMock() expects a string path, but received a ${typeof path}`);
}
const importer = getImporter("doMock");
_mocker().queueMock(path, importer, typeof factory === "function" ? () => factory(() => _mocker().importActual(path, importer)) : factory);
},
doUnmock(path) {
if (typeof path !== "string") {
throw new TypeError(`vi.doUnmock() expects a string path, but received a ${typeof path}`);
}
_mocker().queueUnmock(path, getImporter("doUnmock"));
},
async importActual(path) {
return _mocker().importActual(path, getImporter("importActual"));
},
async importMock(path) {
return _mocker().importMock(path, getImporter("importMock"));
}
};
}
function getImporter(name) {
const stackTrace = /* @__PURE__ */ createSimpleStackTrace({ stackTraceLimit: 5 });
const stackArray = stackTrace.split("\n");
// if there is no message in a stack trace, use the item - 1
const importerStackIndex = stackArray.findIndex((stack) => {
return stack.includes(` at Object.${name}`) || stack.includes(`${name}@`);
});
const stack = /* @__PURE__ */ parseSingleStack(stackArray[importerStackIndex + 1]);
return stack?.file || "";
}
const hot = import.meta.hot || {
on: warn,
off: warn,
send: warn
};
function warn() {
console.warn("Vitest mocker cannot work if Vite didn't establish WS connection.");
}
function rpc(event, data) {
hot.send(event, data);
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error(`Failed to resolve ${event} in time`));
}, 5e3);
hot.on(`${event}:result`, function r(data) {
resolve(data);
clearTimeout(timeout);
hot.off(`${event}:result`, r);
});
});
}
const { now } = Date;
class ModuleMocker {
registry = new MockerRegistry();
queue = new Set();
mockedIds = new Set();
constructor(interceptor, rpc, createMockInstance, config) {
this.interceptor = interceptor;
this.rpc = rpc;
this.createMockInstance = createMockInstance;
this.config = config;
}
async prepare() {
if (!this.queue.size) {
return;
}
await Promise.all([...this.queue.values()]);
}
async resolveFactoryModule(id) {
const mock = this.registry.get(id);
if (!mock || mock.type !== "manual") {
throw new Error(`Mock ${id} wasn't registered. This is probably a Vitest error. Please, open a new issue with reproduction.`);
}
const result = await mock.resolve();
return result;
}
getFactoryModule(id) {
const mock = this.registry.get(id);
if (!mock || mock.type !== "manual") {
throw new Error(`Mock ${id} wasn't registered. This is probably a Vitest error. Please, open a new issue with reproduction.`);
}
if (!mock.cache) {
throw new Error(`Mock ${id} wasn't resolved. This is probably a Vitest error. Please, open a new issue with reproduction.`);
}
return mock.cache;
}
async invalidate() {
const ids = Array.from(this.mockedIds);
if (!ids.length) {
return;
}
await this.rpc.invalidate(ids);
await this.interceptor.invalidate();
this.registry.clear();
}
async importActual(id, importer) {
const resolved = await this.rpc.resolveId(id, importer);
if (resolved == null) {
throw new Error(`[vitest] Cannot resolve "${id}" imported from "${importer}"`);
}
const ext = extname(resolved.id);
const url = new URL(resolved.url, this.getBaseUrl());
const query = `_vitest_original&ext${ext}`;
const actualUrl = `${url.pathname}${url.search ? `${url.search}&${query}` : `?${query}`}${url.hash}`;
return this.wrapDynamicImport(() => import(
/* @vite-ignore */
actualUrl
)).then((mod) => {
if (!resolved.optimized || typeof mod.default === "undefined") {
return mod;
}
// vite injects this helper for optimized modules, so we try to follow the same behavior
const m = mod.default;
return m?.__esModule ? m : {
...typeof m === "object" && !Array.isArray(m) || typeof m === "function" ? m : {},
default: m
};
});
}
getBaseUrl() {
return location.href;
}
async importMock(rawId, importer) {
await this.prepare();
const { resolvedId, resolvedUrl, redirectUrl } = await this.rpc.resolveMock(rawId, importer, { mock: "auto" });
const mockUrl = this.resolveMockPath(cleanVersion(resolvedUrl));
let mock = this.registry.get(mockUrl);
if (!mock) {
if (redirectUrl) {
const resolvedRedirect = new URL(this.resolveMockPath(cleanVersion(redirectUrl)), this.getBaseUrl()).toString();
mock = new RedirectedModule(rawId, resolvedId, mockUrl, resolvedRedirect);
} else {
mock = new AutomockedModule(rawId, resolvedId, mockUrl);
}
}
if (mock.type === "manual") {
return await mock.resolve();
}
if (mock.type === "automock" || mock.type === "autospy") {
const url = new URL(`/@id/${resolvedId}`, this.getBaseUrl());
const query = url.search ? `${url.search}&t=${now()}` : `?t=${now()}`;
const moduleObject = await import(
/* @vite-ignore */
`${url.pathname}${query}&mock=${mock.type}${url.hash}`
);
return this.mockObject(moduleObject, mock.type);
}
return import(
/* @vite-ignore */
mock.redirect
);
}
mockObject(object, mockExportsOrModuleType, moduleType) {
let mockExports;
if (mockExportsOrModuleType === "automock" || mockExportsOrModuleType === "autospy") {
moduleType = mockExportsOrModuleType;
mockExports = undefined;
} else {
mockExports = mockExportsOrModuleType;
}
moduleType ??= "automock";
const result = mockObject({
globalConstructors: {
Object,
Function,
Array,
Map,
RegExp
},
createMockInstance: this.createMockInstance,
type: moduleType
}, object, mockExports);
return result;
}
getMockContext() {
return { callstack: null };
}
queueMock(rawId, importer, factoryOrOptions) {
const promise = this.rpc.resolveMock(rawId, importer, { mock: typeof factoryOrOptions === "function" ? "factory" : factoryOrOptions?.spy ? "spy" : "auto" }).then(async ({ redirectUrl, resolvedId, resolvedUrl, needsInterop, mockType }) => {
const mockUrl = this.resolveMockPath(cleanVersion(resolvedUrl));
this.mockedIds.add(resolvedId);
const factory = typeof factoryOrOptions === "function" ? async () => {
const data = await factoryOrOptions();
// vite wraps all external modules that have "needsInterop" in a function that
// merges all exports from default into the module object
return needsInterop ? { default: data } : data;
} : undefined;
const mockRedirect = typeof redirectUrl === "string" ? new URL(this.resolveMockPath(cleanVersion(redirectUrl)), this.getBaseUrl()).toString() : null;
let module;
if (mockType === "manual") {
module = this.registry.register("manual", rawId, resolvedId, mockUrl, factory);
} else if (mockType === "autospy") {
module = this.registry.register("autospy", rawId, resolvedId, mockUrl);
} else if (mockType === "redirect") {
module = this.registry.register("redirect", rawId, resolvedId, mockUrl, mockRedirect);
} else {
module = this.registry.register("automock", rawId, resolvedId, mockUrl);
}
await this.interceptor.register(module);
}).finally(() => {
this.queue.delete(promise);
});
this.queue.add(promise);
}
queueUnmock(id, importer) {
const promise = this.rpc.resolveId(id, importer).then(async (resolved) => {
if (!resolved) {
return;
}
const mockUrl = this.resolveMockPath(cleanVersion(resolved.url));
this.mockedIds.add(resolved.id);
this.registry.delete(mockUrl);
await this.interceptor.delete(mockUrl);
}).finally(() => {
this.queue.delete(promise);
});
this.queue.add(promise);
}
// We need to await mock registration before importing the actual module
// In case there is a mocked module in the import chain
wrapDynamicImport(moduleFactory) {
if (typeof moduleFactory === "function") {
const promise = new Promise((resolve, reject) => {
this.prepare().finally(() => {
moduleFactory().then(resolve, reject);
});
});
return promise;
}
return moduleFactory;
}
getMockedModuleById(id) {
return this.registry.getById(id);
}
reset() {
this.registry.clear();
this.mockedIds.clear();
this.queue.clear();
}
resolveMockPath(path) {
const config = this.config;
const fsRoot = join("/@fs/", config.root);
// URL can be /file/path.js, but path is resolved to /file/path
if (path.startsWith(config.root)) {
return path.slice(config.root.length);
}
if (path.startsWith(fsRoot)) {
return path.slice(fsRoot.length);
}
return path;
}
}
const versionRegexp = /(\?|&)v=\w{8}/;
function cleanVersion(url) {
return url.replace(versionRegexp, "");
}
export { ModuleMocker as M, createCompilerHints as c, hot as h, rpc as r };
+174
View File
@@ -0,0 +1,174 @@
const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
function normalizeWindowsPath(input = "") {
if (!input) {
return input;
}
return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
}
const _UNC_REGEX = /^[/\\]{2}/;
const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
const _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
const _EXTNAME_RE = /.(\.[^./]+|\.)$/;
const normalize = function(path) {
if (path.length === 0) {
return ".";
}
path = normalizeWindowsPath(path);
const isUNCPath = path.match(_UNC_REGEX);
const isPathAbsolute = isAbsolute(path);
const trailingSeparator = path[path.length - 1] === "/";
path = normalizeString(path, !isPathAbsolute);
if (path.length === 0) {
if (isPathAbsolute) {
return "/";
}
return trailingSeparator ? "./" : ".";
}
if (trailingSeparator) {
path += "/";
}
if (_DRIVE_LETTER_RE.test(path)) {
path += "/";
}
if (isUNCPath) {
if (!isPathAbsolute) {
return `//./${path}`;
}
return `//${path}`;
}
return isPathAbsolute && !isAbsolute(path) ? `/${path}` : path;
};
const join = function(...segments) {
let path = "";
for (const seg of segments) {
if (!seg) {
continue;
}
if (path.length > 0) {
const pathTrailing = path[path.length - 1] === "/";
const segLeading = seg[0] === "/";
const both = pathTrailing && segLeading;
if (both) {
path += seg.slice(1);
} else {
path += pathTrailing || segLeading ? seg : `/${seg}`;
}
} else {
path += seg;
}
}
return normalize(path);
};
function cwd() {
if (typeof process !== "undefined" && typeof process.cwd === "function") {
return process.cwd().replace(/\\/g, "/");
}
return "/";
}
const resolve = function(...arguments_) {
arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
let resolvedPath = "";
let resolvedAbsolute = false;
for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
const path = index >= 0 ? arguments_[index] : cwd();
if (!path || path.length === 0) {
continue;
}
resolvedPath = `${path}/${resolvedPath}`;
resolvedAbsolute = isAbsolute(path);
}
resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
if (resolvedAbsolute && !isAbsolute(resolvedPath)) {
return `/${resolvedPath}`;
}
return resolvedPath.length > 0 ? resolvedPath : ".";
};
function normalizeString(path, allowAboveRoot) {
let res = "";
let lastSegmentLength = 0;
let lastSlash = -1;
let dots = 0;
let char = null;
for (let index = 0; index <= path.length; ++index) {
if (index < path.length) {
char = path[index];
} else if (char === "/") {
break;
} else {
char = "/";
}
if (char === "/") {
if (lastSlash === index - 1 || dots === 1) ; else if (dots === 2) {
if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
if (res.length > 2) {
const lastSlashIndex = res.lastIndexOf("/");
if (lastSlashIndex === -1) {
res = "";
lastSegmentLength = 0;
} else {
res = res.slice(0, lastSlashIndex);
lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
}
lastSlash = index;
dots = 0;
continue;
} else if (res.length > 0) {
res = "";
lastSegmentLength = 0;
lastSlash = index;
dots = 0;
continue;
}
}
if (allowAboveRoot) {
res += res.length > 0 ? "/.." : "..";
lastSegmentLength = 2;
}
} else {
if (res.length > 0) {
res += `/${path.slice(lastSlash + 1, index)}`;
} else {
res = path.slice(lastSlash + 1, index);
}
lastSegmentLength = index - lastSlash - 1;
}
lastSlash = index;
dots = 0;
} else if (char === "." && dots !== -1) {
++dots;
} else {
dots = -1;
}
}
return res;
}
const isAbsolute = function(p) {
return _IS_ABSOLUTE_RE.test(p);
};
const extname = function(p) {
if (p === "..") return "";
const match = _EXTNAME_RE.exec(normalizeWindowsPath(p));
return match && match[1] || "";
};
const dirname = function(p) {
const segments = normalizeWindowsPath(p).replace(/\/$/, "").split("/").slice(0, -1);
if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) {
segments[0] += "/";
}
return segments.join("/") || (isAbsolute(p) ? "/" : ".");
};
const basename = function(p, extension) {
const segments = normalizeWindowsPath(p).split("/");
let lastSegment = "";
for (let i = segments.length - 1; i >= 0; i--) {
const val = segments[i];
if (val) {
lastSegment = val;
break;
}
}
return extension && lastSegment.endsWith(extension) ? lastSegment.slice(0, -extension.length) : lastSegment;
};
export { basename as b, dirname as d, extname as e, isAbsolute as i, join as j, resolve as r };
+199
View File
@@ -0,0 +1,199 @@
class MockerRegistry {
registryByUrl = new Map();
registryById = new Map();
clear() {
this.registryByUrl.clear();
this.registryById.clear();
}
keys() {
return this.registryByUrl.keys();
}
add(mock) {
this.registryByUrl.set(mock.url, mock);
this.registryById.set(mock.id, mock);
}
register(typeOrEvent, raw, id, url, factoryOrRedirect) {
const type = typeof typeOrEvent === "object" ? typeOrEvent.type : typeOrEvent;
if (typeof typeOrEvent === "object") {
const event = typeOrEvent;
if (event instanceof AutomockedModule || event instanceof AutospiedModule || event instanceof ManualMockedModule || event instanceof RedirectedModule) {
throw new TypeError(`[vitest] Cannot register a mock that is already defined. ` + `Expected a JSON representation from \`MockedModule.toJSON\`, instead got "${event.type}". ` + `Use "registry.add()" to update a mock instead.`);
}
if (event.type === "automock") {
const module = AutomockedModule.fromJSON(event);
this.add(module);
return module;
} else if (event.type === "autospy") {
const module = AutospiedModule.fromJSON(event);
this.add(module);
return module;
} else if (event.type === "redirect") {
const module = RedirectedModule.fromJSON(event);
this.add(module);
return module;
} else if (event.type === "manual") {
throw new Error(`Cannot set serialized manual mock. Define a factory function manually with \`ManualMockedModule.fromJSON()\`.`);
} else {
throw new Error(`Unknown mock type: ${event.type}`);
}
}
if (typeof raw !== "string") {
throw new TypeError("[vitest] Mocks require a raw string.");
}
if (typeof url !== "string") {
throw new TypeError("[vitest] Mocks require a url string.");
}
if (typeof id !== "string") {
throw new TypeError("[vitest] Mocks require an id string.");
}
if (type === "manual") {
if (typeof factoryOrRedirect !== "function") {
throw new TypeError("[vitest] Manual mocks require a factory function.");
}
const mock = new ManualMockedModule(raw, id, url, factoryOrRedirect);
this.add(mock);
return mock;
} else if (type === "automock" || type === "autospy") {
const mock = type === "automock" ? new AutomockedModule(raw, id, url) : new AutospiedModule(raw, id, url);
this.add(mock);
return mock;
} else if (type === "redirect") {
if (typeof factoryOrRedirect !== "string") {
throw new TypeError("[vitest] Redirect mocks require a redirect string.");
}
const mock = new RedirectedModule(raw, id, url, factoryOrRedirect);
this.add(mock);
return mock;
} else {
throw new Error(`[vitest] Unknown mock type: ${type}`);
}
}
delete(id) {
this.registryByUrl.delete(id);
}
deleteById(id) {
this.registryById.delete(id);
}
get(id) {
return this.registryByUrl.get(id);
}
getById(id) {
return this.registryById.get(id);
}
has(id) {
return this.registryByUrl.has(id);
}
}
class AutomockedModule {
type = "automock";
constructor(raw, id, url) {
this.raw = raw;
this.id = id;
this.url = url;
}
static fromJSON(data) {
return new AutospiedModule(data.raw, data.id, data.url);
}
toJSON() {
return {
type: this.type,
url: this.url,
raw: this.raw,
id: this.id
};
}
}
class AutospiedModule {
type = "autospy";
constructor(raw, id, url) {
this.raw = raw;
this.id = id;
this.url = url;
}
static fromJSON(data) {
return new AutospiedModule(data.raw, data.id, data.url);
}
toJSON() {
return {
type: this.type,
url: this.url,
id: this.id,
raw: this.raw
};
}
}
class RedirectedModule {
type = "redirect";
constructor(raw, id, url, redirect) {
this.raw = raw;
this.id = id;
this.url = url;
this.redirect = redirect;
}
static fromJSON(data) {
return new RedirectedModule(data.raw, data.id, data.url, data.redirect);
}
toJSON() {
return {
type: this.type,
url: this.url,
raw: this.raw,
id: this.id,
redirect: this.redirect
};
}
}
class ManualMockedModule {
cache;
type = "manual";
constructor(raw, id, url, factory) {
this.raw = raw;
this.id = id;
this.url = url;
this.factory = factory;
}
resolve() {
if (this.cache) {
return this.cache;
}
let exports$1;
try {
exports$1 = this.factory();
} catch (err) {
throw createHelpfulError(err);
}
if (typeof exports$1 === "object" && typeof exports$1?.then === "function") {
return exports$1.then((result) => {
assertValidExports(this.raw, result);
return this.cache = result;
}, (error) => {
throw createHelpfulError(error);
});
}
assertValidExports(this.raw, exports$1);
return this.cache = exports$1;
}
static fromJSON(data, factory) {
return new ManualMockedModule(data.raw, data.id, data.url, factory);
}
toJSON() {
return {
type: this.type,
url: this.url,
id: this.id,
raw: this.raw
};
}
}
function createHelpfulError(cause) {
const error = new Error("[vitest] There was an error when mocking a module. " + "If you are using \"vi.mock\" factory, make sure there are no top level variables inside, since this call is hoisted to top of the file. " + "Read more: https://vitest.dev/api/vi.html#vi-mock");
error.cause = cause;
return error;
}
function assertValidExports(raw, exports$1) {
if (exports$1 === null || typeof exports$1 !== "object" || Array.isArray(exports$1)) {
throw new TypeError(`[vitest] vi.mock("${raw}", factory?: () => unknown) is not returning an object. Did you mean to return an object with a "default" key?`);
}
}
export { AutomockedModule as A, MockerRegistry as M, RedirectedModule as R, ManualMockedModule as a, AutospiedModule as b };
+27
View File
@@ -0,0 +1,27 @@
const postfixRE = /[?#].*$/;
function cleanUrl(url) {
return url.replace(postfixRE, "");
}
function createManualModuleSource(moduleUrl, exports$1, globalAccessor = "\"__vitest_mocker__\"") {
const source = `
const __factoryModule__ = await globalThis[${globalAccessor}].getFactoryModule("${moduleUrl}");
`;
const keys = exports$1.map((name, index) => {
return `let __${index} = __factoryModule__["${name}"]
export { __${index} as "${name}" }`;
}).join("\n");
let code = `${source}\n${keys}`;
// this prevents recursion
code += `
if (__factoryModule__.__factoryPromise != null) {
__factoryModule__.__factoryPromise.then((resolvedModule) => {
${exports$1.map((name, index) => {
return `__${index} = resolvedModule["${name}"];`;
}).join("\n")}
})
}
`;
return code;
}
export { cleanUrl as a, createManualModuleSource as c };
+739
View File
@@ -0,0 +1,739 @@
import MagicString from 'magic-string';
declare function createManualModuleSource(moduleUrl: string, exports: string[], globalAccessor?: string): string;
// This definition file follows a somewhat unusual format. ESTree allows
// runtime type checks based on the `type` parameter. In order to explain this
// to typescript we want to use discriminated union types:
// https://github.com/Microsoft/TypeScript/pull/9163
//
// For ESTree this is a bit tricky because the high level interfaces like
// Node or Function are pulling double duty. We want to pass common fields down
// to the interfaces that extend them (like Identifier or
// ArrowFunctionExpression), but you can't extend a type union or enforce
// common fields on them. So we've split the high level interfaces into two
// types, a base type which passes down inherited fields, and a type union of
// all types which extend the base type. Only the type union is exported, and
// the union is how other types refer to the collection of inheriting types.
//
// This makes the definitions file here somewhat more difficult to maintain,
// but it has the notable advantage of making ESTree much easier to use as
// an end user.
interface BaseNodeWithoutComments {
// Every leaf interface that extends BaseNode must specify a type property.
// The type property should be a string literal. For example, Identifier
// has: `type: "Identifier"`
type: string;
loc?: SourceLocation | null | undefined;
range?: [number, number] | undefined;
}
interface BaseNode extends BaseNodeWithoutComments {
leadingComments?: Comment[] | undefined;
trailingComments?: Comment[] | undefined;
}
interface NodeMap {
AssignmentProperty: AssignmentProperty;
CatchClause: CatchClause;
Class: Class;
ClassBody: ClassBody;
Expression: Expression;
Function: Function;
Identifier: Identifier;
Literal: Literal;
MethodDefinition: MethodDefinition;
ModuleDeclaration: ModuleDeclaration;
ModuleSpecifier: ModuleSpecifier;
Pattern: Pattern;
PrivateIdentifier: PrivateIdentifier;
Program: Program;
Property: Property;
PropertyDefinition: PropertyDefinition;
SpreadElement: SpreadElement;
Statement: Statement;
Super: Super;
SwitchCase: SwitchCase;
TemplateElement: TemplateElement;
VariableDeclarator: VariableDeclarator;
}
type Node$1 = NodeMap[keyof NodeMap];
interface Comment extends BaseNodeWithoutComments {
type: "Line" | "Block";
value: string;
}
interface SourceLocation {
source?: string | null | undefined;
start: Position;
end: Position;
}
interface Position {
/** >= 1 */
line: number;
/** >= 0 */
column: number;
}
interface Program extends BaseNode {
type: "Program";
sourceType: "script" | "module";
body: Array<Directive | Statement | ModuleDeclaration>;
comments?: Comment[] | undefined;
}
interface Directive extends BaseNode {
type: "ExpressionStatement";
expression: Literal;
directive: string;
}
interface BaseFunction extends BaseNode {
params: Pattern[];
generator?: boolean | undefined;
async?: boolean | undefined;
// The body is either BlockStatement or Expression because arrow functions
// can have a body that's either. FunctionDeclarations and
// FunctionExpressions have only BlockStatement bodies.
body: BlockStatement | Expression;
}
type Function = FunctionDeclaration | FunctionExpression | ArrowFunctionExpression;
type Statement =
| ExpressionStatement
| BlockStatement
| StaticBlock
| EmptyStatement
| DebuggerStatement
| WithStatement
| ReturnStatement
| LabeledStatement
| BreakStatement
| ContinueStatement
| IfStatement
| SwitchStatement
| ThrowStatement
| TryStatement
| WhileStatement
| DoWhileStatement
| ForStatement
| ForInStatement
| ForOfStatement
| Declaration;
interface BaseStatement extends BaseNode {}
interface EmptyStatement extends BaseStatement {
type: "EmptyStatement";
}
interface BlockStatement extends BaseStatement {
type: "BlockStatement";
body: Statement[];
innerComments?: Comment[] | undefined;
}
interface StaticBlock extends Omit<BlockStatement, "type"> {
type: "StaticBlock";
}
interface ExpressionStatement extends BaseStatement {
type: "ExpressionStatement";
expression: Expression;
}
interface IfStatement extends BaseStatement {
type: "IfStatement";
test: Expression;
consequent: Statement;
alternate?: Statement | null | undefined;
}
interface LabeledStatement extends BaseStatement {
type: "LabeledStatement";
label: Identifier;
body: Statement;
}
interface BreakStatement extends BaseStatement {
type: "BreakStatement";
label?: Identifier | null | undefined;
}
interface ContinueStatement extends BaseStatement {
type: "ContinueStatement";
label?: Identifier | null | undefined;
}
interface WithStatement extends BaseStatement {
type: "WithStatement";
object: Expression;
body: Statement;
}
interface SwitchStatement extends BaseStatement {
type: "SwitchStatement";
discriminant: Expression;
cases: SwitchCase[];
}
interface ReturnStatement extends BaseStatement {
type: "ReturnStatement";
argument?: Expression | null | undefined;
}
interface ThrowStatement extends BaseStatement {
type: "ThrowStatement";
argument: Expression;
}
interface TryStatement extends BaseStatement {
type: "TryStatement";
block: BlockStatement;
handler?: CatchClause | null | undefined;
finalizer?: BlockStatement | null | undefined;
}
interface WhileStatement extends BaseStatement {
type: "WhileStatement";
test: Expression;
body: Statement;
}
interface DoWhileStatement extends BaseStatement {
type: "DoWhileStatement";
body: Statement;
test: Expression;
}
interface ForStatement extends BaseStatement {
type: "ForStatement";
init?: VariableDeclaration | Expression | null | undefined;
test?: Expression | null | undefined;
update?: Expression | null | undefined;
body: Statement;
}
interface BaseForXStatement extends BaseStatement {
left: VariableDeclaration | Pattern;
right: Expression;
body: Statement;
}
interface ForInStatement extends BaseForXStatement {
type: "ForInStatement";
}
interface DebuggerStatement extends BaseStatement {
type: "DebuggerStatement";
}
type Declaration = FunctionDeclaration | VariableDeclaration | ClassDeclaration;
interface BaseDeclaration extends BaseStatement {}
interface MaybeNamedFunctionDeclaration extends BaseFunction, BaseDeclaration {
type: "FunctionDeclaration";
/** It is null when a function declaration is a part of the `export default function` statement */
id: Identifier | null;
body: BlockStatement;
}
interface FunctionDeclaration extends MaybeNamedFunctionDeclaration {
id: Identifier;
}
interface VariableDeclaration extends BaseDeclaration {
type: "VariableDeclaration";
declarations: VariableDeclarator[];
kind: "var" | "let" | "const" | "using" | "await using";
}
interface VariableDeclarator extends BaseNode {
type: "VariableDeclarator";
id: Pattern;
init?: Expression | null | undefined;
}
interface ExpressionMap {
ArrayExpression: ArrayExpression;
ArrowFunctionExpression: ArrowFunctionExpression;
AssignmentExpression: AssignmentExpression;
AwaitExpression: AwaitExpression;
BinaryExpression: BinaryExpression;
CallExpression: CallExpression;
ChainExpression: ChainExpression;
ClassExpression: ClassExpression;
ConditionalExpression: ConditionalExpression;
FunctionExpression: FunctionExpression;
Identifier: Identifier;
ImportExpression: ImportExpression;
Literal: Literal;
LogicalExpression: LogicalExpression;
MemberExpression: MemberExpression;
MetaProperty: MetaProperty;
NewExpression: NewExpression;
ObjectExpression: ObjectExpression;
SequenceExpression: SequenceExpression;
TaggedTemplateExpression: TaggedTemplateExpression;
TemplateLiteral: TemplateLiteral;
ThisExpression: ThisExpression;
UnaryExpression: UnaryExpression;
UpdateExpression: UpdateExpression;
YieldExpression: YieldExpression;
}
type Expression = ExpressionMap[keyof ExpressionMap];
interface BaseExpression extends BaseNode {}
type ChainElement = SimpleCallExpression | MemberExpression;
interface ChainExpression extends BaseExpression {
type: "ChainExpression";
expression: ChainElement;
}
interface ThisExpression extends BaseExpression {
type: "ThisExpression";
}
interface ArrayExpression extends BaseExpression {
type: "ArrayExpression";
elements: Array<Expression | SpreadElement | null>;
}
interface ObjectExpression extends BaseExpression {
type: "ObjectExpression";
properties: Array<Property | SpreadElement>;
}
interface PrivateIdentifier extends BaseNode {
type: "PrivateIdentifier";
name: string;
}
interface Property extends BaseNode {
type: "Property";
key: Expression | PrivateIdentifier;
value: Expression | Pattern; // Could be an AssignmentProperty
kind: "init" | "get" | "set";
method: boolean;
shorthand: boolean;
computed: boolean;
}
interface PropertyDefinition extends BaseNode {
type: "PropertyDefinition";
key: Expression | PrivateIdentifier;
value?: Expression | null | undefined;
computed: boolean;
static: boolean;
}
interface FunctionExpression extends BaseFunction, BaseExpression {
id?: Identifier | null | undefined;
type: "FunctionExpression";
body: BlockStatement;
}
interface SequenceExpression extends BaseExpression {
type: "SequenceExpression";
expressions: Expression[];
}
interface UnaryExpression extends BaseExpression {
type: "UnaryExpression";
operator: UnaryOperator;
prefix: true;
argument: Expression;
}
interface BinaryExpression extends BaseExpression {
type: "BinaryExpression";
operator: BinaryOperator;
left: Expression | PrivateIdentifier;
right: Expression;
}
interface AssignmentExpression extends BaseExpression {
type: "AssignmentExpression";
operator: AssignmentOperator;
left: Pattern | MemberExpression;
right: Expression;
}
interface UpdateExpression extends BaseExpression {
type: "UpdateExpression";
operator: UpdateOperator;
argument: Expression;
prefix: boolean;
}
interface LogicalExpression extends BaseExpression {
type: "LogicalExpression";
operator: LogicalOperator;
left: Expression;
right: Expression;
}
interface ConditionalExpression extends BaseExpression {
type: "ConditionalExpression";
test: Expression;
alternate: Expression;
consequent: Expression;
}
interface BaseCallExpression extends BaseExpression {
callee: Expression | Super;
arguments: Array<Expression | SpreadElement>;
}
type CallExpression = SimpleCallExpression | NewExpression;
interface SimpleCallExpression extends BaseCallExpression {
type: "CallExpression";
optional: boolean;
}
interface NewExpression extends BaseCallExpression {
type: "NewExpression";
}
interface MemberExpression extends BaseExpression, BasePattern {
type: "MemberExpression";
object: Expression | Super;
property: Expression | PrivateIdentifier;
computed: boolean;
optional: boolean;
}
type Pattern = Identifier | ObjectPattern | ArrayPattern | RestElement | AssignmentPattern | MemberExpression;
interface BasePattern extends BaseNode {}
interface SwitchCase extends BaseNode {
type: "SwitchCase";
test?: Expression | null | undefined;
consequent: Statement[];
}
interface CatchClause extends BaseNode {
type: "CatchClause";
param: Pattern | null;
body: BlockStatement;
}
interface Identifier extends BaseNode, BaseExpression, BasePattern {
type: "Identifier";
name: string;
}
type Literal = SimpleLiteral | RegExpLiteral | BigIntLiteral;
interface SimpleLiteral extends BaseNode, BaseExpression {
type: "Literal";
value: string | boolean | number | null;
raw?: string | undefined;
}
interface RegExpLiteral extends BaseNode, BaseExpression {
type: "Literal";
value?: RegExp | null | undefined;
regex: {
pattern: string;
flags: string;
};
raw?: string | undefined;
}
interface BigIntLiteral extends BaseNode, BaseExpression {
type: "Literal";
value?: bigint | null | undefined;
bigint: string;
raw?: string | undefined;
}
type UnaryOperator = "-" | "+" | "!" | "~" | "typeof" | "void" | "delete";
type BinaryOperator =
| "=="
| "!="
| "==="
| "!=="
| "<"
| "<="
| ">"
| ">="
| "<<"
| ">>"
| ">>>"
| "+"
| "-"
| "*"
| "/"
| "%"
| "**"
| "|"
| "^"
| "&"
| "in"
| "instanceof";
type LogicalOperator = "||" | "&&" | "??";
type AssignmentOperator =
| "="
| "+="
| "-="
| "*="
| "/="
| "%="
| "**="
| "<<="
| ">>="
| ">>>="
| "|="
| "^="
| "&="
| "||="
| "&&="
| "??=";
type UpdateOperator = "++" | "--";
interface ForOfStatement extends BaseForXStatement {
type: "ForOfStatement";
await: boolean;
}
interface Super extends BaseNode {
type: "Super";
}
interface SpreadElement extends BaseNode {
type: "SpreadElement";
argument: Expression;
}
interface ArrowFunctionExpression extends BaseExpression, BaseFunction {
type: "ArrowFunctionExpression";
expression: boolean;
body: BlockStatement | Expression;
}
interface YieldExpression extends BaseExpression {
type: "YieldExpression";
argument?: Expression | null | undefined;
delegate: boolean;
}
interface TemplateLiteral extends BaseExpression {
type: "TemplateLiteral";
quasis: TemplateElement[];
expressions: Expression[];
}
interface TaggedTemplateExpression extends BaseExpression {
type: "TaggedTemplateExpression";
tag: Expression;
quasi: TemplateLiteral;
}
interface TemplateElement extends BaseNode {
type: "TemplateElement";
tail: boolean;
value: {
/** It is null when the template literal is tagged and the text has an invalid escape (e.g. - tag`\unicode and \u{55}`) */
cooked?: string | null | undefined;
raw: string;
};
}
interface AssignmentProperty extends Property {
value: Pattern;
kind: "init";
method: boolean; // false
}
interface ObjectPattern extends BasePattern {
type: "ObjectPattern";
properties: Array<AssignmentProperty | RestElement>;
}
interface ArrayPattern extends BasePattern {
type: "ArrayPattern";
elements: Array<Pattern | null>;
}
interface RestElement extends BasePattern {
type: "RestElement";
argument: Pattern;
}
interface AssignmentPattern extends BasePattern {
type: "AssignmentPattern";
left: Pattern;
right: Expression;
}
type Class = ClassDeclaration | ClassExpression;
interface BaseClass extends BaseNode {
superClass?: Expression | null | undefined;
body: ClassBody;
}
interface ClassBody extends BaseNode {
type: "ClassBody";
body: Array<MethodDefinition | PropertyDefinition | StaticBlock>;
}
interface MethodDefinition extends BaseNode {
type: "MethodDefinition";
key: Expression | PrivateIdentifier;
value: FunctionExpression;
kind: "constructor" | "method" | "get" | "set";
computed: boolean;
static: boolean;
}
interface MaybeNamedClassDeclaration extends BaseClass, BaseDeclaration {
type: "ClassDeclaration";
/** It is null when a class declaration is a part of the `export default class` statement */
id: Identifier | null;
}
interface ClassDeclaration extends MaybeNamedClassDeclaration {
id: Identifier;
}
interface ClassExpression extends BaseClass, BaseExpression {
type: "ClassExpression";
id?: Identifier | null | undefined;
}
interface MetaProperty extends BaseExpression {
type: "MetaProperty";
meta: Identifier;
property: Identifier;
}
type ModuleDeclaration =
| ImportDeclaration
| ExportNamedDeclaration
| ExportDefaultDeclaration
| ExportAllDeclaration;
interface BaseModuleDeclaration extends BaseNode {}
type ModuleSpecifier = ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier | ExportSpecifier;
interface BaseModuleSpecifier extends BaseNode {
local: Identifier;
}
interface ImportDeclaration extends BaseModuleDeclaration {
type: "ImportDeclaration";
specifiers: Array<ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier>;
attributes: ImportAttribute[];
source: Literal;
}
interface ImportSpecifier extends BaseModuleSpecifier {
type: "ImportSpecifier";
imported: Identifier | Literal;
}
interface ImportAttribute extends BaseNode {
type: "ImportAttribute";
key: Identifier | Literal;
value: Literal;
}
interface ImportExpression extends BaseExpression {
type: "ImportExpression";
source: Expression;
options?: Expression | null | undefined;
}
interface ImportDefaultSpecifier extends BaseModuleSpecifier {
type: "ImportDefaultSpecifier";
}
interface ImportNamespaceSpecifier extends BaseModuleSpecifier {
type: "ImportNamespaceSpecifier";
}
interface ExportNamedDeclaration extends BaseModuleDeclaration {
type: "ExportNamedDeclaration";
declaration?: Declaration | null | undefined;
specifiers: ExportSpecifier[];
attributes: ImportAttribute[];
source?: Literal | null | undefined;
}
interface ExportSpecifier extends Omit<BaseModuleSpecifier, "local"> {
type: "ExportSpecifier";
local: Identifier | Literal;
exported: Identifier | Literal;
}
interface ExportDefaultDeclaration extends BaseModuleDeclaration {
type: "ExportDefaultDeclaration";
declaration: MaybeNamedFunctionDeclaration | MaybeNamedClassDeclaration | Expression;
}
interface ExportAllDeclaration extends BaseModuleDeclaration {
type: "ExportAllDeclaration";
exported: Identifier | Literal | null;
attributes: ImportAttribute[];
source: Literal;
}
interface AwaitExpression extends BaseExpression {
type: "AwaitExpression";
argument: Expression;
}
type Positioned<T> = T & {
start: number;
end: number;
};
type Node = Positioned<Node$1>;
interface HoistMocksOptions {
/**
* List of modules that should always be imported before compiler hints.
* @default 'vitest'
*/
hoistedModule?: string;
/**
* @default ["vi", "vitest"]
*/
utilsObjectNames?: string[];
/**
* @default ["mock", "unmock"]
*/
hoistableMockMethodNames?: string[];
/**
* @default ["mock", "unmock", "doMock", "doUnmock"]
*/
dynamicImportMockMethodNames?: string[];
/**
* @default ["hoisted"]
*/
hoistedMethodNames?: string[];
globalThisAccessor?: string;
regexpHoistable?: RegExp;
codeFrameGenerator?: CodeFrameGenerator;
magicString?: () => MagicString;
}
declare function hoistMocks(code: string, id: string, parse: (code: string) => any, options?: HoistMocksOptions): MagicString | undefined;
interface CodeFrameGenerator {
(node: Positioned<Node>, id: string, code: string): string;
}
export { createManualModuleSource as c, hoistMocks as h };
export type { HoistMocksOptions as H };
+25
View File
@@ -0,0 +1,25 @@
import './types.d-BjI5eAwu.js';
type Key = string | symbol;
type CreateMockInstanceProcedure = (options?: {
prototypeMembers?: (string | symbol)[];
name?: string | symbol;
originalImplementation?: (...args: any[]) => any;
keepMembersImplementation?: boolean;
}) => any;
interface MockObjectOptions {
type: "automock" | "autospy";
globalConstructors: GlobalConstructors;
createMockInstance: CreateMockInstanceProcedure;
}
declare function mockObject(options: MockObjectOptions, object: Record<Key, any>, mockExports?: Record<Key, any>): Record<Key, any>;
interface GlobalConstructors {
Object: ObjectConstructor;
Function: FunctionConstructor;
RegExp: RegExpConstructor;
Array: ArrayConstructor;
Map: MapConstructor;
}
export { mockObject as m };
export type { CreateMockInstanceProcedure as C, GlobalConstructors as G, MockObjectOptions as M };
+2
View File
@@ -0,0 +1,2 @@
export { G as GlobalConstructors, M as MockObjectOptions, m as mockObject } from './index.d-B41z0AuW.js';
export { A as AutomockedModule, g as AutomockedModuleSerialized, h as AutospiedModule, i as AutospiedModuleSerialized, j as ManualMockedModule, k as ManualMockedModuleSerialized, a as MockedModule, l as MockedModuleSerialized, d as MockedModuleType, M as MockerRegistry, e as ModuleMockContext, m as ModuleMockFactory, c as ModuleMockFactoryWithHelper, b as ModuleMockOptions, R as RedirectedModule, n as RedirectedModuleSerialized, f as ServerIdResolution, S as ServerMockResolution, T as TestModuleMocker } from './types.d-BjI5eAwu.js';
+200
View File
@@ -0,0 +1,200 @@
export { A as AutomockedModule, b as AutospiedModule, a as ManualMockedModule, M as MockerRegistry, R as RedirectedModule } from './chunk-registry.js';
function mockObject(options, object, mockExports = {}) {
const finalizers = new Array();
const refs = new RefTracker();
const define = (container, key, value) => {
try {
container[key] = value;
return true;
} catch {
return false;
}
};
const createMock = (currentValue) => {
if (!options.createMockInstance) {
throw new Error("[@vitest/mocker] `createMockInstance` is not defined. This is a Vitest error. Please open a new issue with reproduction.");
}
const createMockInstance = options.createMockInstance;
const prototypeMembers = currentValue.prototype ? collectFunctionProperties(currentValue.prototype) : [];
return createMockInstance({
name: currentValue.name,
prototypeMembers,
originalImplementation: options.type === "autospy" ? currentValue : undefined,
keepMembersImplementation: options.type === "autospy"
});
};
const mockPropertiesOf = (container, newContainer) => {
const containerType = getType(container);
const isModule = containerType === "Module" || !!container.__esModule;
for (const { key: property, descriptor } of getAllMockableProperties(container, isModule, options.globalConstructors)) {
// Modules define their exports as getters. We want to process those.
if (!isModule && descriptor.get) {
try {
if (options.type === "autospy") {
Object.defineProperty(newContainer, property, descriptor);
} else {
Object.defineProperty(newContainer, property, {
configurable: descriptor.configurable,
enumerable: descriptor.enumerable,
get: () => {},
set: descriptor.set ? () => {} : undefined
});
}
} catch {}
continue;
}
// Skip special read-only props, we don't want to mess with those.
if (isReadonlyProp(container[property], property)) {
continue;
}
const value = container[property];
// Special handling of references we've seen before to prevent infinite
// recursion in circular objects.
const refId = refs.getId(value);
if (refId !== undefined) {
finalizers.push(() => define(newContainer, property, refs.getMockedValue(refId)));
continue;
}
const type = getType(value);
if (Array.isArray(value)) {
if (options.type === "automock") {
define(newContainer, property, []);
} else {
const array = value.map((value) => {
if (value && typeof value === "object") {
const newObject = {};
mockPropertiesOf(value, newObject);
return newObject;
}
if (typeof value === "function") {
return createMock(value);
}
return value;
});
define(newContainer, property, array);
}
continue;
}
const isFunction = type.includes("Function") && typeof value === "function";
if ((!isFunction || value._isMockFunction) && type !== "Object" && type !== "Module") {
define(newContainer, property, value);
continue;
}
if (options.type === "autospy" && type === "Module") {
// Replace with clean object to recursively autospy exported module objects:
// export * as ns from "./ns"
// or
// import * as ns from "./ns"
// export { ns }
const exports$1 = Object.create(null);
Object.defineProperty(exports$1, Symbol.toStringTag, {
value: "Module",
configurable: true,
writable: true
});
try {
newContainer[property] = exports$1;
} catch {
continue;
}
} else if (!define(newContainer, property, isFunction || options.type === "autospy" ? value : {})) {
continue;
}
if (isFunction) {
const mock = createMock(newContainer[property]);
newContainer[property] = mock;
}
refs.track(value, newContainer[property]);
mockPropertiesOf(value, newContainer[property]);
}
};
const mockedObject = mockExports;
mockPropertiesOf(object, mockedObject);
// Plug together refs
for (const finalizer of finalizers) {
finalizer();
}
return mockedObject;
}
class RefTracker {
idMap = new Map();
mockedValueMap = new Map();
getId(value) {
return this.idMap.get(value);
}
getMockedValue(id) {
return this.mockedValueMap.get(id);
}
track(originalValue, mockedValue) {
const newId = this.idMap.size;
this.idMap.set(originalValue, newId);
this.mockedValueMap.set(newId, mockedValue);
return newId;
}
}
function getType(value) {
return Object.prototype.toString.apply(value).slice(8, -1);
}
function isReadonlyProp(object, prop) {
if (prop === "arguments" || prop === "caller" || prop === "callee" || prop === "name" || prop === "length") {
const typeName = getType(object);
return typeName === "Function" || typeName === "AsyncFunction" || typeName === "GeneratorFunction" || typeName === "AsyncGeneratorFunction";
}
if (prop === "source" || prop === "global" || prop === "ignoreCase" || prop === "multiline") {
return getType(object) === "RegExp";
}
return false;
}
function getAllMockableProperties(obj, isModule, constructors) {
const { Map, Object, Function, RegExp, Array } = constructors;
const allProps = new Map();
let curr = obj;
do {
// we don't need properties from these
if (curr === Object.prototype || curr === Function.prototype || curr === RegExp.prototype) {
break;
}
collectOwnProperties(curr, (key) => {
const descriptor = Object.getOwnPropertyDescriptor(curr, key);
if (descriptor) {
allProps.set(key, {
key,
descriptor
});
}
});
} while (curr = Object.getPrototypeOf(curr));
// default is not specified in ownKeys, if module is interoped
if (isModule && !allProps.has("default") && "default" in obj) {
const descriptor = Object.getOwnPropertyDescriptor(obj, "default");
if (descriptor) {
allProps.set("default", {
key: "default",
descriptor
});
}
}
return Array.from(allProps.values());
}
function collectOwnProperties(obj, collector) {
const collect = typeof collector === "function" ? collector : (key) => collector.add(key);
Object.getOwnPropertyNames(obj).forEach(collect);
Object.getOwnPropertySymbols(obj).forEach(collect);
}
function collectFunctionProperties(prototype) {
const properties = new Set();
collectOwnProperties(prototype, (prop) => {
const descriptor = Object.getOwnPropertyDescriptor(prototype, prop);
if (!descriptor || descriptor.get) {
return;
}
const type = getType(descriptor.value);
if (type.includes("Function") && !isReadonlyProp(descriptor.value, prop)) {
properties.add(prop);
}
});
return Array.from(properties);
}
export { mockObject };
+86
View File
@@ -0,0 +1,86 @@
import { MaybeMockedDeep } from '@vitest/spy';
import { b as ModuleMockOptions, c as ModuleMockFactoryWithHelper, a as MockedModule, T as TestModuleMocker, M as MockerRegistry, d as MockedModuleType, e as ModuleMockContext } from './types.d-BjI5eAwu.js';
import { C as CreateMockInstanceProcedure } from './index.d-B41z0AuW.js';
interface CompilerHintsOptions {
/**
* This is the key used to access the globalThis object in the worker.
* Unlike `globalThisAccessor` in other APIs, this is not injected into the script.
* ```ts
* // globalThisKey: '__my_variable__' produces:
* globalThis['__my_variable__']
* // globalThisKey: '"__my_variable__"' produces:
* globalThis['"__my_variable__"'] // notice double quotes
* ```
* @default '__vitest_mocker__'
*/
globalThisKey?: string;
}
interface ModuleMockerCompilerHints {
hoisted: <T>(factory: () => T) => T;
mock: (path: string | Promise<unknown>, factory?: ModuleMockOptions | ModuleMockFactoryWithHelper) => void;
unmock: (path: string | Promise<unknown>) => void;
doMock: (path: string | Promise<unknown>, factory?: ModuleMockOptions | ModuleMockFactoryWithHelper) => void;
doUnmock: (path: string | Promise<unknown>) => void;
importActual: <T>(path: string) => Promise<T>;
importMock: <T>(path: string) => Promise<MaybeMockedDeep<T>>;
}
declare function createCompilerHints(options?: CompilerHintsOptions): ModuleMockerCompilerHints;
interface ModuleMockerInterceptor {
register: (module: MockedModule) => Promise<void>;
delete: (url: string) => Promise<void>;
invalidate: () => Promise<void>;
}
declare class ModuleMocker implements TestModuleMocker {
private interceptor;
private rpc;
private createMockInstance;
private config;
protected registry: MockerRegistry;
private queue;
private mockedIds;
constructor(interceptor: ModuleMockerInterceptor, rpc: ModuleMockerRPC, createMockInstance: CreateMockInstanceProcedure, config: ModuleMockerConfig);
prepare(): Promise<void>;
resolveFactoryModule(id: string): Promise<Record<string | symbol, any>>;
getFactoryModule(id: string): any;
invalidate(): Promise<void>;
importActual<T>(id: string, importer: string): Promise<T>;
protected getBaseUrl(): string;
importMock<T>(rawId: string, importer: string): Promise<T>;
mockObject(object: Record<string | symbol, any>, moduleType?: "automock" | "autospy"): Record<string | symbol, any>;
mockObject(object: Record<string | symbol, any>, mockExports: Record<string | symbol, any> | undefined, moduleType?: "automock" | "autospy"): Record<string | symbol, any>;
getMockContext(): ModuleMockContext;
queueMock(rawId: string, importer: string, factoryOrOptions?: ModuleMockOptions | (() => any)): void;
queueUnmock(id: string, importer: string): void;
wrapDynamicImport<T>(moduleFactory: () => Promise<T>): Promise<T>;
getMockedModuleById(id: string): MockedModule | undefined;
reset(): void;
private resolveMockPath;
}
interface ResolveIdResult {
id: string;
url: string;
optimized: boolean;
}
interface ResolveMockResult {
mockType: MockedModuleType;
resolvedId: string;
resolvedUrl: string;
redirectUrl?: string | null;
needsInterop?: boolean;
}
interface ModuleMockerRPC {
invalidate: (ids: string[]) => Promise<void>;
resolveId: (id: string, importer: string) => Promise<ResolveIdResult | null>;
resolveMock: (id: string, importer: string, options: {
mock: "spy" | "factory" | "auto";
}) => Promise<ResolveMockResult>;
}
interface ModuleMockerConfig {
root: string;
}
export { ModuleMocker as b, createCompilerHints as f };
export type { CompilerHintsOptions as C, ModuleMockerInterceptor as M, ResolveIdResult as R, ModuleMockerCompilerHints as a, ModuleMockerConfig as c, ModuleMockerRPC as d, ResolveMockResult as e };
+71
View File
@@ -0,0 +1,71 @@
import { H as HoistMocksOptions } from './hoistMocks.d-w2ILr1dG.js';
export { c as createManualModuleSource } from './hoistMocks.d-w2ILr1dG.js';
import { AutomockOptions } from './automock.js';
export { automockModule } from './automock.js';
import { Plugin, Rollup, ViteDevServer } from 'vite';
import { SourceMap } from 'magic-string';
import { M as MockerRegistry, S as ServerMockResolution, f as ServerIdResolution } from './types.d-BjI5eAwu.js';
export { findMockRedirect } from './redirect.js';
declare function automockPlugin(options?: AutomockOptions): Plugin;
interface DynamicImportPluginOptions {
/**
* @default `"__vitest_mocker__"`
*/
globalThisAccessor?: string;
filter?: (id: string) => boolean;
}
declare function dynamicImportPlugin(options?: DynamicImportPluginOptions): Plugin;
interface HoistMocksPluginOptions extends Omit<HoistMocksOptions, "regexpHoistable"> {
include?: string | RegExp | (string | RegExp)[];
exclude?: string | RegExp | (string | RegExp)[];
/**
* overrides include/exclude options
*/
filter?: (id: string) => boolean;
}
declare function hoistMocksPlugin(options?: HoistMocksPluginOptions): Plugin;
declare function hoistMockAndResolve(code: string, id: string, parse: Rollup.PluginContext["parse"], options?: HoistMocksOptions): HoistMocksResult | undefined;
interface HoistMocksResult {
code: string;
map: SourceMap;
}
interface InterceptorPluginOptions {
/**
* @default "__vitest_mocker__"
*/
globalThisAccessor?: string;
registry?: MockerRegistry;
}
declare function interceptorPlugin(options?: InterceptorPluginOptions): Plugin;
interface MockerPluginOptions extends AutomockOptions {
hoistMocks?: HoistMocksPluginOptions;
}
declare function mockerPlugin(options?: MockerPluginOptions): Plugin[];
interface ServerResolverOptions {
/**
* @default ['/node_modules/']
*/
moduleDirectories?: string[];
}
declare class ServerMockResolver {
private server;
private options;
constructor(server: ViteDevServer, options?: ServerResolverOptions);
resolveMock(rawId: string, importer: string, options: {
mock: "spy" | "factory" | "auto";
}): Promise<ServerMockResolution>;
invalidate(ids: string[]): void;
resolveId(id: string, importer?: string): Promise<ServerIdResolution | null>;
private normalizeResolveIdToUrl;
private resolveMockId;
private resolveModule;
}
export { AutomockOptions as AutomockPluginOptions, ServerMockResolver, automockPlugin, dynamicImportPlugin, hoistMockAndResolve as hoistMocks, hoistMocksPlugin, interceptorPlugin, mockerPlugin };
export type { HoistMocksPluginOptions, HoistMocksResult, InterceptorPluginOptions, ServerResolverOptions };
+409
View File
@@ -0,0 +1,409 @@
import { a as cleanUrl, c as createManualModuleSource } from './chunk-utils.js';
import { a as automockModule, e as esmWalker } from './chunk-automock.js';
import MagicString from 'magic-string';
import { createFilter } from 'vite';
import { h as hoistMocks } from './chunk-hoistMocks.js';
import { readFile } from 'node:fs/promises';
import { join } from 'node:path/posix';
import { M as MockerRegistry, a as ManualMockedModule } from './chunk-registry.js';
import { fileURLToPath } from 'node:url';
import { existsSync, readFileSync } from 'node:fs';
import { findMockRedirect } from './redirect.js';
import { i as isAbsolute, j as join$1, r as resolve } from './chunk-pathe.M-eThtNZ.js';
import 'estree-walker';
import 'node:module';
import 'node:path';
import './chunk-helpers.js';
function automockPlugin(options = {}) {
return {
name: "vitest:automock",
enforce: "post",
transform(code, id) {
if (id.includes("mock=automock") || id.includes("mock=autospy")) {
const mockType = id.includes("mock=automock") ? "automock" : "autospy";
const ms = automockModule(code, mockType, this.parse, options);
return {
code: ms.toString(),
map: ms.generateMap({
hires: "boundary",
source: cleanUrl(id)
})
};
}
}
};
}
const regexDynamicImport = /import\s*\(/;
function dynamicImportPlugin(options = {}) {
return {
name: "vitest:browser:esm-injector",
enforce: "post",
transform(source, id) {
// TODO: test is not called for static imports
if (!regexDynamicImport.test(source)) {
return;
}
if (options.filter && !options.filter(id)) {
return;
}
return injectDynamicImport(source, id, this.parse, options);
}
};
}
function injectDynamicImport(code, id, parse, options = {}) {
if (code.includes("wrapDynamicImport")) {
return;
}
const s = new MagicString(code);
let ast;
try {
ast = parse(code);
} catch (err) {
console.error(`Cannot parse ${id}:\n${err.message}`);
return;
}
// 3. convert references to import bindings & import.meta references
esmWalker(ast, {
onImportMeta() {
// s.update(node.start, node.end, viImportMetaKey)
},
onDynamicImport(node) {
const globalThisAccessor = options.globalThisAccessor || "\"__vitest_mocker__\"";
const replaceString = `globalThis[${globalThisAccessor}].wrapDynamicImport(() => import(`;
const importSubstring = code.substring(node.start, node.end);
const hasIgnore = importSubstring.includes("/* @vite-ignore */");
s.overwrite(node.start, node.source.start, replaceString + (hasIgnore ? "/* @vite-ignore */ " : ""));
s.overwrite(node.end - 1, node.end, "))");
}
});
return {
code: s.toString(),
map: s.generateMap({
hires: "boundary",
source: id
})
};
}
function hoistMocksPlugin(options = {}) {
const filter = options.filter || createFilter(options.include, options.exclude);
const { hoistableMockMethodNames = ["mock", "unmock"], dynamicImportMockMethodNames = [
"mock",
"unmock",
"doMock",
"doUnmock"
], hoistedMethodNames = ["hoisted"], utilsObjectNames = ["vi", "vitest"] } = options;
const methods = new Set([
...hoistableMockMethodNames,
...hoistedMethodNames,
...dynamicImportMockMethodNames
]);
const regexpHoistable = new RegExp(`\\b(?:${utilsObjectNames.join("|")})\\s*\.\\s*(?:${Array.from(methods).join("|")})\\s*\\(`);
return {
name: "vitest:mocks",
enforce: "post",
transform(code, id) {
if (!filter(id)) {
return;
}
const s = hoistMocks(code, id, this.parse, {
regexpHoistable,
hoistableMockMethodNames,
hoistedMethodNames,
utilsObjectNames,
dynamicImportMockMethodNames,
...options
});
if (s) {
return {
code: s.toString(),
map: s.generateMap({
hires: "boundary",
source: cleanUrl(id)
})
};
}
}
};
}
// to keeb backwards compat
function hoistMockAndResolve(code, id, parse, options = {}) {
const s = hoistMocks(code, id, parse, options);
if (s) {
return {
code: s.toString(),
map: s.generateMap({
hires: "boundary",
source: cleanUrl(id)
})
};
}
}
function interceptorPlugin(options = {}) {
const registry = options.registry || new MockerRegistry();
return {
name: "vitest:mocks:interceptor",
enforce: "pre",
load: {
order: "pre",
async handler(id) {
const mock = registry.getById(id);
if (!mock) {
return;
}
if (mock.type === "manual") {
const exports$1 = Object.keys(await mock.resolve());
const accessor = options.globalThisAccessor || "\"__vitest_mocker__\"";
return createManualModuleSource(mock.url, exports$1, accessor);
}
if (mock.type === "redirect") {
return readFile(mock.redirect, "utf-8");
}
}
},
transform: {
order: "post",
handler(code, id) {
const mock = registry.getById(id);
if (!mock) {
return;
}
if (mock.type === "automock" || mock.type === "autospy") {
const m = automockModule(code, mock.type, this.parse, { globalThisAccessor: options.globalThisAccessor });
return {
code: m.toString(),
map: m.generateMap({
hires: "boundary",
source: cleanUrl(id)
})
};
}
}
},
configureServer(server) {
server.ws.on("vitest:interceptor:register", (event) => {
if (event.type === "manual") {
const module = ManualMockedModule.fromJSON(event, async () => {
const keys = await getFactoryExports(event.url);
return Object.fromEntries(keys.map((key) => [key, null]));
});
registry.add(module);
} else {
if (event.type === "redirect") {
const redirectUrl = new URL(event.redirect);
event.redirect = join(server.config.root, redirectUrl.pathname);
}
registry.register(event);
}
server.ws.send("vitest:interceptor:register:result");
});
server.ws.on("vitest:interceptor:delete", (id) => {
registry.delete(id);
server.ws.send("vitest:interceptor:delete:result");
});
server.ws.on("vitest:interceptor:invalidate", () => {
registry.clear();
server.ws.send("vitest:interceptor:invalidate:result");
});
function getFactoryExports(url) {
server.ws.send("vitest:interceptor:resolve", url);
let timeout;
return new Promise((resolve, reject) => {
timeout = setTimeout(() => {
reject(new Error(`Timeout while waiting for factory exports of ${url}`));
}, 1e4);
server.ws.on("vitest:interceptor:resolved", ({ url: resolvedUrl, keys }) => {
if (resolvedUrl === url) {
clearTimeout(timeout);
resolve(keys);
}
});
});
}
}
};
}
const VALID_ID_PREFIX = "/@id/";
class ServerMockResolver {
constructor(server, options = {}) {
this.server = server;
this.options = options;
}
async resolveMock(rawId, importer, options) {
const { id, fsPath, external } = await this.resolveMockId(rawId, importer);
const resolvedUrl = this.normalizeResolveIdToUrl({ id }).url;
if (options.mock === "factory") {
const manifest = getViteDepsManifest(this.server.config);
const needsInterop = manifest?.[fsPath]?.needsInterop ?? false;
return {
mockType: "manual",
resolvedId: id,
resolvedUrl,
needsInterop
};
}
if (options.mock === "spy") {
return {
mockType: "autospy",
resolvedId: id,
resolvedUrl
};
}
const redirectUrl = findMockRedirect(this.server.config.root, fsPath, external);
return {
mockType: redirectUrl === null ? "automock" : "redirect",
redirectUrl,
resolvedId: id,
resolvedUrl
};
}
invalidate(ids) {
ids.forEach((id) => {
const moduleGraph = this.server.moduleGraph;
const module = moduleGraph.getModuleById(id);
if (module) {
module.transformResult = null;
}
});
}
async resolveId(id, importer) {
const resolved = await this.server.pluginContainer.resolveId(id, importer, { ssr: false });
if (!resolved) {
return null;
}
return this.normalizeResolveIdToUrl(resolved);
}
normalizeResolveIdToUrl(resolved) {
const isOptimized = resolved.id.startsWith(withTrailingSlash(this.server.config.cacheDir));
let url;
// normalise the URL to be acceptable by the browser
// https://github.com/vitejs/vite/blob/14027b0f2a9b01c14815c38aab22baf5b29594bb/packages/vite/src/node/plugins/importAnalysis.ts#L103
const root = this.server.config.root;
if (resolved.id.startsWith(withTrailingSlash(root))) {
url = resolved.id.slice(root.length);
} else if (resolved.id !== "/@react-refresh" && isAbsolute(resolved.id) && existsSync(cleanUrl(resolved.id))) {
url = join$1("/@fs/", resolved.id);
} else {
url = resolved.id;
}
if (url[0] !== "." && url[0] !== "/") {
url = resolved.id.startsWith(VALID_ID_PREFIX) ? resolved.id : VALID_ID_PREFIX + resolved.id.replace("\0", "__x00__");
}
return {
id: resolved.id,
url,
optimized: isOptimized
};
}
async resolveMockId(rawId, importer) {
if (!this.server.moduleGraph.getModuleById(importer) && !importer.startsWith(this.server.config.root)) {
importer = join$1(this.server.config.root, importer);
}
const resolved = await this.server.pluginContainer.resolveId(rawId, importer, { ssr: false });
return this.resolveModule(rawId, resolved);
}
resolveModule(rawId, resolved) {
const id = resolved?.id || rawId;
const external = !isAbsolute(id) || isModuleDirectory(this.options, id) ? rawId : null;
return {
id,
fsPath: cleanUrl(id),
external
};
}
}
function isModuleDirectory(config, path) {
const moduleDirectories = config.moduleDirectories || ["/node_modules/"];
return moduleDirectories.some((dir) => path.includes(dir));
}
const metadata = new WeakMap();
function getViteDepsManifest(config) {
if (metadata.has(config)) {
return metadata.get(config);
}
const cacheDirPath = getDepsCacheDir(config);
const metadataPath = resolve(cacheDirPath, "_metadata.json");
if (!existsSync(metadataPath)) {
return null;
}
const { optimized } = JSON.parse(readFileSync(metadataPath, "utf-8"));
const newManifest = {};
for (const name in optimized) {
const dep = optimized[name];
const file = resolve(cacheDirPath, dep.file);
newManifest[file] = {
hash: dep.fileHash,
needsInterop: dep.needsInterop
};
}
metadata.set(config, newManifest);
return newManifest;
}
function getDepsCacheDir(config) {
return resolve(config.cacheDir, "deps");
}
function withTrailingSlash(path) {
if (path.at(-1) !== "/") {
return `${path}/`;
}
return path;
}
// this is an implementation for public usage
// vitest doesn't use this plugin directly
function mockerPlugin(options = {}) {
let server;
const registerPath = resolve(fileURLToPath(new URL("./register.js", import.meta.url)));
return [
{
name: "vitest:mocker:ws-rpc",
config(_, { command }) {
if (command !== "serve") {
return;
}
return {
server: { preTransformRequests: false },
optimizeDeps: { exclude: ["@vitest/mocker/register", "@vitest/mocker/browser"] }
};
},
configureServer(server_) {
server = server_;
const mockResolver = new ServerMockResolver(server);
server.ws.on("vitest:mocks:resolveId", async ({ id, importer }) => {
const resolved = await mockResolver.resolveId(id, importer);
server.ws.send("vitest:mocks:resolvedId:result", resolved);
});
server.ws.on("vitest:mocks:resolveMock", async ({ id, importer, options }) => {
const resolved = await mockResolver.resolveMock(id, importer, options);
server.ws.send("vitest:mocks:resolveMock:result", resolved);
});
server.ws.on("vitest:mocks:invalidate", async ({ ids }) => {
mockResolver.invalidate(ids);
server.ws.send("vitest:mocks:invalidate:result");
});
},
async load(id) {
if (id !== registerPath) {
return;
}
if (!server) {
// mocker doesn't work during build
return "export {}";
}
const content = await readFile(registerPath, "utf-8");
const result = content.replace(/__VITEST_GLOBAL_THIS_ACCESSOR__/g, options.globalThisAccessor ?? "\"__vitest_mocker__\"").replace("__VITEST_MOCKER_ROOT__", JSON.stringify(server.config.root));
return result;
}
},
hoistMocksPlugin(options.hoistMocks),
interceptorPlugin(options),
automockPlugin(options),
dynamicImportPlugin(options)
];
}
export { ServerMockResolver, automockModule, automockPlugin, createManualModuleSource, dynamicImportPlugin, findMockRedirect, hoistMockAndResolve as hoistMocks, hoistMocksPlugin, interceptorPlugin, mockerPlugin };
+3
View File
@@ -0,0 +1,3 @@
declare function findMockRedirect(root: string, mockPath: string, external: string | null): string | null;
export { findMockRedirect };
+79
View File
@@ -0,0 +1,79 @@
import fs from 'node:fs';
import module$1 from 'node:module';
import { d as dirname, j as join, b as basename, r as resolve, e as extname } from './chunk-pathe.M-eThtNZ.js';
const { existsSync, readdirSync, statSync } = fs;
function findMockRedirect(root, mockPath, external) {
const path = external || mockPath;
// it's a node_module alias
// all mocks should be inside <root>/__mocks__
if (external || isNodeBuiltin(mockPath) || !existsSync(mockPath)) {
const mockDirname = dirname(path);
const mockFolder = join(root, "__mocks__", mockDirname);
if (!existsSync(mockFolder)) {
return null;
}
const baseOriginal = basename(path);
function findFile(mockFolder, baseOriginal) {
const files = readdirSync(mockFolder);
for (const file of files) {
const baseFile = basename(file, extname(file));
if (baseFile === baseOriginal) {
const path = resolve(mockFolder, file);
// if the same name, return the file
if (statSync(path).isFile()) {
return path;
} else {
// find folder/index.{js,ts}
const indexFile = findFile(path, "index");
if (indexFile) {
return indexFile;
}
}
}
}
return null;
}
return findFile(mockFolder, baseOriginal);
}
const dir = dirname(path);
const baseId = basename(path);
const fullPath = resolve(dir, "__mocks__", baseId);
return existsSync(fullPath) ? fullPath : null;
}
const builtins = new Set([
...module$1.builtinModules,
"assert/strict",
"diagnostics_channel",
"dns/promises",
"fs/promises",
"path/posix",
"path/win32",
"readline/promises",
"stream/consumers",
"stream/promises",
"stream/web",
"timers/promises",
"util/types",
"wasi"
]);
// https://nodejs.org/api/modules.html#built-in-modules-with-mandatory-node-prefix
const prefixedBuiltins = new Set([
"node:sea",
"node:sqlite",
"node:test",
"node:test/reporters"
]);
const NODE_BUILTIN_NAMESPACE = "node:";
function isNodeBuiltin(id) {
// Added in v18.6.0
if (module$1.isBuiltin) {
return module$1.isBuiltin(id);
}
if (prefixedBuiltins.has(id)) {
return true;
}
return builtins.has(id.startsWith(NODE_BUILTIN_NAMESPACE) ? id.slice(NODE_BUILTIN_NAMESPACE.length) : id);
}
export { findMockRedirect };
+9
View File
@@ -0,0 +1,9 @@
import { M as ModuleMockerInterceptor, a as ModuleMockerCompilerHints, b as ModuleMocker } from './mocker.d-QEntlm6J.js';
import '@vitest/spy';
import './types.d-BjI5eAwu.js';
import './index.d-B41z0AuW.js';
declare function registerModuleMocker(interceptor: (accessor: string) => ModuleMockerInterceptor): ModuleMockerCompilerHints;
declare function registerNativeFactoryResolver(mocker: ModuleMocker): void;
export { registerModuleMocker, registerNativeFactoryResolver };
+42
View File
@@ -0,0 +1,42 @@
import { createMockInstance } from '@vitest/spy';
import { M as ModuleMocker, r as rpc, c as createCompilerHints, h as hot } from './chunk-mocker.js';
import './chunk-helpers.js';
import './index.js';
import './chunk-registry.js';
import './chunk-pathe.M-eThtNZ.js';
function registerModuleMocker(interceptor) {
const mocker = new ModuleMocker(interceptor(__VITEST_GLOBAL_THIS_ACCESSOR__), {
resolveId(id, importer) {
return rpc("vitest:mocks:resolveId", {
id,
importer
});
},
resolveMock(id, importer, options) {
return rpc("vitest:mocks:resolveMock", {
id,
importer,
options
});
},
async invalidate(ids) {
return rpc("vitest:mocks:invalidate", { ids });
}
}, createMockInstance, { root: __VITEST_MOCKER_ROOT__ });
globalThis[__VITEST_GLOBAL_THIS_ACCESSOR__] = mocker;
registerNativeFactoryResolver(mocker);
return createCompilerHints({ globalThisKey: __VITEST_GLOBAL_THIS_ACCESSOR__ });
}
function registerNativeFactoryResolver(mocker) {
hot.on("vitest:interceptor:resolve", async (url) => {
const exports$1 = await mocker.resolveFactoryModule(url);
const keys = Object.keys(exports$1);
hot.send("vitest:interceptor:resolved", {
url,
keys
});
});
}
export { registerModuleMocker, registerNativeFactoryResolver };
+8
View File
@@ -0,0 +1,8 @@
export { c as createManualModuleSource, h as hoistMocks } from './hoistMocks.d-w2ILr1dG.js';
export { automockModule } from './automock.js';
import 'magic-string';
declare function initSyntaxLexers(): Promise<void>;
declare function collectModuleExports(filename: string, code: string, format: "module" | "commonjs", exports?: string[]): string[];
export { collectModuleExports, initSyntaxLexers };
+10
View File
@@ -0,0 +1,10 @@
export { c as createManualModuleSource } from './chunk-utils.js';
export { a as automockModule, c as collectModuleExports, i as initSyntaxLexers } from './chunk-automock.js';
export { h as hoistMocks } from './chunk-hoistMocks.js';
import 'node:fs';
import 'node:url';
import 'magic-string';
import 'estree-walker';
import 'node:module';
import 'node:path';
import './chunk-helpers.js';
+123
View File
@@ -0,0 +1,123 @@
declare class MockerRegistry {
private readonly registryByUrl;
private readonly registryById;
clear(): void;
keys(): IterableIterator<string>;
add(mock: MockedModule): void;
register(json: MockedModuleSerialized): MockedModule;
register(type: "redirect", raw: string, id: string, url: string, redirect: string): RedirectedModule;
register(type: "manual", raw: string, id: string, url: string, factory: () => any): ManualMockedModule;
register(type: "automock", raw: string, id: string, url: string): AutomockedModule;
register(type: "autospy", id: string, raw: string, url: string): AutospiedModule;
delete(id: string): void;
deleteById(id: string): void;
get(id: string): MockedModule | undefined;
getById(id: string): MockedModule | undefined;
has(id: string): boolean;
}
type MockedModule = AutomockedModule | AutospiedModule | ManualMockedModule | RedirectedModule;
type MockedModuleType = "automock" | "autospy" | "manual" | "redirect";
type MockedModuleSerialized = AutomockedModuleSerialized | AutospiedModuleSerialized | ManualMockedModuleSerialized | RedirectedModuleSerialized;
declare class AutomockedModule {
raw: string;
id: string;
url: string;
readonly type = "automock";
constructor(raw: string, id: string, url: string);
static fromJSON(data: AutomockedModuleSerialized): AutospiedModule;
toJSON(): AutomockedModuleSerialized;
}
interface AutomockedModuleSerialized {
type: "automock";
url: string;
raw: string;
id: string;
}
declare class AutospiedModule {
raw: string;
id: string;
url: string;
readonly type = "autospy";
constructor(raw: string, id: string, url: string);
static fromJSON(data: AutospiedModuleSerialized): AutospiedModule;
toJSON(): AutospiedModuleSerialized;
}
interface AutospiedModuleSerialized {
type: "autospy";
url: string;
raw: string;
id: string;
}
declare class RedirectedModule {
raw: string;
id: string;
url: string;
redirect: string;
readonly type = "redirect";
constructor(raw: string, id: string, url: string, redirect: string);
static fromJSON(data: RedirectedModuleSerialized): RedirectedModule;
toJSON(): RedirectedModuleSerialized;
}
interface RedirectedModuleSerialized {
type: "redirect";
url: string;
id: string;
raw: string;
redirect: string;
}
declare class ManualMockedModule<T = any> {
raw: string;
id: string;
url: string;
factory: () => T;
cache: T | undefined;
readonly type = "manual";
constructor(raw: string, id: string, url: string, factory: () => T);
resolve(): T;
static fromJSON(data: ManualMockedModuleSerialized, factory: () => any): ManualMockedModule;
toJSON(): ManualMockedModuleSerialized;
}
interface ManualMockedModuleSerialized {
type: "manual";
url: string;
id: string;
raw: string;
}
type Awaitable<T> = T | PromiseLike<T>;
type ModuleMockFactoryWithHelper<M = unknown> = (importOriginal: <T extends M = M>() => Promise<T>) => Awaitable<Partial<M>>;
type ModuleMockFactory = () => any;
interface ModuleMockOptions {
spy?: boolean;
}
interface ServerMockResolution {
mockType: "manual" | "redirect" | "automock" | "autospy";
resolvedId: string;
resolvedUrl: string;
needsInterop?: boolean;
redirectUrl?: string | null;
}
interface ServerIdResolution {
id: string;
url: string;
optimized: boolean;
}
interface ModuleMockContext {
/**
* When mocking with a factory, this refers to the module that imported the mock.
*/
callstack: null | string[];
}
interface TestModuleMocker {
queueMock(id: string, importer: string, factoryOrOptions?: ModuleMockFactory | ModuleMockOptions): void;
queueUnmock(id: string, importer: string): void;
importActual<T>(rawId: string, importer: string, callstack?: string[] | null): Promise<T>;
importMock(rawId: string, importer: string): Promise<any>;
mockObject(object: Record<string | symbol, any>, moduleType?: "automock" | "autospy"): Record<string | symbol, any>;
mockObject(object: Record<string | symbol, any>, mockExports: Record<string | symbol, any> | undefined, moduleType?: "automock" | "autospy"): Record<string | symbol, any>;
getMockContext(): ModuleMockContext;
reset(): void;
}
export { AutomockedModule as A, MockerRegistry as M, RedirectedModule as R, AutospiedModule as h, ManualMockedModule as j };
export type { ServerMockResolution as S, TestModuleMocker as T, MockedModule as a, ModuleMockOptions as b, ModuleMockFactoryWithHelper as c, MockedModuleType as d, ModuleMockContext as e, ServerIdResolution as f, AutomockedModuleSerialized as g, AutospiedModuleSerialized as i, ManualMockedModuleSerialized as k, MockedModuleSerialized as l, ModuleMockFactory as m, RedirectedModuleSerialized as n };
+97
View File
@@ -0,0 +1,97 @@
{
"name": "@vitest/mocker",
"type": "module",
"version": "4.1.8",
"description": "Vitest module mocker implementation",
"license": "MIT",
"funding": "https://opencollective.com/vitest",
"homepage": "https://github.com/vitest-dev/vitest/tree/main/packages/mocker",
"repository": {
"type": "git",
"url": "git+https://github.com/vitest-dev/vitest.git",
"directory": "packages/mocker"
},
"bugs": {
"url": "https://github.com/vitest-dev/vitest/issues"
},
"keywords": [
"vitest",
"test",
"mock"
],
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./node": {
"types": "./dist/node.d.ts",
"default": "./dist/node.js"
},
"./browser": {
"types": "./dist/browser.d.ts",
"default": "./dist/browser.js"
},
"./redirect": {
"types": "./dist/redirect.d.ts",
"default": "./dist/redirect.js"
},
"./automock": {
"types": "./dist/automock.d.ts",
"default": "./dist/automock.js"
},
"./register": {
"types": "./dist/register.d.ts",
"default": "./dist/register.js"
},
"./auto-register": {
"types": "./dist/register.d.ts",
"default": "./dist/register.js"
},
"./transforms": {
"types": "./dist/transforms.d.ts",
"default": "./dist/transforms.js"
},
"./package.json": "./package.json"
},
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"*.d.ts",
"dist"
],
"peerDependencies": {
"msw": "^2.4.9",
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
},
"peerDependenciesMeta": {
"msw": {
"optional": true
},
"vite": {
"optional": true
}
},
"dependencies": {
"estree-walker": "^3.0.3",
"magic-string": "^0.30.21",
"@vitest/spy": "4.1.8"
},
"devDependencies": {
"@types/estree": "^1.0.8",
"acorn-walk": "^8.3.5",
"cjs-module-lexer": "^2.2.0",
"es-module-lexer": "^2.0.0",
"msw": "^2.12.10",
"pathe": "^2.0.3",
"vite": "^6.3.5",
"@vitest/spy": "4.1.8",
"@vitest/utils": "4.1.8"
},
"scripts": {
"build": "premove dist && rollup -c",
"dev": "rollup -c --watch"
}
}
+21
View File
@@ -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.
+7
View File
@@ -0,0 +1,7 @@
# @vitest/pretty-format
[![NPM version](https://img.shields.io/npm/v/@vitest/pretty-format?color=a1b858&label=)](https://npmx.dev/package/@vitest/pretty-format)
Vitest's fork of Jest's [`pretty-format`](https://npmx.dev/package/pretty-format).
[GitHub](https://github.com/vitest-dev/vitest/tree/main/packages/pretty-format) | [Documentation](https://github.com/vitest-dev/vitest/blob/main/packages/pretty-format/USAGE.md)
+195
View File
@@ -0,0 +1,195 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
interface Colors {
comment: {
close: string;
open: string;
};
content: {
close: string;
open: string;
};
prop: {
close: string;
open: string;
};
tag: {
close: string;
open: string;
};
value: {
close: string;
open: string;
};
}
type Indent = (arg0: string) => string;
type Refs = Array<unknown>;
type Print = (arg0: unknown) => string;
type Theme = Required<{
comment?: string;
content?: string;
prop?: string;
tag?: string;
value?: string;
}>;
/**
* compare function used when sorting object keys, `null` can be used to skip over sorting.
*/
type CompareKeys = ((a: string, b: string) => number) | null | undefined;
type RequiredOptions = Required<PrettyFormatOptions>;
interface Options extends Omit<RequiredOptions, "compareKeys" | "theme"> {
compareKeys: CompareKeys;
theme: Theme;
}
interface PrettyFormatOptions {
/**
* Call `toJSON` on objects before formatting them.
* Ignored after the formatter has already called `toJSON` once for a value.
* @default true
*/
callToJSON?: boolean;
/**
* Whether to escape special characters in regular expressions.
* @default false
*/
escapeRegex?: boolean;
/**
* Whether to escape special characters in strings.
* @default true
*/
escapeString?: boolean;
/**
* Whether to highlight syntax using terminal colors.
* @default false
*/
highlight?: boolean;
/**
* Number of spaces to use for each level of indentation.
* @default 2
*/
indent?: number;
/**
* Maximum depth to recurse into nested values.
* @default Infinity
*/
maxDepth?: number;
/**
* Maximum number of items to print in arrays, sets, maps, and similar collections.
* @default Infinity
*/
maxWidth?: number;
/**
* Approximate per-depth-level budget for output length.
* When the accumulated output at any single depth level exceeds this value,
* further nesting is collapsed. This is a heuristic safety valve, not a hard
* limit — total output can reach up to roughly `maxDepth × maxOutputLength`.
* @default 1_000_000
*/
maxOutputLength?: number;
/**
* Whether to minimize added whitespace, including indentation and line breaks.
* @default false
*/
min?: boolean;
/**
* Whether to print `Object` / `Array` prefixes for plain objects and arrays.
* @default true
*/
printBasicPrototype?: boolean;
/**
* Whether to include the function name when formatting functions.
* @default true
*/
printFunctionName?: boolean;
/**
* Whether to include shadow-root contents when formatting DOM nodes.
* @default true
*/
printShadowRoot?: boolean;
/**
* Compare function used when sorting object keys. Set to `null` to disable sorting.
*/
compareKeys?: CompareKeys;
/**
* Plugins used to serialize application-specific data types.
* @default []
*/
plugins?: Plugins;
}
type OptionsReceived = PrettyFormatOptions;
interface Config {
callToJSON: boolean;
compareKeys: CompareKeys;
colors: Colors;
escapeRegex: boolean;
escapeString: boolean;
indent: string;
maxDepth: number;
maxWidth: number;
min: boolean;
plugins: Plugins;
printBasicPrototype: boolean;
printFunctionName: boolean;
printShadowRoot: boolean;
spacingInner: string;
spacingOuter: string;
maxOutputLength: number;
}
type Printer = (val: unknown, config: Config, indentation: string, depth: number, refs: Refs, hasCalledToJSON?: boolean) => string;
type Test = (arg0: any) => boolean;
interface NewPlugin {
serialize: (val: any, config: Config, indentation: string, depth: number, refs: Refs, printer: Printer) => string;
test: Test;
}
interface PluginOptions {
edgeSpacing: string;
min: boolean;
spacing: string;
}
interface OldPlugin {
print: (val: unknown, print: Print, indent: Indent, options: PluginOptions, colors: Colors) => string;
test: Test;
}
type Plugin = NewPlugin | OldPlugin;
type Plugins = Array<Plugin>;
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
declare function createDOMElementFilter(filterNode?: (node: any) => boolean): NewPlugin;
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
declare const DEFAULT_OPTIONS: Options;
/**
* Returns a presentation string of your `val` object
* @param val any potential JavaScript object
* @param options Custom settings
*/
declare function format(val: unknown, options?: OptionsReceived): string;
declare const plugins: {
AsymmetricMatcher: NewPlugin;
DOMCollection: NewPlugin;
DOMElement: NewPlugin;
Immutable: NewPlugin;
ReactElement: NewPlugin;
ReactTestComponent: NewPlugin;
Error: NewPlugin;
};
export { DEFAULT_OPTIONS, createDOMElementFilter, format, plugins };
export type { Colors, CompareKeys, Config, NewPlugin, OldPlugin, Options, OptionsReceived, Plugin, Plugins, PrettyFormatOptions, Printer, Refs, Theme };
+1069
View File
File diff suppressed because it is too large Load Diff
+50
View File
@@ -0,0 +1,50 @@
{
"name": "@vitest/pretty-format",
"type": "module",
"version": "4.1.8",
"description": "Fork of pretty-format with support for ESM",
"license": "MIT",
"funding": "https://opencollective.com/vitest",
"homepage": "https://github.com/vitest-dev/vitest/tree/main/packages/pretty-format",
"repository": {
"type": "git",
"url": "git+https://github.com/vitest-dev/vitest.git",
"directory": "packages/pretty-format"
},
"bugs": {
"url": "https://github.com/vitest-dev/vitest/issues"
},
"keywords": [
"vitest",
"test",
"pretty",
"pretty-format"
],
"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": [
"*.d.ts",
"dist"
],
"dependencies": {
"tinyrainbow": "^3.1.0"
},
"devDependencies": {
"@types/react-is": "^19.2.0",
"react-is": "^19.2.4",
"react-is-18": "npm:react-is@18.3.1"
},
"scripts": {
"build": "premove dist && rollup -c",
"dev": "rollup -c --watch"
}
}
+21
View File
@@ -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.
+7
View File
@@ -0,0 +1,7 @@
# @vitest/runner
[![NPM version](https://img.shields.io/npm/v/@vitest/runner?color=a1b858&label=)](https://npmx.dev/package/@vitest/runner)
Vitest mechanism to collect and run tests.
[GitHub](https://github.com/vitest-dev/vitest/tree/main/packages/runner) | [Documentation](https://vitest.dev/api/advanced/runner)
File diff suppressed because it is too large Load Diff
+179
View File
@@ -0,0 +1,179 @@
import { T as TestArtifact, a as Test, S as Suite, b as SuiteHooks, F as FileSpecification, V as VitestRunner, c as File, d as TaskUpdateEvent, e as Task, f as TestAPI, g as SuiteAPI, h as SuiteCollector } from './tasks.d-DEYaIMIu.js';
export { A as AfterAllListener, i as AfterEachListener, j as AroundAllListener, k as AroundEachListener, B as BeforeAllListener, l as BeforeEachListener, C as CancelReason, m as FailureScreenshotArtifact, n as Fixture, o as FixtureFn, p as FixtureOptions, q as Fixtures, I as ImportDuration, r as InferFixturesTypes, O as OnTestFailedHandler, s as OnTestFinishedHandler, R as Retry, t as RunMode, u as RuntimeContext, v as SequenceHooks, w as SequenceSetupFiles, x as SerializableRetry, y as SuiteFactory, z as SuiteOptions, D as TaskBase, E as TaskCustomOptions, G as TaskEventPack, H as TaskHook, J as TaskMeta, K as TaskPopulated, L as TaskResult, M as TaskResultPack, N as TaskState, P as TestAnnotation, Q as TestAnnotationArtifact, U as TestAnnotationLocation, W as TestArtifactBase, X as TestArtifactLocation, Y as TestArtifactRegistry, Z as TestAttachment, _ as TestContext, $ as TestFunction, a0 as TestOptions, a1 as TestTagDefinition, a2 as TestTags, a3 as Use, a4 as VisualRegressionArtifact, a5 as VitestRunnerConfig, a6 as VitestRunnerConstructor, a7 as VitestRunnerImportSource, a8 as afterAll, a9 as afterEach, aa as aroundAll, ab as aroundEach, ac as beforeAll, ad as beforeEach, ae as onTestFailed, af as onTestFinished } from './tasks.d-DEYaIMIu.js';
import { Awaitable } from '@vitest/utils';
import '@vitest/utils/diff';
/**
* @experimental
* @advanced
*
* Records a custom test artifact during test execution.
*
* This function allows you to attach structured data, files, or metadata to a test.
*
* Vitest automatically injects the source location where the artifact was created and manages any attachments you include.
*
* **Note:** artifacts must be recorded before the task is reported. Any artifacts recorded after that will not be included in the task.
*
* @param task - The test task context, typically accessed via `this.task` in custom matchers or `context.task` in tests
* @param artifact - The artifact to record. Must extend {@linkcode TestArtifactBase}
*
* @returns A promise that resolves to the recorded artifact with location injected
*
* @throws {Error} If the test runner doesn't support artifacts
*
* @example
* ```ts
* // In a custom assertion
* async function toHaveValidSchema(this: MatcherState, actual: unknown) {
* const validation = validateSchema(actual)
*
* await recordArtifact(this.task, {
* type: 'my-plugin:schema-validation',
* passed: validation.valid,
* errors: validation.errors,
* })
*
* return { pass: validation.valid, message: () => '...' }
* }
* ```
*/
declare function recordArtifact<Artifact extends TestArtifact>(task: Test, artifact: Artifact): Promise<Artifact>;
declare function setFn(key: Test, fn: () => Awaitable<void>): void;
declare function getFn<Task = Test>(key: Task): () => Awaitable<void>;
declare function setHooks(key: Suite, hooks: SuiteHooks): void;
declare function getHooks(key: Suite): SuiteHooks;
declare function updateTask(event: TaskUpdateEvent, task: Task, runner: VitestRunner): void;
declare function startTests(specs: string[] | FileSpecification[], runner: VitestRunner): Promise<File[]>;
declare function publicCollect(specs: string[] | FileSpecification[], runner: VitestRunner): Promise<File[]>;
/**
* Creates a suite of tests, allowing for grouping and hierarchical organization of tests.
* Suites can contain both tests and other suites, enabling complex test structures.
*
* @param {string} name - The name of the suite, used for identification and reporting.
* @param {Function} fn - A function that defines the tests and suites within this suite.
* @example
* ```ts
* // Define a suite with two tests
* suite('Math operations', () => {
* test('should add two numbers', () => {
* expect(add(1, 2)).toBe(3);
* });
*
* test('should subtract two numbers', () => {
* expect(subtract(5, 2)).toBe(3);
* });
* });
* ```
* @example
* ```ts
* // Define nested suites
* suite('String operations', () => {
* suite('Trimming', () => {
* test('should trim whitespace from start and end', () => {
* expect(' hello '.trim()).toBe('hello');
* });
* });
*
* suite('Concatenation', () => {
* test('should concatenate two strings', () => {
* expect('hello' + ' ' + 'world').toBe('hello world');
* });
* });
* });
* ```
*/
declare const suite: SuiteAPI;
/**
* Defines a test case with a given name and test function. The test function can optionally be configured with test options.
*
* @param {string | Function} name - The name of the test or a function that will be used as a test name.
* @param {TestOptions | TestFunction} [optionsOrFn] - Optional. The test options or the test function if no explicit name is provided.
* @param {number | TestOptions | TestFunction} [optionsOrTest] - Optional. The test function or options, depending on the previous parameters.
* @throws {Error} If called inside another test function.
* @example
* ```ts
* // Define a simple test
* test('should add two numbers', () => {
* expect(add(1, 2)).toBe(3);
* });
* ```
* @example
* ```ts
* // Define a test with options
* test('should subtract two numbers', { retry: 3 }, () => {
* expect(subtract(5, 2)).toBe(3);
* });
* ```
*/
declare const test: TestAPI;
/**
* Creates a suite of tests, allowing for grouping and hierarchical organization of tests.
* Suites can contain both tests and other suites, enabling complex test structures.
*
* @param {string} name - The name of the suite, used for identification and reporting.
* @param {Function} fn - A function that defines the tests and suites within this suite.
* @example
* ```ts
* // Define a suite with two tests
* describe('Math operations', () => {
* test('should add two numbers', () => {
* expect(add(1, 2)).toBe(3);
* });
*
* test('should subtract two numbers', () => {
* expect(subtract(5, 2)).toBe(3);
* });
* });
* ```
* @example
* ```ts
* // Define nested suites
* describe('String operations', () => {
* describe('Trimming', () => {
* test('should trim whitespace from start and end', () => {
* expect(' hello '.trim()).toBe('hello');
* });
* });
*
* describe('Concatenation', () => {
* test('should concatenate two strings', () => {
* expect('hello' + ' ' + 'world').toBe('hello world');
* });
* });
* });
* ```
*/
declare const describe: SuiteAPI;
/**
* Defines a test case with a given name and test function. The test function can optionally be configured with test options.
*
* @param {string | Function} name - The name of the test or a function that will be used as a test name.
* @param {TestOptions | TestFunction} [optionsOrFn] - Optional. The test options or the test function if no explicit name is provided.
* @param {number | TestOptions | TestFunction} [optionsOrTest] - Optional. The test function or options, depending on the previous parameters.
* @throws {Error} If called inside another test function.
* @example
* ```ts
* // Define a simple test
* it('adds two numbers', () => {
* expect(add(1, 2)).toBe(3);
* });
* ```
* @example
* ```ts
* // Define a test with options
* it('subtracts two numbers', { retry: 3 }, () => {
* expect(subtract(5, 2)).toBe(3);
* });
* ```
*/
declare const it: TestAPI;
declare function getCurrentSuite<ExtraContext = object>(): SuiteCollector<ExtraContext>;
declare function createTaskCollector(fn: (...args: any[]) => any): TestAPI;
declare function getCurrentTest<T extends Test | undefined>(): T;
export { File, FileSpecification, Suite, SuiteAPI, SuiteCollector, SuiteHooks, Task, TaskUpdateEvent, Test, TestAPI, TestArtifact, VitestRunner, publicCollect as collectTests, createTaskCollector, describe, getCurrentSuite, getCurrentTest, getFn, getHooks, it, recordArtifact, setFn, setHooks, startTests, suite, test, updateTask };
+7
View File
@@ -0,0 +1,7 @@
export { a as afterAll, b as afterEach, c as aroundAll, d as aroundEach, e as beforeAll, f as beforeEach, p as collectTests, g as createTaskCollector, h as describe, i as getCurrentSuite, j as getCurrentTest, k as getFn, l as getHooks, m as it, o as onTestFailed, n as onTestFinished, r as recordArtifact, s as setFn, q as setHooks, t as startTests, u as suite, v as test, w as updateTask } from './chunk-artifact.js';
import '@vitest/utils/error';
import '@vitest/utils/helpers';
import '@vitest/utils/timers';
import '@vitest/utils/display';
import '@vitest/utils/source-map';
import 'pathe';
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
export { A as AfterAllListener, i as AfterEachListener, j as AroundAllListener, k as AroundEachListener, B as BeforeAllListener, l as BeforeEachListener, C as CancelReason, m as FailureScreenshotArtifact, c as File, F as FileSpecification, n as Fixture, o as FixtureFn, p as FixtureOptions, q as Fixtures, I as ImportDuration, r as InferFixturesTypes, O as OnTestFailedHandler, s as OnTestFinishedHandler, R as Retry, t as RunMode, u as RuntimeContext, v as SequenceHooks, w as SequenceSetupFiles, x as SerializableRetry, S as Suite, g as SuiteAPI, h as SuiteCollector, y as SuiteFactory, b as SuiteHooks, z as SuiteOptions, e as Task, D as TaskBase, E as TaskCustomOptions, G as TaskEventPack, H as TaskHook, J as TaskMeta, K as TaskPopulated, L as TaskResult, M as TaskResultPack, N as TaskState, d as TaskUpdateEvent, a as Test, f as TestAPI, P as TestAnnotation, Q as TestAnnotationArtifact, U as TestAnnotationLocation, T as TestArtifact, W as TestArtifactBase, X as TestArtifactLocation, Y as TestArtifactRegistry, Z as TestAttachment, _ as TestContext, $ as TestFunction, a0 as TestOptions, a1 as TestTagDefinition, a2 as TestTags, a3 as Use, a4 as VisualRegressionArtifact, V as VitestRunner, a5 as VitestRunnerConfig, a6 as VitestRunnerConstructor, a7 as VitestRunnerImportSource } from './tasks.d-DEYaIMIu.js';
import '@vitest/utils';
import '@vitest/utils/diff';
+1
View File
@@ -0,0 +1 @@
+57
View File
@@ -0,0 +1,57 @@
import { S as Suite, c as File, e as Task, a1 as TestTagDefinition, a5 as VitestRunnerConfig, a as Test } from './tasks.d-DEYaIMIu.js';
export { ag as ChainableFunction, ah as createChainable } from './tasks.d-DEYaIMIu.js';
import { ParsedStack, Arrayable } from '@vitest/utils';
import '@vitest/utils/diff';
/**
* If any tasks been marked as `only`, mark all other tasks as `skip`.
*/
declare function interpretTaskModes(file: Suite, namePattern?: string | RegExp, testLocations?: number[] | undefined, testIds?: string[] | undefined, testTagsFilter?: ((testTags: string[]) => boolean) | undefined, onlyMode?: boolean, parentIsOnly?: boolean, allowOnly?: boolean): void;
declare function someTasksAreOnly(suite: Suite): boolean;
declare function generateHash(str: string): string;
declare function calculateSuiteHash(parent: Suite): void;
declare function createFileTask(filepath: string, root: string, projectName: string | undefined, pool?: string, viteEnvironment?: string): File;
/**
* Generate a unique ID for a file based on its path and project name
* @param file File relative to the root of the project to keep ID the same between different machines
* @param projectName The name of the test project
*/
declare function generateFileHash(file: string, projectName: string | undefined): string;
declare function findTestFileStackTrace(testFilePath: string, error: string): ParsedStack | undefined;
interface ConcurrencyLimiter extends ConcurrencyLimiterFn {
acquire: () => (() => void) | Promise<() => void>;
}
type ConcurrencyLimiterFn = <
Args extends unknown[],
T
>(func: (...args: Args) => PromiseLike<T> | T, ...args: Args) => Promise<T>;
/**
* Return a function for running multiple async operations with limited concurrency.
*/
declare function limitConcurrency(concurrency?: number): ConcurrencyLimiter;
/**
* Partition in tasks groups by consecutive concurrent
*/
declare function partitionSuiteChildren(suite: Suite): Task[][];
/**
* @experimental
*/
declare function matchesTags(testTags: string[]): boolean;
declare function validateTags(config: VitestRunnerConfig, tags: string[]): void;
declare function createTagsFilter(tagsExpr: string[], availableTags: TestTagDefinition[]): (testTags: string[]) => boolean;
declare function isTestCase(s: Task): s is Test;
declare function getTests(suite: Arrayable<Task>): Test[];
declare function getTasks(tasks?: Arrayable<Task>): Task[];
declare function getSuites(suite: Arrayable<Task>): Suite[];
declare function hasTests(suite: Arrayable<Suite>): boolean;
declare function hasFailed(suite: Arrayable<Task>): boolean;
declare function getNames(task: Task): string[];
declare function getFullName(task: Task, separator?: string): string;
declare function getTestName(task: Task, separator?: string): string;
declare function createTaskName(names: readonly (string | undefined)[], separator?: string): string;
export { calculateSuiteHash, createFileTask, createTagsFilter, createTaskName, findTestFileStackTrace, generateFileHash, generateHash, getFullName, getNames, getSuites, getTasks, getTestName, getTests, hasFailed, hasTests, interpretTaskModes, isTestCase, limitConcurrency, matchesTags, partitionSuiteChildren, someTasksAreOnly, validateTags };
+7
View File
@@ -0,0 +1,7 @@
export { x as calculateSuiteHash, y as createChainable, z as createFileTask, A as createTagsFilter, B as createTaskName, C as findTestFileStackTrace, D as generateFileHash, E as generateHash, F as getFullName, G as getNames, H as getSuites, I as getTasks, J as getTestName, K as getTests, L as hasFailed, M as hasTests, N as interpretTaskModes, O as isTestCase, P as limitConcurrency, Q as matchesTags, R as partitionSuiteChildren, S as someTasksAreOnly, T as validateTags } from './chunk-artifact.js';
import '@vitest/utils/error';
import '@vitest/utils/helpers';
import '@vitest/utils/timers';
import '@vitest/utils/display';
import '@vitest/utils/source-map';
import 'pathe';
+53
View File
@@ -0,0 +1,53 @@
{
"name": "@vitest/runner",
"type": "module",
"version": "4.1.8",
"description": "Vitest test runner",
"license": "MIT",
"funding": "https://opencollective.com/vitest",
"homepage": "https://vitest.dev/api/advanced/runner",
"repository": {
"type": "git",
"url": "git+https://github.com/vitest-dev/vitest.git",
"directory": "packages/runner"
},
"bugs": {
"url": "https://github.com/vitest-dev/vitest/issues"
},
"keywords": [
"vitest",
"test",
"test-runner"
],
"sideEffects": true,
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./utils": {
"types": "./dist/utils.d.ts",
"default": "./dist/utils.js"
},
"./types": {
"types": "./dist/types.d.ts",
"default": "./dist/types.js"
},
"./*": "./*"
},
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"*.d.ts",
"dist"
],
"dependencies": {
"pathe": "^2.0.3",
"@vitest/utils": "4.1.8"
},
"scripts": {
"build": "premove dist && rollup -c",
"dev": "rollup -c --watch"
}
}
+1
View File
@@ -0,0 +1 @@
export * from './dist/types.js'
+1
View File
@@ -0,0 +1 @@
export * from './dist/utils.js'
+21
View File
@@ -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.
+88
View File
@@ -0,0 +1,88 @@
# @vitest/snapshot
[![NPM version](https://img.shields.io/npm/v/@vitest/snapshot?color=a1b858&label=)](https://npmx.dev/package/@vitest/snapshot)
Lightweight implementation of Jest's snapshots.
## Usage
```js
import { SnapshotClient } from '@vitest/snapshot'
import { NodeSnapshotEnvironment } from '@vitest/snapshot/environment'
import { SnapshotManager } from '@vitest/snapshot/manager'
const client = new SnapshotClient({
// you need to provide your own equality check implementation if you use it
// this function is called when `.toMatchSnapshot({ property: 1 })` is called
isEqual: (received, expected) =>
equals(received, expected, [iterableEquality, subsetEquality]),
})
// class that implements snapshot saving and reading
// by default uses fs module, but you can provide your own implementation depending on the environment
const environment = new NodeSnapshotEnvironment()
// you need to implement this yourselves,
// this depends on your runner
function getCurrentFilepath() {
return '/file.spec.js'
}
function getCurrentTestName() {
return 'test1'
}
// example for inline snapshots, nothing is required to support regular snapshots,
// just call `assert` with `isInline: false`
function wrapper(received) {
function __INLINE_SNAPSHOT__(inlineSnapshot, message) {
client.assert({
received,
message,
isInline: true,
inlineSnapshot,
filepath: getCurrentFilepath(),
name: getCurrentTestName(),
})
}
return {
// the name is hard-coded, it should be inside another function, so Vitest can find the actual test file where it was called (parses call stack trace + 2)
// you can override this behaviour in SnapshotState's `_inferInlineSnapshotStack` method by providing your own SnapshotState to SnapshotClient constructor
toMatchInlineSnapshot: (...args) => __INLINE_SNAPSHOT__(...args),
}
}
const options = {
updateSnapshot: 'new',
snapshotEnvironment: environment,
}
await client.startCurrentRun(
getCurrentFilepath(),
getCurrentTestName(),
options
)
// this will save snapshot to a file which is returned by "snapshotEnvironment.resolvePath"
client.assert({
received: 'some text',
isInline: false,
})
// uses "pretty-format", so it requires quotes
// also naming is hard-coded when parsing test files
wrapper('text 1').toMatchInlineSnapshot()
wrapper('text 2').toMatchInlineSnapshot('"text 2"')
const result = await client.finishCurrentRun() // this saves files and returns SnapshotResult
// you can use manager to manage several clients
const manager = new SnapshotManager(options)
manager.add(result)
// do something
// and then read the summary
console.log(manager.summary)
```
[GitHub](https://github.com/vitest-dev/vitest/tree/main/packages/snapshot) | [Documentation](https://vitest.dev/guide/snapshot)
+17
View File
@@ -0,0 +1,17 @@
import { ParsedStack } from '@vitest/utils';
interface SnapshotEnvironment {
getVersion: () => string;
getHeader: () => string;
resolvePath: (filepath: string) => Promise<string>;
resolveRawPath: (testPath: string, rawPath: string) => Promise<string>;
saveSnapshotFile: (filepath: string, snapshot: string) => Promise<void>;
readSnapshotFile: (filepath: string) => Promise<string | null>;
removeSnapshotFile: (filepath: string) => Promise<void>;
processStackTrace?: (stack: ParsedStack) => ParsedStack;
}
interface SnapshotEnvironmentOptions {
snapshotsDirName?: string;
}
export type { SnapshotEnvironment as S, SnapshotEnvironmentOptions as a };
+17
View File
@@ -0,0 +1,17 @@
import { S as SnapshotEnvironment, a as SnapshotEnvironmentOptions } from './environment.d-DOJxxZV9.js';
import '@vitest/utils';
declare class NodeSnapshotEnvironment implements SnapshotEnvironment {
private options;
constructor(options?: SnapshotEnvironmentOptions);
getVersion(): string;
getHeader(): string;
resolveRawPath(testPath: string, rawPath: string): Promise<string>;
resolvePath(filepath: string): Promise<string>;
prepareDirectory(dirPath: string): Promise<void>;
saveSnapshotFile(filepath: string, snapshot: string): Promise<void>;
readSnapshotFile(filepath: string): Promise<string | null>;
removeSnapshotFile(filepath: string): Promise<void>;
}
export { NodeSnapshotEnvironment, SnapshotEnvironment };
+40
View File
@@ -0,0 +1,40 @@
import { promises, existsSync } from 'node:fs';
import { isAbsolute, resolve, dirname, join, basename } from 'pathe';
class NodeSnapshotEnvironment {
constructor(options = {}) {
this.options = options;
}
getVersion() {
return "1";
}
getHeader() {
return `// Snapshot v${this.getVersion()}`;
}
async resolveRawPath(testPath, rawPath) {
return isAbsolute(rawPath) ? rawPath : resolve(dirname(testPath), rawPath);
}
async resolvePath(filepath) {
return join(join(dirname(filepath), this.options.snapshotsDirName ?? "__snapshots__"), `${basename(filepath)}.snap`);
}
async prepareDirectory(dirPath) {
await promises.mkdir(dirPath, { recursive: true });
}
async saveSnapshotFile(filepath, snapshot) {
await promises.mkdir(dirname(filepath), { recursive: true });
await promises.writeFile(filepath, snapshot, "utf-8");
}
async readSnapshotFile(filepath) {
if (!existsSync(filepath)) {
return null;
}
return promises.readFile(filepath, "utf-8");
}
async removeSnapshotFile(filepath) {
if (existsSync(filepath)) {
await promises.unlink(filepath);
}
}
}
export { NodeSnapshotEnvironment };
+74
View File
@@ -0,0 +1,74 @@
import { S as SnapshotState, a as SnapshotStateOptions, b as SnapshotResult, R as RawSnapshotInfo, D as DomainSnapshotAdapter } from './rawSnapshot.d-D_X3-62x.js';
export { c as DomainMatchResult, d as SnapshotData, e as SnapshotMatchOptions, f as SnapshotSerializer, g as SnapshotSummary, h as SnapshotUpdateState, U as UncheckedSnapshot } from './rawSnapshot.d-D_X3-62x.js';
import { Plugin, Plugins } from '@vitest/pretty-format';
export { S as SnapshotEnvironment } from './environment.d-DOJxxZV9.js';
import '@vitest/utils';
interface AssertOptions {
received: unknown;
filepath: string;
name: string;
/**
* Not required but needed for `SnapshotClient.clearTest` to implement test-retry behavior.
* @default name
*/
testId?: string;
message?: string;
isInline?: boolean;
properties?: object;
inlineSnapshot?: string;
error?: Error;
errorMessage?: string;
rawSnapshot?: RawSnapshotInfo;
assertionName?: string;
}
interface AssertDomainOptions extends Omit<AssertOptions, "received"> {
received: unknown;
adapter: DomainSnapshotAdapter<any, any>;
}
interface AssertDomainPollOptions extends Omit<AssertDomainOptions, "received"> {
poll: () => Promise<unknown> | unknown;
timeout?: number;
interval?: number;
}
/** Same shape as expect.extend custom matcher result (SyncExpectationResult from @vitest/expect) */
interface MatchResult {
pass: boolean;
message: () => string;
actual?: unknown;
expected?: unknown;
}
interface SnapshotClientOptions {
isEqual?: (received: unknown, expected: unknown) => boolean;
}
declare class SnapshotClient {
private options;
snapshotStateMap: Map<string, SnapshotState>;
constructor(options?: SnapshotClientOptions);
setup(filepath: string, options: SnapshotStateOptions): Promise<void>;
finish(filepath: string): Promise<SnapshotResult>;
skipTest(filepath: string, testName: string): void;
clearTest(filepath: string, testId: string): void;
getSnapshotState(filepath: string): SnapshotState;
match(options: AssertOptions): MatchResult;
assert(options: AssertOptions): void;
matchDomain(options: AssertDomainOptions): MatchResult;
pollMatchDomain(options: AssertDomainPollOptions): Promise<MatchResult>;
assertRaw(options: AssertOptions): Promise<void>;
clear(): void;
}
declare function stripSnapshotIndentation(inlineSnapshot: string): string;
/**
* 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.
*/
declare function addSerializer(plugin: Plugin): void;
declare function getSerializers(): Plugins;
export { DomainSnapshotAdapter, SnapshotClient, SnapshotResult, SnapshotState, SnapshotStateOptions, addSerializer, getSerializers, stripSnapshotIndentation };
export type { MatchResult };
+1188
View File
File diff suppressed because it is too large Load Diff
+19
View File
@@ -0,0 +1,19 @@
import { a as SnapshotStateOptions, g as SnapshotSummary, b as SnapshotResult } from './rawSnapshot.d-D_X3-62x.js';
import '@vitest/pretty-format';
import '@vitest/utils';
import './environment.d-DOJxxZV9.js';
declare class SnapshotManager {
options: Omit<SnapshotStateOptions, "snapshotEnvironment">;
summary: SnapshotSummary;
extension: string;
constructor(options: Omit<SnapshotStateOptions, "snapshotEnvironment">);
clear(): void;
add(result: SnapshotResult): void;
resolvePath<T = any>(testPath: string, context?: T): string;
resolveRawPath(testPath: string, rawPath: string): string;
}
declare function emptySummary(options: Omit<SnapshotStateOptions, "snapshotEnvironment">): SnapshotSummary;
declare function addSnapshotResult(summary: SnapshotSummary, result: SnapshotResult): void;
export { SnapshotManager, addSnapshotResult, emptySummary };
+73
View File
@@ -0,0 +1,73 @@
import { join, dirname, basename, isAbsolute, resolve } from 'pathe';
class SnapshotManager {
summary;
extension = ".snap";
constructor(options) {
this.options = options;
this.clear();
}
clear() {
this.summary = emptySummary(this.options);
}
add(result) {
addSnapshotResult(this.summary, result);
}
resolvePath(testPath, context) {
const resolver = this.options.resolveSnapshotPath || (() => {
return join(join(dirname(testPath), "__snapshots__"), `${basename(testPath)}${this.extension}`);
});
const path = resolver(testPath, this.extension, context);
return path;
}
resolveRawPath(testPath, rawPath) {
return isAbsolute(rawPath) ? rawPath : resolve(dirname(testPath), rawPath);
}
}
function emptySummary(options) {
const summary = {
added: 0,
failure: false,
filesAdded: 0,
filesRemoved: 0,
filesRemovedList: [],
filesUnmatched: 0,
filesUpdated: 0,
matched: 0,
total: 0,
unchecked: 0,
uncheckedKeysByFile: [],
unmatched: 0,
updated: 0,
didUpdate: options.updateSnapshot === "all"
};
return summary;
}
function addSnapshotResult(summary, result) {
if (result.added) {
summary.filesAdded++;
}
if (result.fileDeleted) {
summary.filesRemoved++;
}
if (result.unmatched) {
summary.filesUnmatched++;
}
if (result.updated) {
summary.filesUpdated++;
}
summary.added += result.added;
summary.matched += result.matched;
summary.unchecked += result.unchecked;
if (result.uncheckedKeys && result.uncheckedKeys.length > 0) {
summary.uncheckedKeysByFile.push({
filePath: result.filepath,
keys: result.uncheckedKeys
});
}
summary.unmatched += result.unmatched;
summary.updated += result.updated;
summary.total += result.added + result.matched + result.unmatched + result.updated;
}
export { SnapshotManager, addSnapshotResult, emptySummary };
+205
View File
@@ -0,0 +1,205 @@
import { OptionsReceived, Plugin } from '@vitest/pretty-format';
import { ParsedStack } from '@vitest/utils';
import { S as SnapshotEnvironment } from './environment.d-DOJxxZV9.js';
interface DomainMatchResult {
pass: boolean;
message?: string;
/**
* The captured value viewed through the template's lens.
*
* Where the template uses patterns (e.g. regexes) or omits details,
* the resolved string adopts those patterns. Where the template doesn't
* match, the resolved string uses literal captured values instead.
*
* Used for two purposes:
* - **Diff display** (actual side): compared against `expected`
* so the diff highlights only genuine mismatches, not pattern-vs-literal noise.
* - **Snapshot update** (`--update`): written as the new snapshot content,
* preserving user-edited patterns from matched regions while incorporating
* actual values for mismatched regions.
*
* When omitted, falls back to `render(capture(received))` (the raw rendered value).
*/
resolved?: string;
/**
* The stored template re-rendered as a string, representing what the user
* originally wrote or last saved.
*
* Used as the expected side in diff display.
*
* When omitted, falls back to the raw snapshot string from the snap file
* or inline snapshot.
*/
expected?: string;
}
interface DomainSnapshotAdapter<
Captured = unknown,
Expected = unknown
> {
name: string;
capture: (received: unknown) => Captured;
render: (captured: Captured) => string;
parseExpected: (input: string) => Expected;
match: (captured: Captured, expected: Expected) => DomainMatchResult;
}
/**
* 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.
*/
declare class DefaultMap<
K,
V
> extends Map<K, V> {
private defaultFn;
constructor(defaultFn: (key: K) => V, entries?: Iterable<readonly [K, V]>);
get(key: K): V;
}
declare class CounterMap<K> extends DefaultMap<K, number> {
constructor();
_total: number | undefined;
valueOf(): number;
increment(key: K): void;
total(): number;
}
interface SnapshotReturnOptions {
actual: string;
count: number;
expected?: string;
key: string;
pass: boolean;
}
interface SaveStatus {
deleted: boolean;
saved: boolean;
}
interface ExpectedSnapshot {
key: string;
count: number;
data?: string;
markAsChecked: () => void;
}
declare class SnapshotState {
testFilePath: string;
snapshotPath: string;
private _counters;
private _dirty;
private _updateSnapshot;
private _snapshotData;
private _initialData;
private _inlineSnapshots;
private _inlineSnapshotStacks;
private _testIdToKeys;
private _rawSnapshots;
private _uncheckedKeys;
private _snapshotFormat;
private _environment;
private _fileExists;
expand: boolean;
private _added;
private _matched;
private _unmatched;
private _updated;
get added(): CounterMap<string>;
set added(value: number);
get matched(): CounterMap<string>;
set matched(value: number);
get unmatched(): CounterMap<string>;
set unmatched(value: number);
get updated(): CounterMap<string>;
set updated(value: number);
private constructor();
static create(testFilePath: string, options: SnapshotStateOptions): Promise<SnapshotState>;
get snapshotUpdateState(): SnapshotUpdateState;
get environment(): SnapshotEnvironment;
markSnapshotsAsCheckedForTest(testName: string): void;
clearTest(testId: string): void;
protected _inferInlineSnapshotStack(stacks: ParsedStack[]): ParsedStack | null;
private _addSnapshot;
private _resolveKey;
private _resolveInlineStack;
private _reconcile;
save(): Promise<SaveStatus>;
getUncheckedCount(): number;
getUncheckedKeys(): Array<string>;
removeUncheckedKeys(): void;
probeExpectedSnapshot(options: Pick<SnapshotMatchOptions, "testName" | "testId" | "isInline" | "inlineSnapshot">): ExpectedSnapshot;
match({ testId, testName, received, key, inlineSnapshot, isInline, error, rawSnapshot, assertionName }: SnapshotMatchOptions): SnapshotReturnOptions;
processDomainSnapshot({ testId, received, expectedSnapshot, matchResult, isInline, error, assertionName }: ProcessDomainSnapshotOptions): SnapshotReturnOptions;
pack(): Promise<SnapshotResult>;
}
type SnapshotData = Record<string, string>;
type SnapshotUpdateState = "all" | "new" | "none";
type SnapshotSerializer = Plugin;
interface SnapshotStateOptions {
updateSnapshot: SnapshotUpdateState;
snapshotEnvironment: SnapshotEnvironment;
expand?: boolean;
snapshotFormat?: OptionsReceived;
resolveSnapshotPath?: (path: string, extension: string, context?: any) => string;
}
interface SnapshotMatchOptions {
testId: string;
testName: string;
received: unknown;
key?: string;
inlineSnapshot?: string;
isInline: boolean;
error?: Error;
rawSnapshot?: RawSnapshotInfo;
assertionName?: string;
}
interface ProcessDomainSnapshotOptions {
testId: string;
received: string;
expectedSnapshot: ExpectedSnapshot;
matchResult?: DomainMatchResult;
isInline?: boolean;
assertionName?: string;
error?: Error;
}
interface SnapshotResult {
filepath: string;
added: number;
fileDeleted: boolean;
matched: number;
unchecked: number;
uncheckedKeys: Array<string>;
unmatched: number;
updated: number;
}
interface UncheckedSnapshot {
filePath: string;
keys: Array<string>;
}
interface SnapshotSummary {
added: number;
didUpdate: boolean;
failure: boolean;
filesAdded: number;
filesRemoved: number;
filesRemovedList: Array<string>;
filesUnmatched: number;
filesUpdated: number;
matched: number;
total: number;
unchecked: number;
uncheckedKeysByFile: Array<UncheckedSnapshot>;
unmatched: number;
updated: number;
}
interface RawSnapshotInfo {
file: string;
readonly?: boolean;
content?: string;
}
export { SnapshotState as S };
export type { DomainSnapshotAdapter as D, RawSnapshotInfo as R, UncheckedSnapshot as U, SnapshotStateOptions as a, SnapshotResult as b, DomainMatchResult as c, SnapshotData as d, SnapshotMatchOptions as e, SnapshotSerializer as f, SnapshotSummary as g, SnapshotUpdateState as h };
+1
View File
@@ -0,0 +1 @@
export * from './dist/environment.js'
+1
View File
@@ -0,0 +1 @@
export * from './dist/manager.js'
+59
View File
@@ -0,0 +1,59 @@
{
"name": "@vitest/snapshot",
"type": "module",
"version": "4.1.8",
"description": "Vitest snapshot manager",
"license": "MIT",
"funding": "https://opencollective.com/vitest",
"homepage": "https://vitest.dev/guide/snapshot",
"repository": {
"type": "git",
"url": "git+https://github.com/vitest-dev/vitest.git",
"directory": "packages/snapshot"
},
"bugs": {
"url": "https://github.com/vitest-dev/vitest/issues"
},
"keywords": [
"vitest",
"test",
"snapshot"
],
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./environment": {
"types": "./dist/environment.d.ts",
"default": "./dist/environment.js"
},
"./manager": {
"types": "./dist/manager.d.ts",
"default": "./dist/manager.js"
},
"./*": "./*"
},
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"*.d.ts",
"dist"
],
"dependencies": {
"magic-string": "^0.30.21",
"pathe": "^2.0.3",
"@vitest/pretty-format": "4.1.8",
"@vitest/utils": "4.1.8"
},
"devDependencies": {
"@types/natural-compare": "^1.4.3",
"natural-compare": "^1.4.0"
},
"scripts": {
"build": "premove dist && rollup -c",
"dev": "rollup -c --watch"
}
}
+21
View File
@@ -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.
+7
View File
@@ -0,0 +1,7 @@
# @vitest/spy
[![NPM version](https://img.shields.io/npm/v/@vitest/spy?color=a1b858&label=)](https://npmx.dev/package/@vitest/spy)
Lightweight Jest-compatible mocking implementation.
[GitHub](https://github.com/vitest-dev/vitest/tree/main/packages/spy) | [Documentation](https://vitest.dev/api/mock)
+416
View File
@@ -0,0 +1,416 @@
import { Disposable } from '@vitest/spy/optional-types.js';
interface MockResultReturn<T> {
type: "return";
/**
* The value that was returned from the function. If function returned a Promise, then this will be a resolved value.
*/
value: T;
}
interface MockResultIncomplete {
type: "incomplete";
value: undefined;
}
interface MockResultThrow {
type: "throw";
/**
* An error that was thrown during function execution.
*/
value: any;
}
interface MockSettledResultIncomplete {
type: "incomplete";
value: undefined;
}
interface MockSettledResultFulfilled<T> {
type: "fulfilled";
value: T;
}
interface MockSettledResultRejected {
type: "rejected";
value: any;
}
type MockResult<T> = MockResultReturn<T> | MockResultThrow | MockResultIncomplete;
type MockSettledResult<T> = MockSettledResultFulfilled<T> | MockSettledResultRejected | MockSettledResultIncomplete;
type MockParameters<T extends Procedure | Constructable> = T extends Constructable ? ConstructorParameters<T> : T extends Procedure ? Parameters<T> : never;
type MockReturnType<T extends Procedure | Constructable> = T extends Constructable ? InstanceType<T> : T extends Procedure ? ReturnType<T> : never;
type MockProcedureContext<T extends Procedure | Constructable> = T extends Constructable ? InstanceType<T> : ThisParameterType<T>;
interface MockContext<T extends Procedure | Constructable = Procedure> {
/**
* This is an array containing all arguments for each call. One item of the array is the arguments of that call.
*
* @see https://vitest.dev/api/mock#mock-calls
* @example
* const fn = vi.fn()
*
* fn('arg1', 'arg2')
* fn('arg3')
*
* fn.mock.calls === [
* ['arg1', 'arg2'], // first call
* ['arg3'], // second call
* ]
*/
calls: MockParameters<T>[];
/**
* This is an array containing all instances that were instantiated when mock was called with a `new` keyword. Note that this is an actual context (`this`) of the function, not a return value.
* @see https://vitest.dev/api/mock#mock-instances
*/
instances: MockProcedureContext<T>[];
/**
* An array of `this` values that were used during each call to the mock function.
* @see https://vitest.dev/api/mock#mock-contexts
*/
contexts: MockProcedureContext<T>[];
/**
* The order of mock's execution. This returns an array of numbers which are shared between all defined mocks.
*
* @see https://vitest.dev/api/mock#mock-invocationcallorder
* @example
* const fn1 = vi.fn()
* const fn2 = vi.fn()
*
* fn1()
* fn2()
* fn1()
*
* fn1.mock.invocationCallOrder === [1, 3]
* fn2.mock.invocationCallOrder === [2]
*/
invocationCallOrder: number[];
/**
* This is an array containing all values that were `returned` from the function.
*
* The `value` property contains the returned value or thrown error. If the function returned a `Promise`, then `result` will always be `'return'` even if the promise was rejected.
*
* @see https://vitest.dev/api/mock#mock-results
* @example
* const fn = vi.fn()
* .mockReturnValueOnce('result')
* .mockImplementationOnce(() => { throw new Error('thrown error') })
*
* const result = fn()
*
* try {
* fn()
* }
* catch {}
*
* fn.mock.results === [
* {
* type: 'return',
* value: 'result',
* },
* {
* type: 'throw',
* value: Error,
* },
* ]
*/
results: MockResult<MockReturnType<T>>[];
/**
* An array containing all values that were `resolved` or `rejected` from the function.
*
* This array will be empty if the function was never resolved or rejected.
*
* @see https://vitest.dev/api/mock#mock-settledresults
* @example
* const fn = vi.fn().mockResolvedValueOnce('result')
*
* const result = fn()
*
* fn.mock.settledResults === [
* {
* type: 'incomplete',
* value: undefined,
* }
* ]
* fn.mock.results === [
* {
* type: 'return',
* value: Promise<'result'>,
* },
* ]
*
* await result
*
* fn.mock.settledResults === [
* {
* type: 'fulfilled',
* value: 'result',
* },
* ]
*/
settledResults: MockSettledResult<Awaited<MockReturnType<T>>>[];
/**
* This contains the arguments of the last call. If spy wasn't called, will return `undefined`.
* @see https://vitest.dev/api/mock#mock-lastcall
*/
lastCall: MockParameters<T> | undefined;
}
type Procedure = (...args: any[]) => any;
type NormalizedProcedure<T extends Procedure | Constructable> = T extends Constructable ? ({
new (...args: ConstructorParameters<T>): InstanceType<T>;
}) | ({
(this: InstanceType<T>, ...args: ConstructorParameters<T>): void;
}) : T extends Procedure ? (...args: Parameters<T>) => ReturnType<T> : never;
type Methods<T> = keyof { [K in keyof T as T[K] extends Procedure ? K : never] : T[K] };
type Properties<T> = { [K in keyof T] : T[K] extends Procedure ? never : K }[keyof T] & (string | symbol);
type Classes<T> = { [K in keyof T] : T[K] extends new (...args: any[]) => any ? K : never }[keyof T] & (string | symbol);
interface MockInstance<T extends Procedure | Constructable = Procedure> extends Disposable {
/**
* Use it to return the name assigned to the mock with the `.mockName(name)` method. By default, it will return `vi.fn()`.
* @see https://vitest.dev/api/mock#getmockname
*/
getMockName(): string;
/**
* Sets the internal mock name. This is useful for identifying the mock when an assertion fails.
* @see https://vitest.dev/api/mock#mockname
*/
mockName(name: string): this;
/**
* Current context of the mock. It stores information about all invocation calls, instances, and results.
*/
mock: MockContext<T>;
/**
* Clears all information about every call. After calling it, all properties on `.mock` will return to their initial state. This method does not reset implementations. It is useful for cleaning up mocks between different assertions.
*
* To automatically call this method before each test, enable the [`clearMocks`](https://vitest.dev/config/clearmocks) setting in the configuration.
* @see https://vitest.dev/api/mock#mockclear
*/
mockClear(): this;
/**
* Does what `mockClear` does and resets inner implementation to the original function. This also resets all "once" implementations.
*
* Note that resetting a mock from `vi.fn()` will set implementation to an empty function that returns `undefined`.
* Resetting a mock from `vi.fn(impl)` will set implementation to `impl`. It is useful for completely resetting a mock to its default state.
*
* To automatically call this method before each test, enable the [`mockReset`](https://vitest.dev/config/mockreset) setting in the configuration.
* @see https://vitest.dev/api/mock#mockreset
*/
mockReset(): this;
/**
* Does what `mockReset` does and restores original descriptors of spied-on objects.
* @see https://vitest.dev/api/mock#mockrestore
*/
mockRestore(): void;
/**
* Returns current permanent mock implementation if there is one.
*
* If mock was created with `vi.fn`, it will consider passed down method as a mock implementation.
*
* If mock was created with `vi.spyOn`, it will return `undefined` unless a custom implementation was provided.
*/
getMockImplementation(): NormalizedProcedure<T> | undefined;
/**
* Accepts a function to be used as the mock implementation. TypeScript expects the arguments and return type to match those of the original function.
* @see https://vitest.dev/api/mock#mockimplementation
* @example
* const increment = vi.fn().mockImplementation(count => count + 1);
* expect(increment(3)).toBe(4);
*/
mockImplementation(fn: NormalizedProcedure<T>): this;
/**
* Accepts a function to be used as the mock implementation. TypeScript expects the arguments and return type to match those of the original function. This method can be chained to produce different results for multiple function calls.
*
* When the mocked function runs out of implementations, it will invoke the default implementation set with `vi.fn(() => defaultValue)` or `.mockImplementation(() => defaultValue)` if they were called.
* @see https://vitest.dev/api/mock#mockimplementationonce
* @example
* const fn = vi.fn(count => count).mockImplementationOnce(count => count + 1);
* expect(fn(3)).toBe(4);
* expect(fn(3)).toBe(3);
*/
mockImplementationOnce(fn: NormalizedProcedure<T>): this;
/**
* Overrides the original mock implementation temporarily while the callback is being executed.
*
* Note that this method takes precedence over the [`mockImplementationOnce`](https://vitest.dev/api/mock#mockimplementationonce).
* @see https://vitest.dev/api/mock#withimplementation
* @example
* const myMockFn = vi.fn(() => 'original')
*
* myMockFn.withImplementation(() => 'temp', () => {
* myMockFn() // 'temp'
* })
*
* myMockFn() // 'original'
*/
withImplementation(fn: NormalizedProcedure<T>, cb: () => Promise<unknown>): Promise<this>;
withImplementation(fn: NormalizedProcedure<T>, cb: () => unknown): this;
/**
* Use this if you need to return the `this` context from the method without invoking the actual implementation.
* @see https://vitest.dev/api/mock#mockreturnthis
*/
mockReturnThis(): this;
/**
* Accepts a value that will be returned whenever the mock function is called. TypeScript will only accept values that match the return type of the original function.
* @see https://vitest.dev/api/mock#mockreturnvalue
* @example
* const mock = vi.fn()
* mock.mockReturnValue(42)
* mock() // 42
* mock.mockReturnValue(43)
* mock() // 43
*/
mockReturnValue(value: MockReturnType<T>): this;
/**
* Accepts a value that will be returned whenever the mock function is called. TypeScript will only accept values that match the return type of the original function.
*
* When the mocked function runs out of implementations, it will invoke the default implementation set with `vi.fn(() => defaultValue)` or `.mockImplementation(() => defaultValue)` if they were called.
* @example
* const myMockFn = vi
* .fn()
* .mockReturnValue('default')
* .mockReturnValueOnce('first call')
* .mockReturnValueOnce('second call')
*
* // 'first call', 'second call', 'default'
* console.log(myMockFn(), myMockFn(), myMockFn())
*/
mockReturnValueOnce(value: MockReturnType<T>): this;
/**
* Accepts a value that will be thrown whenever the mock function is called.
* @see https://vitest.dev/api/mock#mockthrow
* @example
* const myMockFn = vi.fn().mockThrow(new Error('error'))
* myMockFn() // throws 'error'
*/
mockThrow(value: unknown): this;
/**
* Accepts a value that will be thrown during the next function call. If chained, every consecutive call will throw the specified value.
* @example
* const myMockFn = vi
* .fn()
* .mockReturnValue('default')
* .mockThrowOnce(new Error('first call error'))
* .mockThrowOnce('second call error')
*
* expect(() => myMockFn()).toThrowError('first call error')
* expect(() => myMockFn()).toThrowError('second call error')
* expect(myMockFn()).toEqual('default')
*/
mockThrowOnce(value: unknown): this;
/**
* Accepts a value that will be resolved when the async function is called. TypeScript will only accept values that match the return type of the original function.
* @example
* const asyncMock = vi.fn().mockResolvedValue(42)
* asyncMock() // Promise<42>
*/
mockResolvedValue(value: Awaited<MockReturnType<T>>): this;
/**
* Accepts a value that will be resolved during the next function call. TypeScript will only accept values that match the return type of the original function. If chained, each consecutive call will resolve the specified value.
* @example
* const myMockFn = vi
* .fn()
* .mockResolvedValue('default')
* .mockResolvedValueOnce('first call')
* .mockResolvedValueOnce('second call')
*
* // Promise<'first call'>, Promise<'second call'>, Promise<'default'>
* console.log(myMockFn(), myMockFn(), myMockFn())
*/
mockResolvedValueOnce(value: Awaited<MockReturnType<T>>): this;
/**
* Accepts an error that will be rejected when async function is called.
* @example
* const asyncMock = vi.fn().mockRejectedValue(new Error('Async error'))
* await asyncMock() // throws Error<'Async error'>
*/
mockRejectedValue(error: unknown): this;
/**
* Accepts a value that will be rejected during the next function call. If chained, each consecutive call will reject the specified value.
* @example
* const asyncMock = vi
* .fn()
* .mockResolvedValueOnce('first call')
* .mockRejectedValueOnce(new Error('Async error'))
*
* await asyncMock() // first call
* await asyncMock() // throws Error<'Async error'>
*/
mockRejectedValueOnce(error: unknown): this;
}
type Mock<T extends Procedure | Constructable = Procedure> = MockInstance<T> & (T extends Constructable ? (T extends Procedure ? {
new (...args: ConstructorParameters<T>): InstanceType<T>;
(...args: Parameters<T>): ReturnType<T>;
} : {
new (...args: ConstructorParameters<T>): InstanceType<T>;
}) : {
new (...args: MockParameters<T>): MockReturnType<T>;
(...args: MockParameters<T>): MockReturnType<T>;
}) & { [P in keyof T] : T[P] };
type PartialMaybePromise<T> = T extends Promise<Awaited<T>> ? Promise<Partial<Awaited<T>>> : Partial<T>;
type PartialResultFunction<T> = T extends Constructable ? ({
new (...args: ConstructorParameters<T>): InstanceType<T>;
}) | ({
(this: InstanceType<T>, ...args: ConstructorParameters<T>): void;
}) : T extends Procedure ? (...args: Parameters<T>) => PartialMaybePromise<ReturnType<T>> : T;
type PartialMock<T extends Procedure | Constructable = Procedure> = Mock<PartialResultFunction<T extends Mock ? NonNullable<ReturnType<T["getMockImplementation"]>> : T>>;
type DeepPartial<T> = T extends Procedure ? T : T extends Array<infer U> ? Array<DeepPartial<U>> : T extends object ? { [K in keyof T]? : DeepPartial<T[K]> } : T;
type DeepPartialMaybePromise<T> = T extends Promise<Awaited<T>> ? Promise<DeepPartial<Awaited<T>>> : DeepPartial<T>;
type DeepPartialResultFunction<T> = T extends Constructable ? ({
new (...args: ConstructorParameters<T>): InstanceType<T>;
}) | ({
(this: InstanceType<T>, ...args: ConstructorParameters<T>): void;
}) : T extends Procedure ? (...args: Parameters<T>) => DeepPartialMaybePromise<ReturnType<T>> : T;
type DeepPartialMock<T extends Procedure | Constructable = Procedure> = Mock<DeepPartialResultFunction<T extends Mock ? NonNullable<ReturnType<T["getMockImplementation"]>> : T>>;
type MaybeMockedConstructor<T> = T extends Constructable ? Mock<T> : T;
type MockedFunction<T extends Procedure | Constructable> = Mock<T> & MockedObject<T>;
type PartiallyMockedFunction<T extends Procedure | Constructable> = PartialMock<T> & MockedObject<T>;
type MockedFunctionDeep<T extends Procedure | Constructable> = Mock<T> & MockedObjectDeep<T>;
type PartiallyMockedFunctionDeep<T extends Procedure | Constructable> = DeepPartialMock<T> & MockedObjectDeep<T>;
type MockedObject<T> = MaybeMockedConstructor<T> & { [K in Methods<T>] : T[K] extends Procedure ? MockedFunction<T[K]> : T[K] } & { [K in Properties<T>] : T[K] };
type MockedObjectDeep<T> = MaybeMockedConstructor<T> & { [K in Methods<T>] : T[K] extends Procedure ? MockedFunctionDeep<T[K]> : T[K] } & { [K in Properties<T>] : MaybeMockedDeep<T[K]> };
type MaybeMockedDeep<T> = T extends Procedure | Constructable ? MockedFunctionDeep<T> : T extends object ? MockedObjectDeep<T> : T;
type MaybePartiallyMockedDeep<T> = T extends Procedure | Constructable ? PartiallyMockedFunctionDeep<T> : T extends object ? MockedObjectDeep<T> : T;
type MaybeMocked<T> = T extends Procedure | Constructable ? MockedFunction<T> : T extends object ? MockedObject<T> : T;
type MaybePartiallyMocked<T> = T extends Procedure | Constructable ? PartiallyMockedFunction<T> : T extends object ? MockedObject<T> : T;
interface Constructable {
new (...args: any[]): any;
}
type MockedClass<T extends Constructable> = MockInstance<T> & {
prototype: T extends {
prototype: any;
} ? Mocked<T["prototype"]> : never;
} & T;
type Mocked<T> = { [P in keyof T] : T[P] extends Procedure ? MockInstance<T[P]> : T[P] extends Constructable ? MockedClass<T[P]> : T[P] } & T;
interface MockConfig {
mockImplementation: Procedure | Constructable | undefined;
mockOriginal: Procedure | Constructable | undefined;
mockName: string;
onceMockImplementations: Array<Procedure | Constructable>;
}
interface MockInstanceOption {
originalImplementation?: Procedure | Constructable;
mockImplementation?: Procedure | Constructable;
resetToMockImplementation?: boolean;
restore?: () => void;
prototypeMembers?: (string | symbol)[];
keepMembersImplementation?: boolean;
prototypeState?: MockContext;
prototypeConfig?: MockConfig;
resetToMockName?: boolean;
name?: string | symbol;
}
declare function isMockFunction(fn: any): fn is Mock;
declare function createMockInstance(options?: MockInstanceOption): Mock<Procedure | Constructable>;
declare function fn<T extends Procedure | Constructable = Procedure>(originalImplementation?: T): Mock<T>;
declare function spyOn<
T extends object,
S extends Properties<Required<T>>
>(object: T, key: S, accessor: "get"): Mock<() => T[S]>;
declare function spyOn<
T extends object,
G extends Properties<Required<T>>
>(object: T, key: G, accessor: "set"): Mock<(arg: T[G]) => void>;
declare function spyOn<
T extends object,
M extends Classes<Required<T>> | Methods<Required<T>>
>(object: T, key: M): Required<T>[M] extends Constructable | Procedure ? Mock<Required<T>[M]> : never;
declare function restoreAllMocks(): void;
declare function clearAllMocks(): void;
declare function resetAllMocks(): void;
export { clearAllMocks, createMockInstance, fn, isMockFunction, resetAllMocks, restoreAllMocks, spyOn };
export type { Constructable, MaybeMocked, MaybeMockedConstructor, MaybeMockedDeep, MaybePartiallyMocked, MaybePartiallyMockedDeep, Mock, MockContext, MockInstance, MockInstanceOption, MockParameters, MockProcedureContext, MockResult, MockResultIncomplete, MockResultReturn, MockResultThrow, MockReturnType, MockSettledResult, MockSettledResultFulfilled, MockSettledResultIncomplete, MockSettledResultRejected, Mocked, MockedClass, MockedFunction, MockedFunctionDeep, MockedObject, MockedObjectDeep, PartialMock, PartiallyMockedFunction, PartiallyMockedFunctionDeep, Procedure };
+483
View File
@@ -0,0 +1,483 @@
function isMockFunction(fn) {
return typeof fn === "function" && "_isMockFunction" in fn && fn._isMockFunction === true;
}
const MOCK_RESTORE = new Set();
// Jest keeps the state in a separate WeakMap which is good for memory,
// but it makes the state slower to access and return different values
// if you stored it before calling `mockClear` where it will be recreated
const REGISTERED_MOCKS = new Set();
const MOCK_CONFIGS = new WeakMap();
function createMockInstance(options = {}) {
const { originalImplementation, restore, mockImplementation, resetToMockImplementation, resetToMockName } = options;
if (restore) {
MOCK_RESTORE.add(restore);
}
const config = getDefaultConfig(originalImplementation);
const state = getDefaultState();
const mock = createMock({
config,
state,
...options
});
const mockLength = (mockImplementation || originalImplementation)?.length ?? 0;
Object.defineProperty(mock, "length", {
writable: true,
enumerable: false,
value: mockLength,
configurable: true
});
// inherit the default name so it appears in snapshots and logs
// this is used by `vi.spyOn()` for better debugging.
// when `vi.fn()` is called, we just use the default string
if (resetToMockName) {
config.mockName = mock.name || "vi.fn()";
}
MOCK_CONFIGS.set(mock, config);
REGISTERED_MOCKS.add(mock);
mock._isMockFunction = true;
mock.getMockImplementation = () => {
// Jest only returns `config.mockImplementation` here,
// but we think it makes sense to return what the next function will be called
return config.onceMockImplementations[0] || config.mockImplementation;
};
Object.defineProperty(mock, "mock", {
configurable: false,
enumerable: true,
writable: false,
value: state
});
mock.mockImplementation = function mockImplementation(implementation) {
config.mockImplementation = implementation;
return mock;
};
mock.mockImplementationOnce = function mockImplementationOnce(implementation) {
config.onceMockImplementations.push(implementation);
return mock;
};
mock.withImplementation = function withImplementation(implementation, callback) {
const previousImplementation = config.mockImplementation;
const previousOnceImplementations = config.onceMockImplementations;
const reset = () => {
config.mockImplementation = previousImplementation;
config.onceMockImplementations = previousOnceImplementations;
};
config.mockImplementation = implementation;
config.onceMockImplementations = [];
const returnValue = callback();
if (typeof returnValue === "object" && typeof returnValue?.then === "function") {
return returnValue.then(() => {
reset();
return mock;
});
} else {
reset();
}
return mock;
};
mock.mockReturnThis = function mockReturnThis() {
return mock.mockImplementation(function() {
return this;
});
};
mock.mockReturnValue = function mockReturnValue(value) {
return mock.mockImplementation(function() {
if (new.target) {
throwConstructorError("mockReturnValue");
}
return value;
});
};
mock.mockReturnValueOnce = function mockReturnValueOnce(value) {
return mock.mockImplementationOnce(function() {
if (new.target) {
throwConstructorError("mockReturnValueOnce");
}
return value;
});
};
mock.mockThrow = function mockThrow(value) {
// eslint-disable-next-line prefer-arrow-callback
return mock.mockImplementation(function() {
throw value;
});
};
mock.mockThrowOnce = function mockThrowOnce(value) {
// eslint-disable-next-line prefer-arrow-callback
return mock.mockImplementationOnce(function() {
throw value;
});
};
mock.mockResolvedValue = function mockResolvedValue(value) {
return mock.mockImplementation(function() {
if (new.target) {
throwConstructorError("mockResolvedValue");
}
return Promise.resolve(value);
});
};
mock.mockResolvedValueOnce = function mockResolvedValueOnce(value) {
return mock.mockImplementationOnce(function() {
if (new.target) {
throwConstructorError("mockResolvedValueOnce");
}
return Promise.resolve(value);
});
};
mock.mockRejectedValue = function mockRejectedValue(value) {
return mock.mockImplementation(function() {
if (new.target) {
throwConstructorError("mockRejectedValue");
}
return Promise.reject(value);
});
};
mock.mockRejectedValueOnce = function mockRejectedValueOnce(value) {
return mock.mockImplementationOnce(function() {
if (new.target) {
throwConstructorError("mockRejectedValueOnce");
}
return Promise.reject(value);
});
};
mock.mockClear = function mockClear() {
state.calls = [];
state.contexts = [];
state.instances = [];
state.invocationCallOrder = [];
state.results = [];
state.settledResults = [];
return mock;
};
mock.mockReset = function mockReset() {
mock.mockClear();
config.mockImplementation = resetToMockImplementation ? mockImplementation : undefined;
config.mockName = resetToMockName ? mock.name || "vi.fn()" : "vi.fn()";
config.onceMockImplementations = [];
return mock;
};
mock.mockRestore = function mockRestore() {
mock.mockReset();
return restore?.();
};
mock.mockName = function mockName(name) {
if (typeof name === "string") {
config.mockName = name;
}
return mock;
};
mock.getMockName = function getMockName() {
return config.mockName || "vi.fn()";
};
if (Symbol.dispose) {
mock[Symbol.dispose] = () => mock.mockRestore();
}
if (mockImplementation) {
mock.mockImplementation(mockImplementation);
}
return mock;
}
function fn(originalImplementation) {
// if the function is already a mock, just return the same function,
// similarly to how vi.spyOn() works
if (originalImplementation != null && isMockFunction(originalImplementation)) {
return originalImplementation;
}
return createMockInstance({
mockImplementation: originalImplementation,
resetToMockImplementation: true
});
}
function spyOn(object, key, accessor) {
assert(object != null, "The vi.spyOn() function could not find an object to spy upon. The first argument must be defined.");
assert(typeof object === "object" || typeof object === "function", "Vitest cannot spy on a primitive value.");
const [originalDescriptorObject, originalDescriptor] = getDescriptor(object, key) || [];
assert(originalDescriptor || key in object, `The property "${String(key)}" is not defined on the ${typeof object}.`);
let accessType = accessor || "value";
let ssr = false;
// vite ssr support - actual function is stored inside a getter
if (accessType === "value" && originalDescriptor && originalDescriptor.value == null && originalDescriptor.get) {
accessType = "get";
ssr = true;
}
let original;
if (originalDescriptor) {
original = originalDescriptor[accessType];
// weird Proxy edge case where descriptor's value is undefined,
// but there's still a value on the object when called
// https://github.com/vitest-dev/vitest/issues/9439
if (original == null && accessType === "value") {
original = object[key];
}
} else if (accessType !== "value") {
original = () => object[key];
} else {
original = object[key];
}
const originalImplementation = ssr && original ? original() : original;
const originalType = typeof originalImplementation;
assert(
// allow only functions
originalType === "function" || accessType !== "value" && original == null,
`vi.spyOn() can only spy on a function. Received ${originalType}.`
);
if (isMockFunction(originalImplementation)) {
return originalImplementation;
}
const reassign = (cb) => {
const { value, ...desc } = originalDescriptor || {
configurable: true,
writable: true
};
if (accessType !== "value") {
delete desc.writable;
}
desc[accessType] = cb;
Object.defineProperty(object, key, desc);
};
const restore = () => {
// if method is defined on the prototype, we can just remove it from
// the current object instead of redefining a copy of it
if (originalDescriptorObject !== object) {
Reflect.deleteProperty(object, key);
} else if (originalDescriptor && !original) {
Object.defineProperty(object, key, originalDescriptor);
} else {
reassign(original);
}
};
const mock = createMockInstance({
restore,
originalImplementation,
resetToMockName: true
});
try {
reassign(ssr ? () => mock : mock);
} catch (error) {
if (error instanceof TypeError && Symbol.toStringTag && object[Symbol.toStringTag] === "Module" && (error.message.includes("Cannot redefine property") || error.message.includes("Cannot replace module namespace") || error.message.includes("can't redefine non-configurable property"))) {
throw new TypeError(`Cannot spy on export "${String(key)}". Module namespace is not configurable in ESM. See: https://vitest.dev/guide/browser/#limitations`, { cause: error });
}
throw error;
}
return mock;
}
function getDescriptor(obj, method) {
const objDescriptor = Object.getOwnPropertyDescriptor(obj, method);
if (objDescriptor) {
return [obj, objDescriptor];
}
let currentProto = Object.getPrototypeOf(obj);
while (currentProto !== null) {
const descriptor = Object.getOwnPropertyDescriptor(currentProto, method);
if (descriptor) {
return [currentProto, descriptor];
}
currentProto = Object.getPrototypeOf(currentProto);
}
}
function assert(condition, message) {
if (!condition) {
throw new Error(message);
}
}
let invocationCallCounter = 1;
function createMock({ state, config, name: mockName, prototypeState, prototypeConfig, keepMembersImplementation, mockImplementation, prototypeMembers = [] }) {
const original = config.mockOriginal;
const pseudoOriginal = mockImplementation;
const name = mockName || original?.name || "Mock";
const namedObject = { [name]: (function(...args) {
registerCalls(args, state, prototypeState);
registerInvocationOrder(invocationCallCounter++, state, prototypeState);
const result = {
type: "incomplete",
value: undefined
};
const settledResult = {
type: "incomplete",
value: undefined
};
registerResult(result, state, prototypeState);
registerSettledResult(settledResult, state, prototypeState);
const context = new.target ? undefined : this;
const [instanceIndex, instancePrototypeIndex] = registerInstance(context, state, prototypeState);
const [contextIndex, contextPrototypeIndex] = registerContext(context, state, prototypeState);
const implementation = config.onceMockImplementations.shift() || config.mockImplementation || prototypeConfig?.onceMockImplementations.shift() || prototypeConfig?.mockImplementation || original || function() {};
let returnValue;
let thrownValue;
let didThrow = false;
try {
if (new.target) {
returnValue = Reflect.construct(implementation, args, new.target);
// jest calls this before the implementation, but we have to resolve this _after_
// because we cannot do it before the `Reflect.construct` called the custom implementation.
// fortunately, the constructor is always an empty function because `prototypeMethods`
// are only used by the automocker, so this doesn't matter
for (const prop of prototypeMembers) {
const prototypeMock = returnValue[prop];
// the method was overridden because of inheritance, ignore it
// eslint-disable-next-line ts/no-use-before-define
if (prototypeMock !== mock.prototype[prop]) {
continue;
}
const isMock = isMockFunction(prototypeMock);
const prototypeState = isMock ? prototypeMock.mock : undefined;
const prototypeConfig = isMock ? MOCK_CONFIGS.get(prototypeMock) : undefined;
returnValue[prop] = createMockInstance({
originalImplementation: keepMembersImplementation ? prototypeConfig?.mockOriginal : undefined,
prototypeState,
prototypeConfig,
keepMembersImplementation
});
}
} else {
returnValue = implementation.apply(this, args);
}
} catch (error) {
thrownValue = error;
didThrow = true;
if (error instanceof TypeError && error.message.includes("is not a constructor")) {
console.warn(`[vitest] The ${namedObject[name].getMockName()} mock did not use 'function' or 'class' in its implementation, see https://vitest.dev/api/vi#vi-spyon for examples.`);
}
throw error;
} finally {
if (didThrow) {
result.type = "throw";
result.value = thrownValue;
settledResult.type = "rejected";
settledResult.value = thrownValue;
} else {
result.type = "return";
result.value = returnValue;
if (new.target) {
state.contexts[contextIndex - 1] = returnValue;
state.instances[instanceIndex - 1] = returnValue;
if (contextPrototypeIndex != null && prototypeState) {
prototypeState.contexts[contextPrototypeIndex - 1] = returnValue;
}
if (instancePrototypeIndex != null && prototypeState) {
prototypeState.instances[instancePrototypeIndex - 1] = returnValue;
}
}
if (returnValue instanceof Promise) {
returnValue.then((settledValue) => {
settledResult.type = "fulfilled";
settledResult.value = settledValue;
}, (rejectedValue) => {
settledResult.type = "rejected";
settledResult.value = rejectedValue;
});
} else {
settledResult.type = "fulfilled";
settledResult.value = returnValue;
}
}
}
return returnValue;
}) };
const mock = namedObject[name];
const copyPropertiesFrom = original || pseudoOriginal;
if (copyPropertiesFrom) {
copyOriginalStaticProperties(mock, copyPropertiesFrom);
}
return mock;
}
function registerCalls(args, state, prototypeState) {
state.calls.push(args);
prototypeState?.calls.push(args);
}
function registerInvocationOrder(order, state, prototypeState) {
state.invocationCallOrder.push(order);
prototypeState?.invocationCallOrder.push(order);
}
function registerResult(result, state, prototypeState) {
state.results.push(result);
prototypeState?.results.push(result);
}
function registerSettledResult(result, state, prototypeState) {
state.settledResults.push(result);
prototypeState?.settledResults.push(result);
}
function registerInstance(instance, state, prototypeState) {
const instanceIndex = state.instances.push(instance);
const instancePrototypeIndex = prototypeState?.instances.push(instance);
return [instanceIndex, instancePrototypeIndex];
}
function registerContext(context, state, prototypeState) {
const contextIndex = state.contexts.push(context);
const contextPrototypeIndex = prototypeState?.contexts.push(context);
return [contextIndex, contextPrototypeIndex];
}
function copyOriginalStaticProperties(mock, original) {
const { properties, descriptors } = getAllProperties(original);
for (const key of properties) {
const descriptor = descriptors[key];
const mockDescriptor = getDescriptor(mock, key);
if (mockDescriptor) {
continue;
}
Object.defineProperty(mock, key, descriptor);
}
}
const ignoreProperties = new Set([
"length",
"name",
"prototype",
Symbol.for("nodejs.util.promisify.custom")
]);
function getAllProperties(original) {
const properties = new Set();
const descriptors = {};
while (original && original !== Object.prototype && original !== Function.prototype) {
const ownProperties = [...Object.getOwnPropertyNames(original), ...Object.getOwnPropertySymbols(original)];
for (const prop of ownProperties) {
if (descriptors[prop] || ignoreProperties.has(prop)) {
continue;
}
properties.add(prop);
descriptors[prop] = Object.getOwnPropertyDescriptor(original, prop);
}
original = Object.getPrototypeOf(original);
}
return {
properties,
descriptors
};
}
function getDefaultConfig(original) {
return {
mockImplementation: undefined,
mockOriginal: original,
mockName: "vi.fn()",
onceMockImplementations: []
};
}
function getDefaultState() {
const state = {
calls: [],
contexts: [],
instances: [],
invocationCallOrder: [],
settledResults: [],
results: [],
get lastCall() {
return state.calls.at(-1);
}
};
return state;
}
function restoreAllMocks() {
for (const restore of MOCK_RESTORE) {
restore();
}
MOCK_RESTORE.clear();
}
function clearAllMocks() {
REGISTERED_MOCKS.forEach((mock) => mock.mockClear());
}
function resetAllMocks() {
REGISTERED_MOCKS.forEach((mock) => mock.mockReset());
}
function throwConstructorError(shorthand) {
throw new TypeError(`Cannot use \`${shorthand}\` when called with \`new\`. Use \`mockImplementation\` with a \`class\` keyword instead. See https://vitest.dev/api/mock#class-support for more information.`);
}
export { clearAllMocks, createMockInstance, fn, isMockFunction, resetAllMocks, restoreAllMocks, spyOn };
+6
View File
@@ -0,0 +1,6 @@
/* eslint-disable ts/ban-ts-comment */
export interface Disposable {
// @ts-ignore -- Symbol.dispose might not be in user types
[Symbol.dispose]: () => void
}
+46
View File
@@ -0,0 +1,46 @@
{
"name": "@vitest/spy",
"type": "module",
"version": "4.1.8",
"description": "Lightweight Jest compatible spy implementation",
"license": "MIT",
"funding": "https://opencollective.com/vitest",
"homepage": "https://vitest.dev/api/mock",
"repository": {
"type": "git",
"url": "git+https://github.com/vitest-dev/vitest.git",
"directory": "packages/spy"
},
"bugs": {
"url": "https://github.com/vitest-dev/vitest/issues"
},
"keywords": [
"vitest",
"test",
"mock",
"spy",
"intercept"
],
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./optional-types.js": {
"types": "./optional-types.d.ts"
},
"./*": "./*"
},
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist",
"optional-types.d.ts"
],
"scripts": {
"build": "premove dist && rollup -c",
"dev": "rollup -c --watch"
}
}
+21
View File
@@ -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.
+7
View File
@@ -0,0 +1,7 @@
# @vitest/utils
[![NPM version](https://img.shields.io/npm/v/@vitest/utils?color=a1b858&label=)](https://npmx.dev/package/@vitest/utils)
Internal shared utilities used by other Vitest packages.
[GitHub](https://github.com/vitest-dev/vitest/tree/main/packages/utils) | [Documentation](https://vitest.dev/)
+1
View File
@@ -0,0 +1 @@
export * from './dist/diff.js'
+156
View File
@@ -0,0 +1,156 @@
const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
function normalizeWindowsPath(input = "") {
if (!input) {
return input;
}
return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
}
const _UNC_REGEX = /^[/\\]{2}/;
const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
const _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
const normalize = function(path) {
if (path.length === 0) {
return ".";
}
path = normalizeWindowsPath(path);
const isUNCPath = path.match(_UNC_REGEX);
const isPathAbsolute = isAbsolute(path);
const trailingSeparator = path[path.length - 1] === "/";
path = normalizeString(path, !isPathAbsolute);
if (path.length === 0) {
if (isPathAbsolute) {
return "/";
}
return trailingSeparator ? "./" : ".";
}
if (trailingSeparator) {
path += "/";
}
if (_DRIVE_LETTER_RE.test(path)) {
path += "/";
}
if (isUNCPath) {
if (!isPathAbsolute) {
return `//./${path}`;
}
return `//${path}`;
}
return isPathAbsolute && !isAbsolute(path) ? `/${path}` : path;
};
const join = function(...segments) {
let path = "";
for (const seg of segments) {
if (!seg) {
continue;
}
if (path.length > 0) {
const pathTrailing = path[path.length - 1] === "/";
const segLeading = seg[0] === "/";
const both = pathTrailing && segLeading;
if (both) {
path += seg.slice(1);
} else {
path += pathTrailing || segLeading ? seg : `/${seg}`;
}
} else {
path += seg;
}
}
return normalize(path);
};
function cwd() {
if (typeof process !== "undefined" && typeof process.cwd === "function") {
return process.cwd().replace(/\\/g, "/");
}
return "/";
}
const resolve = function(...arguments_) {
arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
let resolvedPath = "";
let resolvedAbsolute = false;
for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
const path = index >= 0 ? arguments_[index] : cwd();
if (!path || path.length === 0) {
continue;
}
resolvedPath = `${path}/${resolvedPath}`;
resolvedAbsolute = isAbsolute(path);
}
resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
if (resolvedAbsolute && !isAbsolute(resolvedPath)) {
return `/${resolvedPath}`;
}
return resolvedPath.length > 0 ? resolvedPath : ".";
};
function normalizeString(path, allowAboveRoot) {
let res = "";
let lastSegmentLength = 0;
let lastSlash = -1;
let dots = 0;
let char = null;
for (let index = 0; index <= path.length; ++index) {
if (index < path.length) {
char = path[index];
} else if (char === "/") {
break;
} else {
char = "/";
}
if (char === "/") {
if (lastSlash === index - 1 || dots === 1) ; else if (dots === 2) {
if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
if (res.length > 2) {
const lastSlashIndex = res.lastIndexOf("/");
if (lastSlashIndex === -1) {
res = "";
lastSegmentLength = 0;
} else {
res = res.slice(0, lastSlashIndex);
lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
}
lastSlash = index;
dots = 0;
continue;
} else if (res.length > 0) {
res = "";
lastSegmentLength = 0;
lastSlash = index;
dots = 0;
continue;
}
}
if (allowAboveRoot) {
res += res.length > 0 ? "/.." : "..";
lastSegmentLength = 2;
}
} else {
if (res.length > 0) {
res += `/${path.slice(lastSlash + 1, index)}`;
} else {
res = path.slice(lastSlash + 1, index);
}
lastSegmentLength = index - lastSlash - 1;
}
lastSlash = index;
dots = 0;
} else if (char === "." && dots !== -1) {
++dots;
} else {
dots = -1;
}
}
return res;
}
const isAbsolute = function(p) {
return _IS_ABSOLUTE_RE.test(p);
};
const dirname = function(p) {
const segments = normalizeWindowsPath(p).replace(/\/$/, "").split("/").slice(0, -1);
if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) {
segments[0] += "/";
}
return segments.join("/") || (isAbsolute(p) ? "/" : ".");
};
export { dirname as d, join as j, resolve as r };
+21
View File
@@ -0,0 +1,21 @@
declare const KNOWN_ASSET_TYPES: string[];
declare const KNOWN_ASSET_RE: RegExp;
declare const CSS_LANGS_RE: RegExp;
/**
* Prefix for resolved Ids that are not valid browser import specifiers
*/
declare const VALID_ID_PREFIX = "/@id/";
/**
* Plugins that use 'virtual modules' (e.g. for helper functions), prefix the
* module ID with `\0`, a convention from the rollup ecosystem.
* This prevents other plugins from trying to process the id (like node resolution),
* and core features like sourcemaps can use this info to differentiate between
* virtual modules and regular files.
* `\0` is not a permitted char in import URLs so we have to replace them during
* import analysis. The id will be decoded back before entering the plugins pipeline.
* These encoded virtual ids are also prefixed by the VALID_ID_PREFIX, so virtual
* modules in the browser end up encoded as `/@id/__x00__{id}`
*/
declare const NULL_BYTE_PLACEHOLDER = "__x00__";
export { CSS_LANGS_RE, KNOWN_ASSET_RE, KNOWN_ASSET_TYPES, NULL_BYTE_PLACEHOLDER, VALID_ID_PREFIX };
+49
View File
@@ -0,0 +1,49 @@
// TODO: this is all copy pasted from Vite - can they expose a module that exports only constants?
const KNOWN_ASSET_TYPES = [
"apng",
"bmp",
"png",
"jpe?g",
"jfif",
"pjpeg",
"pjp",
"gif",
"svg",
"ico",
"webp",
"avif",
"mp4",
"webm",
"ogg",
"mp3",
"wav",
"flac",
"aac",
"woff2?",
"eot",
"ttf",
"otf",
"webmanifest",
"pdf",
"txt"
];
const KNOWN_ASSET_RE = new RegExp(`\\.(${KNOWN_ASSET_TYPES.join("|")})$`);
const CSS_LANGS_RE = /\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/;
/**
* Prefix for resolved Ids that are not valid browser import specifiers
*/
const VALID_ID_PREFIX = `/@id/`;
/**
* Plugins that use 'virtual modules' (e.g. for helper functions), prefix the
* module ID with `\0`, a convention from the rollup ecosystem.
* This prevents other plugins from trying to process the id (like node resolution),
* and core features like sourcemaps can use this info to differentiate between
* virtual modules and regular files.
* `\0` is not a permitted char in import URLs so we have to replace them during
* import analysis. The id will be decoded back before entering the plugins pipeline.
* These encoded virtual ids are also prefixed by the VALID_ID_PREFIX, so virtual
* modules in the browser end up encoded as `/@id/__x00__{id}`
*/
const NULL_BYTE_PLACEHOLDER = `__x00__`;
export { CSS_LANGS_RE, KNOWN_ASSET_RE, KNOWN_ASSET_TYPES, NULL_BYTE_PLACEHOLDER, VALID_ID_PREFIX };
+109
View File
@@ -0,0 +1,109 @@
import { PrettyFormatOptions } from '@vitest/pretty-format';
import { D as DiffOptions } from './types.d-BCElaP-c.js';
export { a as DiffOptionsColor, S as SerializedDiffOptions } from './types.d-BCElaP-c.js';
/**
* Diff Match and Patch
* Copyright 2018 The diff-match-patch Authors.
* https://github.com/google/diff-match-patch
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Computes the difference between two texts to create a patch.
* Applies the patch onto another text, allowing for errors.
* @author fraser@google.com (Neil Fraser)
*/
/**
* CHANGES by pedrottimark to diff_match_patch_uncompressed.ts file:
*
* 1. Delete anything not needed to use diff_cleanupSemantic method
* 2. Convert from prototype properties to var declarations
* 3. Convert Diff to class from constructor and prototype
* 4. Add type annotations for arguments and return values
* 5. Add exports
*/
/**
* The data structure representing a diff is an array of tuples:
* [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
* which means: delete 'Hello', add 'Goodbye' and keep ' world.'
*/
declare const DIFF_DELETE = -1;
declare const DIFF_INSERT = 1;
declare const DIFF_EQUAL = 0;
/**
* Class representing one diff tuple.
* Attempts to look like a two-element array (which is what this used to be).
* @param {number} op Operation, one of: DIFF_DELETE, DIFF_INSERT, DIFF_EQUAL.
* @param {string} text Text to be deleted, inserted, or retained.
* @constructor
*/
declare class Diff {
0: number;
1: string;
constructor(op: number, text: string);
}
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
declare function diffLinesUnified(aLines: Array<string>, bLines: Array<string>, options?: DiffOptions): string;
declare function diffLinesUnified2(aLinesDisplay: Array<string>, bLinesDisplay: Array<string>, aLinesCompare: Array<string>, bLinesCompare: Array<string>, options?: DiffOptions): string;
declare function diffLinesRaw(aLines: Array<string>, bLines: Array<string>, options?: DiffOptions): [Array<Diff>, boolean];
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
declare function diffStringsUnified(a: string, b: string, options?: DiffOptions): string;
declare function diffStringsRaw(a: string, b: string, cleanup: boolean, options?: DiffOptions): [Array<Diff>, boolean];
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
interface StringifiedMemory {
expected?: string;
actual?: string;
}
interface Memorize {
(pointer: "expected" | "actual", stringifiedValue: string): string;
}
/**
* @param a Expected value
* @param b Received value
* @param options Diff options
* @returns {string | null} a string diff
*/
declare function diff(a: any, b: any, options?: DiffOptions, memorize?: Memorize): string | undefined;
declare function getDefaultFormatOptions(options?: DiffOptions): PrettyFormatOptions;
declare function printDiffOrStringify(received: unknown, expected: unknown, options?: DiffOptions, memory?: StringifiedMemory): string | undefined;
declare function replaceAsymmetricMatcher(actual: any, expected: any, actualReplaced?: WeakSet<WeakKey>, expectedReplaced?: WeakSet<WeakKey>): {
replacedActual: any;
replacedExpected: any;
};
type PrintLabel = (string: string) => string;
declare function getLabelPrinter(...strings: Array<string>): PrintLabel;
export { DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, Diff, DiffOptions, diff, diffLinesRaw, diffLinesUnified, diffLinesUnified2, diffStringsRaw, diffStringsUnified, getDefaultFormatOptions, getLabelPrinter, printDiffOrStringify, replaceAsymmetricMatcher };
export type { StringifiedMemory };
+2212
View File
File diff suppressed because it is too large Load Diff
+30
View File
@@ -0,0 +1,30 @@
import { PrettyFormatOptions } from '@vitest/pretty-format';
type Inspect = (value: unknown, options: Options) => string;
interface Options {
showHidden: boolean;
depth: number;
colors: boolean;
customInspect: boolean;
showProxy: boolean;
maxArrayLength: number;
breakLength: number;
truncate: number;
seen: unknown[];
inspect: Inspect;
stylize: (value: string, styleType: string) => string;
}
type LoupeOptions = Partial<Options>;
interface StringifyOptions extends PrettyFormatOptions {
maxLength?: number;
filterNode?: string | ((node: any) => boolean);
}
declare function stringify(object: unknown, maxDepth?: number, { maxLength, filterNode, ...options }?: StringifyOptions): string;
declare const formatRegExp: RegExp;
declare function format(...args: unknown[]): string;
declare function browserFormat(...args: unknown[]): string;
declare function inspect(obj: unknown, options?: LoupeOptions): string;
declare function objDisplay(obj: unknown, options?: LoupeOptions): string;
export { browserFormat, format, formatRegExp, inspect, objDisplay, stringify };
export type { LoupeOptions, StringifyOptions };
+775
View File
@@ -0,0 +1,775 @@
import { plugins, createDOMElementFilter, format as format$1 } from '@vitest/pretty-format';
const ansiColors = {
bold: ['1', '22'],
dim: ['2', '22'],
italic: ['3', '23'],
underline: ['4', '24'],
// 5 & 6 are blinking
inverse: ['7', '27'],
hidden: ['8', '28'],
strike: ['9', '29'],
// 10-20 are fonts
// 21-29 are resets for 1-9
black: ['30', '39'],
red: ['31', '39'],
green: ['32', '39'],
yellow: ['33', '39'],
blue: ['34', '39'],
magenta: ['35', '39'],
cyan: ['36', '39'],
white: ['37', '39'],
brightblack: ['30;1', '39'],
brightred: ['31;1', '39'],
brightgreen: ['32;1', '39'],
brightyellow: ['33;1', '39'],
brightblue: ['34;1', '39'],
brightmagenta: ['35;1', '39'],
brightcyan: ['36;1', '39'],
brightwhite: ['37;1', '39'],
grey: ['90', '39'],
};
const styles = {
special: 'cyan',
number: 'yellow',
bigint: 'yellow',
boolean: 'yellow',
undefined: 'grey',
null: 'bold',
string: 'green',
symbol: 'green',
date: 'magenta',
regexp: 'red',
};
const truncator = '…';
function colorise(value, styleType) {
const color = ansiColors[styles[styleType]] || ansiColors[styleType] || '';
if (!color) {
return String(value);
}
return `\u001b[${color[0]}m${String(value)}\u001b[${color[1]}m`;
}
function normaliseOptions({ showHidden = false, depth = 2, colors = false, customInspect = true, showProxy = false, maxArrayLength = Infinity, breakLength = Infinity, seen = [],
// eslint-disable-next-line no-shadow
truncate = Infinity, stylize = String, } = {}, inspect) {
const options = {
showHidden: Boolean(showHidden),
depth: Number(depth),
colors: Boolean(colors),
customInspect: Boolean(customInspect),
showProxy: Boolean(showProxy),
maxArrayLength: Number(maxArrayLength),
breakLength: Number(breakLength),
truncate: Number(truncate),
seen,
inspect,
stylize,
};
if (options.colors) {
options.stylize = colorise;
}
return options;
}
function isHighSurrogate(char) {
return char >= '\ud800' && char <= '\udbff';
}
function truncate(string, length, tail = truncator) {
string = String(string);
const tailLength = tail.length;
const stringLength = string.length;
if (tailLength > length && stringLength > tailLength) {
return tail;
}
if (stringLength > length && stringLength > tailLength) {
let end = length - tailLength;
if (end > 0 && isHighSurrogate(string[end - 1])) {
end = end - 1;
}
return `${string.slice(0, end)}${tail}`;
}
return string;
}
// eslint-disable-next-line complexity
function inspectList(list, options, inspectItem, separator = ', ') {
inspectItem = inspectItem || options.inspect;
const size = list.length;
if (size === 0)
return '';
const originalLength = options.truncate;
let output = '';
let peek = '';
let truncated = '';
for (let i = 0; i < size; i += 1) {
const last = i + 1 === list.length;
const secondToLast = i + 2 === list.length;
truncated = `${truncator}(${list.length - i})`;
const value = list[i];
// If there is more than one remaining we need to account for a separator of `, `
options.truncate = originalLength - output.length - (last ? 0 : separator.length);
const string = peek || inspectItem(value, options) + (last ? '' : separator);
const nextLength = output.length + string.length;
const truncatedLength = nextLength + truncated.length;
// If this is the last element, and adding it would
// take us over length, but adding the truncator wouldn't - then break now
if (last && nextLength > originalLength && output.length + truncated.length <= originalLength) {
break;
}
// If this isn't the last or second to last element to scan,
// but the string is already over length then break here
if (!last && !secondToLast && truncatedLength > originalLength) {
break;
}
// Peek at the next string to determine if we should
// break early before adding this item to the output
peek = last ? '' : inspectItem(list[i + 1], options) + (secondToLast ? '' : separator);
// If we have one element left, but this element and
// the next takes over length, the break early
if (!last && secondToLast && truncatedLength > originalLength && nextLength + peek.length > originalLength) {
break;
}
output += string;
// If the next element takes us to length -
// but there are more after that, then we should truncate now
if (!last && !secondToLast && nextLength + peek.length >= originalLength) {
truncated = `${truncator}(${list.length - i - 1})`;
break;
}
truncated = '';
}
return `${output}${truncated}`;
}
function quoteComplexKey(key) {
if (key.match(/^[a-zA-Z_][a-zA-Z_0-9]*$/)) {
return key;
}
return JSON.stringify(key)
.replace(/'/g, "\\'")
.replace(/\\"/g, '"')
.replace(/(^"|"$)/g, "'");
}
function inspectProperty([key, value], options) {
options.truncate -= 2;
if (typeof key === 'string') {
key = quoteComplexKey(key);
}
else if (typeof key !== 'number') {
key = `[${options.inspect(key, options)}]`;
}
options.truncate -= key.length;
value = options.inspect(value, options);
return `${key}: ${value}`;
}
function inspectArray(array, options) {
// Object.keys will always output the Array indices first, so we can slice by
// `array.length` to get non-index properties
const nonIndexProperties = Object.keys(array).slice(array.length);
if (!array.length && !nonIndexProperties.length)
return '[]';
options.truncate -= 4;
const listContents = inspectList(array, options);
options.truncate -= listContents.length;
let propertyContents = '';
if (nonIndexProperties.length) {
propertyContents = inspectList(nonIndexProperties.map(key => [key, array[key]]), options, inspectProperty);
}
return `[ ${listContents}${propertyContents ? `, ${propertyContents}` : ''} ]`;
}
const getArrayName = (array) => {
// We need to special case Node.js' Buffers, which report to be Uint8Array
// @ts-ignore
if (typeof Buffer === 'function' && array instanceof Buffer) {
return 'Buffer';
}
if (array[Symbol.toStringTag]) {
return array[Symbol.toStringTag];
}
return array.constructor.name;
};
function inspectTypedArray(array, options) {
const name = getArrayName(array);
options.truncate -= name.length + 4;
// Object.keys will always output the Array indices first, so we can slice by
// `array.length` to get non-index properties
const nonIndexProperties = Object.keys(array).slice(array.length);
if (!array.length && !nonIndexProperties.length)
return `${name}[]`;
// As we know TypedArrays only contain Unsigned Integers, we can skip inspecting each one and simply
// stylise the toString() value of them
let output = '';
for (let i = 0; i < array.length; i++) {
const string = `${options.stylize(truncate(array[i], options.truncate), 'number')}${i === array.length - 1 ? '' : ', '}`;
options.truncate -= string.length;
if (array[i] !== array.length && options.truncate <= 3) {
output += `${truncator}(${array.length - array[i] + 1})`;
break;
}
output += string;
}
let propertyContents = '';
if (nonIndexProperties.length) {
propertyContents = inspectList(nonIndexProperties.map(key => [key, array[key]]), options, inspectProperty);
}
return `${name}[ ${output}${propertyContents ? `, ${propertyContents}` : ''} ]`;
}
function inspectDate(dateObject, options) {
const stringRepresentation = dateObject.toJSON();
if (stringRepresentation === null) {
return 'Invalid Date';
}
const split = stringRepresentation.split('T');
const date = split[0];
// If we need to - truncate the time portion, but never the date
return options.stylize(`${date}T${truncate(split[1], options.truncate - date.length - 1)}`, 'date');
}
function inspectFunction(func, options) {
const functionType = func[Symbol.toStringTag] || 'Function';
const name = func.name;
if (!name) {
return options.stylize(`[${functionType}]`, 'special');
}
return options.stylize(`[${functionType} ${truncate(name, options.truncate - 11)}]`, 'special');
}
function inspectMapEntry([key, value], options) {
options.truncate -= 4;
key = options.inspect(key, options);
options.truncate -= key.length;
value = options.inspect(value, options);
return `${key} => ${value}`;
}
// IE11 doesn't support `map.entries()`
function mapToEntries(map) {
const entries = [];
map.forEach((value, key) => {
entries.push([key, value]);
});
return entries;
}
function inspectMap(map, options) {
if (map.size === 0)
return 'Map{}';
options.truncate -= 7;
return `Map{ ${inspectList(mapToEntries(map), options, inspectMapEntry)} }`;
}
const isNaN = Number.isNaN || (i => i !== i); // eslint-disable-line no-self-compare
function inspectNumber(number, options) {
if (isNaN(number)) {
return options.stylize('NaN', 'number');
}
if (number === Infinity) {
return options.stylize('Infinity', 'number');
}
if (number === -Infinity) {
return options.stylize('-Infinity', 'number');
}
if (number === 0) {
return options.stylize(1 / number === Infinity ? '+0' : '-0', 'number');
}
return options.stylize(truncate(String(number), options.truncate), 'number');
}
function inspectBigInt(number, options) {
let nums = truncate(number.toString(), options.truncate - 1);
if (nums !== truncator)
nums += 'n';
return options.stylize(nums, 'bigint');
}
function inspectRegExp(value, options) {
const flags = value.toString().split('/')[2];
const sourceLength = options.truncate - (2 + flags.length);
const source = value.source;
return options.stylize(`/${truncate(source, sourceLength)}/${flags}`, 'regexp');
}
// IE11 doesn't support `Array.from(set)`
function arrayFromSet(set) {
const values = [];
set.forEach(value => {
values.push(value);
});
return values;
}
function inspectSet(set, options) {
if (set.size === 0)
return 'Set{}';
options.truncate -= 7;
return `Set{ ${inspectList(arrayFromSet(set), options)} }`;
}
const stringEscapeChars = new RegExp("['\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5" +
'\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]', 'g');
const escapeCharacters = {
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
"'": "\\'",
'\\': '\\\\',
};
const hex = 16;
function escape(char) {
return (escapeCharacters[char] ||
`\\u${`0000${char.charCodeAt(0).toString(hex)}`.slice(-4)}`);
}
function inspectString(string, options) {
if (stringEscapeChars.test(string)) {
string = string.replace(stringEscapeChars, escape);
}
return options.stylize(`'${truncate(string, options.truncate - 2)}'`, 'string');
}
function inspectSymbol(value) {
if ('description' in Symbol.prototype) {
return value.description ? `Symbol(${value.description})` : 'Symbol()';
}
return value.toString();
}
const getPromiseValue = () => 'Promise{…}';
function inspectObject$1(object, options) {
const properties = Object.getOwnPropertyNames(object);
const symbols = Object.getOwnPropertySymbols ? Object.getOwnPropertySymbols(object) : [];
if (properties.length === 0 && symbols.length === 0) {
return '{}';
}
options.truncate -= 4;
options.seen = options.seen || [];
if (options.seen.includes(object)) {
return '[Circular]';
}
options.seen.push(object);
const propertyContents = inspectList(properties.map(key => [key, object[key]]), options, inspectProperty);
const symbolContents = inspectList(symbols.map(key => [key, object[key]]), options, inspectProperty);
options.seen.pop();
let sep = '';
if (propertyContents && symbolContents) {
sep = ', ';
}
return `{ ${propertyContents}${sep}${symbolContents} }`;
}
const toStringTag = typeof Symbol !== 'undefined' && Symbol.toStringTag ? Symbol.toStringTag : false;
function inspectClass(value, options) {
let name = '';
if (toStringTag && toStringTag in value) {
name = value[toStringTag];
}
name = name || value.constructor.name;
// Babel transforms anonymous classes to the name `_class`
if (!name || name === '_class') {
name = '<Anonymous Class>';
}
options.truncate -= name.length;
return `${name}${inspectObject$1(value, options)}`;
}
function inspectArguments(args, options) {
if (args.length === 0)
return 'Arguments[]';
options.truncate -= 13;
return `Arguments[ ${inspectList(args, options)} ]`;
}
const errorKeys = [
'stack',
'line',
'column',
'name',
'message',
'fileName',
'lineNumber',
'columnNumber',
'number',
'description',
'cause',
];
function inspectObject(error, options) {
const properties = Object.getOwnPropertyNames(error).filter(key => errorKeys.indexOf(key) === -1);
const name = error.name;
options.truncate -= name.length;
let message = '';
if (typeof error.message === 'string') {
message = truncate(error.message, options.truncate);
}
else {
properties.unshift('message');
}
message = message ? `: ${message}` : '';
options.truncate -= message.length + 5;
options.seen = options.seen || [];
if (options.seen.includes(error)) {
return '[Circular]';
}
options.seen.push(error);
const propertyContents = inspectList(properties.map(key => [key, error[key]]), options, inspectProperty);
return `${name}${message}${propertyContents ? ` { ${propertyContents} }` : ''}`;
}
function inspectAttribute([key, value], options) {
options.truncate -= 3;
if (!value) {
return `${options.stylize(String(key), 'yellow')}`;
}
return `${options.stylize(String(key), 'yellow')}=${options.stylize(`"${value}"`, 'string')}`;
}
function inspectNodeCollection(collection, options) {
return inspectList(collection, options, inspectNode, '\n');
}
function inspectNode(node, options) {
switch (node.nodeType) {
case 1:
return inspectHTML(node, options);
case 3:
return options.inspect(node.data, options);
default:
return options.inspect(node, options);
}
}
// @ts-ignore (Deno doesn't have Element)
function inspectHTML(element, options) {
const properties = element.getAttributeNames();
const name = element.tagName.toLowerCase();
const head = options.stylize(`<${name}`, 'special');
const headClose = options.stylize(`>`, 'special');
const tail = options.stylize(`</${name}>`, 'special');
options.truncate -= name.length * 2 + 5;
let propertyContents = '';
if (properties.length > 0) {
propertyContents += ' ';
propertyContents += inspectList(properties.map((key) => [key, element.getAttribute(key)]), options, inspectAttribute, ' ');
}
options.truncate -= propertyContents.length;
const truncate = options.truncate;
let children = inspectNodeCollection(element.children, options);
if (children && children.length > truncate) {
children = `${truncator}(${element.children.length})`;
}
return `${head}${propertyContents}${headClose}${children}${tail}`;
}
/* !
* loupe
* Copyright(c) 2013 Jake Luer <jake@alogicalparadox.com>
* MIT Licensed
*/
const symbolsSupported = typeof Symbol === 'function' && typeof Symbol.for === 'function';
const chaiInspect = symbolsSupported ? Symbol.for('chai/inspect') : '@@chai/inspect';
const nodeInspect = Symbol.for('nodejs.util.inspect.custom');
const constructorMap = new WeakMap();
const stringTagMap = {};
const baseTypesMap = {
undefined: (value, options) => options.stylize('undefined', 'undefined'),
null: (value, options) => options.stylize('null', 'null'),
boolean: (value, options) => options.stylize(String(value), 'boolean'),
Boolean: (value, options) => options.stylize(String(value), 'boolean'),
number: inspectNumber,
Number: inspectNumber,
bigint: inspectBigInt,
BigInt: inspectBigInt,
string: inspectString,
String: inspectString,
function: inspectFunction,
Function: inspectFunction,
symbol: inspectSymbol,
// A Symbol polyfill will return `Symbol` not `symbol` from typedetect
Symbol: inspectSymbol,
Array: inspectArray,
Date: inspectDate,
Map: inspectMap,
Set: inspectSet,
RegExp: inspectRegExp,
Promise: getPromiseValue,
// WeakSet, WeakMap are totally opaque to us
WeakSet: (value, options) => options.stylize('WeakSet{…}', 'special'),
WeakMap: (value, options) => options.stylize('WeakMap{…}', 'special'),
Arguments: inspectArguments,
Int8Array: inspectTypedArray,
Uint8Array: inspectTypedArray,
Uint8ClampedArray: inspectTypedArray,
Int16Array: inspectTypedArray,
Uint16Array: inspectTypedArray,
Int32Array: inspectTypedArray,
Uint32Array: inspectTypedArray,
Float32Array: inspectTypedArray,
Float64Array: inspectTypedArray,
Generator: () => '',
DataView: () => '',
ArrayBuffer: () => '',
Error: inspectObject,
HTMLCollection: inspectNodeCollection,
NodeList: inspectNodeCollection,
};
// eslint-disable-next-line complexity
const inspectCustom = (value, options, type, inspectFn) => {
if (chaiInspect in value && typeof value[chaiInspect] === 'function') {
return value[chaiInspect](options);
}
if (nodeInspect in value && typeof value[nodeInspect] === 'function') {
return value[nodeInspect](options.depth, options, inspectFn);
}
if ('inspect' in value && typeof value.inspect === 'function') {
return value.inspect(options.depth, options);
}
if ('constructor' in value && constructorMap.has(value.constructor)) {
return constructorMap.get(value.constructor)(value, options);
}
if (stringTagMap[type]) {
return stringTagMap[type](value, options);
}
return '';
};
const toString = Object.prototype.toString;
// eslint-disable-next-line complexity
function inspect$1(value, opts = {}) {
const options = normaliseOptions(opts, inspect$1);
const { customInspect } = options;
let type = value === null ? 'null' : typeof value;
if (type === 'object') {
type = toString.call(value).slice(8, -1);
}
// If it is a base value that we already support, then use Loupe's inspector
if (type in baseTypesMap) {
return baseTypesMap[type](value, options);
}
// If `options.customInspect` is set to true then try to use the custom inspector
if (customInspect && value) {
const output = inspectCustom(value, options, type, inspect$1);
if (output) {
if (typeof output === 'string')
return output;
return inspect$1(output, options);
}
}
const proto = value ? Object.getPrototypeOf(value) : false;
// If it's a plain Object then use Loupe's inspector
if (proto === Object.prototype || proto === null) {
return inspectObject$1(value, options);
}
// Specifically account for HTMLElements
// @ts-ignore
if (value && typeof HTMLElement === 'function' && value instanceof HTMLElement) {
return inspectHTML(value, options);
}
if ('constructor' in value) {
// If it is a class, inspect it like an object but add the constructor name
if (value.constructor !== Object) {
return inspectClass(value, options);
}
// If it is an object with an anonymous prototype, display it as an object.
return inspectObject$1(value, options);
}
// last chance to check if it's an object
if (value === Object(value)) {
return inspectObject$1(value, options);
}
// We have run out of options! Just stringify the value
return options.stylize(String(value), type);
}
const { AsymmetricMatcher, DOMCollection, DOMElement, Immutable, ReactElement, ReactTestComponent } = plugins;
const PLUGINS = [
ReactTestComponent,
ReactElement,
DOMElement,
DOMCollection,
Immutable,
AsymmetricMatcher
];
function stringify(object, maxDepth = 10, { maxLength, filterNode, ...options } = {}) {
const MAX_LENGTH = maxLength ?? 1e4;
let result;
// Convert string selector to filter function
const filterFn = typeof filterNode === "string" ? createNodeFilterFromSelector(filterNode) : filterNode;
const plugins = filterFn ? [
ReactTestComponent,
ReactElement,
createDOMElementFilter(filterFn),
DOMCollection,
Immutable,
AsymmetricMatcher
] : PLUGINS;
try {
result = format$1(object, {
maxDepth,
escapeString: false,
plugins,
...options
});
} catch {
result = format$1(object, {
callToJSON: false,
maxDepth,
escapeString: false,
plugins,
...options
});
}
// Prevents infinite loop https://github.com/vitest-dev/vitest/issues/7249
return result.length >= MAX_LENGTH && maxDepth > 1 ? stringify(object, Math.floor(Math.min(maxDepth, Number.MAX_SAFE_INTEGER) / 2), {
maxLength,
filterNode,
...options
}) : result;
}
function createNodeFilterFromSelector(selector) {
const ELEMENT_NODE = 1;
const COMMENT_NODE = 8;
return (node) => {
// Filter out comments
if (node.nodeType === COMMENT_NODE) {
return false;
}
// Filter out elements matching the selector
if (node.nodeType === ELEMENT_NODE && node.matches) {
try {
return !node.matches(selector);
} catch {
return true;
}
}
return true;
};
}
const formatRegExp = /%[sdjifoOc%]/g;
function baseFormat(args, options = {}) {
const formatArg = (item, inspecOptions) => {
if (options.prettifyObject) {
return stringify(item, undefined, {
printBasicPrototype: false,
escapeString: false
});
}
return inspect(item, inspecOptions);
};
if (typeof args[0] !== "string") {
const objects = [];
for (let i = 0; i < args.length; i++) {
objects.push(formatArg(args[i], {
depth: 0,
colors: false
}));
}
return objects.join(" ");
}
const len = args.length;
let i = 1;
const template = args[0];
let str = String(template).replace(formatRegExp, (x) => {
if (x === "%%") {
return "%";
}
if (i >= len) {
return x;
}
switch (x) {
case "%s": {
const value = args[i++];
if (typeof value === "bigint") {
return `${value.toString()}n`;
}
if (typeof value === "number" && value === 0 && 1 / value < 0) {
return "-0";
}
if (typeof value === "object" && value !== null) {
if (typeof value.toString === "function" && value.toString !== Object.prototype.toString) {
return value.toString();
}
return formatArg(value, {
depth: 0,
colors: false
});
}
return String(value);
}
case "%d": {
const value = args[i++];
if (typeof value === "bigint") {
return `${value.toString()}n`;
}
if (typeof value === "symbol") {
return "NaN";
}
return Number(value).toString();
}
case "%i": {
const value = args[i++];
if (typeof value === "bigint") {
return `${value.toString()}n`;
}
return Number.parseInt(String(value)).toString();
}
case "%f": return Number.parseFloat(String(args[i++])).toString();
case "%o": return formatArg(args[i++], {
showHidden: true,
showProxy: true
});
case "%O": return formatArg(args[i++]);
case "%c": {
i++;
return "";
}
case "%j": try {
return JSON.stringify(args[i++]);
} catch (err) {
const m = err.message;
if (m.includes("circular structure") || m.includes("cyclic structures") || m.includes("cyclic object")) {
return "[Circular]";
}
throw err;
}
default: return x;
}
});
for (let x = args[i]; i < len; x = args[++i]) {
if (x === null || typeof x !== "object") {
str += ` ${typeof x === "symbol" ? x.toString() : x}`;
} else {
str += ` ${formatArg(x)}`;
}
}
return str;
}
function format(...args) {
return baseFormat(args);
}
function browserFormat(...args) {
return baseFormat(args, { prettifyObject: true });
}
function inspect(obj, options = {}) {
if (options.truncate === 0) {
options.truncate = Number.POSITIVE_INFINITY;
}
return inspect$1(obj, options);
}
function objDisplay(obj, options = {}) {
if (typeof options.truncate === "undefined") {
options.truncate = 40;
}
const str = inspect(obj, options);
const type = Object.prototype.toString.call(obj);
if (options.truncate && str.length >= options.truncate) {
if (type === "[object Function]") {
const fn = obj;
return !fn.name ? "[Function]" : `[Function: ${fn.name}]`;
} else if (type === "[object Array]") {
return `[ Array(${obj.length}) ]`;
} else if (type === "[object Object]") {
const keys = Object.keys(obj);
const kstr = keys.length > 2 ? `${keys.splice(0, 2).join(", ")}, ...` : keys.join(", ");
return `{ Object (${kstr}) }`;
} else {
return str;
}
}
return str;
}
export { browserFormat, format, formatRegExp, inspect, objDisplay, stringify };
+8
View File
@@ -0,0 +1,8 @@
import { D as DiffOptions } from './types.d-BCElaP-c.js';
import { TestError } from './types.js';
export { serializeValue as serializeError } from './serialize.js';
import '@vitest/pretty-format';
declare function processError(_err: any, diffOptions?: DiffOptions, seen?: WeakSet<WeakKey>): TestError;
export { processError };
+44
View File
@@ -0,0 +1,44 @@
import { format } from '@vitest/pretty-format';
import { printDiffOrStringify, getDefaultFormatOptions } from './diff.js';
import { serializeValue } from './serialize.js';
import 'tinyrainbow';
import './display.js';
import './helpers.js';
import './constants.js';
function processError(_err, diffOptions, seen = new WeakSet()) {
if (!_err || typeof _err !== "object") {
return { message: String(_err) };
}
const err = _err;
if (err.showDiff || err.showDiff === undefined && err.expected !== undefined && err.actual !== undefined) {
const options = {
...diffOptions,
...err.diffOptions
};
err.diff = printDiffOrStringify(err.actual, err.expected, options, err);
err.expected = prettifyValue(err.expected, options);
err.actual = prettifyValue(err.actual, options);
}
// some Error implementations may not allow rewriting cause
// in most cases, the assignment will lead to "err.cause = err.cause"
try {
if (!seen.has(err) && typeof err.cause === "object") {
seen.add(err);
err.cause = processError(err.cause, diffOptions, seen);
}
} catch {}
try {
return serializeValue(err);
} catch (e) {
return serializeValue(new Error(`Failed to fully serialize error: ${e?.message}\nInner error message: ${err?.message}`));
}
}
function prettifyValue(value, options) {
if (typeof value !== "string") {
return format(value, getDefaultFormatOptions(options));
}
return value;
}
export { processError, serializeValue as serializeError };
+80
View File
@@ -0,0 +1,80 @@
import { Nullable, Arrayable } from './types.js';
declare function nanoid(size?: number): string;
declare function shuffle<T>(array: T[], seed?: number): T[];
interface CloneOptions {
forceWritable?: boolean;
}
interface ErrorOptions {
message?: string;
stackTraceLimit?: number;
}
/**
* Get original stacktrace without source map support the most performant way.
* - Create only 1 stack frame.
* - Rewrite prepareStackTrace to bypass "support-stack-trace" (usually takes ~250ms).
*/
declare function createSimpleStackTrace(options?: ErrorOptions): string;
declare function notNullish<T>(v: T | null | undefined): v is NonNullable<T>;
declare function assertTypes(value: unknown, name: string, types: string[]): void;
declare function isPrimitive(value: unknown): boolean;
declare function slash(path: string): string;
declare function cleanUrl(url: string): string;
declare function splitFileAndPostfix(path: string): {
file: string;
postfix: string;
};
declare const isExternalUrl: (url: string) => boolean;
/**
* Prepend `/@id/` and replace null byte so the id is URL-safe.
* This is prepended to resolved ids that are not valid browser
* import specifiers by the importAnalysis plugin.
*/
declare function wrapId(id: string): string;
/**
* Undo {@link wrapId}'s `/@id/` and null byte replacements.
*/
declare function unwrapId(id: string): string;
declare function withTrailingSlash(path: string): string;
declare function filterOutComments(s: string): string;
declare function isBareImport(id: string): boolean;
declare function toArray<T>(array?: Nullable<Arrayable<T>>): Array<T>;
declare function isObject(item: unknown): boolean;
declare function getType(value: unknown): string;
declare function getOwnProperties(obj: any): (string | symbol)[];
declare function deepClone<T>(val: T, options?: CloneOptions): T;
declare function clone<T>(val: T, seen: WeakMap<any, any>, options?: CloneOptions): T;
declare function noop(): void;
declare function objectAttr(source: any, path: string, defaultValue?: undefined): any;
type DeferPromise<T> = Promise<T> & {
resolve: (value: T | PromiseLike<T>) => void;
reject: (reason?: any) => void;
};
declare function createDefer<T>(): DeferPromise<T>;
/**
* If code starts with a function call, will return its last index, respecting arguments.
* This will return 25 - last ending character of toMatch ")"
* Also works with callbacks
* ```
* toMatch({ test: '123' });
* toBeAliased('123')
* ```
*/
declare function getCallLastIndex(code: string): number | null;
declare function isNegativeNaN(val: number): boolean;
declare function ordinal(i: number): string;
/**
* Deep merge :P
*
* Will merge objects only if they are plain
*
* Do not merge types - it is very expensive and usually it's better to case a type here
*/
declare function deepMerge<T extends object = object>(target: T, ...sources: any[]): T;
declare function unique<T>(array: T[]): T[];
export { assertTypes, cleanUrl, clone, createDefer, createSimpleStackTrace, deepClone, deepMerge, filterOutComments, getCallLastIndex, getOwnProperties, getType, isBareImport, isExternalUrl, isNegativeNaN, isObject, isPrimitive, nanoid, noop, notNullish, objectAttr, ordinal, shuffle, slash, splitFileAndPostfix, toArray, unique, unwrapId, withTrailingSlash, wrapId };
export type { DeferPromise };
+344
View File
@@ -0,0 +1,344 @@
import { VALID_ID_PREFIX, NULL_BYTE_PLACEHOLDER } from './constants.js';
// port from nanoid
// https://github.com/ai/nanoid
const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
function nanoid(size = 21) {
let id = "";
let i = size;
while (i--) {
id += urlAlphabet[Math.random() * 64 | 0];
}
return id;
}
const RealDate = Date;
function random(seed) {
const x = Math.sin(seed++) * 1e4;
return x - Math.floor(x);
}
function shuffle(array, seed = RealDate.now()) {
let length = array.length;
while (length) {
const index = Math.floor(random(seed) * length--);
const previous = array[length];
array[length] = array[index];
array[index] = previous;
++seed;
}
return array;
}
/**
* Get original stacktrace without source map support the most performant way.
* - Create only 1 stack frame.
* - Rewrite prepareStackTrace to bypass "support-stack-trace" (usually takes ~250ms).
*/
function createSimpleStackTrace(options) {
const { message = "$$stack trace error", stackTraceLimit = 1 } = options || {};
const limit = Error.stackTraceLimit;
const prepareStackTrace = Error.prepareStackTrace;
Error.stackTraceLimit = stackTraceLimit;
Error.prepareStackTrace = (e) => e.stack;
const err = new Error(message);
const stackTrace = err.stack || "";
Error.prepareStackTrace = prepareStackTrace;
Error.stackTraceLimit = limit;
return stackTrace;
}
function notNullish(v) {
return v != null;
}
function assertTypes(value, name, types) {
const receivedType = typeof value;
const pass = types.includes(receivedType);
if (!pass) {
throw new TypeError(`${name} value must be ${types.join(" or ")}, received "${receivedType}"`);
}
}
function isPrimitive(value) {
return value === null || typeof value !== "function" && typeof value !== "object";
}
function slash(path) {
return path.replace(/\\/g, "/");
}
const postfixRE = /[?#].*$/;
function cleanUrl(url) {
return url.replace(postfixRE, "");
}
function splitFileAndPostfix(path) {
const file = cleanUrl(path);
return {
file,
postfix: path.slice(file.length)
};
}
const externalRE = /^(?:[a-z]+:)?\/\//;
const isExternalUrl = (url) => externalRE.test(url);
/**
* Prepend `/@id/` and replace null byte so the id is URL-safe.
* This is prepended to resolved ids that are not valid browser
* import specifiers by the importAnalysis plugin.
*/
function wrapId(id) {
return id.startsWith(VALID_ID_PREFIX) ? id : VALID_ID_PREFIX + id.replace("\0", NULL_BYTE_PLACEHOLDER);
}
/**
* Undo {@link wrapId}'s `/@id/` and null byte replacements.
*/
function unwrapId(id) {
return id.startsWith(VALID_ID_PREFIX) ? id.slice(VALID_ID_PREFIX.length).replace(NULL_BYTE_PLACEHOLDER, "\0") : id;
}
function withTrailingSlash(path) {
if (path.at(-1) !== "/") {
return `${path}/`;
}
return path;
}
function filterOutComments(s) {
const result = [];
let commentState = "none";
for (let i = 0; i < s.length; ++i) {
if (commentState === "singleline") {
if (s[i] === "\n") {
commentState = "none";
}
} else if (commentState === "multiline") {
if (s[i - 1] === "*" && s[i] === "/") {
commentState = "none";
}
} else if (commentState === "none") {
if (s[i] === "/" && s[i + 1] === "/") {
commentState = "singleline";
} else if (s[i] === "/" && s[i + 1] === "*") {
commentState = "multiline";
i += 2;
} else {
result.push(s[i]);
}
}
}
return result.join("");
}
const bareImportRE = /^(?![a-z]:)[\w@](?!.*:\/\/)/i;
function isBareImport(id) {
return bareImportRE.test(id);
}
function toArray(array) {
if (array === null || array === undefined) {
array = [];
}
if (Array.isArray(array)) {
return array;
}
return [array];
}
function isObject(item) {
return item != null && typeof item === "object" && !Array.isArray(item);
}
function isFinalObj(obj) {
return obj === Object.prototype || obj === Function.prototype || obj === RegExp.prototype;
}
function getType(value) {
return Object.prototype.toString.apply(value).slice(8, -1);
}
function collectOwnProperties(obj, collector) {
const collect = typeof collector === "function" ? collector : (key) => collector.add(key);
Object.getOwnPropertyNames(obj).forEach(collect);
Object.getOwnPropertySymbols(obj).forEach(collect);
}
function getOwnProperties(obj) {
const ownProps = new Set();
if (isFinalObj(obj)) {
return [];
}
collectOwnProperties(obj, ownProps);
return Array.from(ownProps);
}
const defaultCloneOptions = { forceWritable: false };
function deepClone(val, options = defaultCloneOptions) {
const seen = new WeakMap();
return clone(val, seen, options);
}
function clone(val, seen, options = defaultCloneOptions) {
let k, out;
if (seen.has(val)) {
return seen.get(val);
}
if (Array.isArray(val)) {
out = Array.from({ length: k = val.length });
seen.set(val, out);
while (k--) {
out[k] = clone(val[k], seen, options);
}
return out;
}
if (Object.prototype.toString.call(val) === "[object Object]") {
out = Object.create(Object.getPrototypeOf(val));
seen.set(val, out);
// we don't need properties from prototype
const props = getOwnProperties(val);
for (const k of props) {
const descriptor = Object.getOwnPropertyDescriptor(val, k);
if (!descriptor) {
continue;
}
const cloned = clone(val[k], seen, options);
if (options.forceWritable) {
Object.defineProperty(out, k, {
enumerable: descriptor.enumerable,
configurable: true,
writable: true,
value: cloned
});
} else if ("get" in descriptor) {
Object.defineProperty(out, k, {
...descriptor,
get() {
return cloned;
}
});
} else {
Object.defineProperty(out, k, {
...descriptor,
value: cloned
});
}
}
return out;
}
return val;
}
function noop() {}
function objectAttr(source, path, defaultValue = undefined) {
// a[3].b -> a.3.b
const paths = path.replace(/\[(\d+)\]/g, ".$1").split(".");
let result = source;
for (const p of paths) {
result = new Object(result)[p];
if (result === undefined) {
return defaultValue;
}
}
return result;
}
function createDefer() {
let resolve = null;
let reject = null;
const p = new Promise((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
p.resolve = resolve;
p.reject = reject;
return p;
}
/**
* If code starts with a function call, will return its last index, respecting arguments.
* This will return 25 - last ending character of toMatch ")"
* Also works with callbacks
* ```
* toMatch({ test: '123' });
* toBeAliased('123')
* ```
*/
function getCallLastIndex(code) {
let charIndex = -1;
let inString = null;
let startedBracers = 0;
let endedBracers = 0;
let beforeChar = null;
while (charIndex <= code.length) {
beforeChar = code[charIndex];
charIndex++;
const char = code[charIndex];
const isCharString = char === "\"" || char === "'" || char === "`";
if (isCharString && beforeChar !== "\\") {
if (inString === char) {
inString = null;
} else if (!inString) {
inString = char;
}
}
if (!inString) {
if (char === "(") {
startedBracers++;
}
if (char === ")") {
endedBracers++;
}
}
if (startedBracers && endedBracers && startedBracers === endedBracers) {
return charIndex;
}
}
return null;
}
function isNegativeNaN(val) {
if (!Number.isNaN(val)) {
return false;
}
const f64 = new Float64Array(1);
f64[0] = val;
const u32 = new Uint32Array(f64.buffer);
const isNegative = u32[1] >>> 31 === 1;
return isNegative;
}
function toString(v) {
return Object.prototype.toString.call(v);
}
function isPlainObject(val) {
return toString(val) === "[object Object]" && (!val.constructor || val.constructor.name === "Object");
}
function isMergeableObject(item) {
return isPlainObject(item) && !Array.isArray(item);
}
function ordinal(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`;
}
/**
* Deep merge :P
*
* Will merge objects only if they are plain
*
* Do not merge types - it is very expensive and usually it's better to case a type here
*/
function deepMerge(target, ...sources) {
if (!sources.length) {
return target;
}
const source = sources.shift();
if (source === undefined) {
return target;
}
if (isMergeableObject(target) && isMergeableObject(source)) {
Object.keys(source).forEach((key) => {
const _source = source;
if (isMergeableObject(_source[key])) {
if (!target[key]) {
target[key] = {};
}
deepMerge(target[key], _source[key]);
} else {
target[key] = _source[key];
}
});
}
return deepMerge(target, ...sources);
}
function unique(array) {
return Array.from(new Set(array));
}
export { assertTypes, cleanUrl, clone, createDefer, createSimpleStackTrace, deepClone, deepMerge, filterOutComments, getCallLastIndex, getOwnProperties, getType, isBareImport, isExternalUrl, isNegativeNaN, isObject, isPrimitive, nanoid, noop, notNullish, objectAttr, ordinal, shuffle, slash, splitFileAndPostfix, toArray, unique, unwrapId, withTrailingSlash, wrapId };
+5
View File
@@ -0,0 +1,5 @@
export { LoupeOptions, StringifyOptions } from './display.js';
export { DeferPromise } from './helpers.js';
export { SafeTimers } from './timers.js';
export { ArgumentsType, Arrayable, Awaitable, Constructable, DeepMerge, MergeInsertions, Nullable, ParsedStack, SerializedError, TestError } from './types.js';
import '@vitest/pretty-format';
+1
View File
@@ -0,0 +1 @@
+5
View File
@@ -0,0 +1,5 @@
declare const lineSplitRE: RegExp;
declare function positionToOffset(source: string, lineNumber: number, columnNumber: number): number;
declare function offsetToLineNumber(source: string, offset: number): number;
export { lineSplitRE, offsetToLineNumber, positionToOffset };
+32
View File
@@ -0,0 +1,32 @@
const lineSplitRE = /\r?\n/;
function positionToOffset(source, lineNumber, columnNumber) {
const lines = source.split(lineSplitRE);
const nl = /\r\n/.test(source) ? 2 : 1;
let start = 0;
if (lineNumber > lines.length) {
return source.length;
}
for (let i = 0; i < lineNumber - 1; i++) {
start += lines[i].length + nl;
}
return start + columnNumber;
}
function offsetToLineNumber(source, offset) {
if (offset > source.length) {
throw new Error(`offset is longer than source length! offset ${offset} > length ${source.length}`);
}
const lines = source.split(lineSplitRE);
const nl = /\r\n/.test(source) ? 2 : 1;
let counted = 0;
let line = 0;
for (; line < lines.length; line++) {
const lineLength = lines[line].length + nl;
if (counted + lineLength >= offset) {
break;
}
counted += lineLength;
}
return line + 1;
}
export { lineSplitRE, offsetToLineNumber, positionToOffset };
+7
View File
@@ -0,0 +1,7 @@
declare function findNearestPackageData(basedir: string): {
type?: "module" | "commonjs";
};
declare function getCachedData<T>(cache: Map<string, T>, basedir: string, originalBasedir: string): NonNullable<T> | undefined;
declare function setCacheData<T>(cache: Map<string, T>, data: T, basedir: string, originalBasedir: string): void;
export { findNearestPackageData, getCachedData, setCacheData };
+70
View File
@@ -0,0 +1,70 @@
import fs from 'node:fs';
import { j as join, d as dirname } from './chunk-pathe.M-eThtNZ.js';
const packageCache = new Map();
function findNearestPackageData(basedir) {
const originalBasedir = basedir;
while (basedir) {
const cached = getCachedData(packageCache, basedir, originalBasedir);
if (cached) {
return cached;
}
const pkgPath = join(basedir, "package.json");
if (tryStatSync(pkgPath)?.isFile()) {
const pkgData = JSON.parse(stripBomTag(fs.readFileSync(pkgPath, "utf8")));
if (packageCache) {
setCacheData(packageCache, pkgData, basedir, originalBasedir);
}
return pkgData;
}
const nextBasedir = dirname(basedir);
if (nextBasedir === basedir) {
break;
}
basedir = nextBasedir;
}
return {};
}
function stripBomTag(content) {
if (content.charCodeAt(0) === 65279) {
return content.slice(1);
}
return content;
}
function tryStatSync(file) {
try {
// The "throwIfNoEntry" is a performance optimization for cases where the file does not exist
return fs.statSync(file, { throwIfNoEntry: false });
} catch {}
}
function getCachedData(cache, basedir, originalBasedir) {
const pkgData = cache.get(getFnpdCacheKey(basedir));
if (pkgData) {
traverseBetweenDirs(originalBasedir, basedir, (dir) => {
cache.set(getFnpdCacheKey(dir), pkgData);
});
return pkgData;
}
}
function setCacheData(cache, data, basedir, originalBasedir) {
cache.set(getFnpdCacheKey(basedir), data);
traverseBetweenDirs(originalBasedir, basedir, (dir) => {
cache.set(getFnpdCacheKey(dir), data);
});
}
function getFnpdCacheKey(basedir) {
return `fnpd_${basedir}`;
}
/**
* Traverse between `longerDir` (inclusive) and `shorterDir` (exclusive) and call `cb` for each dir.
* @param longerDir Longer dir path, e.g. `/User/foo/bar/baz`
* @param shorterDir Shorter dir path, e.g. `/User/foo`
*/
function traverseBetweenDirs(longerDir, shorterDir, cb) {
while (longerDir !== shorterDir) {
cb(longerDir);
longerDir = dirname(longerDir);
}
}
export { findNearestPackageData, getCachedData, setCacheData };
+3
View File
@@ -0,0 +1,3 @@
declare function serializeValue(val: any, seen?: WeakMap<WeakKey, any>): any;
export { serializeValue };
+118
View File
@@ -0,0 +1,118 @@
const IS_RECORD_SYMBOL = "@@__IMMUTABLE_RECORD__@@";
const IS_COLLECTION_SYMBOL = "@@__IMMUTABLE_ITERABLE__@@";
function isImmutable(v) {
return v && (v[IS_COLLECTION_SYMBOL] || v[IS_RECORD_SYMBOL]);
}
const OBJECT_PROTO = Object.getPrototypeOf({});
function getUnserializableMessage(err) {
if (err instanceof Error) {
return `<unserializable>: ${err.message}`;
}
if (typeof err === "string") {
return `<unserializable>: ${err}`;
}
return "<unserializable>";
}
// https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
function serializeValue(val, seen = new WeakMap()) {
if (!val || typeof val === "string") {
return val;
}
if (val instanceof Error && "toJSON" in val && typeof val.toJSON === "function") {
const jsonValue = val.toJSON();
if (jsonValue && jsonValue !== val && typeof jsonValue === "object") {
if (typeof val.message === "string") {
safe(() => jsonValue.message ??= normalizeErrorMessage(val.message));
}
if (typeof val.stack === "string") {
safe(() => jsonValue.stack ??= val.stack);
}
if (typeof val.name === "string") {
safe(() => jsonValue.name ??= val.name);
}
if (val.cause != null) {
safe(() => jsonValue.cause ??= serializeValue(val.cause, seen));
}
}
return serializeValue(jsonValue, seen);
}
if (typeof val === "function") {
return `Function<${val.name || "anonymous"}>`;
}
if (typeof val === "symbol") {
return val.toString();
}
if (typeof val !== "object") {
return val;
}
if (typeof Buffer !== "undefined" && val instanceof Buffer) {
return `<Buffer(${val.length}) ...>`;
}
if (typeof Uint8Array !== "undefined" && val instanceof Uint8Array) {
return `<Uint8Array(${val.length}) ...>`;
}
// cannot serialize immutables as immutables
if (isImmutable(val)) {
return serializeValue(val.toJSON(), seen);
}
if (val instanceof Promise || val.constructor && val.constructor.prototype === "AsyncFunction") {
return "Promise";
}
if (typeof Element !== "undefined" && val instanceof Element) {
return val.tagName;
}
if (typeof val.toJSON === "function") {
return serializeValue(val.toJSON(), seen);
}
if (seen.has(val)) {
return seen.get(val);
}
if (Array.isArray(val)) {
// eslint-disable-next-line unicorn/no-new-array -- we need to keep sparse arrays ([1,,3])
const clone = new Array(val.length);
seen.set(val, clone);
val.forEach((e, i) => {
try {
clone[i] = serializeValue(e, seen);
} catch (err) {
clone[i] = getUnserializableMessage(err);
}
});
return clone;
} else {
// Objects with `Error` constructors appear to cause problems during worker communication
// using `MessagePort`, so the serialized error object is being recreated as plain object.
const clone = Object.create(null);
seen.set(val, clone);
let obj = val;
while (obj && obj !== OBJECT_PROTO) {
Object.getOwnPropertyNames(obj).forEach((key) => {
if (key in clone) {
return;
}
try {
clone[key] = serializeValue(val[key], seen);
} catch (err) {
// delete in case it has a setter from prototype that might throw
delete clone[key];
clone[key] = getUnserializableMessage(err);
}
});
obj = Object.getPrototypeOf(obj);
}
if (val instanceof Error) {
safe(() => clone.message = normalizeErrorMessage(val.message));
}
return clone;
}
}
function safe(fn) {
try {
return fn();
} catch {}
}
function normalizeErrorMessage(message) {
return message.replace(/\(0\s?,\s?__vite_ssr_import_\d+__.(\w+)\)/g, "$1").replace(/__(vite_ssr_import|vi_import)_\d+__\./g, "").replace(/getByTestId('__vitest_\d+__')/g, "page");
}
export { serializeValue };
+55
View File
@@ -0,0 +1,55 @@
import { TestError, ParsedStack } from './types.js';
type OriginalMapping = {
source: string | null;
line: number;
column: number;
name: string | null;
};
interface StackTraceParserOptions {
ignoreStackEntries?: (RegExp | string)[];
getSourceMap?: (file: string) => unknown;
getUrlId?: (id: string) => string;
frameFilter?: (error: TestError, frame: ParsedStack) => boolean | void;
}
declare const stackIgnorePatterns: (string | RegExp)[];
declare function parseSingleFFOrSafariStack(raw: string): ParsedStack | null;
declare function parseSingleStack(raw: string): ParsedStack | null;
declare function parseSingleV8Stack(raw: string): ParsedStack | null;
declare function createStackString(stacks: ParsedStack[]): string;
declare function parseStacktrace(stack: string, options?: StackTraceParserOptions): ParsedStack[];
declare function parseErrorStacktrace(e: TestError | Error, options?: StackTraceParserOptions): ParsedStack[];
interface SourceMapLike {
version: number;
mappings?: string;
names?: string[];
sources?: string[];
sourcesContent?: string[];
sourceRoot?: string;
}
interface Needle {
line: number;
column: number;
}
declare class DecodedMap {
map: SourceMapLike;
_encoded: string;
_decoded: undefined | number[][][];
_decodedMemo: Stats;
url: string;
version: number;
names: string[];
resolvedSources: string[];
constructor(map: SourceMapLike, from: string);
}
interface Stats {
lastKey: number;
lastNeedle: number;
lastIndex: number;
}
declare function getOriginalPosition(map: DecodedMap, needle: Needle): OriginalMapping | null;
export { DecodedMap, createStackString, stackIgnorePatterns as defaultStackIgnorePatterns, getOriginalPosition, parseErrorStacktrace, parseSingleFFOrSafariStack, parseSingleStack, parseSingleV8Stack, parseStacktrace };
export type { StackTraceParserOptions };
+488
View File
@@ -0,0 +1,488 @@
import { isPrimitive, notNullish } from './helpers.js';
import { r as resolve } from './chunk-pathe.M-eThtNZ.js';
import './constants.js';
// src/vlq.ts
var comma = ",".charCodeAt(0);
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var intToChar = new Uint8Array(64);
var charToInt = new Uint8Array(128);
for (let i = 0; i < chars.length; i++) {
const c = chars.charCodeAt(i);
intToChar[i] = c;
charToInt[c] = i;
}
function decodeInteger(reader, relative) {
let value = 0;
let shift = 0;
let integer = 0;
do {
const c = reader.next();
integer = charToInt[c];
value |= (integer & 31) << shift;
shift += 5;
} while (integer & 32);
const shouldNegate = value & 1;
value >>>= 1;
if (shouldNegate) {
value = -2147483648 | -value;
}
return relative + value;
}
function hasMoreVlq(reader, max) {
if (reader.pos >= max) return false;
return reader.peek() !== comma;
}
var StringReader = class {
constructor(buffer) {
this.pos = 0;
this.buffer = buffer;
}
next() {
return this.buffer.charCodeAt(this.pos++);
}
peek() {
return this.buffer.charCodeAt(this.pos);
}
indexOf(char) {
const { buffer, pos } = this;
const idx = buffer.indexOf(char, pos);
return idx === -1 ? buffer.length : idx;
}
};
// src/sourcemap-codec.ts
function decode(mappings) {
const { length } = mappings;
const reader = new StringReader(mappings);
const decoded = [];
let genColumn = 0;
let sourcesIndex = 0;
let sourceLine = 0;
let sourceColumn = 0;
let namesIndex = 0;
do {
const semi = reader.indexOf(";");
const line = [];
let sorted = true;
let lastCol = 0;
genColumn = 0;
while (reader.pos < semi) {
let seg;
genColumn = decodeInteger(reader, genColumn);
if (genColumn < lastCol) sorted = false;
lastCol = genColumn;
if (hasMoreVlq(reader, semi)) {
sourcesIndex = decodeInteger(reader, sourcesIndex);
sourceLine = decodeInteger(reader, sourceLine);
sourceColumn = decodeInteger(reader, sourceColumn);
if (hasMoreVlq(reader, semi)) {
namesIndex = decodeInteger(reader, namesIndex);
seg = [genColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex];
} else {
seg = [genColumn, sourcesIndex, sourceLine, sourceColumn];
}
} else {
seg = [genColumn];
}
line.push(seg);
reader.pos++;
}
if (!sorted) sort(line);
decoded.push(line);
reader.pos = semi + 1;
} while (reader.pos <= length);
return decoded;
}
function sort(line) {
line.sort(sortComparator);
}
function sortComparator(a, b) {
return a[0] - b[0];
}
// src/trace-mapping.ts
// src/sourcemap-segment.ts
var COLUMN = 0;
var SOURCES_INDEX = 1;
var SOURCE_LINE = 2;
var SOURCE_COLUMN = 3;
var NAMES_INDEX = 4;
// src/binary-search.ts
var found = false;
function binarySearch(haystack, needle, low, high) {
while (low <= high) {
const mid = low + (high - low >> 1);
const cmp = haystack[mid][COLUMN] - needle;
if (cmp === 0) {
found = true;
return mid;
}
if (cmp < 0) {
low = mid + 1;
} else {
high = mid - 1;
}
}
found = false;
return low - 1;
}
function upperBound(haystack, needle, index) {
for (let i = index + 1; i < haystack.length; index = i++) {
if (haystack[i][COLUMN] !== needle) break;
}
return index;
}
function lowerBound(haystack, needle, index) {
for (let i = index - 1; i >= 0; index = i--) {
if (haystack[i][COLUMN] !== needle) break;
}
return index;
}
function memoizedBinarySearch(haystack, needle, state, key) {
const { lastKey, lastNeedle, lastIndex } = state;
let low = 0;
let high = haystack.length - 1;
if (key === lastKey) {
if (needle === lastNeedle) {
found = lastIndex !== -1 && haystack[lastIndex][COLUMN] === needle;
return lastIndex;
}
if (needle >= lastNeedle) {
low = lastIndex === -1 ? 0 : lastIndex;
} else {
high = lastIndex;
}
}
state.lastKey = key;
state.lastNeedle = needle;
return state.lastIndex = binarySearch(haystack, needle, low, high);
}
// src/trace-mapping.ts
var LINE_GTR_ZERO = "`line` must be greater than 0 (lines start at line 1)";
var COL_GTR_EQ_ZERO = "`column` must be greater than or equal to 0 (columns start at column 0)";
var LEAST_UPPER_BOUND = -1;
var GREATEST_LOWER_BOUND = 1;
function cast(map) {
return map;
}
function decodedMappings(map) {
var _a;
return (_a = cast(map))._decoded || (_a._decoded = decode(cast(map)._encoded));
}
function originalPositionFor(map, needle) {
let { line, column, bias } = needle;
line--;
if (line < 0) throw new Error(LINE_GTR_ZERO);
if (column < 0) throw new Error(COL_GTR_EQ_ZERO);
const decoded = decodedMappings(map);
if (line >= decoded.length) return OMapping(null, null, null, null);
const segments = decoded[line];
const index = traceSegmentInternal(
segments,
cast(map)._decodedMemo,
line,
column,
bias || GREATEST_LOWER_BOUND
);
if (index === -1) return OMapping(null, null, null, null);
const segment = segments[index];
if (segment.length === 1) return OMapping(null, null, null, null);
const { names, resolvedSources } = map;
return OMapping(
resolvedSources[segment[SOURCES_INDEX]],
segment[SOURCE_LINE] + 1,
segment[SOURCE_COLUMN],
segment.length === 5 ? names[segment[NAMES_INDEX]] : null
);
}
function OMapping(source, line, column, name) {
return { source, line, column, name };
}
function traceSegmentInternal(segments, memo, line, column, bias) {
let index = memoizedBinarySearch(segments, column, memo, line);
if (found) {
index = (bias === LEAST_UPPER_BOUND ? upperBound : lowerBound)(segments, column, index);
} else if (bias === LEAST_UPPER_BOUND) index++;
if (index === -1 || index === segments.length) return -1;
return index;
}
const CHROME_IE_STACK_REGEXP = /^\s*at .*(?:\S:\d+|\(native\))/m;
const SAFARI_NATIVE_CODE_REGEXP = /^(?:eval@)?(?:\[native code\])?$/;
const stackIgnorePatterns = [
"node:internal",
/\/packages\/\w+\/dist\//,
/\/@vitest\/\w+\/dist\//,
"/vitest/dist/",
"/vitest/src/",
"/node_modules/chai/",
"/node_modules/tinyspy/",
"/vite/dist/node/module-runner",
"/rolldown-vite/dist/node/module-runner",
"/deps/chunk-",
"/deps/@vitest",
"/deps/loupe",
"/deps/chai",
"/browser-playwright/dist/locators.js",
"/browser-webdriverio/dist/locators.js",
"/browser-preview/dist/locators.js",
/node:\w+/,
/__vitest_test__/,
/__vitest_browser__/,
"/@id/__x00__vitest/browser",
/\/deps\/vitest_/
];
const NOW_LENGTH = Date.now().toString().length;
const REGEXP_VITEST = new RegExp(`vitest=\\d{${NOW_LENGTH}}`);
function extractLocation(urlLike) {
// Fail-fast but return locations like "(native)"
if (!urlLike.includes(":")) {
return [urlLike];
}
const regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
const parts = regExp.exec(urlLike.replace(/^\(|\)$/g, ""));
if (!parts) {
return [urlLike];
}
let url = parts[1];
if (url.startsWith("async ")) {
url = url.slice(6);
}
if (url.startsWith("http:") || url.startsWith("https:")) {
const urlObj = new URL(url);
urlObj.searchParams.delete("import");
urlObj.searchParams.delete("browserv");
url = urlObj.pathname + urlObj.hash + urlObj.search;
}
if (url.startsWith("/@fs/")) {
const isWindows = /^\/@fs\/[a-zA-Z]:\//.test(url);
url = url.slice(isWindows ? 5 : 4);
}
if (url.includes("vitest=")) {
url = url.replace(REGEXP_VITEST, "").replace(/[?&]$/, "");
}
return [
url,
parts[2] || undefined,
parts[3] || undefined
];
}
function parseSingleFFOrSafariStack(raw) {
let line = raw.trim();
if (SAFARI_NATIVE_CODE_REGEXP.test(line)) {
return null;
}
if (line.includes(" > eval")) {
line = line.replace(/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g, ":$1");
}
// Early return for lines that don't look like Firefox/Safari stack traces
// Firefox/Safari stack traces must contain '@' and should have location info after it
if (!line.includes("@")) {
return null;
}
// Find the correct @ that separates function name from location
// For cases like '@https://@fs/path' or 'functionName@https://@fs/path'
// we need to find the first @ that precedes a valid location (containing :)
let atIndex = -1;
let locationPart = "";
let functionName;
// Try each @ from left to right to find the one that gives us a valid location
for (let i = 0; i < line.length; i++) {
if (line[i] === "@") {
const candidateLocation = line.slice(i + 1);
// Minimum length 3 for valid location: 1 for filename + 1 for colon + 1 for line number (e.g., "a:1")
if (candidateLocation.includes(":") && candidateLocation.length >= 3) {
atIndex = i;
locationPart = candidateLocation;
functionName = i > 0 ? line.slice(0, i) : undefined;
break;
}
}
}
// Validate we found a valid location with minimum length (filename:line format)
if (atIndex === -1 || !locationPart.includes(":") || locationPart.length < 3) {
return null;
}
const [url, lineNumber, columnNumber] = extractLocation(locationPart);
if (!url || !lineNumber || !columnNumber) {
return null;
}
return {
file: url,
method: functionName || "",
line: Number.parseInt(lineNumber),
column: Number.parseInt(columnNumber)
};
}
function parseSingleStack(raw) {
const line = raw.trim();
if (!CHROME_IE_STACK_REGEXP.test(line)) {
return parseSingleFFOrSafariStack(line);
}
return parseSingleV8Stack(line);
}
// Based on https://github.com/stacktracejs/error-stack-parser
// Credit to stacktracejs
function parseSingleV8Stack(raw) {
let line = raw.trim();
if (!CHROME_IE_STACK_REGEXP.test(line)) {
return null;
}
if (line.includes("(eval ")) {
line = line.replace(/eval code/g, "eval").replace(/(\(eval at [^()]*)|(,.*$)/g, "");
}
let sanitizedLine = line.replace(/^\s+/, "").replace(/\(eval code/g, "(").replace(/^.*?\s+/, "");
// capture and preserve the parenthesized location "(/foo/my bar.js:12:87)" in
// case it has spaces in it, as the string is split on \s+ later on
const location = sanitizedLine.match(/ (\(.+\)$)/);
// remove the parenthesized location from the line, if it was matched
sanitizedLine = location ? sanitizedLine.replace(location[0], "") : sanitizedLine;
// if a location was matched, pass it to extractLocation() otherwise pass all sanitizedLine
// because this line doesn't have function name
const [url, lineNumber, columnNumber] = extractLocation(location ? location[1] : sanitizedLine);
let method = location && sanitizedLine || "";
let file = url && ["eval", "<anonymous>"].includes(url) ? undefined : url;
if (!file || !lineNumber || !columnNumber) {
return null;
}
if (method.startsWith("async ")) {
method = method.slice(6);
}
if (file.startsWith("file://")) {
file = file.slice(7);
}
// normalize Windows path (\ -> /)
file = file.startsWith("node:") || file.startsWith("internal:") ? file : resolve(file);
if (method) {
method = method.replace(/\(0\s?,\s?__vite_ssr_import_\d+__.(\w+)\)/g, "$1").replace(/__(vite_ssr_import|vi_import)_\d+__\./g, "").replace(/(Object\.)?__vite_ssr_export_default__\s?/g, "");
}
return {
method,
file,
line: Number.parseInt(lineNumber),
column: Number.parseInt(columnNumber)
};
}
function createStackString(stacks) {
return stacks.map((stack) => {
const line = `${stack.file}:${stack.line}:${stack.column}`;
if (stack.method) {
return ` at ${stack.method}(${line})`;
}
return ` at ${line}`;
}).join("\n");
}
function parseStacktrace(stack, options = {}) {
const { ignoreStackEntries = stackIgnorePatterns } = options;
let stacks = !CHROME_IE_STACK_REGEXP.test(stack) ? parseFFOrSafariStackTrace(stack) : parseV8Stacktrace(stack);
// remove vi.defineHelper's internal stacks
const helperIndex = stacks.findLastIndex((s) => s.method.includes("__VITEST_HELPER__"));
if (helperIndex >= 0) {
stacks = stacks.slice(helperIndex + 1);
}
return stacks.map((stack) => {
if (options.getUrlId) {
stack.file = options.getUrlId(stack.file);
}
const map = options.getSourceMap?.(stack.file);
if (!map || typeof map !== "object" || !map.version) {
return shouldFilter(ignoreStackEntries, stack.file) ? null : stack;
}
const traceMap = new DecodedMap(map, stack.file);
const position = getOriginalPosition(traceMap, stack);
if (!position) {
return stack;
}
const { line, column, source, name } = position;
let file = source || stack.file;
if (file.match(/\/\w:\//)) {
file = file.slice(1);
}
if (shouldFilter(ignoreStackEntries, file)) {
return null;
}
if (line != null && column != null) {
return {
line,
column,
file,
method: name || stack.method
};
}
return stack;
}).filter((s) => s != null);
}
function shouldFilter(ignoreStackEntries, file) {
return ignoreStackEntries.some((p) => file.match(p));
}
function parseFFOrSafariStackTrace(stack) {
return stack.split("\n").map((line) => parseSingleFFOrSafariStack(line)).filter(notNullish);
}
function parseV8Stacktrace(stack) {
return stack.split("\n").map((line) => parseSingleV8Stack(line)).filter(notNullish);
}
function parseErrorStacktrace(e, options = {}) {
if (!e || isPrimitive(e)) {
return [];
}
if ("stacks" in e && e.stacks) {
return e.stacks;
}
const stackStr = e.stack || "";
// if "stack" property was overwritten at runtime to be something else,
// ignore the value because we don't know how to process it
let stackFrames = typeof stackStr === "string" ? parseStacktrace(stackStr, options) : [];
if (!stackFrames.length) {
const e_ = e;
if (e_.fileName != null && e_.lineNumber != null && e_.columnNumber != null) {
stackFrames = parseStacktrace(`${e_.fileName}:${e_.lineNumber}:${e_.columnNumber}`, options);
}
if (e_.sourceURL != null && e_.line != null && e_._column != null) {
stackFrames = parseStacktrace(`${e_.sourceURL}:${e_.line}:${e_.column}`, options);
}
}
if (options.frameFilter) {
stackFrames = stackFrames.filter((f) => options.frameFilter(e, f) !== false);
}
e.stacks = stackFrames;
return stackFrames;
}
class DecodedMap {
_encoded;
_decoded;
_decodedMemo;
url;
version;
names = [];
resolvedSources;
constructor(map, from) {
this.map = map;
const { mappings, names, sources } = map;
this.version = map.version;
this.names = names || [];
this._encoded = mappings || "";
this._decodedMemo = memoizedState();
this.url = from;
this.resolvedSources = (sources || []).map((s) => resolve(from, "..", s || ""));
}
}
function memoizedState() {
return {
lastKey: -1,
lastNeedle: -1,
lastIndex: -1
};
}
function getOriginalPosition(map, needle) {
const result = originalPositionFor(map, needle);
if (result.column == null) {
return null;
}
return result;
}
export { DecodedMap, createStackString, stackIgnorePatterns as defaultStackIgnorePatterns, getOriginalPosition, parseErrorStacktrace, parseSingleFFOrSafariStack, parseSingleStack, parseSingleV8Stack, parseStacktrace };
+6
View File
@@ -0,0 +1,6 @@
interface ExtractedSourceMap {
map: any;
}
declare function extractSourcemapFromFile(code: string, filePath: string): ExtractedSourceMap | undefined;
export { extractSourcemapFromFile };
+23
View File
@@ -0,0 +1,23 @@
import { existsSync, readFileSync } from 'node:fs';
import path from 'node:path';
import convertSourceMap from 'convert-source-map';
// based on vite
// https://github.com/vitejs/vite/blob/84079a84ad94de4c1ef4f1bdb2ab448ff2c01196/packages/vite/src/node/server/sourcemap.ts#L149
function extractSourcemapFromFile(code, filePath) {
const map = (convertSourceMap.fromSource(code) || convertSourceMap.fromMapFileSource(code, createConvertSourceMapReadMap(filePath)))?.toObject();
return map ? { map } : undefined;
}
function createConvertSourceMapReadMap(originalFileName) {
return (filename) => {
// convertSourceMap can detect invalid filename from comments.
// fallback to empty source map to avoid errors.
const targetPath = path.resolve(path.dirname(originalFileName), filename);
if (existsSync(targetPath)) {
return readFileSync(targetPath, "utf-8");
}
return "{}";
};
}
export { extractSourcemapFromFile };
+33
View File
@@ -0,0 +1,33 @@
interface SafeTimers {
nextTick?: (cb: () => void) => void;
setImmediate?: {
<TArgs extends any[]>(callback: (...args: TArgs) => void, ...args: TArgs): any;
__promisify__: <T = void>(value?: T, options?: any) => Promise<T>;
};
clearImmediate?: (immediateId: any) => void;
setTimeout: typeof setTimeout;
setInterval: typeof setInterval;
clearInterval: typeof clearInterval;
clearTimeout: typeof clearTimeout;
queueMicrotask: typeof queueMicrotask;
}
declare function getSafeTimers(): SafeTimers;
declare function setSafeTimers(): void;
/**
* Returns a promise that resolves after the specified duration.
*
* @param timeout - Delay in milliseconds
* @param scheduler - Timer function to use, defaults to `setTimeout`. Useful for mocked timers.
*
* @example
* await delay(100)
*
* @example
* // With mocked timers
* const { setTimeout } = getSafeTimers()
* await delay(100, setTimeout)
*/
declare function delay(timeout: number, scheduler?: typeof setTimeout): Promise<void>;
export { delay, getSafeTimers, setSafeTimers };
export type { SafeTimers };

Some files were not shown because too many files have changed in this diff Show More