styled-components / jest-styled-components

🔧 💅 Jest utilities for Styled Components
MIT License
1.59k stars 145 forks source link

React-Native: TypeError: Cannot read property 'filter' of undefined #110

Open ghost opened 6 years ago

ghost commented 6 years ago

I cannot use the toHaveStyleRule function, it throws errors.

import React from 'react'
import { shallow } from 'enzyme'
import 'jest-styled-components/native'

import C from './C'

describe('<C>', () => {
  it('should set the top padding', () => {
    const component = shallow(<C {...props} />)
    expect(component).toHaveStyleRule('paddingTop', expectedPadding)
  })
})

The resulting error is

    TypeError: Cannot read property 'filter' of undefined

      at Object.<anonymous> (src/components/C/C.test.js:64:19)
      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

I am using following dependencies:

    "enzyme": "^3.3.0",
    "enzyme-adapter-react-16": "^1.1.1",
    "enzyme-to-json": "^3.3.0",
    "jest-expo": "23.0.0",
    "jest-styled-components": "^4.10.0",

config

  "jest": {
    "preset": "jest-expo",
    "snapshotSerializers": [
      "enzyme-to-json/serializer"
    ]
  },
vitbokisch commented 6 years ago

I'm having similar issue.

package.json:

    "enzyme": "^3.3.0",
    "enzyme-adapter-react-16": "^1.1.1",
    "enzyme-to-json": "^3.3.0",
    "jest-enzyme": "^4.0.2",
    "jest-expo": "^24.0.0",
    "jest-styled-components": "^4.10.0",

I got an error:

    /projectDir/node_modules/jest-styled-components/src/native/toHaveStyleRule.js:15
      const mergedStyles = styles.reduce((acc, item) => ({ ...acc, ...item }), {})
                                                           ^^^
    SyntaxError: Unexpected token ...

      at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:305:17)
      at Object.<anonymous> (node_modules/jest-styled-components/native/index.js:1:114)
      at Object.<anonymous> (setupTest.js:2:1)

jest.config.js:

module.exports = {
  preset: 'jest-expo',
  verbose: true,
  setupFiles: ['./jest.setup.js'],
  snapshotSerializers: ['enzyme-to-json/serializer'],
  setupTestFrameworkScriptFile: './setupTest.js',
}

It seems, it breaks because of spread operator. When using Object.assign instead I can run my test.

vitbokisch commented 6 years ago

In my case I was using const tree = renderer.create(<Component />).toJSON(), now I switched to enzyme shallow and getting the same error as mentioned by @MoeSattler

vitbokisch commented 6 years ago

Everything works when invoking toJSON function from enzyme-to-json manually.

import toJSON from 'enzyme-to-json'

const tree = toJSON(shallow(<Component />))
expect(tree).toMatchSnapshot()
expect(tree).toHaveStyleRule('color', 'red')

So, as a workaround I've created custom shallow function in jest.setup.js, so I don't have to invoke it manually in each file anymore:

import Enzyme, { shallow } from 'enzyme'
import toJSON from 'enzyme-to-json'

Enzyme.configure({ adapter: new Adapter() })

const customShallow = (tree) => {
  return toJSON(shallow(tree))
}

global.shallow = customShallow
Kureev commented 6 years ago

Hey there! I didn't design it having enzyme in mind tbh, but I'm glad you figured out! For me, it feels like the problem that you experience @vitbokisch is related to your jest run environment rather than to the toHaveStyleRule matcher itself. Try to run it using babel-node (I believe I tested it with it), it should do the trick.

rodcisal commented 6 years ago

I was having the same issue.

Apparently, it's because of the difference in the versions of NODE. I had 6.x.x on my server and 9.x.x locally (where they were passing)

marcelkalveram commented 5 years ago

