From 2116afa6c7f3707098505937318aad4992ef1145 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Thu, 4 Jul 2024 05:46:25 +0200 Subject: [PATCH] Merge pull request #28445 from storybookjs/kasper/reactive-this Test: Reactive spies preserve the this instance (cherry picked from commit 74e3aa9ee5faf21ac73a557bd3058c1d55e55e66) --- code/lib/test/src/spy.test.ts | 26 +++++++++++++++++++++++++- code/lib/test/src/spy.ts | 6 +++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/code/lib/test/src/spy.test.ts b/code/lib/test/src/spy.test.ts index 5c3dcf13d924..1a59a0e114c5 100644 --- a/code/lib/test/src/spy.test.ts +++ b/code/lib/test/src/spy.test.ts @@ -1,5 +1,5 @@ import { it, vi, expect, beforeEach } from 'vitest'; -import { fn, onMockCall } from './spy'; +import { fn, onMockCall, spyOn } from './spy'; const vitestSpy = vi.fn(); @@ -13,3 +13,27 @@ it('mocks are reactive', () => { storybookSpy(1); expect(vitestSpy).toHaveBeenCalledWith(storybookSpy, [1]); }); + +class Foo { + bar = 'bar'; + + transform(postfix: string) { + return this.bar + postfix; + } +} +const foo = new Foo(); + +it('this is correctly binded when making spies reactive', () => { + const storybookSpy = spyOn(foo, 'transform'); + expect(foo.transform('!')).toEqual('bar!'); + expect(vitestSpy).toHaveBeenCalledWith(storybookSpy, ['!']); +}); + +it('this is correctly binded after mock implementation', () => { + const storybookSpy = spyOn(foo, 'transform').mockImplementation(function (this: Foo) { + return this.bar + 'mocked'; + }); + + expect(foo.transform('!')).toEqual('barmocked'); + expect(vitestSpy).toHaveBeenCalledWith(storybookSpy, ['!']); +}); diff --git a/code/lib/test/src/spy.ts b/code/lib/test/src/spy.ts index 6a97a19c390e..53c95c99d9c4 100644 --- a/code/lib/test/src/spy.ts +++ b/code/lib/test/src/spy.ts @@ -46,10 +46,10 @@ function reactiveMock(mock: MockInstance) { function listenWhenCalled(mock: MockInstance) { const state = tinyspy.getInternalState(mock as unknown as SpyInternalImpl); - const impl = state.impl?.bind(null); - state.willCall((...args) => { + const impl = state.impl; + state.willCall(function (this: unknown, ...args) { listeners.forEach((listener) => listener(mock, args)); - return impl?.(...args); + return impl?.apply(this, args); }); return mock; }