jestjs / jest

Delightful JavaScript Testing.
https://jestjs.io
MIT License
44.28k stars 6.47k forks source link

Platform.select defaults to iOS when explicitly setting Platform with jest.mock #4455

Closed turnipdabeets closed 7 years ago

turnipdabeets commented 7 years ago

Below I am testing a component to have a red background in iOS and a blue background in Android. However, the jest snapshot always defaults to iOS if your component uses react native's method: Platform.select.

component.js

import React from 'react';
import {Text, View, Platform, StyleSheet} from 'react-native'

const TestComponent = () => <View style={styles.container}><Text>Hello Jest</Text></View>

const styles = StyleSheet.create({
  container: {
    flex: 1,
    ...Platform.select({
      ios: {
        backgroundColor: 'red',
      },
      android: {
        backgroundColor: 'blue',
      },
    }),
  },
});
export default TestComponent; 

component.spec.js

import 'react-native';
import React from 'react';
import renderer from 'react-test-renderer';
import TestComponent from '.add.js';

jest.mock('Platform', () => {
  const Platform = require.requireActual('Platform');
  Platform.OS = 'android';
  return Platform;
});

describe('TestComponent', () => {
  it('renders default android ui', () => {
    const tree = renderer
      .create(<TestComponent />)
      .toJSON();
    expect(tree).toMatchSnapshot();
  });
});

component.js.snap

// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`TestComponent renders default android ui 1`] = 
<View
  style={
    Object {
      "backgroundColor": "red",
      "flex": 1,
    }
  }
>
  <Text
    accessible={true}
    allowFontScaling={true}
    disabled={false}
    ellipsizeMode="tail"
  >Hello Jest </Text>
</View>;

If I change component.js to the following (removing Platform.select with an alternative option):

import React from 'react';
import { Text, Platform, StyleSheet, View } from 'react-native';

const TestComponent = () =>
  <View style={styles.container}>
    <Text>Hello Jest</Text>
  </View>;

const color = Platform.OS === 'ios' ? 'red' : 'blue';
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: color,
  },
});
export default TestComponent;

npm run test displays the following error showing jest's snapshot is now displaying the desirable blue background for android.

TestComponent › renders default android ui

    expect(value).toMatchSnapshot()

    Received value does not match stored snapshot 1.

    - Snapshot
    + Received

    @@ -1,9 +1,9 @@
      <View
        style={
          Object {
    -       "backgroundColor": "red",
    +       "backgroundColor": "blue",
            "flex": 1,
          }
        }
      >
        <Text

      at Object.<anonymous> (__tests__/components/test.js:15:14)
      at tryCallTwo (node_modules/promise/lib/core.js:45:5)
      at doResolve (node_modules/promise/lib/core.js:200:13)
      at new Promise (node_modules/promise/lib/core.js:66:3)
      at tryCallOne (node_modules/promise/lib/core.js:37:12)
      at node_modules/promise/lib/core.js:123:15

 › 1 snapshot test failed.
  TestComponent
    ✕ renders default android ui (34ms)
cpojer commented 7 years ago

Unfortunately mocking Platform isn't the only thing that requires changing. The default is probably iOS from the preset, but you can provide your own config for the "haste" field that overwrites the default platform: See https://github.com/facebook/react-native/blob/master/jest-preset.json#L3

Try:

  "haste": {
    "defaultPlatform": "android",
    "platforms": ["android", "ios", "native"],
    "providesModuleNodeModules": [
      "react-native"
    ]
  }

In your Android config.

Please note that there is currently no good way to toggle between two configs. You could however use the multi project runner :)

grigored commented 6 years ago

Based on @cpojer 's suggestion, I'm using this solution, works great.

i-oliva commented 5 years ago

Having the same issue, I have an utility function like:

const mockPlatform = (OS, version) => {
  jest.resetModules();
  jest.doMock("Platform", () => ({
    OS,
    select: objs => objs[OS],
    Version: version || undefined
  }));
};

then I do

mockPlatform("android", 23);

inside the test but when I console.log Platform I get:

{ OS: 'ios',
        Version: [Getter],
        isPad: [Getter],
        isTVOS: [Getter],
        isTV: [Getter],
        isTesting: [Getter],
        select: [Function: select] }

and then

{ OS: 'android', select: [Function: select], Version: 23 }

after

i-oliva commented 5 years ago

Fixed by doing

  beforeEach(() => {
    jest.restoreAllMocks();
    mockPlatform("android", 24);
  });
dschinkel commented 5 years ago

Just set Platform.OS in your tests, I did not need a mock, and I like to stay away from mocking frameworks personally.

vyarmak commented 4 years ago

Bumped on the same problem. It seems like haste for now is the only way to solve this. Created a dummy test project to have a possibility to run 2 set of tests (iOS and Android):

https://github.com/vyarmak/react-native-platform-testing

Kjaer commented 4 years ago

haste still works but it prints warning on the console

● Validation Warning:

  Unknown option "haste.providesModuleNodeModules" with value ["react-native"] was found.
  This is probably a typing mistake. Fixing it will remove this message.

  Configuration Documentation:
  https://jestjs.io/docs/configuration.html

● Validation Warning:

  Unknown option "haste.providesModuleNodeModules" with value ["react-native"] was found.
  This is probably a typing mistake. Fixing it will remove this message.

  Configuration Documentation:
  https://jestjs.io/docs/configuration.html
Kjaer commented 4 years ago

Unfortunately mocking Platform isn't the only thing that requires changing. The default is probably iOS from the preset, but you can provide your own config for the "haste" field that overwrites the default platform: See https://github.com/facebook/react-native/blob/master/jest-preset.json#L3

Try:

  "haste": {
    "defaultPlatform": "android",
    "platforms": ["android", "ios", "native"],
    "providesModuleNodeModules": [
      "react-native"
    ]
  }

In your Android config.

Please note that there is currently no good way to toggle between two configs. You could however use the multi project runner :)

hey @cpojer anything new for platform setting?

Kjaer commented 4 years ago

Alright I get going on this: found this: https://github.com/facebook/react-native/blob/master/jest-preset.js

and removed providesModuleNodeModules. My tests are shined again ✨

conor909 commented 3 years ago

@Kjaer So after you set the haste in config, what do you do?

github-actions[bot] commented 3 years ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.