fix(jest): keep mocked hooks working after jest.resetAllMocks() - #749
Open
giaBaoJS wants to merge 1 commit into
Open
fix(jest): keep mocked hooks working after jest.resetAllMocks()#749giaBaoJS wants to merge 1 commit into
giaBaoJS wants to merge 1 commit into
Conversation
The jest mock wraps useSafeAreaInsets and useSafeAreaFrame in jest.fn() so tests can override them. jest.resetAllMocks(), which apps commonly call from a global beforeEach/afterEach, strips the implementation off every mock function. The hooks then return undefined and any component reading insets.top or frame.width throws. Wrap the two hooks in a Proxy that reinstalls the default implementation when getMockImplementation() reports it has been stripped. Overrides made with mockReturnValue/mockImplementation set an implementation, so they still take precedence, and call tracking is untouched. The generated jest/mock.d.ts is byte identical, so this is not a type change. Fixes appandflow#551
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #551
@jacobp100 said in the issue:
Nobody has picked it up since, so here it is.
The problem
src/jest/mock.tsxwraps the two hooks injest.fn(...)(added in #449) so tests can override them:jest.resetAllMocks()removes the implementation from every mock function. Apps commonly call it from a globalbeforeEach/afterEach, and after that both hooks returnundefined, so any component readinginsets.toporframe.widththrows:This matches what @adamgeorgsson and @eppisapiafsl reported: the first test in a suite passes and every one after it fails. The current workarounds are patching the package to revert #449, or switching to
jest.clearAllMocks()— which is not equivalent, and consumers should not have to know this.The fix
Wrap the hooks in a
Proxythat reinstalls the default implementation whengetMockImplementation()reports it was stripped:mockReturnValue/mockImplementationset an implementation, sogetMockImplementation()is non-null and the default is not reinstalled — overrides still win. Call tracking is untouched, since the proxy forwards to the same mock function.This keeps the #449 behaviour (the hooks remain overridable
jest.fns) rather than reverting it.What I verified
Everything below was run locally on this branch, not inferred.
The defect reproduces on current
main(f65d4d9, v5.9.0). The new test renders a component throughjest.mock('react-native-safe-area-context', () => mockSafeAreaContext)— the exact setup the README prescribes — withjest.resetAllMocks()in abeforeEach. On unmodifiedmainall three cases fail withTypeError: Cannot read properties of undefined (reading 'top' / 'width'). Removing only thejest.resetAllMocks()line makes them pass on unmodifiedmain, which isolates the cause to the reset.Counterfactual. With the test in place and
src/jest/mock.tsxreverted tomain: 3 failed. With the fix applied: 3 passed.The two things people rely on still work, asserted in the test rather than argued:
jest.mocked(useSafeAreaInsets).mockReturnValue(...)after a reset still wins over the reinstated default (useSafeAreaFramein the same render falls back to the default, so one assertion covers both paths).toHaveBeenCalledTimes(1)on both hooks still holds after a reset.No public type change. I built
lib/typescriptbefore and after the fix and diffed the generatedjest/mock.d.ts— byte identical. Both hooks are stillMock<() => EdgeInsets>/Mock<() => Rect>.Suite.
yarn validate:jestgoes from 4 suites / 21 tests to 5 suites / 24 tests, all passing, 11 snapshots unchanged.yarn format:prettier:check,yarn validate:eslint(still the same 3 pre-existingno-deep-importswarnings, none new) andyarn validate:typescriptall pass.Jest 30. I checked the fix against
jest@30.4.2in a scratch project.jest.fn(impl)still returnsundefinedafterresetAllMocks()there, so the bug is not fixed upstream, and the proxied version returns the default, honoursmockReturnValue, and staysjest.isMockFunction-true.Packaging.
npm pack --dry-runconfirms the newsrc/jest/__tests__/directory is excluded from the tarball by the existing!**/__tests__entry infiles.What I could not verify
yarn testalso runsformat:clang:checkandformat:spotless:check.clang-formatcould not run on my machine — the bundlednode_modules/clang-format/bin/darwin_x64/clang-formatis an x86_64 binary and fails withspawn Unknown system error -86on Apple Silicon. It fails identically on a clean checkout with zero changes, and this PR touches no.h/.cpp/.m/.mm/.java/.ktfiles, so neither native formatter is affected. CI runs onubuntu-latest, where both should run normally.