|
import * as tinyspy from 'tinyspy'; |
|
|
|
const mocks = new Set(); |
|
function isMockFunction(fn2) { |
|
return typeof fn2 === "function" && "_isMockFunction" in fn2 && fn2._isMockFunction; |
|
} |
|
function spyOn(obj, method, accessType) { |
|
const dictionary = { |
|
get: "getter", |
|
set: "setter" |
|
}; |
|
const objMethod = accessType ? { [dictionary[accessType]]: method } : method; |
|
const stub = tinyspy.internalSpyOn(obj, objMethod); |
|
return enhanceSpy(stub); |
|
} |
|
let callOrder = 0; |
|
function enhanceSpy(spy) { |
|
const stub = spy; |
|
let implementation; |
|
let instances = []; |
|
let invocations = []; |
|
const state = tinyspy.getInternalState(spy); |
|
const mockContext = { |
|
get calls() { |
|
return state.calls; |
|
}, |
|
get instances() { |
|
return instances; |
|
}, |
|
get invocationCallOrder() { |
|
return invocations; |
|
}, |
|
get results() { |
|
return state.results.map(([callType, value]) => { |
|
const type = callType === "error" ? "throw" : "return"; |
|
return { type, value }; |
|
}); |
|
}, |
|
get lastCall() { |
|
return state.calls[state.calls.length - 1]; |
|
} |
|
}; |
|
let onceImplementations = []; |
|
let implementationChangedTemporarily = false; |
|
function mockCall(...args) { |
|
instances.push(this); |
|
invocations.push(++callOrder); |
|
const impl = implementationChangedTemporarily ? implementation : onceImplementations.shift() || implementation || state.getOriginal() || (() => { |
|
}); |
|
return impl.apply(this, args); |
|
} |
|
let name = stub.name; |
|
stub.getMockName = () => name || "vi.fn()"; |
|
stub.mockName = (n) => { |
|
name = n; |
|
return stub; |
|
}; |
|
stub.mockClear = () => { |
|
state.reset(); |
|
instances = []; |
|
invocations = []; |
|
return stub; |
|
}; |
|
stub.mockReset = () => { |
|
stub.mockClear(); |
|
implementation = () => void 0; |
|
onceImplementations = []; |
|
return stub; |
|
}; |
|
stub.mockRestore = () => { |
|
stub.mockReset(); |
|
state.restore(); |
|
implementation = void 0; |
|
return stub; |
|
}; |
|
stub.getMockImplementation = () => implementation; |
|
stub.mockImplementation = (fn2) => { |
|
implementation = fn2; |
|
state.willCall(mockCall); |
|
return stub; |
|
}; |
|
stub.mockImplementationOnce = (fn2) => { |
|
onceImplementations.push(fn2); |
|
return stub; |
|
}; |
|
function withImplementation(fn2, cb) { |
|
const originalImplementation = implementation; |
|
implementation = fn2; |
|
state.willCall(mockCall); |
|
implementationChangedTemporarily = true; |
|
const reset = () => { |
|
implementation = originalImplementation; |
|
implementationChangedTemporarily = false; |
|
}; |
|
const result = cb(); |
|
if (result instanceof Promise) { |
|
return result.then(() => { |
|
reset(); |
|
return stub; |
|
}); |
|
} |
|
reset(); |
|
return stub; |
|
} |
|
stub.withImplementation = withImplementation; |
|
stub.mockReturnThis = () => stub.mockImplementation(function() { |
|
return this; |
|
}); |
|
stub.mockReturnValue = (val) => stub.mockImplementation(() => val); |
|
stub.mockReturnValueOnce = (val) => stub.mockImplementationOnce(() => val); |
|
stub.mockResolvedValue = (val) => stub.mockImplementation(() => Promise.resolve(val)); |
|
stub.mockResolvedValueOnce = (val) => stub.mockImplementationOnce(() => Promise.resolve(val)); |
|
stub.mockRejectedValue = (val) => stub.mockImplementation(() => Promise.reject(val)); |
|
stub.mockRejectedValueOnce = (val) => stub.mockImplementationOnce(() => Promise.reject(val)); |
|
Object.defineProperty(stub, "mock", { |
|
get: () => mockContext |
|
}); |
|
state.willCall(mockCall); |
|
mocks.add(stub); |
|
return stub; |
|
} |
|
function fn(implementation) { |
|
const enhancedSpy = enhanceSpy(tinyspy.internalSpyOn({ spy: implementation || (() => { |
|
}) }, "spy")); |
|
if (implementation) |
|
enhancedSpy.mockImplementation(implementation); |
|
return enhancedSpy; |
|
} |
|
|
|
export { fn, isMockFunction, mocks, spyOn }; |
|
|