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 we 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 authored and gnprice committed Jul 13, 2020
1 parent 56d8b4b commit 12a5f85
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 12a5f85

Please sign in to comment.