2

TLDR: How can I tell my Enzyme / Jest test it should run the tests as if it was running on iOS? I want to test platform specific behaviour.

I'm building a custom status bar component that adds 20 pixels of height, if it runs on iOS to prevent my content from overlapping with the status bar. (Yes, I know React-Navigation has a SafeAreaView, but this only works for iPhone X, not for e.g. iPad.)

Here is my component:

import React from "react";
import { StatusBar as ReactNativeStatusBar, View } from "react-native";

import styles from "./styles";

const StatusBar = ({ props }) => (
  <View style={styles.container}>
    <ReactNativeStatusBar {...props} />
  </View>
);

export default StatusBar;

Here is the styles.js file:

import { StyleSheet, Platform } from "react-native";

const height = Platform.OS === "ios" ? 20 : 0;

const styles = StyleSheet.create({
  container: {
    height: height
  }
});

export default styles;

And here are the tests so far:

import React from "react";
import { shallow } from "enzyme";
import { View } from "react-native";

import StatusBar from "./StatusBar";

const createTestProps = props => ({
  ...props
});

describe("StatusBar", () => {
  describe("rendering", () => {
    let wrapper;
    let props;
    beforeEach(() => {
      props = createTestProps();
      wrapper = shallow(<StatusBar {...props} />);
    });

    it("should render a <View />", () => {
      expect(wrapper.find(View)).toHaveLength(1);
    });

    it("should give the <View /> the container style", () => {
      expect(wrapper.find(View)).toHaveLength(1);
    });

    it("should render a <StatusBar />", () => {
      expect(wrapper.find("StatusBar")).toHaveLength(1);
    });
  });
});

Now what I would like to do is add two more describe areas that explicitly test for the height to be either 20 on iOS or 0 or Android. The problem is I couldn't find how to emulate the platform with Enzyme / Jest tests.

So how do I tell my test suite that it should run the code for the respective platform?

J. Hesters
  • 13,117
  • 31
  • 133
  • 249

1 Answers1

5

You can override the RN Platform object and perform different tests for each platform. Here's an example for how a test file would like like:

describe('tests', () => {

    let Platform;
    beforeEach(() => {
        Platform = require('react-native').Platform;
    });

    describe('ios tests', () => {
        beforeEach(() => {
            Platform.OS = 'ios';
        });

        it('should test something on iOS', () => {

        });
    });

    describe('android tests', () => {
        beforeEach(() => {
            Platform.OS = 'android';
        });

        it('should test something on Android', () => {

        });
    });

});

By the way, regardless of the question about testing, setting the status-bar height to 20 on iOS is wrong since it can have different sizes on different devices (iPhone X for example)

Artal
  • 8,933
  • 2
  • 27
  • 30
  • Thank you this is exactly what I was looking for! And thank you furthermore for your heads up. The app I want to use this component for will be exclusively available on iPad in landscape mode, which means a status-bar height of 20 will be fine :) – J. Hesters Aug 22 '18 at 08:42
  • Late follow up. I was very busy, but now found the time to actually build the unit tests using what you described here. Unfortunately this behavior only works for iOS but not for Android. [Here is a gist](https://gist.github.com/janhesters/a54ae29c81cb692e450d7ed7192a1e42) for my component's code. And [here for the corresponding tests](https://gist.github.com/janhesters/19fad5cc95b2e2e9a858149eb133e485) based on your example. The test for Android also fails, because the behavior is said to be padding. Do you have any idea what could be wrong? – J. Hesters Oct 03 '18 at 13:36
  • Aaah I found out what was wrong. I need to rerender the component AFTER setting the platform. Now the tests passes. Thanks anyways! – J. Hesters Oct 03 '18 at 13:39
  • Okay follow up to the follow up to the follow up: Testing styles this way is still failing. I'm importing the styles from a `styles.ts` file and no matter in which order I render, it always has the iOS style in the unit test and not the Android style. Sorry for spam, but do you have any idea how to fix this and not having to use inline styles? – J. Hesters Oct 03 '18 at 13:48
  • 1
    yeah, I guess styles are a whole different issue. the style object is created once, and required before you can even mock the platform. you can try looking into solutions like splitting your platform specific styles into separate files, or creating separate jest configs for each platform, see here: https://github.com/facebook/jest/issues/1370 – Artal Oct 03 '18 at 14:14
  • Thank you so much! – J. Hesters Oct 03 '18 at 17:56
  • Another follow up question (by the way thank you for your patience ): How would you imitate Network connection in a test? Is there a way to set the Network connection similar to how you set the platform? – J. Hesters Oct 03 '18 at 18:12
  • What exactly do you mean by testing the network connection? Do you have some UI showing when there’s no connection? And how it’s implemented? It’s hard to tell without more info and some code. Sounds like a new SO question would be better... – Artal Oct 03 '18 at 18:49
  • Okay did a new question: https://stackoverflow.com/questions/52633466/react-native-simulate-offline-device-in-jest-unit-test. If you check it out, thank you very much! You are helping me a lot – J. Hesters Oct 04 '18 at 07:38
  • this does not work, it always defaults to ios no matter if I tried this in my tests. Also people please take the redundant "should" out of your test names and jys say what it does with action verbs – PositiveGuy Oct 04 '19 at 02:06