Skip to content

Commit

Permalink
fix(mock): allow to mock methods in getters (#10156)
Browse files Browse the repository at this point in the history
  • Loading branch information
SomaticIT committed Oct 19, 2020
1 parent 194701f commit 30e8020
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 10 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,8 @@

### Fixes

- `[jest-mock]` Allow to mock methods in getters (TypeScript 3.9 export)

### Chore & Maintenance

### Performance
Expand Down
40 changes: 40 additions & 0 deletions packages/jest-mock/src/__tests__/index.test.ts
Expand Up @@ -1176,6 +1176,46 @@ describe('moduleMocker', () => {
expect(spy1.mock.calls.length).toBe(1);
expect(spy2.mock.calls.length).toBe(1);
});

it('should work with getters', () => {
let isOriginalCalled = false;
let originalCallThis;
let originalCallArguments;
const obj = {
get method() {
return function () {
isOriginalCalled = true;
originalCallThis = this;
originalCallArguments = arguments;
};
},
};

const spy = moduleMocker.spyOn(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.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();
});
});

describe('spyOnProperty', () => {
Expand Down
40 changes: 30 additions & 10 deletions packages/jest-mock/src/index.ts
Expand Up @@ -985,17 +985,37 @@ class ModuleMockerClass {

const isMethodOwner = object.hasOwnProperty(methodName);

// @ts-expect-error overriding original method with a Mock
object[methodName] = this._makeComponent({type: 'function'}, () => {
if (isMethodOwner) {
object[methodName] = original;
} else {
delete object[methodName];
}
});
let descriptor = Object.getOwnPropertyDescriptor(object, methodName);
let proto = Object.getPrototypeOf(object);

while (!descriptor && proto !== null) {
descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
proto = Object.getPrototypeOf(proto);
}

let mock: JestMock.Mock<unknown, Array<unknown>>;

if (descriptor && descriptor.get) {
const originalGet = descriptor.get;
mock = this._makeComponent({type: 'function'}, () => {
descriptor!.get = originalGet;
Object.defineProperty(object, methodName, descriptor!);
});
descriptor.get = () => mock;
Object.defineProperty(object, methodName, descriptor);
} else {
mock = this._makeComponent({type: 'function'}, () => {
if (isMethodOwner) {
object[methodName] = original;
} else {
delete object[methodName];
}
});
// @ts-expect-error overriding original method with a Mock
object[methodName] = mock;
}

// @ts-expect-error original method is now a Mock
object[methodName].mockImplementation(function (this: unknown) {
mock.mockImplementation(function (this: unknown) {
return original.apply(this, arguments);
});
}
Expand Down

0 comments on commit 30e8020

Please sign in to comment.