I had several issues: 1) it looked like the global serialization config (as described here: https://github.com/styled-components/jest-styled-components#enzyme) didn't work, so I had to use enzyme-to-json and call toJson from my file. 2) second, I was using styled-components and I had to call dive() to get access to the actual component that I can use the toHaveStyleRule call on.

So I ended up writing a helper function like this:

import toJSON from "enzyme-to-json"

const shallowDive = component => {
    return toJSON(shallow(component).dive())
}
throoze commented 3 years ago

Hi! i'm getting the same issue with the latest stable version of react native and everything that comes prepackaged, with the following configuration:

package.json:

{
  "dependencies": {
    "@react-native-community/masked-view": "^0.1.10",
    "@react-native-firebase/app": "^10.8.0",
    "@react-native-firebase/auth": "^10.8.0",
    "@react-navigation/drawer": "^5.12.3",
    "@react-navigation/native": "^5.9.2",
    "@react-navigation/stack": "^5.14.2",
    "formik": "^2.2.6",
    "react": "16.13.1",
    "react-native": "0.63.4",
    "react-native-gesture-handler": "^1.10.1",
    "react-native-paper": "^4.7.1",
    "react-native-reanimated": "^1.13.2",
    "react-native-safe-area-context": "^3.1.9",
    "react-native-screens": "^2.17.1",
    "react-native-svg": "^12.1.0",
    "react-native-vector-icons": "^8.0.0",
    "styled-components": "^5.2.1",
    "yup": "^0.32.9"
  },
  "devDependencies": {
    "@babel/core": "^7.8.4",
    "@babel/runtime": "^7.8.4",
    "@react-native-community/eslint-config": "^1.1.0",
    "babel-jest": "^25.1.0",
    "enzyme": "^3.11.0",
    "enzyme-adapter-react-16": "^1.15.6",
    "enzyme-to-json": "^3.6.1",
    "eslint": "^6.5.1",
    "jest": "^25.1.0",
    "jest-environment-enzyme": "^7.1.2",
    "jest-enzyme": "^7.1.2",
    "jest-styled-components": "^7.0.3",
    "metro-react-native-babel-preset": "^0.59.0",
    "react-dom": "16.13.1",
    "react-native-svg-transformer": "^0.14.3",
    "react-test-renderer": "16.13.1"
  },
  "jest": {
    "preset": "react-native",
    "transform": {
      "^.+\\.js$": "<rootDir>/node_modules/react-native/jest/preprocessor.js"
    },
    "testEnvironment": "enzyme",
    "testEnvironmentOptions": {
      "enzymeAdapter": "react16"
    },
    "collectCoverage": true,
    "collectCoverageFrom": [
      "<rootDir>/src/**/**.js"
    ],
    "coverageThreshold": {
      "global": {
        "branches": 90,
        "functions": 90,
        "lines": 90,
        "statements": 90
      }
    },
    "setupFilesAfterEnv": [
      "jest-enzyme",
      "<rootDir>/jest.setup.js",
      "<rootDir>/node_modules/react-native-gesture-handler/jestSetup.js"
    ],
    "transformIgnorePatterns": [
      "node_modules/(?!(jest-)?react-native|@?react-navigation)"
    ]
  },
  "resolutions": {
    "styled-components": "^5"
  }
}

babel.config.js:

module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  env: {
    production: {
      plugins: ['react-native-paper/babel'],
    },
  },
};

jest.setup.js:

import 'react-native-gesture-handler/jestSetup';
import 'jest-styled-components/native'; // doesn't work!
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

const {JSDOM} = require('jsdom');

const jsdom = new JSDOM();
const {window} = jsdom;

function copyProps(src, target) {
  const props = Object.getOwnPropertyNames(src)
    .filter((prop) => typeof target[prop] === 'undefined')
    .map((prop) => Object.getOwnPropertyDescriptor(src, prop));
  Object.defineProperties(target, props);
}

global.window = window;
global.document = window.document;
global.navigator = {
  userAgent: 'node.js',
};

copyProps(window, global);

jest.mock('react-native-reanimated', () => {
  const Reanimated = require('react-native-reanimated/mock');

  // The mock for `call` immediately calls the callback which is incorrect
  // So we override it with a no-op
  Reanimated.default.call = () => {};

  return Reanimated;
});
import {NativeModules} from 'react-native';

NativeModules.RNGestureHandlerModule = {
  attachGestureHandler: jest.fn(),
  createGestureHandler: jest.fn(),
  dropGestureHandler: jest.fn(),
  updateGestureHandler: jest.fn(),
  State: {},
  Directions: {},
};
NativeModules.ImagePickerManager = {
  showImagePicker: jest.fn(),
};
NativeModules.Linking = {
  canOpenUrl: jest.fn().mockResolvedValue(true),
  openUrl: jest.fn().mockResolvedValue(true),
};
NativeModules.Platform = {
  OS: 'iOS',
};

jest.mock('react-native/Libraries/Animated/src/NativeAnimatedHelper');
jest.mock('react-native/Libraries/Animated/src/animations/TimingAnimation');

Enzyme.configure({adapter: new Adapter()});

I've been reading both this repo's issues, and stack overflow answers, but couldn't find any solution...The error i'm getting is:

TypeError: Cannot read property 'filter' of undefined

    at Object.toHaveStyleRule (.../node_modules/jest-styled-components/src/native/toHaveStyleRule.js:4:40)
    at __EXTERNAL_MATCHER_TRAP__ (.../node_modules/expect/build/index.js:342:30)
    at Object.throwingMatcher (.../node_modules/expect/build/index.js:343:15)
    at .../src/components/atoms/snackbar/snackbar.style.spec.js:26:60
    at Object.<anonymous> (.../node_modules/jest-each/build/bind.js:76:13)
    at Object.asyncJestTest (.../node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:100:37)
    at .../node_modules/jest-jasmine2/build/queueRunner.js:45:12
    at new Promise (<anonymous>)
    at mapper (.../node_modules/jest-jasmine2/build/queueRunner.js:28:19)
    at .../node_modules/jest-jasmine2/build/queueRunner.js:75:41
    at processTicksAndRejections (internal/process/task_queues.js:93:5)

Any help would be appreciated!