diff --git a/packages/jest-mock/src/__tests__/jest_mock.test.js b/packages/jest-mock/src/__tests__/jest_mock.test.js index e081c8ecbc7e..3be5440a597f 100644 --- a/packages/jest-mock/src/__tests__/jest_mock.test.js +++ b/packages/jest-mock/src/__tests__/jest_mock.test.js @@ -666,4 +666,96 @@ describe('moduleMocker', () => { expect(spy2.mock.calls.length).toBe(1); }); }); + + describe('spyOnProperty', () => { + it('should work', () => { + let isOriginalCalled = false; + let originalCallThis; + let originalCallArguments; + const obj = { + get method() { + return function () { + isOriginalCalled = true; + originalCallThis = this; + originalCallArguments = arguments; + } + }, + }; + + const spy = moduleMocker.spyOnProperty(obj, 'method'); + + const thisArg = {this: true}; + const firstArg = {first: true}; + const secondArg = {second: true}; + obj.method.call(thisArg, firstArg, secondArg); + expect(isOriginalCalled).toBe(true); + expect(originalCallThis).toBe(thisArg); + expect(originalCallArguments.length).toBe(2); + expect(originalCallArguments[0]).toBe(firstArg); + expect(originalCallArguments[1]).toBe(secondArg); + expect(spy).toHaveBeenCalled(); + + isOriginalCalled = false; + originalCallThis = null; + originalCallArguments = null; + spy.mockReset(); + spy.mockRestore(); + console.log('porcoddddddiooo', obj, obj.method) + obj.method.call(thisArg, firstArg, secondArg); + expect(isOriginalCalled).toBe(true); + expect(originalCallThis).toBe(thisArg); + expect(originalCallArguments.length).toBe(2); + expect(originalCallArguments[0]).toBe(firstArg); + expect(originalCallArguments[1]).toBe(secondArg); + expect(spy).not.toHaveBeenCalled(); + }); + + it('should throw on invalid input', () => { + expect(() => { + moduleMocker.spyOnProperty(null, 'method'); + }).toThrow(); + expect(() => { + moduleMocker.spyOnProperty({}, 'method'); + }).toThrow(); + expect(() => { + moduleMocker.spyOnProperty({method: 10}, 'method'); + }).toThrow(); + }); + + it('supports restoring all spies', () => { + let methodOneCalls = 0; + let methodTwoCalls = 0; + const obj = { + get methodOne() { + return function () {methodOneCalls++}; + }, + get methodTwo() { + return function () {methodTwoCalls++}; + }, + }; + + const spy1 = moduleMocker.spyOnProperty(obj, 'methodOne'); + const spy2 = moduleMocker.spyOnProperty(obj, 'methodTwo'); + + // First, we call with the spies: both spies and both original functions + // should be called. + obj.methodOne(); + obj.methodTwo(); + expect(methodOneCalls).toBe(1); + expect(methodTwoCalls).toBe(1); + expect(spy1.mock.calls.length).toBe(1); + expect(spy2.mock.calls.length).toBe(1); + + moduleMocker.restoreAllMocks(); + + // Then, after resetting all mocks, we call methods again. Only the real + // methods should bump their count, not the spies. + obj.methodOne(); + obj.methodTwo(); + expect(methodOneCalls).toBe(2); + expect(methodTwoCalls).toBe(2); + expect(spy1.mock.calls.length).toBe(1); + expect(spy2.mock.calls.length).toBe(1); + }); + }); }); diff --git a/packages/jest-mock/src/index.js b/packages/jest-mock/src/index.js index cc0f3d5a854e..2d6a6e2f1ce0 100644 --- a/packages/jest-mock/src/index.js +++ b/packages/jest-mock/src/index.js @@ -739,6 +739,7 @@ class ModuleMockerClass { descriptor[accessType] = this._makeComponent({type: 'function'}, () => { descriptor[accessType] = original; + Object.defineProperty(obj, propertyName, descriptor) }); descriptor[accessType].mockImplementation(function() { @@ -746,6 +747,7 @@ class ModuleMockerClass { }); } + Object.defineProperty(obj, propertyName, descriptor) return descriptor[accessType]; } diff --git a/packages/jest-runtime/src/__tests__/runtime_jest_spy_on.test.js b/packages/jest-runtime/src/__tests__/runtime_jest_spy_on.test.js index 2511e16cf8dd..c640eb6914e4 100644 --- a/packages/jest-runtime/src/__tests__/runtime_jest_spy_on.test.js +++ b/packages/jest-runtime/src/__tests__/runtime_jest_spy_on.test.js @@ -35,4 +35,25 @@ describe('Runtime', () => { expect(spy).toHaveBeenCalled(); })); }); + + describe('jest.spyOnProperty', () => { + it('calls the original function', () => + createRuntime(__filename).then(runtime => { + const root = runtime.requireModule(runtime.__mockRootPath); + + let isOriginalCalled = false; + const obj = { + get method() { + return () => isOriginalCalled = true; + }, + }; + + const spy = root.jest.spyOnProperty(obj, 'method'); + + obj.method(); + + expect(isOriginalCalled).toBe(true); + expect(spy).toHaveBeenCalled(); + })); + }); });