diff --git a/docs/api/mock.md b/docs/api/mock.md index d2e3c7f8854b..bbbc9893886e 100644 --- a/docs/api/mock.md +++ b/docs/api/mock.md @@ -357,7 +357,7 @@ fn.mock.settledResults === [ ## mock.invocationCallOrder -The order of mock's execution. This returns an array of numbers that are shared between all defined mocks. +This property returns the order of the mock function's execution. It is an array of numbers that are shared between all defined mocks. ```js const fn1 = vi.fn() @@ -371,9 +371,24 @@ fn1.mock.invocationCallOrder === [1, 3] fn2.mock.invocationCallOrder === [2] ``` +## mock.contexts + +This property is an array of `this` values used during each call to the mock function. + +```js +const fn = vi.fn() +const context = {} + +fn.apply(context) +fn.call(context) + +fn.mock.contexts[0] === context +fn.mock.contexts[1] === context +``` + ## mock.instances -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. +This property is an array containing all instances that were created when the mock was called with the `new` keyword. Note that this is an actual context (`this`) of the function, not a return value. ::: warning If mock was instantiated with `new MyClass()`, then `mock.instances` will be an array with one value: diff --git a/packages/spy/src/index.ts b/packages/spy/src/index.ts index 98e8c4acb800..029ac0de4f32 100644 --- a/packages/spy/src/index.ts +++ b/packages/spy/src/index.ts @@ -58,6 +58,10 @@ export interface MockContext { * 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. */ instances: ReturnType[] + /** + * An array of `this` values that were used during each call to the mock function. + */ + contexts: ThisParameterType[] /** * The order of mock's execution. This returns an array of numbers which are shared between all defined mocks. * @@ -431,6 +435,7 @@ function enhanceSpy( let implementation: T | undefined let instances: any[] = [] + let contexts: any[] = [] let invocations: number[] = [] const state = tinyspy.getInternalState(spy) @@ -439,6 +444,9 @@ function enhanceSpy( get calls() { return state.calls }, + get contexts() { + return contexts + }, get instances() { return instances }, @@ -469,6 +477,7 @@ function enhanceSpy( function mockCall(this: unknown, ...args: any) { instances.push(this) + contexts.push(this) invocations.push(++callOrder) const impl = implementationChangedTemporarily ? implementation! @@ -490,6 +499,7 @@ function enhanceSpy( stub.mockClear = () => { state.reset() instances = [] + contexts = [] invocations = [] return stub } @@ -584,7 +594,9 @@ function enhanceSpy( export function fn( implementation?: T, ): Mock { - const enhancedSpy = enhanceSpy(tinyspy.internalSpyOn({ spy: implementation || (() => {}) }, 'spy')) + const enhancedSpy = enhanceSpy(tinyspy.internalSpyOn({ + spy: implementation || function () {} as T, + }, 'spy')) if (implementation) { enhancedSpy.mockImplementation(implementation) } diff --git a/test/core/test/jest-mock.test.ts b/test/core/test/jest-mock.test.ts index 00b63d7b10d0..8c633fceaf8d 100644 --- a/test/core/test/jest-mock.test.ts +++ b/test/core/test/jest-mock.test.ts @@ -43,6 +43,35 @@ describe('jest mock compat layer', () => { expect(Spy.mock.instances).toHaveLength(0) }) + it('collects contexts', () => { + // eslint-disable-next-line prefer-arrow-callback + const Spy = vi.fn(function () {}) + + expect(Spy.mock.contexts).toHaveLength(0) + const ctx = new Spy() + expect(Spy.mock.contexts).toHaveLength(1) + expect(Spy.mock.contexts[0]).toBe(ctx) + + Spy.mockReset() + + expect(Spy.mock.contexts).toHaveLength(0) + + const ctx2 = {} + Spy.call(ctx2) + expect(Spy.mock.contexts).toHaveLength(1) + expect(Spy.mock.contexts[0]).toBe(ctx2) + + Spy.bind(ctx2)() + + expect(Spy.mock.contexts).toHaveLength(2) + expect(Spy.mock.contexts[1]).toBe(ctx2) + + Spy.apply(ctx2) + + expect(Spy.mock.contexts).toHaveLength(3) + expect(Spy.mock.contexts[2]).toBe(ctx2) + }) + it('implementation is set correctly on init', () => { const impl = () => 1 const mock1 = vi.fn(impl)