0

I have some issues from Unit testing with Jest and Enzyme. I'm using React with Typescript, Emotion Framework. I added a simple unit test on Button Component just to see if the test works.

// button.test.tsx
import React from 'react'
import { shallow } from 'enzyme'
import { Button } from '../button'

describe('First React component test with Enzyme', () => {
  it('renders without crashing', () => {
    shallow(<Button/>);
  })
})
// button.tsx
import React from 'react'
import { FunctionComponent, HTMLAttributes, ButtonHTMLAttributes } from 'react'
import { Icon } from '~/components'
import { styles } from './styles'

interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  onClick?: () => any

  icon?: string
  color?: 'default' | 'accent' | 'accent-dark' | 'dark'
  size?: 'small' | 'medium' | 'big' | 'large'
  iconSize?: number
  withShadow?: boolean
}

export const Button: FunctionComponent<ButtonProps> = ({
  onClick,
  color = 'default',
  size = 'medium',
  icon,
  withShadow,
  children,
  ...props
}) => (
  <button
    css={[
      styles.base,
      styles.colors[color],
      styles.sizes[size],
      withShadow && styles.withShadow,
    ]}
    onClick={onClick}
    {...props}
  >
    {icon && (
      <span css={styles.icon[color === 'dark' ? 'dark' : 'default']}>
        <Icon icon={icon} size={color === 'dark' ? 16 : 12} />
      </span>
    )}
    {children}
  </button>
)

And the error is shown as follows:

● Test suite failed to run

    ReferenceError: localStorage is not defined

      12 | 
      13 |   constructor() {
    > 14 |     this.token = localStorage.getItem('token') || null
         |                  ^
      15 | 
      16 |     if (this.token) {
      17 |       this.profile = this.decodeToken(this.token)

      at new UserStore (src/stores/user.ts:14:18)
      at Object.<anonymous> (src/stores/index.ts:4:26)
      at Object.<anonymous> (src/components/header/index.tsx:5:1)

I'm using "jsx":"preserve" for typescript compile option for build and "jsx":"react" for typescript compile option for jest.

// tsConfig.js
{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "lib": ["esnext", "dom"],
    "jsx": "preserve",
    "types": ["@emotion/core", "@types/node", "jest"],

    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,

    "baseUrl": "./",
    "paths": {
      "~/*": ["src/*"]
    }
  },
  "presets": ["@babel/preset-env", "@babel/preset-react"],
  "include": ["src"]
}

Here's jest Configuration:

// jestConfig.js

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  testMatch: ['**/src/**/*.test.ts?(x)', '**/?(*.)+(test).ts?(x)'],
  moduleNameMapper: {
    '\\.(jpg|jpeg|png|svg|ttf|woff|woff2)$':
      '<rootDir>/asset-transformer.js',
    '~(.*)$': '<rootDir>/src/$1'
  },
  globals: {
    'ts-jest': {
      tsConfig: {
        jsx: "react"
      }
    }
  },
  snapshotSerializers: [
      "enzyme-to-json/serializer"
    ],
    setupFiles: [
      "./test-shim.js",
      "./test-setup.js"
    ],
    moduleFileExtensions: [
      "ts",
      "tsx",
      "js",
      "jsx"
    ],
    transform: {
      "^.+\\.js(x)$": "babel-jest",
      "^.+\\.(ts|tsx)$": "./test-preprocessor.js"
    },
    testMatch: [
      "**/*.test.(ts|tsx|js|jsx)"
    ]
}
// .babel.rc
{
  "plugins": [
    [
      "transform-inline-environment-variables"
    ],
    [
      "babel-plugin-jsx-pragmatic",
      {
        "export": "jsx",
        "module": "@emotion/core",
        "import": "___EmotionJSX"
      }
    ],
    [
      "@babel/plugin-transform-react-jsx",
      {
        "pragma": "___EmotionJSX",
        "pragmaFrag": "React.Fragment"
      }
    ],
    [
      "emotion",
      {
        "sourceMap": true,
        "autoLabel": false,
        "cssPropOptimization": true
      }
    ],
    [
      "@babel/plugin-syntax-dynamic-import"
    ]
  ],
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": false,
        "targets": {
          "node": "current"
        }
      }
    ],
    "@babel/preset-typescript"
  ],
}
skyboyer
  • 22,209
  • 7
  • 57
  • 64
Ever Dev
  • 1,882
  • 2
  • 14
  • 34

1 Answers1

1

There are a few ways when it comes to getting around this issue, but the idea is to make use of mocking/spying.

1) spying on the prototype of localStorage. There was a discussion on github related to this problem.

jest.spyOn(window.localStorage.__proto__, 'setItem');

2) mocking the methods of localStorage itself. They are sufficiently covered on this other post on Stackoverflow.

wentjun
  • 40,384
  • 10
  • 95
  • 107
  • I tried all of them, but I'm testing only one component - `button` that does not require localStorage. I think it's a problem with configuration. Thank you. – Ever Dev Sep 13 '19 at 19:53