diff --git a/packages/jest-mock/src/__tests__/jest_mock.test.js b/packages/jest-mock/src/__tests__/jest_mock.test.js index ee692bc7efc2..80668db7b7db 100644 --- a/packages/jest-mock/src/__tests__/jest_mock.test.js +++ b/packages/jest-mock/src/__tests__/jest_mock.test.js @@ -830,5 +830,117 @@ describe('moduleMocker', () => { expect(spy1.mock.calls.length).toBe(1); expect(spy2.mock.calls.length).toBe(1); }); + + it('should work with instance object - getter', () => { + let isOriginalCalled = false; + let originalCallThis; + let originalCallArguments; + class Constructor { + get method() { + return function() { + isOriginalCalled = true; + originalCallThis = this; + originalCallArguments = arguments; + }; + } + } + const obj = new Constructor(); + + const spy = moduleMocker.spyOn(obj, 'method', 'get'); + + 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(); + 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(); + }); + + test('should work with instance object - setter', () => { + class Constructor { + constructor() { + this._property = false; + } + + set property(value) { + this._property = value; + } + + get property() { + return this._property; + } + } + const obj = new Constructor(); + + const spy = moduleMocker.spyOn(obj, 'property', 'set'); + obj.property = true; + expect(spy).toHaveBeenCalled(); + expect(obj.property).toBe(true); + obj.property = false; + spy.mockReset(); + spy.mockRestore(); + obj.property = true; + expect(spy).not.toHaveBeenCalled(); + expect(obj.property).toBe(true); + }); + + it('supports restoring all spies on an instance object', () => { + let methodOneCalls = 0; + let methodTwoCalls = 0; + class Constructor { + get methodOne() { + return function() { + methodOneCalls++; + }; + } + + get methodTwo() { + return function() { + methodTwoCalls++; + }; + } + } + const obj = new Constructor(); + + const spy1 = moduleMocker.spyOn(obj, 'methodOne', 'get'); + const spy2 = moduleMocker.spyOn(obj, 'methodTwo', 'get'); + + // 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 b62d81fae5fa..3eac67373c44 100644 --- a/packages/jest-mock/src/index.js +++ b/packages/jest-mock/src/index.js @@ -724,7 +724,13 @@ class ModuleMockerClass { throw new Error('No property name supplied'); } - const descriptor = Object.getOwnPropertyDescriptor(obj, propertyName); + let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName); + let proto = Object.getPrototypeOf(obj); + + while (!descriptor && proto !== null) { + descriptor = Object.getOwnPropertyDescriptor(proto, propertyName); + proto = Object.getPrototypeOf(proto); + } if (!descriptor) { throw new Error(propertyName + ' property does not exist');