Skip to content

Commit

Permalink
jest: Mock "Linking" from react-native properly.
Browse files Browse the repository at this point in the history
With something called Haste, we were allowed to just pass "Linking"
to `jest.mock`, and it would automagically be known that we want
something called "Linking" from React Native.

With RN v0.61, Haste is no longer used, and that way of mocking
breaks.

One possible response is to spell out the entire path to "Linking"
within `react-native`:

jest.mock('react-native/Libraries/Linking/Linking')

. But that's brittle: that path may change with new React Native
versions, and it'll be unpleasant to have to adapt.

The recommended solution [1] is to mock the `react-native` module
ourselves, on top of the mocking that React Native's Jest setup does
for us. And to put our "Linking" mock there. So, do.

The *exact* recommendation is something that uses
`Object.setPrototypeOf`. We don't do that. Instead, Greg found an
earlier revision of the comment where that recommendation appears,
and go from there.

This way, we avoid an awkward problem with
`react-native-vector-icons`. That library re-exports `react-native`
in its `lib/react-native.js`, and imports that when they want
properties from the `react-native` module. Errors ensue; it appears
that their strategy cuts off access to properties we'd intended to
make available by using ReactNative as a prototype.

So, don't mess around with prototypes.

[1] facebook/react-native#26579 (comment)
  • Loading branch information
chrisbobbe committed Jul 11, 2020
1 parent 293b016 commit 474cdcc
Showing 1 changed file with 34 additions and 8 deletions.
42 changes: 34 additions & 8 deletions jest/jestSetup.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,45 @@
import * as ReactNative from 'react-native';

import mockAsyncStorage from '@react-native-community/async-storage/jest/async-storage-mock';

// Mock `react-native` ourselves, following upstream advice [1] [2].
//
// Note that React Native's Jest setup (in their jest/setup.js)
// means that most of `react-native` is already mocked; we add to that
// here.
//
// We can't do `ReactNative.Foo = ...` because all properties Foo on
// the react-native module have getters but no setters. But we can set
// properties freely at the next level down:
//
// ReactNative.Foo.Bar = ...
//
// Or set multiple properties at this level, together:
// `Object.assign(ReactNative.Foo, { ... })`.
//
// If we *really* want to replace a top-level property:
//
// Object.defineProperty(ReactNative, "Foo", { ... });
//
// [1] https://github.com/facebook/react-native/issues/26579#issuecomment-535244001
// [2] https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/.23M3781.20RN.20v0.2E61.20upgrade/near/931219
jest.mock('react-native', () => {
Object.assign(ReactNative.Linking, {
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
openURL: jest.fn(),
canOpenURL: jest.fn(),
getInitialURL: jest.fn(),
});
return ReactNative;
});

jest.mock('@react-native-community/async-storage', () => mockAsyncStorage);

jest.mock('react-native-sound', () => () => ({
play: jest.fn(),
}));

jest.mock('Linking', () => ({
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
openURL: jest.fn(),
canOpenURL: jest.fn(),
getInitialURL: jest.fn(),
}));

jest.mock('rn-fetch-blob', () => ({
DocumentDir: () => {},
}));
Expand Down

0 comments on commit 474cdcc

Please sign in to comment.