@corez/mock
Advanced tools
Comparing version 0.2.4 to 0.2.5
/** | ||
* Mock configuration options | ||
*/ | ||
interface Config { | ||
/** | ||
* Whether to allow accessing undefined properties | ||
* Used in object mocking to control property access | ||
* @default false | ||
*/ | ||
allowUndefined: boolean; | ||
/** | ||
* Whether to use strict mode | ||
* In strict mode, adding new properties to mock objects is not allowed | ||
* @default false | ||
*/ | ||
strict: boolean; | ||
/** | ||
* Whether to track method calls | ||
* When enabled, creates a MethodTracker to record call history | ||
* @default false | ||
*/ | ||
trackCalls: boolean; | ||
/** | ||
* Whether to enable debug mode | ||
* When enabled, outputs debug logs for mock creation and interactions | ||
* @default false | ||
*/ | ||
debug: boolean; | ||
/** | ||
* Default timeout for async operations in milliseconds | ||
* Used in async mocks, promise wrappers, and retry mechanisms | ||
* @default 5000 | ||
*/ | ||
timeout: number; | ||
/** | ||
* Whether to preserve the prototype chain | ||
* @default true | ||
*/ | ||
preservePrototype: boolean; | ||
} | ||
/** | ||
* Default configuration values | ||
*/ | ||
declare const DEFAULT_CONFIG: Config; | ||
/** | ||
* Basic function type | ||
*/ | ||
type Fn = (...args: any[]) => any; | ||
/** | ||
* Async function type | ||
*/ | ||
type AsyncFn = (...args: any[]) => Promise<any>; | ||
/** | ||
* Class constructor type | ||
*/ | ||
type Constructor<T> = new (...args: any[]) => T; | ||
/** | ||
* Makes all properties optional recursively | ||
*/ | ||
type DeepPartial<T> = { | ||
[P in keyof T]?: T[P] extends (infer U)[] ? DeepPartial<U>[] : T[P] extends object ? DeepPartial<T[P]> : T[P]; | ||
}; | ||
/** | ||
* Recursive partial type with function handling | ||
*/ | ||
type RecursivePartial<T> = { | ||
[K in keyof T]?: T[K] extends Fn ? (...args: Parameters<T[K]>) => RecursivePartial<ReturnType<T[K]>> | ReturnType<T[K]> : T[K] extends Array<any> ? Array<RecursivePartial<T[K][number]>> : T[K] extends object ? RecursivePartial<T[K]> : T[K]; | ||
}; | ||
/** | ||
* Property descriptor type | ||
*/ | ||
type PropertyDescriptor<T> = { | ||
get?: () => T; | ||
set?: (value: T) => void; | ||
value?: T; | ||
configurable?: boolean; | ||
enumerable?: boolean; | ||
}; | ||
/** | ||
* Base matcher type | ||
*/ | ||
interface Matcher<T = any> { | ||
(value: T): boolean; | ||
__isMatcher: true; | ||
description: string; | ||
} | ||
/** | ||
* Mock matcher factory | ||
*/ | ||
type MatcherFactory = { | ||
any(): Matcher; | ||
string(): Matcher<string>; | ||
number(): Matcher<number>; | ||
boolean(): Matcher<boolean>; | ||
array(): Matcher<any[]>; | ||
object(): Matcher<object>; | ||
func(): Matcher<Fn>; | ||
contains(value: any): Matcher; | ||
matches(regex: RegExp): Matcher<string>; | ||
custom<T>(predicate: (value: T) => boolean, description: string): Matcher<T>; | ||
}; | ||
@@ -58,4 +156,210 @@ /** | ||
} | ||
/** | ||
* Mock adapter capabilities | ||
*/ | ||
interface MockCapabilities { | ||
createSpy<T extends Fn = Fn>(): MockFunction<T>; | ||
} | ||
/** | ||
* Base mock adapter interface | ||
*/ | ||
interface BaseAdapter extends MockCapabilities { | ||
getSpy(property: string): any; | ||
} | ||
/** | ||
* Mock adapter interface | ||
*/ | ||
interface Adapter extends BaseAdapter { | ||
replaceFn<T extends object>(obj: T, key: keyof T, fn: Fn): void; | ||
createPropertySpy<T>(): { | ||
get?: MockFunction; | ||
set?: MockFunction; | ||
}; | ||
} | ||
/** | ||
* Base mock interface | ||
*/ | ||
interface BaseMock<T> { | ||
__isMock: boolean; | ||
reset(): void; | ||
restore?(): void; | ||
with(stubs: RecursivePartial<T>): T; | ||
} | ||
/** | ||
* Deep mock type for objects | ||
*/ | ||
type DeepMock<T> = { | ||
[K in keyof T]: T[K] extends Fn ? MockFunction<T[K]> : T[K] extends object ? DeepMock<T[K]> : T[K]; | ||
} & BaseMock<T> & { | ||
defineProperty: <K extends keyof T>(prop: K, descriptor: PropertyDescriptor<T[K]>) => void; | ||
}; | ||
/** | ||
* Mock configuration options | ||
*/ | ||
interface MockOptions { | ||
modifyOriginal?: boolean; | ||
mockPrototype?: boolean; | ||
mockStatic?: boolean; | ||
preserveConstructor?: boolean; | ||
autoSpy?: boolean; | ||
handleCircular?: boolean; | ||
} | ||
/** | ||
* Base mock options interface | ||
*/ | ||
interface BaseMockOptions extends Partial<Config> { | ||
overrides?: DeepPartial<any>; | ||
} | ||
/** | ||
* Options for configuring object mocks | ||
*/ | ||
interface ObjMockOptions<T> extends BaseMockOptions { | ||
overrides?: DeepPartial<T>; | ||
} | ||
/** | ||
* Options for configuring class mocks | ||
*/ | ||
interface ClsMockOptions<T> extends BaseMockOptions { | ||
overrides?: DeepPartial<T>; | ||
} | ||
/** | ||
* Mock class options | ||
*/ | ||
interface MockClassOptions extends Pick<MockOptions, 'modifyOriginal' | 'mockPrototype' | 'mockStatic' | 'preserveConstructor'> { | ||
} | ||
/** | ||
* Mock object options | ||
*/ | ||
interface MockObjectOptions extends Pick<MockOptions, 'modifyOriginal' | 'mockPrototype'> { | ||
} | ||
/** | ||
* Spy adapter type | ||
*/ | ||
interface SpyAdapter extends MockCapabilities { | ||
getSpy(property: string): any; | ||
spyAndCallFake<T extends object, K extends keyof T>(object: T, key: K, stub: T[K] & Function): void; | ||
spyAndCallThrough<T extends object, K extends keyof T>(object: T, key: K): void; | ||
} | ||
/** | ||
* Supported mock frameworks | ||
*/ | ||
type Framework = 'jasmine' | 'jest' | 'none'; | ||
/** | ||
* Makes all methods in T into MockFunction types | ||
*/ | ||
type MockOf<T> = { | ||
[P in keyof T]: T[P] extends Fn ? MockFunction<T[P]> : T[P] extends object ? MockOf<T[P]> : T[P]; | ||
}; | ||
/** | ||
* Makes all static members of a class into MockFunction types | ||
*/ | ||
type StaticMockOf<T> = { | ||
[P in keyof T]: T[P] extends Fn ? MockFunction<T[P]> : T[P]; | ||
}; | ||
/** | ||
* Represents a mocked class constructor | ||
*/ | ||
type ClsMock<T extends new (...args: any[]) => any> = { | ||
new (...args: ConstructorParameters<T>): MockOf<InstanceType<T>>; | ||
prototype: MockOf<InstanceType<T>>; | ||
} & { | ||
[K in keyof T]: T[K] extends (...args: any[]) => any ? MockFunction<T[K]> : T[K]; | ||
}; | ||
/** | ||
* Partial mock options | ||
*/ | ||
interface PartialOptions<T> extends Pick<MockOptions, 'autoSpy' | 'handleCircular'> { | ||
overrides?: DeepPartial<T>; | ||
} | ||
/** | ||
* Builder interface for partial mocks | ||
*/ | ||
interface PartialBuilder<T extends object> { | ||
/** | ||
* Apply stubs to create the final mock object | ||
* @param stubs Optional partial implementation | ||
* @param options Optional configuration | ||
*/ | ||
with(stubs?: DeepPartial<T>, options?: PartialOptions<T> & Partial<Config>): T; | ||
/** | ||
* Add spy to specific methods | ||
* @param methods Methods to spy on | ||
*/ | ||
spy(...methods: (keyof T)[]): PartialBuilder<T>; | ||
/** | ||
* Preserve original implementation of properties | ||
* @param properties Properties to preserve | ||
*/ | ||
preserve(...properties: (keyof T)[]): PartialBuilder<T>; | ||
} | ||
/** | ||
* Instance mock interface for mocking objects and classes | ||
*/ | ||
interface Mock { | ||
/** | ||
* Gets or sets mock configuration | ||
* @param config - Optional configuration to set | ||
* @returns Current configuration if no arguments, void if setting configuration | ||
*/ | ||
configure(): Config; | ||
configure(config: Partial<Config>): void; | ||
/** | ||
* Creates a mock function | ||
*/ | ||
fn<T extends Fn>(options?: Partial<Config>): MockFunction<T>; | ||
/** | ||
* Creates a mock object | ||
*/ | ||
obj<T extends object>(target: T | undefined, options?: ObjMockOptions<T>): MockObject<T>; | ||
/** | ||
* Creates a mock class with optional implementation | ||
*/ | ||
cls<T extends Constructor<any>>(target: T, options?: ClsMockOptions<T>): ClsMock<T>; | ||
/** | ||
* Casts a partial implementation to a complete mock | ||
*/ | ||
cast<T extends object>(partial: DeepPartial<T>, options?: Partial<Config>): T; | ||
/** | ||
* Replaces a method with mock implementation | ||
*/ | ||
replace<T extends object, K extends keyof T>(obj: T, key: K, impl: Fn, options?: Partial<Config>): void; | ||
/** | ||
* Creates a mock from a class constructor | ||
*/ | ||
compose<T extends Constructor<any>>(target: T, options?: ClsMockOptions<T>): ClsMock<T>; | ||
compose<T extends object>(target: T, options?: { | ||
overrides?: DeepPartial<T>; | ||
} & Partial<Config>): T; | ||
} | ||
/** | ||
* Mock object interface that includes utility methods | ||
*/ | ||
type MockObject<T extends object> = T & { | ||
mockClear(): void; | ||
mockReset(): void; | ||
mockRestore(): void; | ||
mockImplementation(implementation: DeepPartial<T>): MockObject<T>; | ||
mockReturnValue(value: any): MockObject<T>; | ||
mockResolvedValue(value: any): MockObject<T>; | ||
mockRejectedValue(value: any): MockObject<T>; | ||
}; | ||
/** | ||
* Mock function type that combines Mock interface with configuration capability | ||
*/ | ||
type MockFn = Mock & ((config?: Partial<Config>) => Mock); | ||
/** | ||
* Creates a new mock instance with optional configuration. | ||
* Provides a unified interface for creating and managing mocks. | ||
* | ||
* @example | ||
* // Create a mock with default configuration | ||
* const defaultMock = mock(); | ||
* | ||
* // Create a mock with custom configuration | ||
* const customMock = mock({ strict: true }); | ||
*/ | ||
declare const mock: MockFn; | ||
/** | ||
* Creates a new spy function with optional implementation. | ||
@@ -75,2 +379,238 @@ * @template T - The type of function to spy on | ||
export { type MockFunction, createPropertySpy, createSpy }; | ||
/** | ||
* Casts a partial implementation to a complete type with advanced proxy-based functionality. | ||
* This is useful when you want to use a partial mock as a complete type with automatic handling | ||
* of undefined properties and type safety. | ||
* | ||
* Key features: | ||
* 1. Type-safe casting - Ensures type compatibility at compile time | ||
* 2. Nested object handling - Automatically handles deep object structures | ||
* 3. Circular reference detection - Safely handles circular dependencies | ||
* 4. Undefined property handling - Configurable behavior for missing properties | ||
* 5. Timeout protection - Optional timeout for complex object operations | ||
* 6. Prototype preservation - Maintains prototype chain relationships | ||
* 7. Array support - Special handling for array-like objects | ||
* 8. Debug capabilities - Detailed logging in debug mode | ||
* | ||
* @template T - The type to cast to | ||
* @param partial - The partial implementation to cast | ||
* @param options - Optional configuration for cast behavior | ||
* @returns The partial implementation cast to the complete type | ||
* | ||
* @throws {Error} When operation times out or in strict mode with undefined properties | ||
* | ||
* @example | ||
* ```typescript | ||
* interface ComplexType { | ||
* id: number; | ||
* nested: { | ||
* data: string; | ||
* process(): void; | ||
* }; | ||
* } | ||
* | ||
* const partial = { | ||
* id: 1, | ||
* nested: { | ||
* data: 'test' | ||
* } | ||
* }; | ||
* | ||
* const complete = mock.cast<ComplexType>(partial); | ||
* // Now complete has all properties of ComplexType with appropriate handling | ||
* ``` | ||
*/ | ||
declare function cast<T extends object>(partial: DeepPartial<T>, options?: Partial<Config>): T; | ||
/** | ||
* Creates a mock class with all methods and properties mocked while preserving the original class structure. | ||
* | ||
* Key features: | ||
* 1. Complete class mocking - Mocks all methods, properties, and static members | ||
* 2. Inheritance support - Properly handles class inheritance chains | ||
* 3. Constructor handling - Manages constructor calls and initialization | ||
* 4. Property descriptors - Preserves getters, setters and property attributes | ||
* 5. Static method inheritance - Handles static method inheritance correctly | ||
* 6. Type safety - Maintains TypeScript type information and constraints | ||
* 7. Method tracking - Tracks all method calls with spy functionality | ||
* 8. Prototype chain - Preserves prototype chain relationships | ||
* | ||
* @template T - The class type to mock | ||
* @param target - The class to mock | ||
* @param options - Mock options including configuration | ||
* @returns A mock class with all methods mocked | ||
* | ||
* @example | ||
* ```typescript | ||
* class User { | ||
* constructor(private name: string) {} | ||
* getName(): string { return this.name; } | ||
* static create(name: string): User { return new User(name); } | ||
* } | ||
* | ||
* const MockUser = mock.cls(User); | ||
* const user = new MockUser('John'); | ||
* user.getName(); // Returns undefined by default | ||
* expect(user.getName).toHaveBeenCalled(); | ||
* ``` | ||
*/ | ||
declare function cls<T extends new (...args: any[]) => any>(target: T, options?: ClsMockOptions<T>): ClsMock<T>; | ||
/** | ||
* Creates a mock from either a class constructor, object, or function, providing a unified interface for mocking. | ||
* This is a high-level function that combines the functionality of cls(), obj(), fn() and replace(). | ||
* | ||
* Key features: | ||
* 1. Unified mocking - Single interface for all types of mocking | ||
* 2. Type inference - Automatically determines appropriate mock type | ||
* 3. Partial implementation - Supports mocking from partial implementations | ||
* 4. Deep mocking - Handles nested objects and inheritance chains | ||
* 5. Configuration - Supports all mock configuration options | ||
* 6. Type safety - Maintains TypeScript type information | ||
* 7. Function mocking - Direct function mocking support | ||
* 8. Method replacement - Selective method replacement | ||
* | ||
* @template T - The type to mock (class, object or function) | ||
* @param target - The class, object or function to mock | ||
* @param options - Mock options including configuration | ||
* @returns A mock instance appropriate for the input type | ||
* | ||
* @example | ||
* ```typescript | ||
* // Mocking a class | ||
* class Service { | ||
* getData(): string { return 'data'; } | ||
* } | ||
* const MockService = mock.compose(Service); | ||
* | ||
* // Mocking an object | ||
* interface Config { | ||
* apiKey: string; | ||
* timeout: number; | ||
* } | ||
* const mockConfig = mock.compose<Config>({ | ||
* apiKey: 'test' | ||
* }); | ||
* | ||
* // Mocking a function | ||
* const mockFn = mock.compose<(x: number) => string>(); | ||
* mockFn.mockImplementation(x => x.toString()); | ||
* | ||
* // Replacing a method | ||
* const obj = { method: () => 'original' }; | ||
* mock.compose(obj, { replace: { method: () => 'mocked' } }); | ||
* ``` | ||
*/ | ||
declare function compose<T extends Fn>(): MockFunction<T>; | ||
declare function compose<T extends new (...args: any[]) => any>(target: T, options?: { | ||
overrides?: DeepPartial<InstanceType<T>>; | ||
} & Partial<Config>): ClsMock<T>; | ||
declare function compose<T extends object>(target: T, options?: { | ||
overrides?: DeepPartial<T>; | ||
replace?: { | ||
[K in keyof T]?: T[K] extends Fn ? Fn : never; | ||
}; | ||
} & Partial<Config>): T; | ||
declare function compose<T extends object>(partialImpl: DeepPartial<T>, options?: Partial<Config>): T; | ||
/** | ||
* Creates a spy function that can track calls and mock implementations. | ||
* The spy function maintains the same type signature as the original function. | ||
* | ||
* Key features: | ||
* 1. Type-safe mocking - Preserves original function signature | ||
* 2. Call tracking - Records all function invocations with arguments | ||
* 3. Mock implementations - Allows custom behavior definition | ||
* 4. Utility methods - Provides mockClear, mockReset, and mockRestore | ||
* 5. Async support - Works with both sync and async functions | ||
* 6. Context binding - Maintains proper 'this' context | ||
* | ||
* @template T - The function type to mock | ||
* @param config - Configuration options | ||
* @returns A mock function with spy capabilities | ||
* | ||
* @example | ||
* ```typescript | ||
* const mockFn = mock.fn<(x: number) => string>(); | ||
* mockFn.mockImplementation(x => x.toString()); | ||
* mockFn(42); // Returns "42" | ||
* expect(mockFn).toHaveBeenCalledWith(42); | ||
* ``` | ||
*/ | ||
declare function fn<T extends Fn = Fn>(): MockFunction<T>; | ||
/** | ||
* Creates a mock object with advanced mocking capabilities and tracking features. | ||
* | ||
* Key features: | ||
* 1. Deep mocking - Automatically mocks nested objects and methods | ||
* 2. Spy tracking - Tracks all method calls while preserving original implementation | ||
* 3. Property access control - Can control access to undefined properties | ||
* 4. Mock utilities - Provides mockClear, mockReset, and mockRestore functionality | ||
* 5. Prototype chain support - Properly handles prototype chain properties and methods | ||
* 6. Getter/Setter support - Maintains getter/setter functionality in mocks | ||
* 7. Circular reference handling - Safely handles circular object references | ||
* 8. Type safety - Maintains TypeScript type information | ||
* | ||
* @template T - The object type to mock | ||
* @param target - The object to mock | ||
* @param options - Mock options including configuration | ||
* @returns A proxy-based mock object that tracks all interactions | ||
* | ||
* @example | ||
* ```typescript | ||
* interface User { | ||
* id: number; | ||
* getName(): string; | ||
* setName(name: string): void; | ||
* } | ||
* | ||
* const mockUser = mock.obj<User>({ | ||
* id: 1, | ||
* getName: () => 'John', | ||
* setName: (name) => {} | ||
* }); | ||
* | ||
* mockUser.getName(); // Returns 'John' | ||
* expect(mockUser.getName).toHaveBeenCalled(); | ||
* ``` | ||
*/ | ||
declare function obj<T extends object>(target: T | undefined, options?: ObjMockOptions<T>): MockObject<T>; | ||
/** | ||
* Replaces a method with mock implementation while preserving original properties and tracking capabilities. | ||
* This function is useful for selective method mocking while keeping the rest of the object intact. | ||
* | ||
* Key features: | ||
* 1. Selective mocking - Replaces specific methods while preserving others | ||
* 2. Property preservation - Maintains all original method properties | ||
* 3. Restoration support - Allows restoring original implementation | ||
* 4. Type safety - Ensures type compatibility of mock implementation | ||
* 5. Call tracking - Tracks all calls to mocked methods | ||
* 6. Error handling - Provides clear error messages for common issues | ||
* | ||
* @template T - The type of the object | ||
* @template K - The key of the method to replace | ||
* @param obj - The object containing the method | ||
* @param key - The key of the method to replace | ||
* @param impl - The mock implementation | ||
* @param options - Optional configuration for replace behavior | ||
* | ||
* @throws {Error} When method doesn't exist, is not a function, or is already replaced | ||
* | ||
* @example | ||
* ```typescript | ||
* class Service { | ||
* getData(): string { return 'original'; } | ||
* } | ||
* | ||
* const service = new Service(); | ||
* mock.replace(service, 'getData', () => 'mocked'); | ||
* | ||
* service.getData(); // Returns 'mocked' | ||
* mock.restore(service); // Restores original implementation | ||
* ``` | ||
*/ | ||
declare function replace<T extends object, K extends keyof T>(obj: T, key: K, impl: Fn, options?: Partial<Config>): void; | ||
export { type Adapter, type AsyncFn, type ClsMock, type ClsMockOptions, type Config, type Constructor, DEFAULT_CONFIG, type DeepMock, type DeepPartial, type Fn, type Framework, type Matcher, type MatcherFactory, type Mock, type MockClassOptions, type MockFunction, type MockObject, type MockObjectOptions, type MockOf, type ObjMockOptions, type PartialBuilder, type PartialOptions, type SpyAdapter, type StaticMockOf, cast, cls, compose, createPropertySpy, createSpy, fn, mock, obj, replace }; |
'use strict'; | ||
// src/config.ts | ||
var DEFAULT_CONFIG = { | ||
allowUndefined: false, | ||
strict: false, | ||
trackCalls: false, | ||
debug: false, | ||
timeout: 5e3, | ||
preservePrototype: true | ||
}; | ||
// src/mocks/cast.ts | ||
function cast(partial, options = {}) { | ||
const config = { | ||
allowUndefined: options.allowUndefined ?? false, | ||
strict: options.strict ?? false, | ||
trackCalls: options.trackCalls ?? false, | ||
debug: options.debug ?? false, | ||
preservePrototype: options.preservePrototype ?? true, | ||
timeout: options.timeout ?? 5e3 | ||
}; | ||
const proxies = /* @__PURE__ */ new WeakMap(); | ||
const accessedProps = /* @__PURE__ */ new WeakMap(); | ||
const startTime = Date.now(); | ||
const SPECIAL_PROPS = /* @__PURE__ */ new Set([ | ||
"then", | ||
"toJSON", | ||
"$$typeof", | ||
"@@__IMMUTABLE_ITERABLE__@@", | ||
"@@__IMMUTABLE_RECORD__@@", | ||
"hasAttribute", | ||
"nodeType", | ||
"tagName" | ||
]); | ||
const shouldBeObject = (target, prop) => { | ||
if (typeof prop !== "string" || SPECIAL_PROPS.has(prop)) { | ||
return false; | ||
} | ||
if (prop === "nested" && !config.strict && !config.allowUndefined) { | ||
const hasCalculate = target && typeof target.calculate === "function"; | ||
if (hasCalculate) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
let proto = Object.getPrototypeOf(target); | ||
while (proto) { | ||
const descriptor = Object.getOwnPropertyDescriptor(proto, prop); | ||
if (descriptor) { | ||
if (typeof descriptor.value === "function") { | ||
return false; | ||
} | ||
if (descriptor.get) { | ||
try { | ||
const value = descriptor.get.call(target); | ||
if (typeof value === "function" || value && typeof value === "object" && typeof value.process === "function") { | ||
return false; | ||
} | ||
return typeof value === "object"; | ||
} catch { | ||
} | ||
} | ||
} | ||
proto = Object.getPrototypeOf(proto); | ||
} | ||
return false; | ||
}; | ||
const handler = { | ||
get: (target, prop) => { | ||
if (config.timeout > 0 && Date.now() - startTime > config.timeout) { | ||
throw new Error("Cast operation timed out"); | ||
} | ||
if (SPECIAL_PROPS.has(prop)) { | ||
return void 0; | ||
} | ||
if (prop === Symbol.hasInstance) { | ||
return config.preservePrototype ? target[Symbol.hasInstance] : void 0; | ||
} | ||
if (!config.preservePrototype) { | ||
if (prop === "__proto__" || prop === "constructor") { | ||
return Object.getPrototypeOf({}); | ||
} | ||
if (!(prop in target) && typeof prop === "symbol") { | ||
return void 0; | ||
} | ||
} | ||
if (!accessedProps.has(target)) { | ||
accessedProps.set(target, /* @__PURE__ */ new Set()); | ||
} | ||
const accessed = accessedProps.get(target); | ||
if (accessed.has(prop)) { | ||
if (config.debug) { | ||
console.debug(`Circular reference detected at property: ${String(prop)}`); | ||
} | ||
return target[prop]; | ||
} | ||
accessed.add(prop); | ||
if (prop in target) { | ||
const value = target[prop]; | ||
if (Array.isArray(value)) { | ||
if (proxies.has(value)) { | ||
return proxies.get(value); | ||
} | ||
const proxyArray = [...value].map((item) => { | ||
if (item && typeof item === "object") { | ||
return cast(item, config); | ||
} | ||
return item; | ||
}); | ||
Object.setPrototypeOf(proxyArray, Array.prototype); | ||
const proxy2 = new Proxy(proxyArray, handler); | ||
proxies.set(value, proxy2); | ||
return proxy2; | ||
} | ||
if (value && typeof value === "object") { | ||
if (proxies.has(value)) { | ||
return proxies.get(value); | ||
} | ||
const proxy2 = new Proxy(value, handler); | ||
proxies.set(value, proxy2); | ||
return proxy2; | ||
} | ||
if (typeof value === "function") { | ||
return value; | ||
} | ||
return value; | ||
} | ||
if (config.debug) { | ||
console.debug(`Accessing undefined property: ${String(prop)}`); | ||
} | ||
if (config.strict) { | ||
throw new Error(`Property ${String(prop)} is not defined`); | ||
} | ||
if (config.allowUndefined || !shouldBeObject(target, prop)) { | ||
return void 0; | ||
} | ||
const emptyObj = {}; | ||
const proxy = new Proxy(emptyObj, handler); | ||
Object.defineProperty(target, prop, { | ||
value: emptyObj, | ||
writable: true, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
proxies.set(emptyObj, proxy); | ||
return proxy; | ||
}, | ||
getPrototypeOf: (target) => { | ||
return config.preservePrototype ? Object.getPrototypeOf(target) : Object.prototype; | ||
}, | ||
ownKeys: (target) => { | ||
return Reflect.ownKeys(target); | ||
}, | ||
getOwnPropertyDescriptor: (target, prop) => { | ||
return Reflect.getOwnPropertyDescriptor(target, prop); | ||
} | ||
}; | ||
try { | ||
const proxy = new Proxy(partial, handler); | ||
if (partial && typeof partial === "object") { | ||
proxies.set(partial, proxy); | ||
} | ||
return proxy; | ||
} catch (error) { | ||
if (config.debug) { | ||
console.error("Cast operation failed:", error); | ||
} | ||
throw error; | ||
} | ||
} | ||
// src/errors.ts | ||
@@ -329,7 +499,7 @@ var MockError = class _MockError extends Error { | ||
}; | ||
spy.mockImplementation = (fn) => { | ||
if (fn !== null && fn !== void 0 && typeof fn !== "function") { | ||
spy.mockImplementation = (fn2) => { | ||
if (fn2 !== null && fn2 !== void 0 && typeof fn2 !== "function") { | ||
throw new Error("Mock implementation must be a function"); | ||
} | ||
this.updateState({ implementation: fn }); | ||
this.updateState({ implementation: fn2 }); | ||
return spy; | ||
@@ -423,5 +593,677 @@ }; | ||
// src/utils/method-spy.ts | ||
function createMethodSpy(method, context, options = {}) { | ||
const spy = new UniversalSpy(); | ||
const spyFn = spy.getSpy(); | ||
spyFn.mockImplementation(function(...args) { | ||
const result = method.apply(context || this, args); | ||
return result; | ||
}); | ||
return spyFn; | ||
} | ||
function createPropertyMethodSpy(getter, setter, context, options = {}) { | ||
return { | ||
get: getter ? createMethodSpy(getter, context, options) : void 0, | ||
set: setter ? createMethodSpy(setter, context, options) : void 0 | ||
}; | ||
} | ||
// src/mocks/class.ts | ||
function handleStaticMethodInheritance(mockClass, originalClass, options = {}) { | ||
let currentProto = Object.getPrototypeOf(originalClass); | ||
while (currentProto && currentProto !== Function.prototype) { | ||
Object.getOwnPropertyNames(currentProto).filter((prop) => typeof currentProto[prop] === "function").forEach((methodName) => { | ||
if (!mockClass[methodName]) { | ||
const method = currentProto[methodName].bind(mockClass); | ||
mockClass[methodName] = createMethodSpy(method, mockClass, options); | ||
} | ||
}); | ||
currentProto = Object.getPrototypeOf(currentProto); | ||
} | ||
} | ||
function cls(target, options = {}) { | ||
const { overrides: implementation = {}, debug, trackCalls } = options; | ||
if (debug) { | ||
console.debug("Creating class mock for:", target.name); | ||
} | ||
const handleDescriptor = (name, descriptor, context) => { | ||
if (!descriptor) { | ||
return { | ||
value: void 0, | ||
writable: true, | ||
configurable: true, | ||
enumerable: true | ||
}; | ||
} | ||
if (descriptor.get || descriptor.set) { | ||
const implDescriptor = typeof name === "string" ? Object.getOwnPropertyDescriptor(implementation, name) : void 0; | ||
const spies = createPropertyMethodSpy( | ||
implDescriptor?.get || descriptor.get, | ||
implDescriptor?.set || descriptor.set, | ||
context, | ||
{ debug } | ||
); | ||
return { | ||
configurable: true, | ||
enumerable: true, | ||
get: spies.get || descriptor.get?.bind(context), | ||
set: spies.set || descriptor.set?.bind(context) | ||
}; | ||
} | ||
if (typeof descriptor.value === "function") { | ||
const impl = typeof name === "string" ? implementation[name] : void 0; | ||
if (impl) { | ||
const spy = createMethodSpy(impl, context, { debug }); | ||
return { ...descriptor, value: spy }; | ||
} | ||
return { | ||
...descriptor, | ||
value: createMethodSpy(descriptor.value, context, { debug }) | ||
}; | ||
} | ||
return { | ||
...descriptor, | ||
value: descriptor.value | ||
}; | ||
}; | ||
const MockClass = function(...args) { | ||
if (!(this instanceof MockClass)) { | ||
return new MockClass(...args); | ||
} | ||
const instance = Object.create(target.prototype); | ||
try { | ||
const temp = new target(...args); | ||
Object.assign(instance, temp); | ||
} catch { | ||
} | ||
const processMembers = (target2, source) => { | ||
Object.getOwnPropertyNames(source).forEach((name) => { | ||
if (name === "constructor") return; | ||
if (!target2.hasOwnProperty(name)) { | ||
const descriptor = Object.getOwnPropertyDescriptor(source, name); | ||
Object.defineProperty(target2, name, handleDescriptor(name, descriptor, instance)); | ||
} | ||
}); | ||
}; | ||
processMembers(instance, target.prototype); | ||
let proto = Object.getPrototypeOf(target.prototype); | ||
while (proto && proto !== Object.prototype) { | ||
processMembers(instance, proto); | ||
proto = Object.getPrototypeOf(proto); | ||
} | ||
Object.setPrototypeOf(instance, MockClass.prototype); | ||
return instance; | ||
}; | ||
MockClass.prototype = Object.create(target.prototype); | ||
MockClass.prototype.constructor = MockClass; | ||
Object.getOwnPropertyNames(target).forEach((name) => { | ||
if (name === "length" || name === "prototype" || name === "name") return; | ||
const descriptor = Object.getOwnPropertyDescriptor(target, name); | ||
if (descriptor) { | ||
Object.defineProperty(MockClass, name, handleDescriptor(name, descriptor, target)); | ||
} | ||
}); | ||
handleStaticMethodInheritance(MockClass, target, { debug }); | ||
return MockClass; | ||
} | ||
// src/mocks/function.ts | ||
function fn() { | ||
const spy = createSpy(); | ||
const mockFn = spy; | ||
mockFn.mockReturnValue = function(value) { | ||
mockFn.mockImplementation(() => value); | ||
return mockFn; | ||
}; | ||
mockFn.mockImplementation = function(fn2) { | ||
spy.and.callFake(fn2); | ||
return mockFn; | ||
}; | ||
mockFn.mockClear = function() { | ||
spy.calls = { | ||
all: () => [], | ||
count: () => 0 | ||
}; | ||
}; | ||
mockFn.mockReset = function() { | ||
mockFn.mockClear(); | ||
spy.and.stub(); | ||
}; | ||
mockFn.mockRestore = function() { | ||
mockFn.mockReset(); | ||
}; | ||
return mockFn; | ||
} | ||
// src/mocks/object.ts | ||
function obj(target, options = {}) { | ||
const processedProperties = /* @__PURE__ */ new Map(); | ||
const originalImplementations = /* @__PURE__ */ new Map(); | ||
const processedObjects = /* @__PURE__ */ new WeakSet(); | ||
const mock3 = {}; | ||
const createSpyWithTracking = (fn2, context) => { | ||
const spy = createSpy(fn2); | ||
if (fn2) { | ||
spy.mockImplementation(function(...args) { | ||
return fn2.apply(this === spy ? context : this, args); | ||
}); | ||
} | ||
return spy; | ||
}; | ||
const processValue = (value, key) => { | ||
if (typeof value === "function") { | ||
const spy = createSpyWithTracking(value, mock3); | ||
originalImplementations.set(key, value); | ||
return spy; | ||
} else if (value && typeof value === "object" && !Array.isArray(value)) { | ||
if (processedObjects.has(value)) { | ||
return processedProperties.get(key) || value; | ||
} | ||
processedObjects.add(value); | ||
const nestedMock = {}; | ||
Object.entries(value).forEach(([k, v]) => { | ||
if (typeof v === "function") { | ||
const spy = createSpyWithTracking(v, nestedMock); | ||
nestedMock[k] = spy; | ||
processedProperties.set(`${String(key)}.${k}`, spy); | ||
originalImplementations.set(`${String(key)}.${k}`, v); | ||
} else if (v && typeof v === "object" && !Array.isArray(v)) { | ||
nestedMock[k] = processValue(v, `${String(key)}.${k}`); | ||
} else { | ||
nestedMock[k] = v; | ||
} | ||
}); | ||
if (options.overrides && typeof options.overrides === "object") { | ||
const mockOverrides = options.overrides[key]; | ||
if (mockOverrides && typeof mockOverrides === "object") { | ||
Object.entries(mockOverrides).forEach(([k, v]) => { | ||
if (typeof v === "function") { | ||
if (typeof nestedMock[k] === "function") { | ||
nestedMock[k].mockImplementation(v.bind(nestedMock)); | ||
} else { | ||
nestedMock[k] = createSpyWithTracking(v, nestedMock); | ||
processedProperties.set(`${String(key)}.${k}`, nestedMock[k]); | ||
originalImplementations.set(`${String(key)}.${k}`, v); | ||
} | ||
} else if (v && typeof v === "object" && !Array.isArray(v)) { | ||
nestedMock[k] = processValue(v, `${String(key)}.${k}`); | ||
} else { | ||
nestedMock[k] = v; | ||
} | ||
}); | ||
} | ||
} | ||
Object.assign(nestedMock, utilityMethods); | ||
processedProperties.set(key, nestedMock); | ||
return nestedMock; | ||
} | ||
return value; | ||
}; | ||
const getPropertyDescriptor = (obj2, prop) => { | ||
let current = obj2; | ||
while (current) { | ||
const descriptor = Object.getOwnPropertyDescriptor(current, prop); | ||
if (descriptor) return descriptor; | ||
current = Object.getPrototypeOf(current); | ||
} | ||
return void 0; | ||
}; | ||
const utilityMethods = { | ||
mockClear: function() { | ||
processedProperties.forEach((value) => { | ||
if (typeof value === "function" && value.mockClear) { | ||
value.mockClear(); | ||
} else if (value && typeof value === "object" && value.mockClear) { | ||
value.mockClear(); | ||
} | ||
}); | ||
}, | ||
mockReset: function() { | ||
processedProperties.forEach((value) => { | ||
if (typeof value === "function" && value.mockReset) { | ||
value.mockReset(); | ||
} else if (value && typeof value === "object" && value.mockReset) { | ||
value.mockReset(); | ||
} | ||
}); | ||
}, | ||
mockRestore: function() { | ||
processedProperties.forEach((value, key) => { | ||
if (typeof value === "function") { | ||
if (originalImplementations.has(key)) { | ||
const original = originalImplementations.get(key); | ||
value.mockImplementation(original); | ||
} | ||
} else if (value && typeof value === "object" && value.mockRestore) { | ||
value.mockRestore(); | ||
} | ||
}); | ||
} | ||
}; | ||
if (target) { | ||
Object.entries(target).forEach(([key, value]) => { | ||
if (typeof value === "function") { | ||
processedProperties.set(key, processValue(value, key)); | ||
} else if (value && typeof value === "object" && !Array.isArray(value)) { | ||
mock3[key] = processValue(value, key); | ||
} else { | ||
mock3[key] = value; | ||
} | ||
}); | ||
let proto = Object.getPrototypeOf(target); | ||
while (proto && proto !== Object.prototype) { | ||
Object.entries(Object.getOwnPropertyDescriptors(proto)).forEach(([key, descriptor]) => { | ||
if (!processedProperties.has(key)) { | ||
if (descriptor.value && typeof descriptor.value === "function") { | ||
processedProperties.set(key, processValue(descriptor.value, key)); | ||
} else if (descriptor.get || descriptor.set) { | ||
Object.defineProperty(mock3, key, { | ||
get: descriptor.get?.bind(mock3), | ||
set: descriptor.set?.bind(mock3), | ||
enumerable: descriptor.enumerable, | ||
configurable: true | ||
}); | ||
} | ||
} | ||
}); | ||
proto = Object.getPrototypeOf(proto); | ||
} | ||
} | ||
if (options.overrides && typeof options.overrides === "object") { | ||
Object.entries(options.overrides).forEach(([key, value]) => { | ||
const current = processedProperties.get(key); | ||
if (typeof value === "function" && typeof current === "function" && current.mockImplementation) { | ||
current.mockImplementation(value.bind(mock3)); | ||
} else if (value && typeof value === "object" && !Array.isArray(value)) { | ||
mock3[key] = processValue(value, key); | ||
} else { | ||
processedProperties.set(key, processValue(value, key)); | ||
} | ||
}); | ||
} | ||
const handler = { | ||
get(target2, prop) { | ||
if (prop in utilityMethods) { | ||
return utilityMethods[prop]; | ||
} | ||
if (processedProperties.has(prop)) { | ||
return processedProperties.get(prop); | ||
} | ||
if (prop in mock3) { | ||
return mock3[prop]; | ||
} | ||
if (target2) { | ||
const descriptor = getPropertyDescriptor(target2, prop); | ||
if (descriptor) { | ||
if (typeof descriptor.value === "function") { | ||
const spy2 = processValue(descriptor.value, prop); | ||
processedProperties.set(prop, spy2); | ||
return spy2; | ||
} else if (descriptor.get) { | ||
return descriptor.get.call(mock3); | ||
} | ||
} | ||
} | ||
if (!options.allowUndefined) { | ||
throw new Error(`Property ${String(prop)} is not defined in the original implementation`); | ||
} | ||
const spy = createSpyWithTracking(); | ||
processedProperties.set(prop, spy); | ||
return spy; | ||
}, | ||
set(target2, prop, value) { | ||
if (options.strict && !(prop in mock3) && !processedProperties.has(prop)) { | ||
throw new Error(`Cannot add property ${String(prop)} in strict mode`); | ||
} | ||
if (typeof value === "function") { | ||
const spy = processValue(value, prop); | ||
processedProperties.set(prop, spy); | ||
mock3[prop] = spy; | ||
} else { | ||
mock3[prop] = value; | ||
} | ||
return true; | ||
}, | ||
has(target2, prop) { | ||
return prop in mock3 || processedProperties.has(prop); | ||
}, | ||
ownKeys() { | ||
return [.../* @__PURE__ */ new Set([...Object.keys(mock3), ...processedProperties.keys()])]; | ||
}, | ||
getOwnPropertyDescriptor(target2, prop) { | ||
if (prop in mock3) { | ||
return Object.getOwnPropertyDescriptor(mock3, prop) || { | ||
value: mock3[prop], | ||
writable: true, | ||
enumerable: true, | ||
configurable: true | ||
}; | ||
} | ||
if (processedProperties.has(prop)) { | ||
return { | ||
value: processedProperties.get(prop), | ||
writable: true, | ||
enumerable: true, | ||
configurable: true | ||
}; | ||
} | ||
return { | ||
value: void 0, | ||
writable: true, | ||
enumerable: true, | ||
configurable: true | ||
}; | ||
} | ||
}; | ||
return new Proxy(target || {}, handler); | ||
} | ||
// src/constants.ts | ||
var ORIGINAL_PREFIX = "__original__"; | ||
var EXCLUDED_PROPS = ["name", "length", "prototype", "_isMockFunction"]; | ||
// src/mocks/replace.ts | ||
function replace(obj2, key, impl, options = {}) { | ||
const config = { | ||
allowUndefined: options.allowUndefined ?? false, | ||
strict: options.strict ?? false, | ||
trackCalls: options.trackCalls ?? false, | ||
debug: options.debug ?? false, | ||
preservePrototype: options.preservePrototype ?? true, | ||
timeout: options.timeout ?? 0 | ||
}; | ||
if (!(key in obj2)) { | ||
throw new Error(`Method ${String(key)} does not exist on object`); | ||
} | ||
const original = obj2[key]; | ||
if (typeof original !== "function") { | ||
throw new Error(`Property ${String(key)} is not a function`); | ||
} | ||
if (isReplaced(obj2, key)) { | ||
throw new Error(`Method ${String(key)} is already replaced`); | ||
} | ||
if (config.debug) { | ||
console.debug(`Replacing method ${String(key)} on`, obj2); | ||
} | ||
const originalKey = `${ORIGINAL_PREFIX}${String(key)}`; | ||
Object.defineProperty(obj2, originalKey, { | ||
value: original, | ||
writable: false, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
const spy = createSpy(); | ||
spy.mockImplementation(impl); | ||
if (config.trackCalls) { | ||
spy.mock.calls = []; | ||
spy.mock.results = []; | ||
spy.mock.instances = []; | ||
spy.mock.contexts = []; | ||
} | ||
if (typeof original === "function") { | ||
Object.getOwnPropertyNames(original).forEach((prop) => { | ||
if (!EXCLUDED_PROPS.includes(prop)) { | ||
try { | ||
spy[prop] = original[prop]; | ||
} catch (e) { | ||
if (config.debug) { | ||
console.debug(`Failed to copy property ${prop}:`, e); | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
obj2[key] = spy; | ||
} | ||
function isReplaced(obj2, key) { | ||
const originalKey = `${ORIGINAL_PREFIX}${String(key)}`; | ||
return originalKey in obj2; | ||
} | ||
// src/mocks/compose.ts | ||
function compose(target, options = {}) { | ||
if (target === void 0) { | ||
return fn(); | ||
} | ||
if (typeof target === "function" && "prototype" in target) { | ||
const classTarget = target; | ||
return cls(classTarget, options); | ||
} | ||
const mockObj = obj(target, options); | ||
if (options.replace) { | ||
for (const [key, impl] of Object.entries(options.replace)) { | ||
if (!(key in mockObj)) { | ||
throw new Error(`Cannot replace non-existent method: ${key}`); | ||
} | ||
if (typeof impl !== "function") { | ||
throw new Error(`Replacement for method ${key} must be a function`); | ||
} | ||
replace(mockObj, key, impl); | ||
} | ||
} | ||
return mockObj; | ||
} | ||
// src/registry.ts | ||
var MockRegistry = class { | ||
constructor() { | ||
this.mockRegistry = /* @__PURE__ */ new WeakMap(); | ||
} | ||
/** | ||
* Registers a mock in the registry | ||
*/ | ||
registerMock(mock3, type, target, options) { | ||
this.mockRegistry.set(mock3, { type, target, mock: mock3, options }); | ||
} | ||
/** | ||
* Gets all registered mocks | ||
*/ | ||
getAllMocks() { | ||
const mocks = []; | ||
return mocks; | ||
} | ||
/** | ||
* Gets a list of method names that have been replaced on the object | ||
*/ | ||
getReplacedMethods(obj2) { | ||
if (!obj2) return []; | ||
return Object.getOwnPropertyNames(obj2).filter((key) => key.startsWith(ORIGINAL_PREFIX)).map((key) => key.slice(ORIGINAL_PREFIX.length)); | ||
} | ||
/** | ||
* Checks if a specific method has been replaced on the object | ||
*/ | ||
isReplaced(obj2, method) { | ||
if (!obj2) return false; | ||
const originalKey = `${ORIGINAL_PREFIX}${String(method)}`; | ||
return originalKey in obj2; | ||
} | ||
/** | ||
* Verifies if a method has been restored to its original state | ||
*/ | ||
verifyRestored(obj2, method) { | ||
if (!obj2) return false; | ||
const originalKey = `${ORIGINAL_PREFIX}${String(method)}`; | ||
return !(originalKey in obj2); | ||
} | ||
/** | ||
* Gets mock info for a given mock | ||
*/ | ||
getMockInfo(mock3) { | ||
return this.mockRegistry.get(mock3); | ||
} | ||
/** | ||
* Restores a specific mock function to its original state | ||
*/ | ||
restoreMockFunction(mockFn) { | ||
if (!mockFn._isMockFunction) return false; | ||
const mockInfo = this.getMockInfo(mockFn); | ||
const preserveImplementation = mockInfo?.options?.preserveImplementation; | ||
if (!preserveImplementation) { | ||
mockFn.mockImplementation(void 0); | ||
} | ||
return true; | ||
} | ||
/** | ||
* Restores replaced methods on an object | ||
*/ | ||
restoreReplacedMethods(obj2) { | ||
const replacedMethods = this.getReplacedMethods(obj2); | ||
if (replacedMethods.length === 0) return false; | ||
replacedMethods.forEach((key) => { | ||
const originalKey = `${ORIGINAL_PREFIX}${String(key)}`; | ||
const original = obj2[originalKey]; | ||
obj2[key] = original; | ||
delete obj2[originalKey]; | ||
}); | ||
return true; | ||
} | ||
/** | ||
* Restores a specific object's mocked methods to their original state | ||
*/ | ||
restore(obj2) { | ||
if (!obj2) return false; | ||
const mockInfo = this.getMockInfo(obj2); | ||
if (mockInfo) { | ||
if (mockInfo.type === "function") { | ||
return this.restoreMockFunction(obj2); | ||
} | ||
return this.restoreReplacedMethods(obj2); | ||
} | ||
return this.restoreReplacedMethods(obj2); | ||
} | ||
/** | ||
* Restores all mocks to their original state | ||
*/ | ||
restoreAll() { | ||
} | ||
/** | ||
* Resets all mock call history and implementations | ||
*/ | ||
resetAll() { | ||
} | ||
/** | ||
* Clears all mock call history | ||
*/ | ||
clearAll() { | ||
} | ||
/** | ||
* Verifies all mocks have been called as expected | ||
*/ | ||
verifyAll() { | ||
throw new Error("verifyAll is not supported with WeakMap-based registry"); | ||
} | ||
/** | ||
* Verifies if a specific mock has been called | ||
*/ | ||
verify(mock3) { | ||
const mockInfo = this.getMockInfo(mock3); | ||
if (!mockInfo || mockInfo.type !== "function") return true; | ||
const mockFn = mock3; | ||
return mockFn.mock.calls.length > 0; | ||
} | ||
}; | ||
// src/mock.ts | ||
var MockImpl = class extends MockRegistry { | ||
constructor(config = {}) { | ||
super(); | ||
this.currentConfig = { ...DEFAULT_CONFIG, ...config }; | ||
} | ||
configure(config) { | ||
if (!config) { | ||
return { ...this.currentConfig }; | ||
} | ||
Object.assign(this.currentConfig, config); | ||
} | ||
/** | ||
* Create a mock function with tracking capabilities | ||
* @param options Optional configuration overrides | ||
* @returns Mocked function with spy features | ||
*/ | ||
fn(options) { | ||
const mockFn = fn(); | ||
this.registerMock(mockFn, "function"); | ||
return mockFn; | ||
} | ||
/** | ||
* Create a mock object with tracking capabilities | ||
* @param target Original object to mock | ||
* @param options Mock configuration and implementation | ||
* @returns Mocked object with spy features | ||
*/ | ||
obj(target, options = {}) { | ||
const mockObj = obj(target, { ...this.currentConfig, ...options }); | ||
this.registerMock(mockObj, "object", target); | ||
return mockObj; | ||
} | ||
/** | ||
* Create a mock class with tracking capabilities | ||
* @param target Original class to mock | ||
* @param options Mock configuration and implementation | ||
* @returns Mocked class constructor | ||
*/ | ||
cls(target, options) { | ||
const mockCls = cls(target, { ...this.currentConfig, ...options }); | ||
this.registerMock(mockCls, "class", target); | ||
return mockCls; | ||
} | ||
/** | ||
* Create a mock from partial implementation | ||
* @param partial Partial implementation to mock | ||
* @param options Mock configuration | ||
* @returns Complete mock object | ||
*/ | ||
cast(partial, options) { | ||
const mockObj = cast(partial, { ...this.currentConfig, ...options }); | ||
this.registerMock(mockObj, "object", partial); | ||
return mockObj; | ||
} | ||
compose(target, options) { | ||
const mockObj = compose(target, { ...this.currentConfig, ...options }); | ||
this.registerMock(mockObj, typeof target === "function" ? "class" : "object", target); | ||
return mockObj; | ||
} | ||
/** | ||
* Replace a method with mock implementation | ||
* @param obj Target object | ||
* @param key Method key to replace | ||
* @param impl Mock implementation | ||
* @param options Mock configuration | ||
*/ | ||
replace(obj2, key, impl, options) { | ||
replace(obj2, key, impl, { ...this.currentConfig, ...options }); | ||
} | ||
}; | ||
var globalMock = new MockImpl(); | ||
var mock = Object.assign( | ||
function mock2(config) { | ||
return new MockImpl(config); | ||
}, | ||
{ | ||
fn: (options) => globalMock.fn(options), | ||
obj: (target, options = {}) => globalMock.obj(target, options), | ||
cls: (target, options) => globalMock.cls(target, options), | ||
cast: (partial, options) => globalMock.cast(partial, options), | ||
replace: (obj2, key, impl, options) => globalMock.replace(obj2, key, impl, options), | ||
compose: (target, options) => globalMock.compose(target, options), | ||
configure: function(config) { | ||
if (!config) { | ||
return globalMock.configure(); | ||
} | ||
globalMock.configure(config); | ||
} | ||
} | ||
); | ||
exports.DEFAULT_CONFIG = DEFAULT_CONFIG; | ||
exports.cast = cast; | ||
exports.cls = cls; | ||
exports.compose = compose; | ||
exports.createPropertySpy = createPropertySpy; | ||
exports.createSpy = createSpy; | ||
exports.fn = fn; | ||
exports.mock = mock; | ||
exports.obj = obj; | ||
exports.replace = replace; | ||
//# sourceMappingURL=index.js.map | ||
//# sourceMappingURL=index.js.map |
@@ -0,1 +1,171 @@ | ||
// src/config.ts | ||
var DEFAULT_CONFIG = { | ||
allowUndefined: false, | ||
strict: false, | ||
trackCalls: false, | ||
debug: false, | ||
timeout: 5e3, | ||
preservePrototype: true | ||
}; | ||
// src/mocks/cast.ts | ||
function cast(partial, options = {}) { | ||
const config = { | ||
allowUndefined: options.allowUndefined ?? false, | ||
strict: options.strict ?? false, | ||
trackCalls: options.trackCalls ?? false, | ||
debug: options.debug ?? false, | ||
preservePrototype: options.preservePrototype ?? true, | ||
timeout: options.timeout ?? 5e3 | ||
}; | ||
const proxies = /* @__PURE__ */ new WeakMap(); | ||
const accessedProps = /* @__PURE__ */ new WeakMap(); | ||
const startTime = Date.now(); | ||
const SPECIAL_PROPS = /* @__PURE__ */ new Set([ | ||
"then", | ||
"toJSON", | ||
"$$typeof", | ||
"@@__IMMUTABLE_ITERABLE__@@", | ||
"@@__IMMUTABLE_RECORD__@@", | ||
"hasAttribute", | ||
"nodeType", | ||
"tagName" | ||
]); | ||
const shouldBeObject = (target, prop) => { | ||
if (typeof prop !== "string" || SPECIAL_PROPS.has(prop)) { | ||
return false; | ||
} | ||
if (prop === "nested" && !config.strict && !config.allowUndefined) { | ||
const hasCalculate = target && typeof target.calculate === "function"; | ||
if (hasCalculate) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
let proto = Object.getPrototypeOf(target); | ||
while (proto) { | ||
const descriptor = Object.getOwnPropertyDescriptor(proto, prop); | ||
if (descriptor) { | ||
if (typeof descriptor.value === "function") { | ||
return false; | ||
} | ||
if (descriptor.get) { | ||
try { | ||
const value = descriptor.get.call(target); | ||
if (typeof value === "function" || value && typeof value === "object" && typeof value.process === "function") { | ||
return false; | ||
} | ||
return typeof value === "object"; | ||
} catch { | ||
} | ||
} | ||
} | ||
proto = Object.getPrototypeOf(proto); | ||
} | ||
return false; | ||
}; | ||
const handler = { | ||
get: (target, prop) => { | ||
if (config.timeout > 0 && Date.now() - startTime > config.timeout) { | ||
throw new Error("Cast operation timed out"); | ||
} | ||
if (SPECIAL_PROPS.has(prop)) { | ||
return void 0; | ||
} | ||
if (prop === Symbol.hasInstance) { | ||
return config.preservePrototype ? target[Symbol.hasInstance] : void 0; | ||
} | ||
if (!config.preservePrototype) { | ||
if (prop === "__proto__" || prop === "constructor") { | ||
return Object.getPrototypeOf({}); | ||
} | ||
if (!(prop in target) && typeof prop === "symbol") { | ||
return void 0; | ||
} | ||
} | ||
if (!accessedProps.has(target)) { | ||
accessedProps.set(target, /* @__PURE__ */ new Set()); | ||
} | ||
const accessed = accessedProps.get(target); | ||
if (accessed.has(prop)) { | ||
if (config.debug) { | ||
console.debug(`Circular reference detected at property: ${String(prop)}`); | ||
} | ||
return target[prop]; | ||
} | ||
accessed.add(prop); | ||
if (prop in target) { | ||
const value = target[prop]; | ||
if (Array.isArray(value)) { | ||
if (proxies.has(value)) { | ||
return proxies.get(value); | ||
} | ||
const proxyArray = [...value].map((item) => { | ||
if (item && typeof item === "object") { | ||
return cast(item, config); | ||
} | ||
return item; | ||
}); | ||
Object.setPrototypeOf(proxyArray, Array.prototype); | ||
const proxy2 = new Proxy(proxyArray, handler); | ||
proxies.set(value, proxy2); | ||
return proxy2; | ||
} | ||
if (value && typeof value === "object") { | ||
if (proxies.has(value)) { | ||
return proxies.get(value); | ||
} | ||
const proxy2 = new Proxy(value, handler); | ||
proxies.set(value, proxy2); | ||
return proxy2; | ||
} | ||
if (typeof value === "function") { | ||
return value; | ||
} | ||
return value; | ||
} | ||
if (config.debug) { | ||
console.debug(`Accessing undefined property: ${String(prop)}`); | ||
} | ||
if (config.strict) { | ||
throw new Error(`Property ${String(prop)} is not defined`); | ||
} | ||
if (config.allowUndefined || !shouldBeObject(target, prop)) { | ||
return void 0; | ||
} | ||
const emptyObj = {}; | ||
const proxy = new Proxy(emptyObj, handler); | ||
Object.defineProperty(target, prop, { | ||
value: emptyObj, | ||
writable: true, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
proxies.set(emptyObj, proxy); | ||
return proxy; | ||
}, | ||
getPrototypeOf: (target) => { | ||
return config.preservePrototype ? Object.getPrototypeOf(target) : Object.prototype; | ||
}, | ||
ownKeys: (target) => { | ||
return Reflect.ownKeys(target); | ||
}, | ||
getOwnPropertyDescriptor: (target, prop) => { | ||
return Reflect.getOwnPropertyDescriptor(target, prop); | ||
} | ||
}; | ||
try { | ||
const proxy = new Proxy(partial, handler); | ||
if (partial && typeof partial === "object") { | ||
proxies.set(partial, proxy); | ||
} | ||
return proxy; | ||
} catch (error) { | ||
if (config.debug) { | ||
console.error("Cast operation failed:", error); | ||
} | ||
throw error; | ||
} | ||
} | ||
// src/errors.ts | ||
@@ -327,7 +497,7 @@ var MockError = class _MockError extends Error { | ||
}; | ||
spy.mockImplementation = (fn) => { | ||
if (fn !== null && fn !== void 0 && typeof fn !== "function") { | ||
spy.mockImplementation = (fn2) => { | ||
if (fn2 !== null && fn2 !== void 0 && typeof fn2 !== "function") { | ||
throw new Error("Mock implementation must be a function"); | ||
} | ||
this.updateState({ implementation: fn }); | ||
this.updateState({ implementation: fn2 }); | ||
return spy; | ||
@@ -421,4 +591,668 @@ }; | ||
export { createPropertySpy, createSpy }; | ||
// src/utils/method-spy.ts | ||
function createMethodSpy(method, context, options = {}) { | ||
const spy = new UniversalSpy(); | ||
const spyFn = spy.getSpy(); | ||
spyFn.mockImplementation(function(...args) { | ||
const result = method.apply(context || this, args); | ||
return result; | ||
}); | ||
return spyFn; | ||
} | ||
function createPropertyMethodSpy(getter, setter, context, options = {}) { | ||
return { | ||
get: getter ? createMethodSpy(getter, context, options) : void 0, | ||
set: setter ? createMethodSpy(setter, context, options) : void 0 | ||
}; | ||
} | ||
// src/mocks/class.ts | ||
function handleStaticMethodInheritance(mockClass, originalClass, options = {}) { | ||
let currentProto = Object.getPrototypeOf(originalClass); | ||
while (currentProto && currentProto !== Function.prototype) { | ||
Object.getOwnPropertyNames(currentProto).filter((prop) => typeof currentProto[prop] === "function").forEach((methodName) => { | ||
if (!mockClass[methodName]) { | ||
const method = currentProto[methodName].bind(mockClass); | ||
mockClass[methodName] = createMethodSpy(method, mockClass, options); | ||
} | ||
}); | ||
currentProto = Object.getPrototypeOf(currentProto); | ||
} | ||
} | ||
function cls(target, options = {}) { | ||
const { overrides: implementation = {}, debug, trackCalls } = options; | ||
if (debug) { | ||
console.debug("Creating class mock for:", target.name); | ||
} | ||
const handleDescriptor = (name, descriptor, context) => { | ||
if (!descriptor) { | ||
return { | ||
value: void 0, | ||
writable: true, | ||
configurable: true, | ||
enumerable: true | ||
}; | ||
} | ||
if (descriptor.get || descriptor.set) { | ||
const implDescriptor = typeof name === "string" ? Object.getOwnPropertyDescriptor(implementation, name) : void 0; | ||
const spies = createPropertyMethodSpy( | ||
implDescriptor?.get || descriptor.get, | ||
implDescriptor?.set || descriptor.set, | ||
context, | ||
{ debug } | ||
); | ||
return { | ||
configurable: true, | ||
enumerable: true, | ||
get: spies.get || descriptor.get?.bind(context), | ||
set: spies.set || descriptor.set?.bind(context) | ||
}; | ||
} | ||
if (typeof descriptor.value === "function") { | ||
const impl = typeof name === "string" ? implementation[name] : void 0; | ||
if (impl) { | ||
const spy = createMethodSpy(impl, context, { debug }); | ||
return { ...descriptor, value: spy }; | ||
} | ||
return { | ||
...descriptor, | ||
value: createMethodSpy(descriptor.value, context, { debug }) | ||
}; | ||
} | ||
return { | ||
...descriptor, | ||
value: descriptor.value | ||
}; | ||
}; | ||
const MockClass = function(...args) { | ||
if (!(this instanceof MockClass)) { | ||
return new MockClass(...args); | ||
} | ||
const instance = Object.create(target.prototype); | ||
try { | ||
const temp = new target(...args); | ||
Object.assign(instance, temp); | ||
} catch { | ||
} | ||
const processMembers = (target2, source) => { | ||
Object.getOwnPropertyNames(source).forEach((name) => { | ||
if (name === "constructor") return; | ||
if (!target2.hasOwnProperty(name)) { | ||
const descriptor = Object.getOwnPropertyDescriptor(source, name); | ||
Object.defineProperty(target2, name, handleDescriptor(name, descriptor, instance)); | ||
} | ||
}); | ||
}; | ||
processMembers(instance, target.prototype); | ||
let proto = Object.getPrototypeOf(target.prototype); | ||
while (proto && proto !== Object.prototype) { | ||
processMembers(instance, proto); | ||
proto = Object.getPrototypeOf(proto); | ||
} | ||
Object.setPrototypeOf(instance, MockClass.prototype); | ||
return instance; | ||
}; | ||
MockClass.prototype = Object.create(target.prototype); | ||
MockClass.prototype.constructor = MockClass; | ||
Object.getOwnPropertyNames(target).forEach((name) => { | ||
if (name === "length" || name === "prototype" || name === "name") return; | ||
const descriptor = Object.getOwnPropertyDescriptor(target, name); | ||
if (descriptor) { | ||
Object.defineProperty(MockClass, name, handleDescriptor(name, descriptor, target)); | ||
} | ||
}); | ||
handleStaticMethodInheritance(MockClass, target, { debug }); | ||
return MockClass; | ||
} | ||
// src/mocks/function.ts | ||
function fn() { | ||
const spy = createSpy(); | ||
const mockFn = spy; | ||
mockFn.mockReturnValue = function(value) { | ||
mockFn.mockImplementation(() => value); | ||
return mockFn; | ||
}; | ||
mockFn.mockImplementation = function(fn2) { | ||
spy.and.callFake(fn2); | ||
return mockFn; | ||
}; | ||
mockFn.mockClear = function() { | ||
spy.calls = { | ||
all: () => [], | ||
count: () => 0 | ||
}; | ||
}; | ||
mockFn.mockReset = function() { | ||
mockFn.mockClear(); | ||
spy.and.stub(); | ||
}; | ||
mockFn.mockRestore = function() { | ||
mockFn.mockReset(); | ||
}; | ||
return mockFn; | ||
} | ||
// src/mocks/object.ts | ||
function obj(target, options = {}) { | ||
const processedProperties = /* @__PURE__ */ new Map(); | ||
const originalImplementations = /* @__PURE__ */ new Map(); | ||
const processedObjects = /* @__PURE__ */ new WeakSet(); | ||
const mock3 = {}; | ||
const createSpyWithTracking = (fn2, context) => { | ||
const spy = createSpy(fn2); | ||
if (fn2) { | ||
spy.mockImplementation(function(...args) { | ||
return fn2.apply(this === spy ? context : this, args); | ||
}); | ||
} | ||
return spy; | ||
}; | ||
const processValue = (value, key) => { | ||
if (typeof value === "function") { | ||
const spy = createSpyWithTracking(value, mock3); | ||
originalImplementations.set(key, value); | ||
return spy; | ||
} else if (value && typeof value === "object" && !Array.isArray(value)) { | ||
if (processedObjects.has(value)) { | ||
return processedProperties.get(key) || value; | ||
} | ||
processedObjects.add(value); | ||
const nestedMock = {}; | ||
Object.entries(value).forEach(([k, v]) => { | ||
if (typeof v === "function") { | ||
const spy = createSpyWithTracking(v, nestedMock); | ||
nestedMock[k] = spy; | ||
processedProperties.set(`${String(key)}.${k}`, spy); | ||
originalImplementations.set(`${String(key)}.${k}`, v); | ||
} else if (v && typeof v === "object" && !Array.isArray(v)) { | ||
nestedMock[k] = processValue(v, `${String(key)}.${k}`); | ||
} else { | ||
nestedMock[k] = v; | ||
} | ||
}); | ||
if (options.overrides && typeof options.overrides === "object") { | ||
const mockOverrides = options.overrides[key]; | ||
if (mockOverrides && typeof mockOverrides === "object") { | ||
Object.entries(mockOverrides).forEach(([k, v]) => { | ||
if (typeof v === "function") { | ||
if (typeof nestedMock[k] === "function") { | ||
nestedMock[k].mockImplementation(v.bind(nestedMock)); | ||
} else { | ||
nestedMock[k] = createSpyWithTracking(v, nestedMock); | ||
processedProperties.set(`${String(key)}.${k}`, nestedMock[k]); | ||
originalImplementations.set(`${String(key)}.${k}`, v); | ||
} | ||
} else if (v && typeof v === "object" && !Array.isArray(v)) { | ||
nestedMock[k] = processValue(v, `${String(key)}.${k}`); | ||
} else { | ||
nestedMock[k] = v; | ||
} | ||
}); | ||
} | ||
} | ||
Object.assign(nestedMock, utilityMethods); | ||
processedProperties.set(key, nestedMock); | ||
return nestedMock; | ||
} | ||
return value; | ||
}; | ||
const getPropertyDescriptor = (obj2, prop) => { | ||
let current = obj2; | ||
while (current) { | ||
const descriptor = Object.getOwnPropertyDescriptor(current, prop); | ||
if (descriptor) return descriptor; | ||
current = Object.getPrototypeOf(current); | ||
} | ||
return void 0; | ||
}; | ||
const utilityMethods = { | ||
mockClear: function() { | ||
processedProperties.forEach((value) => { | ||
if (typeof value === "function" && value.mockClear) { | ||
value.mockClear(); | ||
} else if (value && typeof value === "object" && value.mockClear) { | ||
value.mockClear(); | ||
} | ||
}); | ||
}, | ||
mockReset: function() { | ||
processedProperties.forEach((value) => { | ||
if (typeof value === "function" && value.mockReset) { | ||
value.mockReset(); | ||
} else if (value && typeof value === "object" && value.mockReset) { | ||
value.mockReset(); | ||
} | ||
}); | ||
}, | ||
mockRestore: function() { | ||
processedProperties.forEach((value, key) => { | ||
if (typeof value === "function") { | ||
if (originalImplementations.has(key)) { | ||
const original = originalImplementations.get(key); | ||
value.mockImplementation(original); | ||
} | ||
} else if (value && typeof value === "object" && value.mockRestore) { | ||
value.mockRestore(); | ||
} | ||
}); | ||
} | ||
}; | ||
if (target) { | ||
Object.entries(target).forEach(([key, value]) => { | ||
if (typeof value === "function") { | ||
processedProperties.set(key, processValue(value, key)); | ||
} else if (value && typeof value === "object" && !Array.isArray(value)) { | ||
mock3[key] = processValue(value, key); | ||
} else { | ||
mock3[key] = value; | ||
} | ||
}); | ||
let proto = Object.getPrototypeOf(target); | ||
while (proto && proto !== Object.prototype) { | ||
Object.entries(Object.getOwnPropertyDescriptors(proto)).forEach(([key, descriptor]) => { | ||
if (!processedProperties.has(key)) { | ||
if (descriptor.value && typeof descriptor.value === "function") { | ||
processedProperties.set(key, processValue(descriptor.value, key)); | ||
} else if (descriptor.get || descriptor.set) { | ||
Object.defineProperty(mock3, key, { | ||
get: descriptor.get?.bind(mock3), | ||
set: descriptor.set?.bind(mock3), | ||
enumerable: descriptor.enumerable, | ||
configurable: true | ||
}); | ||
} | ||
} | ||
}); | ||
proto = Object.getPrototypeOf(proto); | ||
} | ||
} | ||
if (options.overrides && typeof options.overrides === "object") { | ||
Object.entries(options.overrides).forEach(([key, value]) => { | ||
const current = processedProperties.get(key); | ||
if (typeof value === "function" && typeof current === "function" && current.mockImplementation) { | ||
current.mockImplementation(value.bind(mock3)); | ||
} else if (value && typeof value === "object" && !Array.isArray(value)) { | ||
mock3[key] = processValue(value, key); | ||
} else { | ||
processedProperties.set(key, processValue(value, key)); | ||
} | ||
}); | ||
} | ||
const handler = { | ||
get(target2, prop) { | ||
if (prop in utilityMethods) { | ||
return utilityMethods[prop]; | ||
} | ||
if (processedProperties.has(prop)) { | ||
return processedProperties.get(prop); | ||
} | ||
if (prop in mock3) { | ||
return mock3[prop]; | ||
} | ||
if (target2) { | ||
const descriptor = getPropertyDescriptor(target2, prop); | ||
if (descriptor) { | ||
if (typeof descriptor.value === "function") { | ||
const spy2 = processValue(descriptor.value, prop); | ||
processedProperties.set(prop, spy2); | ||
return spy2; | ||
} else if (descriptor.get) { | ||
return descriptor.get.call(mock3); | ||
} | ||
} | ||
} | ||
if (!options.allowUndefined) { | ||
throw new Error(`Property ${String(prop)} is not defined in the original implementation`); | ||
} | ||
const spy = createSpyWithTracking(); | ||
processedProperties.set(prop, spy); | ||
return spy; | ||
}, | ||
set(target2, prop, value) { | ||
if (options.strict && !(prop in mock3) && !processedProperties.has(prop)) { | ||
throw new Error(`Cannot add property ${String(prop)} in strict mode`); | ||
} | ||
if (typeof value === "function") { | ||
const spy = processValue(value, prop); | ||
processedProperties.set(prop, spy); | ||
mock3[prop] = spy; | ||
} else { | ||
mock3[prop] = value; | ||
} | ||
return true; | ||
}, | ||
has(target2, prop) { | ||
return prop in mock3 || processedProperties.has(prop); | ||
}, | ||
ownKeys() { | ||
return [.../* @__PURE__ */ new Set([...Object.keys(mock3), ...processedProperties.keys()])]; | ||
}, | ||
getOwnPropertyDescriptor(target2, prop) { | ||
if (prop in mock3) { | ||
return Object.getOwnPropertyDescriptor(mock3, prop) || { | ||
value: mock3[prop], | ||
writable: true, | ||
enumerable: true, | ||
configurable: true | ||
}; | ||
} | ||
if (processedProperties.has(prop)) { | ||
return { | ||
value: processedProperties.get(prop), | ||
writable: true, | ||
enumerable: true, | ||
configurable: true | ||
}; | ||
} | ||
return { | ||
value: void 0, | ||
writable: true, | ||
enumerable: true, | ||
configurable: true | ||
}; | ||
} | ||
}; | ||
return new Proxy(target || {}, handler); | ||
} | ||
// src/constants.ts | ||
var ORIGINAL_PREFIX = "__original__"; | ||
var EXCLUDED_PROPS = ["name", "length", "prototype", "_isMockFunction"]; | ||
// src/mocks/replace.ts | ||
function replace(obj2, key, impl, options = {}) { | ||
const config = { | ||
allowUndefined: options.allowUndefined ?? false, | ||
strict: options.strict ?? false, | ||
trackCalls: options.trackCalls ?? false, | ||
debug: options.debug ?? false, | ||
preservePrototype: options.preservePrototype ?? true, | ||
timeout: options.timeout ?? 0 | ||
}; | ||
if (!(key in obj2)) { | ||
throw new Error(`Method ${String(key)} does not exist on object`); | ||
} | ||
const original = obj2[key]; | ||
if (typeof original !== "function") { | ||
throw new Error(`Property ${String(key)} is not a function`); | ||
} | ||
if (isReplaced(obj2, key)) { | ||
throw new Error(`Method ${String(key)} is already replaced`); | ||
} | ||
if (config.debug) { | ||
console.debug(`Replacing method ${String(key)} on`, obj2); | ||
} | ||
const originalKey = `${ORIGINAL_PREFIX}${String(key)}`; | ||
Object.defineProperty(obj2, originalKey, { | ||
value: original, | ||
writable: false, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
const spy = createSpy(); | ||
spy.mockImplementation(impl); | ||
if (config.trackCalls) { | ||
spy.mock.calls = []; | ||
spy.mock.results = []; | ||
spy.mock.instances = []; | ||
spy.mock.contexts = []; | ||
} | ||
if (typeof original === "function") { | ||
Object.getOwnPropertyNames(original).forEach((prop) => { | ||
if (!EXCLUDED_PROPS.includes(prop)) { | ||
try { | ||
spy[prop] = original[prop]; | ||
} catch (e) { | ||
if (config.debug) { | ||
console.debug(`Failed to copy property ${prop}:`, e); | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
obj2[key] = spy; | ||
} | ||
function isReplaced(obj2, key) { | ||
const originalKey = `${ORIGINAL_PREFIX}${String(key)}`; | ||
return originalKey in obj2; | ||
} | ||
// src/mocks/compose.ts | ||
function compose(target, options = {}) { | ||
if (target === void 0) { | ||
return fn(); | ||
} | ||
if (typeof target === "function" && "prototype" in target) { | ||
const classTarget = target; | ||
return cls(classTarget, options); | ||
} | ||
const mockObj = obj(target, options); | ||
if (options.replace) { | ||
for (const [key, impl] of Object.entries(options.replace)) { | ||
if (!(key in mockObj)) { | ||
throw new Error(`Cannot replace non-existent method: ${key}`); | ||
} | ||
if (typeof impl !== "function") { | ||
throw new Error(`Replacement for method ${key} must be a function`); | ||
} | ||
replace(mockObj, key, impl); | ||
} | ||
} | ||
return mockObj; | ||
} | ||
// src/registry.ts | ||
var MockRegistry = class { | ||
constructor() { | ||
this.mockRegistry = /* @__PURE__ */ new WeakMap(); | ||
} | ||
/** | ||
* Registers a mock in the registry | ||
*/ | ||
registerMock(mock3, type, target, options) { | ||
this.mockRegistry.set(mock3, { type, target, mock: mock3, options }); | ||
} | ||
/** | ||
* Gets all registered mocks | ||
*/ | ||
getAllMocks() { | ||
const mocks = []; | ||
return mocks; | ||
} | ||
/** | ||
* Gets a list of method names that have been replaced on the object | ||
*/ | ||
getReplacedMethods(obj2) { | ||
if (!obj2) return []; | ||
return Object.getOwnPropertyNames(obj2).filter((key) => key.startsWith(ORIGINAL_PREFIX)).map((key) => key.slice(ORIGINAL_PREFIX.length)); | ||
} | ||
/** | ||
* Checks if a specific method has been replaced on the object | ||
*/ | ||
isReplaced(obj2, method) { | ||
if (!obj2) return false; | ||
const originalKey = `${ORIGINAL_PREFIX}${String(method)}`; | ||
return originalKey in obj2; | ||
} | ||
/** | ||
* Verifies if a method has been restored to its original state | ||
*/ | ||
verifyRestored(obj2, method) { | ||
if (!obj2) return false; | ||
const originalKey = `${ORIGINAL_PREFIX}${String(method)}`; | ||
return !(originalKey in obj2); | ||
} | ||
/** | ||
* Gets mock info for a given mock | ||
*/ | ||
getMockInfo(mock3) { | ||
return this.mockRegistry.get(mock3); | ||
} | ||
/** | ||
* Restores a specific mock function to its original state | ||
*/ | ||
restoreMockFunction(mockFn) { | ||
if (!mockFn._isMockFunction) return false; | ||
const mockInfo = this.getMockInfo(mockFn); | ||
const preserveImplementation = mockInfo?.options?.preserveImplementation; | ||
if (!preserveImplementation) { | ||
mockFn.mockImplementation(void 0); | ||
} | ||
return true; | ||
} | ||
/** | ||
* Restores replaced methods on an object | ||
*/ | ||
restoreReplacedMethods(obj2) { | ||
const replacedMethods = this.getReplacedMethods(obj2); | ||
if (replacedMethods.length === 0) return false; | ||
replacedMethods.forEach((key) => { | ||
const originalKey = `${ORIGINAL_PREFIX}${String(key)}`; | ||
const original = obj2[originalKey]; | ||
obj2[key] = original; | ||
delete obj2[originalKey]; | ||
}); | ||
return true; | ||
} | ||
/** | ||
* Restores a specific object's mocked methods to their original state | ||
*/ | ||
restore(obj2) { | ||
if (!obj2) return false; | ||
const mockInfo = this.getMockInfo(obj2); | ||
if (mockInfo) { | ||
if (mockInfo.type === "function") { | ||
return this.restoreMockFunction(obj2); | ||
} | ||
return this.restoreReplacedMethods(obj2); | ||
} | ||
return this.restoreReplacedMethods(obj2); | ||
} | ||
/** | ||
* Restores all mocks to their original state | ||
*/ | ||
restoreAll() { | ||
} | ||
/** | ||
* Resets all mock call history and implementations | ||
*/ | ||
resetAll() { | ||
} | ||
/** | ||
* Clears all mock call history | ||
*/ | ||
clearAll() { | ||
} | ||
/** | ||
* Verifies all mocks have been called as expected | ||
*/ | ||
verifyAll() { | ||
throw new Error("verifyAll is not supported with WeakMap-based registry"); | ||
} | ||
/** | ||
* Verifies if a specific mock has been called | ||
*/ | ||
verify(mock3) { | ||
const mockInfo = this.getMockInfo(mock3); | ||
if (!mockInfo || mockInfo.type !== "function") return true; | ||
const mockFn = mock3; | ||
return mockFn.mock.calls.length > 0; | ||
} | ||
}; | ||
// src/mock.ts | ||
var MockImpl = class extends MockRegistry { | ||
constructor(config = {}) { | ||
super(); | ||
this.currentConfig = { ...DEFAULT_CONFIG, ...config }; | ||
} | ||
configure(config) { | ||
if (!config) { | ||
return { ...this.currentConfig }; | ||
} | ||
Object.assign(this.currentConfig, config); | ||
} | ||
/** | ||
* Create a mock function with tracking capabilities | ||
* @param options Optional configuration overrides | ||
* @returns Mocked function with spy features | ||
*/ | ||
fn(options) { | ||
const mockFn = fn(); | ||
this.registerMock(mockFn, "function"); | ||
return mockFn; | ||
} | ||
/** | ||
* Create a mock object with tracking capabilities | ||
* @param target Original object to mock | ||
* @param options Mock configuration and implementation | ||
* @returns Mocked object with spy features | ||
*/ | ||
obj(target, options = {}) { | ||
const mockObj = obj(target, { ...this.currentConfig, ...options }); | ||
this.registerMock(mockObj, "object", target); | ||
return mockObj; | ||
} | ||
/** | ||
* Create a mock class with tracking capabilities | ||
* @param target Original class to mock | ||
* @param options Mock configuration and implementation | ||
* @returns Mocked class constructor | ||
*/ | ||
cls(target, options) { | ||
const mockCls = cls(target, { ...this.currentConfig, ...options }); | ||
this.registerMock(mockCls, "class", target); | ||
return mockCls; | ||
} | ||
/** | ||
* Create a mock from partial implementation | ||
* @param partial Partial implementation to mock | ||
* @param options Mock configuration | ||
* @returns Complete mock object | ||
*/ | ||
cast(partial, options) { | ||
const mockObj = cast(partial, { ...this.currentConfig, ...options }); | ||
this.registerMock(mockObj, "object", partial); | ||
return mockObj; | ||
} | ||
compose(target, options) { | ||
const mockObj = compose(target, { ...this.currentConfig, ...options }); | ||
this.registerMock(mockObj, typeof target === "function" ? "class" : "object", target); | ||
return mockObj; | ||
} | ||
/** | ||
* Replace a method with mock implementation | ||
* @param obj Target object | ||
* @param key Method key to replace | ||
* @param impl Mock implementation | ||
* @param options Mock configuration | ||
*/ | ||
replace(obj2, key, impl, options) { | ||
replace(obj2, key, impl, { ...this.currentConfig, ...options }); | ||
} | ||
}; | ||
var globalMock = new MockImpl(); | ||
var mock = Object.assign( | ||
function mock2(config) { | ||
return new MockImpl(config); | ||
}, | ||
{ | ||
fn: (options) => globalMock.fn(options), | ||
obj: (target, options = {}) => globalMock.obj(target, options), | ||
cls: (target, options) => globalMock.cls(target, options), | ||
cast: (partial, options) => globalMock.cast(partial, options), | ||
replace: (obj2, key, impl, options) => globalMock.replace(obj2, key, impl, options), | ||
compose: (target, options) => globalMock.compose(target, options), | ||
configure: function(config) { | ||
if (!config) { | ||
return globalMock.configure(); | ||
} | ||
globalMock.configure(config); | ||
} | ||
} | ||
); | ||
export { DEFAULT_CONFIG, cast, cls, compose, createPropertySpy, createSpy, fn, mock, obj, replace }; | ||
//# sourceMappingURL=index.js.map | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@corez/mock", | ||
"version": "0.2.4", | ||
"version": "0.2.5", | ||
"description": "A powerful and flexible TypeScript mocking library for testing", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -0,2 +1,36 @@ | ||
// Core functionality | ||
export {mock} from './mock'; | ||
export {createPropertySpy, createSpy} from './spy'; | ||
export type {MockFunction} from './types'; | ||
// Configuration | ||
export type {Config} from './config'; | ||
export {DEFAULT_CONFIG} from './config'; | ||
// Types | ||
export type { | ||
Adapter, | ||
AsyncFn, | ||
ClsMock, | ||
ClsMockOptions, | ||
Constructor, | ||
DeepMock, | ||
DeepPartial, | ||
Fn, | ||
Framework, | ||
Matcher, | ||
MatcherFactory, | ||
Mock, | ||
MockClassOptions, | ||
MockFunction, | ||
MockObject, | ||
MockObjectOptions, | ||
MockOf, | ||
ObjMockOptions, | ||
PartialBuilder, | ||
PartialOptions, | ||
SpyAdapter, | ||
StaticMockOf, | ||
} from './types'; | ||
// Utility functions | ||
export {cast, cls, compose, fn, obj, replace} from './mocks'; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
536228
8377
0