diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9a1c5e6f1d21..c71bf60be001 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,12 +7,13 @@
### Fixes
- `[jest-environment-node]` make `globalThis.performance` writable for Node 19 and fake timers ([#13467](https://github.com/facebook/jest/pull/13467))
+- `[jest-mock]` Revert [#13398](https://github.com/facebook/jest/pull/13398) to restore mocking of setters ([#13472](https://github.com/facebook/jest/pull/13472))
### Chore & Maintenance
### Performance
-- `[*]` Use sha1 instead of sha256 for hashing [#13421](https://github.com/facebook/jest/pull/13421)
+- `[*]` Use sha1 instead of sha256 for hashing ([#13421](https://github.com/facebook/jest/pull/13421))
## 29.2.0
diff --git a/packages/jest-mock/src/__tests__/__fixtures__/class-mocks-types.ts b/packages/jest-mock/src/__tests__/__fixtures__/class-mocks-types.ts
index ccb2eae1fc4f..a6f8aa5634d8 100644
--- a/packages/jest-mock/src/__tests__/__fixtures__/class-mocks-types.ts
+++ b/packages/jest-mock/src/__tests__/__fixtures__/class-mocks-types.ts
@@ -52,3 +52,16 @@ export default class SuperTestClass {
}
export class TestClass extends SuperTestClass {}
+
+export function testFunction1() {
+ return 'testFunction1';
+}
+
+function testFunction() {
+ return 'testFunction2';
+}
+export const testFunction2 = testFunction;
+
+export const testFunction3 = () => {
+ return 'testFunction3';
+};
diff --git a/packages/jest-mock/src/__tests__/class-mocks-dual-import.test.ts b/packages/jest-mock/src/__tests__/class-mocks-dual-import.test.ts
index 610469e9deba..3bcc759e6d68 100644
--- a/packages/jest-mock/src/__tests__/class-mocks-dual-import.test.ts
+++ b/packages/jest-mock/src/__tests__/class-mocks-dual-import.test.ts
@@ -35,96 +35,4 @@ describe('Testing the mocking of a class hierarchy defined in multiple imports',
expect(testClassInstance.testMethod()).toBe('mockTestMethod');
expect(mockTestMethod).toHaveBeenCalledTimes(1);
});
-
- it('can read a value from an instance getter - Auto-mocked class', () => {
- const mockTestMethod = jest
- .spyOn(SuperTestClass.prototype, 'testAccessor', 'get')
- .mockImplementation(() => {
- return 'mockTestAccessor';
- });
- const testClassInstance = new SuperTestClass();
- expect(testClassInstance.testAccessor).toBe('mockTestAccessor');
- expect(mockTestMethod).toHaveBeenCalledTimes(1);
-
- mockTestMethod.mockClear();
- });
-
- it('can read a value from a superclass instance getter - Auto-mocked class', () => {
- const mockTestMethod = jest
- .spyOn(TestClass.prototype, 'testAccessor', 'get')
- .mockImplementation(() => {
- return 'mockTestAccessor';
- });
- const testClassInstance = new TestClass();
- expect(testClassInstance.testAccessor).toBe('mockTestAccessor');
- expect(mockTestMethod).toHaveBeenCalledTimes(1);
- });
-
- it('can write a value to an instance setter - Auto-mocked class', () => {
- const mockTestMethod = jest
- .spyOn(SuperTestClass.prototype, 'testAccessor', 'set')
- .mockImplementation((_x: string) => {
- return () => {};
- });
- const testClassInstance = new SuperTestClass();
- testClassInstance.testAccessor = '';
- expect(mockTestMethod).toHaveBeenCalledTimes(1);
-
- mockTestMethod.mockClear();
- });
-
- it('can write a value to a superclass instance setter - Auto-mocked class', () => {
- const mockTestMethod = jest
- .spyOn(TestClass.prototype, 'testAccessor', 'set')
- .mockImplementation((_x: string) => {
- return () => {};
- });
- const testClassInstance = new TestClass();
- testClassInstance.testAccessor = '';
- expect(mockTestMethod).toHaveBeenCalledTimes(1);
- });
-
- it('can read a value from a static getter - Auto-mocked class', () => {
- const mockTestMethod = jest
- .spyOn(SuperTestClass, 'staticTestAccessor', 'get')
- .mockImplementation(() => {
- return 'mockStaticTestAccessor';
- });
- expect(SuperTestClass.staticTestAccessor).toBe('mockStaticTestAccessor');
- expect(mockTestMethod).toHaveBeenCalledTimes(1);
-
- mockTestMethod.mockClear();
- });
-
- it('can read a value from a superclass static getter - Auto-mocked class', () => {
- const mockTestMethod = jest
- .spyOn(TestClass, 'staticTestAccessor', 'get')
- .mockImplementation(() => {
- return 'mockStaticTestAccessor';
- });
- expect(TestClass.staticTestAccessor).toBe('mockStaticTestAccessor');
- expect(mockTestMethod).toHaveBeenCalledTimes(1);
- });
-
- it('can write a value to a static setter - Auto-mocked class', () => {
- const mockTestMethod = jest
- .spyOn(SuperTestClass, 'staticTestAccessor', 'set')
- .mockImplementation((_x: string) => {
- return () => {};
- });
- SuperTestClass.staticTestAccessor = '';
- expect(mockTestMethod).toHaveBeenCalledTimes(1);
-
- mockTestMethod.mockClear();
- });
-
- it('can write a value to a superclass static setter - Auto-mocked class', () => {
- const mockTestMethod = jest
- .spyOn(TestClass, 'staticTestAccessor', 'set')
- .mockImplementation((_x: string) => {
- return () => {};
- });
- TestClass.staticTestAccessor = '';
- expect(mockTestMethod).toHaveBeenCalledTimes(1);
- });
});
diff --git a/packages/jest-mock/src/__tests__/class-mocks-single-import.test.ts b/packages/jest-mock/src/__tests__/class-mocks-single-import.test.ts
index 9b354db44d95..078c0531ddf2 100644
--- a/packages/jest-mock/src/__tests__/class-mocks-single-import.test.ts
+++ b/packages/jest-mock/src/__tests__/class-mocks-single-import.test.ts
@@ -6,9 +6,32 @@
*
*/
-import SuperTestClass, {TestClass} from './__fixtures__/class-mocks-types';
+import SuperTestClass, * as testTypes from './__fixtures__/class-mocks-types';
jest.mock('./__fixtures__/class-mocks-types');
+describe('Testing the mocking of exported functions', () => {
+ it('can mock a directly exported function', () => {
+ jest.spyOn(testTypes, 'testFunction1').mockImplementation(() => {
+ return 'mockTestFunction';
+ });
+ expect(testTypes.testFunction1()).toBe('mockTestFunction');
+ });
+
+ it('can mock an indirectly exported function', () => {
+ jest.spyOn(testTypes, 'testFunction2').mockImplementation(() => {
+ return 'mockTestFunction';
+ });
+ expect(testTypes.testFunction2()).toBe('mockTestFunction');
+ });
+
+ it('can mock an indirectly exported anonymous function', () => {
+ jest.spyOn(testTypes, 'testFunction3').mockImplementation(() => {
+ return 'mockTestFunction';
+ });
+ expect(testTypes.testFunction3()).toBe('mockTestFunction');
+ });
+});
+
describe('Testing the mocking of a class hierarchy defined in a single import', () => {
it('can call an instance method - Auto-mocked class', () => {
const mockTestMethod = jest
@@ -25,11 +48,11 @@ describe('Testing the mocking of a class hierarchy defined in a single import',
it('can call a superclass instance method - Auto-mocked class', () => {
const mockTestMethod = jest
- .spyOn(TestClass.prototype, 'testMethod')
+ .spyOn(testTypes.TestClass.prototype, 'testMethod')
.mockImplementation(() => {
return 'mockTestMethod';
});
- const testClassInstance = new TestClass();
+ const testClassInstance = new testTypes.TestClass();
expect(testClassInstance.testMethod()).toBe('mockTestMethod');
expect(mockTestMethod).toHaveBeenCalledTimes(1);
});
@@ -49,11 +72,11 @@ describe('Testing the mocking of a class hierarchy defined in a single import',
it('can call a superclass instance method named "get" - Auto-mocked class', () => {
const mockTestMethod = jest
- .spyOn(TestClass.prototype, 'get')
+ .spyOn(testTypes.TestClass.prototype, 'get')
.mockImplementation(() => {
return 'mockTestMethod';
});
- const testClassInstance = new TestClass();
+ const testClassInstance = new testTypes.TestClass();
expect(testClassInstance.get()).toBe('mockTestMethod');
expect(mockTestMethod).toHaveBeenCalledTimes(1);
@@ -75,65 +98,17 @@ describe('Testing the mocking of a class hierarchy defined in a single import',
it('can call a superclass instance method named "set" - Auto-mocked class', () => {
const mockTestMethod = jest
- .spyOn(TestClass.prototype, 'set')
+ .spyOn(testTypes.TestClass.prototype, 'set')
.mockImplementation(() => {
return 'mockTestMethod';
});
- const testClassInstance = new TestClass();
+ const testClassInstance = new testTypes.TestClass();
expect(testClassInstance.set()).toBe('mockTestMethod');
expect(mockTestMethod).toHaveBeenCalledTimes(1);
mockTestMethod.mockClear();
});
- it('can read a value from an instance getter - Auto-mocked class', () => {
- const mockTestMethod = jest
- .spyOn(SuperTestClass.prototype, 'testAccessor', 'get')
- .mockImplementation(() => {
- return 'mockTestAccessor';
- });
- const testClassInstance = new SuperTestClass();
- expect(testClassInstance.testAccessor).toBe('mockTestAccessor');
- expect(mockTestMethod).toHaveBeenCalledTimes(1);
-
- mockTestMethod.mockClear();
- });
-
- it('can read a value from a superclass instance getter - Auto-mocked class', () => {
- const mockTestMethod = jest
- .spyOn(TestClass.prototype, 'testAccessor', 'get')
- .mockImplementation(() => {
- return 'mockTestAccessor';
- });
- const testClassInstance = new TestClass();
- expect(testClassInstance.testAccessor).toBe('mockTestAccessor');
- expect(mockTestMethod).toHaveBeenCalledTimes(1);
- });
-
- it('can write a value to an instance setter - Auto-mocked class', () => {
- const mockTestMethod = jest
- .spyOn(SuperTestClass.prototype, 'testAccessor', 'set')
- .mockImplementation((_x: string) => {
- return () => {};
- });
- const testClassInstance = new SuperTestClass();
- testClassInstance.testAccessor = '';
- expect(mockTestMethod).toHaveBeenCalledTimes(1);
-
- mockTestMethod.mockClear();
- });
-
- it('can write a value to a superclass instance setter - Auto-mocked class', () => {
- const mockTestMethod = jest
- .spyOn(TestClass.prototype, 'testAccessor', 'set')
- .mockImplementation((_x: string) => {
- return () => {};
- });
- const testClassInstance = new TestClass();
- testClassInstance.testAccessor = '';
- expect(mockTestMethod).toHaveBeenCalledTimes(1);
- });
-
it('can call a static method - Auto-mocked class', () => {
const mockTestMethod = jest
.spyOn(SuperTestClass, 'staticTestMethod')
@@ -148,11 +123,11 @@ describe('Testing the mocking of a class hierarchy defined in a single import',
it('can call a superclass static method - Auto-mocked class', () => {
const mockTestMethod = jest
- .spyOn(TestClass, 'staticTestMethod')
+ .spyOn(testTypes.TestClass, 'staticTestMethod')
.mockImplementation(() => {
return 'mockTestMethod';
});
- expect(TestClass.staticTestMethod()).toBe('mockTestMethod');
+ expect(testTypes.TestClass.staticTestMethod()).toBe('mockTestMethod');
expect(mockTestMethod).toHaveBeenCalledTimes(1);
});
@@ -170,11 +145,11 @@ describe('Testing the mocking of a class hierarchy defined in a single import',
it('can call a superclass static method named "get" - Auto-mocked class', () => {
const mockTestMethod = jest
- .spyOn(TestClass, 'get')
+ .spyOn(testTypes.TestClass, 'get')
.mockImplementation(() => {
return 'mockTestMethod';
});
- expect(TestClass.get()).toBe('mockTestMethod');
+ expect(testTypes.TestClass.get()).toBe('mockTestMethod');
expect(mockTestMethod).toHaveBeenCalledTimes(1);
mockTestMethod.mockClear();
@@ -194,57 +169,13 @@ describe('Testing the mocking of a class hierarchy defined in a single import',
it('can call a superclass static method named "set" - Auto-mocked class', () => {
const mockTestMethod = jest
- .spyOn(TestClass, 'set')
+ .spyOn(testTypes.TestClass, 'set')
.mockImplementation(() => {
return 'mockTestMethod';
});
- expect(TestClass.set()).toBe('mockTestMethod');
- expect(mockTestMethod).toHaveBeenCalledTimes(1);
-
- mockTestMethod.mockClear();
- });
-
- it('can read a value from a static getter - Auto-mocked class', () => {
- const mockTestMethod = jest
- .spyOn(SuperTestClass, 'staticTestAccessor', 'get')
- .mockImplementation(() => {
- return 'mockStaticTestAccessor';
- });
- expect(SuperTestClass.staticTestAccessor).toBe('mockStaticTestAccessor');
- expect(mockTestMethod).toHaveBeenCalledTimes(1);
-
- mockTestMethod.mockClear();
- });
-
- it('can read a value from a superclass static getter - Auto-mocked class', () => {
- const mockTestMethod = jest
- .spyOn(TestClass, 'staticTestAccessor', 'get')
- .mockImplementation(() => {
- return 'mockStaticTestAccessor';
- });
- expect(TestClass.staticTestAccessor).toBe('mockStaticTestAccessor');
- expect(mockTestMethod).toHaveBeenCalledTimes(1);
- });
-
- it('can write a value to a static setter - Auto-mocked class', () => {
- const mockTestMethod = jest
- .spyOn(SuperTestClass, 'staticTestAccessor', 'set')
- .mockImplementation((_x: string) => {
- return () => {};
- });
- SuperTestClass.staticTestAccessor = '';
+ expect(testTypes.TestClass.set()).toBe('mockTestMethod');
expect(mockTestMethod).toHaveBeenCalledTimes(1);
mockTestMethod.mockClear();
});
-
- it('can write a value to a superclass static setter - Auto-mocked class', () => {
- const mockTestMethod = jest
- .spyOn(TestClass, 'staticTestAccessor', 'set')
- .mockImplementation((_x: string) => {
- return () => {};
- });
- TestClass.staticTestAccessor = '';
- expect(mockTestMethod).toHaveBeenCalledTimes(1);
- });
});
diff --git a/packages/jest-mock/src/__tests__/class-mocks.test.ts b/packages/jest-mock/src/__tests__/class-mocks.test.ts
index 4b33c6c532dd..ea6b92f66465 100644
--- a/packages/jest-mock/src/__tests__/class-mocks.test.ts
+++ b/packages/jest-mock/src/__tests__/class-mocks.test.ts
@@ -14,11 +14,14 @@ describe('Testing the mocking of a class', () => {
}
}
- jest.spyOn(TestClass.prototype, 'testMethod').mockImplementation(() => {
- return 'mockTestMethod';
- });
+ const mockFn = jest
+ .spyOn(TestClass.prototype, 'testMethod')
+ .mockImplementation(() => 'mockTestMethod');
const testClassInstance = new TestClass();
expect(testClassInstance.testMethod()).toBe('mockTestMethod');
+
+ mockFn.mockRestore();
+ expect(testClassInstance.testMethod()).toBe('testMethod');
});
it('can call a superclass instance method', () => {
@@ -30,11 +33,18 @@ describe('Testing the mocking of a class', () => {
class TestClass extends SuperTestClass {}
- jest.spyOn(TestClass.prototype, 'testMethod').mockImplementation(() => {
- return 'mockTestMethod';
- });
+ const mockFn = jest
+ .spyOn(TestClass.prototype, 'testMethod')
+ .mockImplementation(() => {
+ return 'mockTestMethod';
+ });
const testClassInstance = new TestClass();
expect(testClassInstance.testMethod()).toBe('mockTestMethod');
+
+ mockFn.mockRestore();
+ expect(testClassInstance.testMethod()).toBe('testMethod');
+ // eslint-disable-next-line no-prototype-builtins
+ expect(TestClass.prototype.hasOwnProperty('testMethod')).toBe(false);
});
it('can call an instance method named "get"', () => {
@@ -44,11 +54,16 @@ describe('Testing the mocking of a class', () => {
}
}
- jest.spyOn(TestClass.prototype, 'get').mockImplementation(() => {
- return 'mockTestMethod';
- });
+ const mockFn = jest
+ .spyOn(TestClass.prototype, 'get')
+ .mockImplementation(() => {
+ return 'mockTestMethod';
+ });
const testClassInstance = new TestClass();
expect(testClassInstance.get()).toBe('mockTestMethod');
+
+ mockFn.mockRestore();
+ expect(testClassInstance.get()).toBe('get');
});
it('can call a superclass instance method named "get"', () => {
@@ -60,11 +75,18 @@ describe('Testing the mocking of a class', () => {
class TestClass extends SuperTestClass {}
- jest.spyOn(TestClass.prototype, 'get').mockImplementation(() => {
- return 'mockTestMethod';
- });
+ const mockFn = jest
+ .spyOn(TestClass.prototype, 'get')
+ .mockImplementation(() => {
+ return 'mockTestMethod';
+ });
const testClassInstance = new TestClass();
expect(testClassInstance.get()).toBe('mockTestMethod');
+
+ mockFn.mockRestore();
+ expect(testClassInstance.get()).toBe('get');
+ // eslint-disable-next-line no-prototype-builtins
+ expect(TestClass.prototype.hasOwnProperty('get')).toBe(false);
});
it('can call an instance method named "set"', () => {
@@ -74,11 +96,16 @@ describe('Testing the mocking of a class', () => {
}
}
- jest.spyOn(TestClass.prototype, 'set').mockImplementation(() => {
- return 'mockTestMethod';
- });
+ const mockFn = jest
+ .spyOn(TestClass.prototype, 'set')
+ .mockImplementation(() => {
+ return 'mockTestMethod';
+ });
const testClassInstance = new TestClass();
expect(testClassInstance.set()).toBe('mockTestMethod');
+
+ mockFn.mockRestore();
+ expect(testClassInstance.set()).toBe('set');
});
it('can call a superclass instance method named "set"', () => {
@@ -90,11 +117,18 @@ describe('Testing the mocking of a class', () => {
class TestClass extends SuperTestClass {}
- jest.spyOn(TestClass.prototype, 'set').mockImplementation(() => {
- return 'mockTestMethod';
- });
+ const mockFn = jest
+ .spyOn(TestClass.prototype, 'set')
+ .mockImplementation(() => {
+ return 'mockTestMethod';
+ });
const testClassInstance = new TestClass();
expect(testClassInstance.set()).toBe('mockTestMethod');
+
+ mockFn.mockRestore();
+ expect(testClassInstance.set()).toBe('set');
+ // eslint-disable-next-line no-prototype-builtins
+ expect(TestClass.prototype.hasOwnProperty('set')).toBe(false);
});
it('can read a value from an instance getter', () => {
@@ -104,13 +138,16 @@ describe('Testing the mocking of a class', () => {
}
}
- jest
+ const mockFn = jest
.spyOn(TestClass.prototype, 'testMethod', 'get')
.mockImplementation(() => {
return 'mockTestMethod';
});
const testClassInstance = new TestClass();
expect(testClassInstance.testMethod).toBe('mockTestMethod');
+
+ mockFn.mockRestore();
+ expect(testClassInstance.testMethod).toBe('testMethod');
});
it('can read a value from an superclass instance getter', () => {
@@ -122,13 +159,16 @@ describe('Testing the mocking of a class', () => {
class TestClass extends SuperTestClass {}
- jest
+ const mockFn = jest
.spyOn(TestClass.prototype, 'testMethod', 'get')
.mockImplementation(() => {
return 'mockTestMethod';
});
const testClassInstance = new TestClass();
expect(testClassInstance.testMethod).toBe('mockTestMethod');
+
+ mockFn.mockRestore();
+ expect(testClassInstance.testMethod).toBe('testMethod');
});
it('can write a value to an instance setter', () => {
@@ -147,6 +187,10 @@ describe('Testing the mocking of a class', () => {
const testClassInstance = new TestClass();
testClassInstance.testMethod = '';
expect(mocktestMethod).toHaveBeenCalledTimes(1);
+
+ mocktestMethod.mockRestore();
+ testClassInstance.testMethod = '';
+ expect(mocktestMethod).toHaveBeenCalledTimes(0);
});
it('can write a value to a superclass instance setter', () => {
@@ -167,6 +211,10 @@ describe('Testing the mocking of a class', () => {
const testClassInstance = new TestClass();
testClassInstance.testMethod = '';
expect(mocktestMethod).toHaveBeenCalledTimes(1);
+
+ mocktestMethod.mockRestore();
+ testClassInstance.testMethod = '';
+ expect(mocktestMethod).toHaveBeenCalledTimes(0);
});
it('can call a static method', () => {
@@ -176,10 +224,15 @@ describe('Testing the mocking of a class', () => {
}
}
- jest.spyOn(TestClass, 'testMethod').mockImplementation(() => {
- return 'mockTestMethod';
- });
+ const mockFn = jest
+ .spyOn(TestClass, 'testMethod')
+ .mockImplementation(() => {
+ return 'mockTestMethod';
+ });
expect(TestClass.testMethod()).toBe('mockTestMethod');
+
+ mockFn.mockRestore();
+ expect(TestClass.testMethod()).toBe('testMethod');
});
it('can call a superclass static method', () => {
@@ -191,10 +244,15 @@ describe('Testing the mocking of a class', () => {
class TestClass extends SuperTestClass {}
- jest.spyOn(TestClass, 'testMethod').mockImplementation(() => {
- return 'mockTestMethod';
- });
+ const mockFn = jest
+ .spyOn(TestClass, 'testMethod')
+ .mockImplementation(() => {
+ return 'mockTestMethod';
+ });
expect(TestClass.testMethod()).toBe('mockTestMethod');
+
+ mockFn.mockRestore();
+ expect(TestClass.testMethod()).toBe('testMethod');
});
it('can call a static method named "get"', () => {
@@ -204,10 +262,13 @@ describe('Testing the mocking of a class', () => {
}
}
- jest.spyOn(TestClass, 'get').mockImplementation(() => {
+ const mockFn = jest.spyOn(TestClass, 'get').mockImplementation(() => {
return 'mockTestMethod';
});
expect(TestClass.get()).toBe('mockTestMethod');
+
+ mockFn.mockRestore();
+ expect(TestClass.get()).toBe('get');
});
it('can call a superclass static method named "get"', () => {
@@ -219,10 +280,15 @@ describe('Testing the mocking of a class', () => {
class TestClass extends SuperTestClass {}
- jest.spyOn(TestClass, 'get').mockImplementation(() => {
+ const mockFn = jest.spyOn(TestClass, 'get').mockImplementation(() => {
return 'mockTestMethod';
});
expect(TestClass.get()).toBe('mockTestMethod');
+
+ mockFn.mockRestore();
+ expect(TestClass.get()).toBe('get');
+ // eslint-disable-next-line no-prototype-builtins
+ expect(TestClass.hasOwnProperty('get')).toBe(false);
});
it('can call a static method named "set"', () => {
@@ -232,10 +298,13 @@ describe('Testing the mocking of a class', () => {
}
}
- jest.spyOn(TestClass, 'set').mockImplementation(() => {
+ const mockFn = jest.spyOn(TestClass, 'set').mockImplementation(() => {
return 'mockTestMethod';
});
expect(TestClass.set()).toBe('mockTestMethod');
+
+ mockFn.mockRestore();
+ expect(TestClass.set()).toBe('set');
});
it('can call a superclass static method named "set"', () => {
@@ -247,10 +316,15 @@ describe('Testing the mocking of a class', () => {
class TestClass extends SuperTestClass {}
- jest.spyOn(TestClass, 'set').mockImplementation(() => {
+ const mockFn = jest.spyOn(TestClass, 'set').mockImplementation(() => {
return 'mockTestMethod';
});
expect(TestClass.set()).toBe('mockTestMethod');
+
+ mockFn.mockRestore();
+ expect(TestClass.set()).toBe('set');
+ // eslint-disable-next-line no-prototype-builtins
+ expect(TestClass.hasOwnProperty('set')).toBe(false);
});
it('can read a value from a static getter', () => {
@@ -260,10 +334,15 @@ describe('Testing the mocking of a class', () => {
}
}
- jest.spyOn(TestClass, 'testMethod', 'get').mockImplementation(() => {
- return 'mockTestMethod';
- });
+ const mockFn = jest
+ .spyOn(TestClass, 'testMethod', 'get')
+ .mockImplementation(() => {
+ return 'mockTestMethod';
+ });
expect(TestClass.testMethod).toBe('mockTestMethod');
+
+ mockFn.mockRestore();
+ expect(TestClass.testMethod).toBe('testMethod');
});
it('can read a value from a superclass static getter', () => {
@@ -275,10 +354,15 @@ describe('Testing the mocking of a class', () => {
class TestClass extends SuperTestClass {}
- jest.spyOn(TestClass, 'testMethod', 'get').mockImplementation(() => {
- return 'mockTestMethod';
- });
+ const mockFn = jest
+ .spyOn(TestClass, 'testMethod', 'get')
+ .mockImplementation(() => {
+ return 'mockTestMethod';
+ });
expect(TestClass.testMethod).toBe('mockTestMethod');
+
+ mockFn.mockRestore();
+ expect(TestClass.testMethod).toBe('testMethod');
});
it('can write a value to a static setter', () => {
@@ -296,6 +380,9 @@ describe('Testing the mocking of a class', () => {
});
TestClass.testMethod = '';
expect(mocktestMethod).toHaveBeenCalledTimes(1);
+
+ mocktestMethod.mockRestore();
+ expect(mocktestMethod).toHaveBeenCalledTimes(0);
});
it('can write a value to a superclass static setter', () => {
diff --git a/packages/jest-mock/src/__tests__/index.test.ts b/packages/jest-mock/src/__tests__/index.test.ts
index 8f1436f4b042..71680852eca7 100644
--- a/packages/jest-mock/src/__tests__/index.test.ts
+++ b/packages/jest-mock/src/__tests__/index.test.ts
@@ -1264,7 +1264,9 @@ describe('moduleMocker', () => {
}).toThrow('spyOn could not find an object to spy upon for method');
expect(() => {
moduleMocker.spyOn({}, 'method');
- }).toThrow('method property does not exist');
+ }).toThrow(
+ 'Cannot spy the method property because it is not a function; undefined given instead',
+ );
expect(() => {
moduleMocker.spyOn({method: 10}, 'method');
}).toThrow(
@@ -1432,7 +1434,9 @@ describe('moduleMocker', () => {
}).toThrow('spyOn could not find an object to spy upon for method');
expect(() => {
moduleMocker.spyOn({}, 'method');
- }).toThrow('method property does not exist');
+ }).toThrow(
+ 'Cannot spy the method property because it is not a function; undefined given instead',
+ );
expect(() => {
moduleMocker.spyOn({method: 10}, 'method');
}).toThrow(
diff --git a/packages/jest-mock/src/__tests__/window-spy.test.ts b/packages/jest-mock/src/__tests__/window-spy.test.ts
new file mode 100644
index 000000000000..43285c041706
--- /dev/null
+++ b/packages/jest-mock/src/__tests__/window-spy.test.ts
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @jest-environment jsdom
+ */
+
+///
+
+/* eslint-env browser*/
+
+function exampleDispatch() {
+ window.dispatchEvent(new CustomEvent('event', {}));
+}
+
+describe('spy on `dispatchEvent`', () => {
+ const dispatchEventSpy = jest.spyOn(window, 'dispatchEvent');
+
+ it('should be called', () => {
+ exampleDispatch();
+
+ expect(dispatchEventSpy).toHaveBeenCalled();
+ });
+});
diff --git a/packages/jest-mock/src/index.ts b/packages/jest-mock/src/index.ts
index d0c6ba44ac8c..3c0b6f108bd5 100644
--- a/packages/jest-mock/src/index.ts
+++ b/packages/jest-mock/src/index.ts
@@ -535,10 +535,7 @@ export class ModuleMocker {
if (!isReadonlyProp(object, prop)) {
const propDesc = Object.getOwnPropertyDescriptor(object, prop);
- if (
- propDesc !== undefined &&
- !(propDesc.get && prop == '__proto__')
- ) {
+ if ((propDesc !== undefined && !propDesc.get) || object.__esModule) {
slots.add(prop);
}
}
@@ -928,9 +925,7 @@ export class ModuleMocker {
}
this._getSlots(metadata.members).forEach(slot => {
- let slotMock: Mocked;
const slotMetadata = (metadata.members && metadata.members[slot]) || {};
-
if (slotMetadata.ref != null) {
callbacks.push(
(function (ref) {
@@ -938,40 +933,7 @@ export class ModuleMocker {
})(slotMetadata.ref),
);
} else {
- slotMock = this._generateMock(slotMetadata, callbacks, refs);
-
- // For superclass accessor properties the subclass metadata contains the definitions
- // for the getter and setter methods, and the superclass refs to them.
- // The mock implementations are not available until the callbacks have been executed.
- // Missing getter and setter refs will be resolved as their callbacks have been
- // stacked before the setting of the accessor definition is stacked.
-
- // In some cases, e.g. third-party APIs, a 'prototype' ancestor to be
- // mocked has a function property called 'get'. In this circumstance
- // the 'prototype' property cannot be redefined and doing so causes an
- // exception. Checks have been added for the 'configurable' and
- // 'enumberable' properties present on true accessor property
- // descriptors to prevent the attempt to replace the API.
- if (
- (slotMetadata.members?.get?.ref !== undefined ||
- slotMetadata.members?.set?.ref !== undefined) &&
- slotMetadata.members?.configurable &&
- slotMetadata.members?.enumerable
- ) {
- callbacks.push(
- (function (ref) {
- return () => Object.defineProperty(mock, slot, ref);
- })(slotMock as PropertyDescriptor),
- );
- } else if (
- (slotMetadata.members?.get || slotMetadata.members?.set) &&
- slotMetadata.members?.configurable &&
- slotMetadata.members?.enumerable
- ) {
- Object.defineProperty(mock, slot, slotMock as PropertyDescriptor);
- } else {
- mock[slot] = slotMock;
- }
+ mock[slot] = this._generateMock(slotMetadata, callbacks, refs);
}
});
@@ -1055,33 +1017,8 @@ export class ModuleMocker {
) {
return;
}
-
- let descriptor = Object.getOwnPropertyDescriptor(component, slot);
- let proto = Object.getPrototypeOf(component);
- while (!descriptor && proto !== null) {
- descriptor = Object.getOwnPropertyDescriptor(proto, slot);
- proto = Object.getPrototypeOf(proto);
- }
-
- let slotMetadata: MockMetadata | null = null;
- if (descriptor?.get || descriptor?.set) {
- // Specific case required for mocking class definitions imported via modules.
- // In this case the class definitions are stored in accessor properties.
- // All getters were previously ignored except where the containing object had __esModule == true
- // Now getters are mocked the class definitions must still be read.
- // @ts-expect-error ignore type mismatch
- if (component.__esModule) {
- // @ts-expect-error no index signature
- slotMetadata = this.getMetadata(component[slot], refs);
- } else {
- // @ts-expect-error ignore type mismatch
- slotMetadata = this.getMetadata(descriptor, refs);
- }
- } else {
- // @ts-expect-error no index signature
- slotMetadata = this.getMetadata(component[slot], refs);
- }
-
+ // @ts-expect-error no index signature
+ const slotMetadata = this.getMetadata(component[slot], refs);
if (slotMetadata) {
if (!members) {
members = {};
@@ -1151,6 +1088,12 @@ export class ModuleMocker {
methodKey: K,
accessType?: 'get' | 'set',
) {
+ if (typeof object !== 'object' && typeof object !== 'function') {
+ throw new Error(
+ `Cannot spyOn on a primitive value; ${this._typeOf(object)} given`,
+ );
+ }
+
if (!object) {
throw new Error(
`spyOn could not find an object to spy upon for ${String(methodKey)}`,
@@ -1161,120 +1104,124 @@ export class ModuleMocker {
throw new Error('No property name supplied');
}
- if (accessType && accessType != 'get' && accessType != 'set') {
- throw new Error('Invalid accessType supplied');
+ if (accessType) {
+ return this._spyOnProperty(object, methodKey, accessType);
}
- if (typeof object !== 'object' && typeof object !== 'function') {
- throw new Error(
- `Cannot spyOn on a primitive value; ${this._typeOf(object)} given`,
+ const original = object[methodKey];
+
+ if (!this.isMockFunction(original)) {
+ if (typeof original !== 'function') {
+ throw new Error(
+ `Cannot spy the ${String(
+ methodKey,
+ )} property because it is not a function; ${this._typeOf(
+ original,
+ )} given instead`,
+ );
+ }
+
+ const isMethodOwner = Object.prototype.hasOwnProperty.call(
+ object,
+ methodKey,
);
+
+ let descriptor = Object.getOwnPropertyDescriptor(object, methodKey);
+ let proto = Object.getPrototypeOf(object);
+
+ while (!descriptor && proto !== null) {
+ descriptor = Object.getOwnPropertyDescriptor(proto, methodKey);
+ proto = Object.getPrototypeOf(proto);
+ }
+
+ let mock: Mock;
+
+ if (descriptor && descriptor.get) {
+ const originalGet = descriptor.get;
+ mock = this._makeComponent({type: 'function'}, () => {
+ descriptor!.get = originalGet;
+ Object.defineProperty(object, methodKey, descriptor!);
+ });
+ descriptor.get = () => mock;
+ Object.defineProperty(object, methodKey, descriptor);
+ } else {
+ mock = this._makeComponent({type: 'function'}, () => {
+ if (isMethodOwner) {
+ object[methodKey] = original;
+ } else {
+ delete object[methodKey];
+ }
+ });
+ // @ts-expect-error overriding original method with a Mock
+ object[methodKey] = mock;
+ }
+
+ mock.mockImplementation(function (this: unknown) {
+ return original.apply(this, arguments);
+ });
}
- let descriptor = Object.getOwnPropertyDescriptor(object, methodKey);
- let proto = Object.getPrototypeOf(object);
+ return object[methodKey];
+ }
+
+ private _spyOnProperty>(
+ obj: T,
+ propertyKey: K,
+ accessType: 'get' | 'set',
+ ): Mock<() => T> {
+ let descriptor = Object.getOwnPropertyDescriptor(obj, propertyKey);
+ let proto = Object.getPrototypeOf(obj);
+
while (!descriptor && proto !== null) {
- descriptor = Object.getOwnPropertyDescriptor(proto, methodKey);
+ descriptor = Object.getOwnPropertyDescriptor(proto, propertyKey);
proto = Object.getPrototypeOf(proto);
}
+
if (!descriptor) {
- throw new Error(`${String(methodKey)} property does not exist`);
- }
- if (!descriptor.configurable) {
- throw new Error(`${String(methodKey)} is not declared configurable`);
+ throw new Error(`${String(propertyKey)} property does not exist`);
}
- if (this.isMockFunction(descriptor.value)) {
- return object[methodKey];
- } else if (accessType == 'get' && this.isMockFunction(descriptor.get)) {
- return descriptor.get;
- } else if (accessType == 'set' && this.isMockFunction(descriptor.set)) {
- return descriptor.set;
+ if (!descriptor.configurable) {
+ throw new Error(`${String(propertyKey)} is not declared configurable`);
}
- if (accessType) {
- if (typeof descriptor[accessType] !== 'function') {
- throw new Error(
- `Cannot spy the ${String(accessType)} ${String(
- methodKey,
- )} property because it is not a function;
- ${this._typeOf(descriptor?.[accessType])} given instead`,
- );
- }
- } else if (typeof descriptor.value !== 'function') {
+ if (!descriptor[accessType]) {
throw new Error(
- `Cannot spy the ${String(
- methodKey,
- )} property because it is not a function; ${this._typeOf(
- descriptor.value,
- )} given instead`,
+ `Property ${String(
+ propertyKey,
+ )} does not have access type ${accessType}`,
);
}
- let mock: Mock;
+ const original = descriptor[accessType];
- if (accessType == 'get' && descriptor.get) {
- const originalAccessor = descriptor.get;
- mock = this._makeComponent(
- {
- type: 'function',
- },
- () => {
- descriptor![accessType] = originalAccessor;
- Object.defineProperty(object, methodKey, descriptor!);
- },
- );
-
- descriptor[accessType] = mock;
- mock.mockImplementation(function (this: unknown) {
- return originalAccessor.call(this);
- });
- Object.defineProperty(object, methodKey, descriptor);
- } else if (accessType == 'set' && descriptor.set) {
- const originalAccessor = descriptor.set;
- mock = this._makeComponent(
- {
- type: 'function',
- },
- () => {
- descriptor![accessType] = originalAccessor;
- Object.defineProperty(object, methodKey, descriptor!);
- },
- );
+ if (!this.isMockFunction(original)) {
+ if (typeof original !== 'function') {
+ throw new Error(
+ `Cannot spy the ${String(
+ propertyKey,
+ )} property because it is not a function; ${this._typeOf(
+ original,
+ )} given instead`,
+ );
+ }
- descriptor[accessType] = mock;
- mock.mockImplementation(function (this: unknown) {
- return originalAccessor.call(this, arguments[0]);
+ descriptor[accessType] = this._makeComponent({type: 'function'}, () => {
+ // @ts-expect-error: mock is assignable
+ descriptor![accessType] = original;
+ Object.defineProperty(obj, propertyKey, descriptor!);
});
- Object.defineProperty(object, methodKey, descriptor);
- } else {
- const isMethodOwner = Object.prototype.hasOwnProperty.call(
- object,
- methodKey,
- );
- const original = descriptor;
- mock = this._makeComponent(
- {
- type: 'function',
- },
- () => {
- if (isMethodOwner) {
- object[methodKey] = original.value;
- } else {
- delete object[methodKey];
- }
- },
- );
-
- // @ts-expect-error overriding original method with a Mock
- object[methodKey] = mock;
- mock.mockImplementation(function (this: unknown) {
- return original.value.apply(this, arguments);
+ (descriptor[accessType] as Mock<() => T>).mockImplementation(function (
+ this: unknown,
+ ) {
+ // @ts-expect-error - wrong context
+ return original.apply(this, arguments);
});
}
- return mock;
+ Object.defineProperty(obj, propertyKey, descriptor);
+ return descriptor[accessType] as Mock<() => T>;
}
clearAllMocks(): void {
@@ -1291,7 +1238,7 @@ export class ModuleMocker {
this._spyState = new Set();
}
- private _typeOf(value: any): string {
+ private _typeOf(value: unknown): string {
return value == null ? `${value}` : typeof value;
}