jest-styled-components icon indicating copy to clipboard operation
jest-styled-components copied to clipboard

Cannot read property 'style' or 'filter' of undefined (react-native)

Open gretzky opened this issue 5 years ago ā€¢ 12 comments

Probably related to #110.

I have themed styled-components for React Native and am testing them with just jest and react-test-renderer. The test for this example should pass, according to the theming section of the docs:

import { View } from 'react-native'
import renderer from 'react-test-renderer'
import styled from 'styled-components'

const theme = {
  primary: '#000000'
}

const Foo = styled(View)`
  background-color: ${props => props.theme.primary};
`

describe('Foo', () => {
  it('renders the background color', () => {
    const tree = renderer.create(<Foo theme={theme} />).toJSON()
    expect(tree).toHaveStyleRule('background-color', '#000000')
  })
})

I receive TypeError: Cannot read property 'filter' of undefined, which stems from here but I couldn't tell you why it won't filter through the style prop.

I have jest-styled-components/native imported in a test setup file, but other than that I have no other configs. Node versions are the same, packages are all latest versions.

gretzky avatar Jan 15 '19 17:01 gretzky

Did you have any luck resolving this issue? I am also experiencing the same problem.

connected-space avatar Feb 18 '19 17:02 connected-space

@connected-space Unfortunately I didn't and it's now a blocker for me. Hopefully someone will chime in soon.

gretzky avatar Feb 21 '19 15:02 gretzky

Same problem here:

 TypeError: Cannot read property 'filter' of undefined

      16 |       // Assert
      17 |       expect(getByTestId('label').props.children).toBe(label);
    > 18 |       expect(getByTestId('wrapper')).toHaveStyleRule('color', 'red');
         |                                      ^
      19 |     });
      20 |   });
      21 | });

      at Object.toHaveStyleRule (node_modules/jest-styled-components/src/native/toHaveStyleRule.js:4:38)
      at Object.throwingMatcher (node_modules/expect/build/index.js:320:33)
      at Object.<anonymous> (src/components/atoms/DatePicker/DatePicker.test.js:18:38)

I am also using react-native-testing-library in my tests.

mdapper avatar Mar 28 '19 15:03 mdapper

I don't know about other versions of styled-components with react-native, but I'm using version 4.2 and using jest-styled-components in react-native the styles are no longer added as a prop to the actual component. If using react-test-renderer you can find your component in the result of calling toJSON() and it'll work:

// ...
import renderer from 'react-test-renderer'
import 'jest-styled-components'

const StyledButton = styled(Button)`
  background-color: red;
`

test('nested styles', () => {
  const jsonTree = renderer.create(<View><StyledButton>Test</StyledButton></View>)
  const buttonComponent = jsonTree.children[0]
  expect(buttonComponent).toHaveStyleRule('background-color','red')
})

However, this isn't practical if you're using react-native-testing-library or native-testing-library, or even if you want to find a deeply nested native component to check the styles on it, so I found the following work-around:

  1. Don't use jest-styled-components/native but jest-styled-components
  2. Force the component $$typeof property to act as if it were being generated from a toJSON() tree, like in the following example:
// ...
import { render } from 'native-testing-library'
import 'jest-styled-components'

const StyledButton = styled(Button)`
  background-color: red;
`

test('nested styles', () => {
  const { getByTestId } = render(<View><StyledButton testID='myTestTarget'>Test</StyledButton></View>)
  const buttonComponent = getByTestId('myTestTarget')
  // This below is the work-around, is basically faking the component as a 'react.test.json' one
  buttonComponent.$$typeof = Symbol.for('react.test.json')
  expect(buttonComponent).toHaveStyleRule('background-color','red')
})

This is obviously not ideal, but it'll work while jest-styled-component adds support for v4 of styled-components including for react-native

elyalvarado avatar May 01 '19 19:05 elyalvarado

Iā€™m having the same issue.

My environment is:

"jest": "24.9.0",
"jest-expo": "^36.0.1",
"jest-styled-components": "^7.0.0",
"styled-components": "5.0.0",
"react-native-testing-library": "^1.12.0"

renanmav avatar Jan 25 '20 20:01 renanmav

Who are the maintainers of this?? I'm still seeing this same issue, over a year later. šŸ˜­

gretzky avatar Feb 11 '20 18:02 gretzky

`ā— should render

TypeError: Cannot read property 'primary' of undefined

   5 | console.log(theme.colors.primary);
   6 | const H1 = styled.h1`
>  7 |   color: ${props => props.theme.colors.primary};
     |                                        ^
   8 |   font-size: ${props => props.theme.fontSize.heading1};
   9 |   font-weight: ${props => props.theme.fontWeight.bold};
  10 |   text-align: center;

`

Is there any solution for this? Im also facing same issue. Using styled components and trying to test it with react testing library... throws me error

im stuck with this! :(

pawankanhere avatar Feb 25 '20 01:02 pawankanhere

I had experienced this problem too. Then I realized I had accidentally imported styled from styled-components and not styled-components/native when styling the component I was testing.

Hope that helps someone.

jrapala avatar Apr 29 '20 01:04 jrapala

Same here :(

belgamo avatar Jun 15 '20 14:06 belgamo

@jrapala Thanks! For me, I was using styled-components/native for all my components, but things like DefaultTheme and ThemeProvider was being imported from styled-components! I didn't know styled-components/native also had ThemeProvider and all

tony3918 avatar Jan 08 '21 04:01 tony3918

I had the same error, but I was able to fix it by changing the folder name from styled-components to styledComponents. So make sure there's no file or folder named styled-components.

dieudonneAwa avatar Feb 18 '21 09:02 dieudonneAwa

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!

throoze avatar Feb 23 '21 02:02 throoze