From 7b8393e5ce073ac954027bb767bed846672aac97 Mon Sep 17 00:00:00 2001 From: Giorgi Rostomashvili Date: Thu, 22 Aug 2019 12:24:23 +0200 Subject: [PATCH] Fix `mockReturnValue` overriding `mockImplementationOnce` (#8398) --- CHANGELOG.md | 1 + .../jest-mock/src/__tests__/index.test.ts | 9 +++ packages/jest-mock/src/index.ts | 59 ++++--------------- 3 files changed, 23 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a69936a8fb79..8c608af9c29d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - `[expect]` Display expectedDiff more carefully in toBeCloseTo ([#8389](https://github.com/facebook/jest/pull/8389)) - `[jest-fake-timers]` `getTimerCount` will not include cancelled immediates ([#8764](https://github.com/facebook/jest/pull/8764)) - `[jest-leak-detector]` [**BREAKING**] Use `weak-napi` instead of `weak` package ([#8686](https://github.com/facebook/jest/pull/8686)) +- `[jest-mock]` Fix for mockReturnValue overriding mockImplementationOnce ([#8398](https://github.com/facebook/jest/pull/8398)) - `[jest-snapshot]` Remove only the added newlines in multiline snapshots ([#8859](https://github.com/facebook/jest/pull/8859)) ### Chore & Maintenance diff --git a/packages/jest-mock/src/__tests__/index.test.ts b/packages/jest-mock/src/__tests__/index.test.ts index f665def33ea6..2a3b83c9d323 100644 --- a/packages/jest-mock/src/__tests__/index.test.ts +++ b/packages/jest-mock/src/__tests__/index.test.ts @@ -1020,6 +1020,15 @@ describe('moduleMocker', () => { }); }); + test('mockReturnValue does not override mockImplementationOnce', () => { + const mockFn = jest + .fn() + .mockReturnValue(1) + .mockImplementationOnce(() => 2); + expect(mockFn()).toBe(2); + expect(mockFn()).toBe(1); + }); + test('mockImplementation resets the mock', () => { const fn = jest.fn(); expect(fn()).toBeUndefined(); diff --git a/packages/jest-mock/src/index.ts b/packages/jest-mock/src/index.ts index be19de0df52f..8afcd60b6a56 100644 --- a/packages/jest-mock/src/index.ts +++ b/packages/jest-mock/src/index.ts @@ -71,8 +71,6 @@ type MockFunctionState> = { }; type MockFunctionConfig = { - isReturnValueLastSet: boolean; - defaultReturnValue: unknown; mockImpl: Function | undefined; mockName: string; specificReturnValues: Array; @@ -456,8 +454,6 @@ class ModuleMockerClass { private _defaultMockConfig(): MockFunctionConfig { return { - defaultReturnValue: undefined, - isReturnValueLastSet: false, mockImpl: undefined, mockName: 'jest.fn()', specificMockImpls: [], @@ -586,40 +582,21 @@ class ModuleMockerClass { return mockImpl && mockImpl.apply(this, arguments); } - const returnValue = mockConfig.defaultReturnValue; - // If return value is last set, either specific or default, i.e. - // mockReturnValueOnce()/mockReturnValue() is called and no - // mockImplementationOnce()/mockImplementation() is called after - // that. - // use the set return value. - if (mockConfig.specificReturnValues.length) { - return mockConfig.specificReturnValues.shift(); - } - - if (mockConfig.isReturnValueLastSet) { - return mockConfig.defaultReturnValue; - } - // If mockImplementationOnce()/mockImplementation() is last set, - // or specific return values are used up, use the mock - // implementation. - let specificMockImpl; - if (returnValue === undefined) { - specificMockImpl = mockConfig.specificMockImpls.shift(); - if (specificMockImpl === undefined) { - specificMockImpl = mockConfig.mockImpl; - } - if (specificMockImpl) { - return specificMockImpl.apply(this, arguments); - } + // implementation use the mock + let specificMockImpl = mockConfig.specificMockImpls.shift(); + if (specificMockImpl === undefined) { + specificMockImpl = mockConfig.mockImpl; + } + if (specificMockImpl) { + return specificMockImpl.apply(this, arguments); } - // Otherwise use prototype implementation - if (returnValue === undefined && f._protoImpl) { + if (f._protoImpl) { return f._protoImpl.apply(this, arguments); } - return returnValue; + return undefined; })(); } catch (error) { // Store the thrown error so we can record it, then re-throw it. @@ -675,12 +652,9 @@ class ModuleMockerClass { return restore ? restore() : undefined; }; - f.mockReturnValueOnce = (value: T) => { + f.mockReturnValueOnce = (value: T) => // next function call will return this value or default return value - const mockConfig = this._ensureMockConfig(f); - mockConfig.specificReturnValues.push(value); - return f; - }; + f.mockImplementationOnce(() => value); f.mockResolvedValueOnce = (value: T) => f.mockImplementationOnce(() => Promise.resolve(value)); @@ -688,13 +662,9 @@ class ModuleMockerClass { f.mockRejectedValueOnce = (value: T) => f.mockImplementationOnce(() => Promise.reject(value)); - f.mockReturnValue = (value: T) => { + f.mockReturnValue = (value: T) => // next function call will return specified return value or this one - const mockConfig = this._ensureMockConfig(f); - mockConfig.isReturnValueLastSet = true; - mockConfig.defaultReturnValue = value; - return f; - }; + f.mockImplementation(() => value); f.mockResolvedValue = (value: T) => f.mockImplementation(() => Promise.resolve(value)); @@ -708,7 +678,6 @@ class ModuleMockerClass { // next function call will use this mock implementation return value // or default mock implementation return value const mockConfig = this._ensureMockConfig(f); - mockConfig.isReturnValueLastSet = false; mockConfig.specificMockImpls.push(fn); return f; }; @@ -718,8 +687,6 @@ class ModuleMockerClass { ): Mock => { // next function call will use mock implementation return value const mockConfig = this._ensureMockConfig(f); - mockConfig.isReturnValueLastSet = false; - mockConfig.defaultReturnValue = undefined; mockConfig.mockImpl = fn; return f; };