ben-rogerson / twin.macro

🦹‍♂️ Twin blends the magic of Tailwind with the flexibility of css-in-js (emotion, styled-components, solid-styled-components, stitches and goober) at build time.
MIT License
7.92k stars 183 forks source link

Jest test in Next return error when use tw&styled from twin.macro #717

Closed dianyuyi closed 2 years ago

dianyuyi commented 2 years ago

Describe

When I test the component with jest, it returns an error. I'm using Nextjs to build my project, and structure just like

|--pages
|--src
|        |---components
|               |---blocks
|                      |---home
|                            |---index
|                            |---styled

This is my test file in src/components/blocks/home

/**
 * @jest-environment jsdom
 */

import React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom/extend-expect'
import 'jest-styled-components'
import '@testing-library/jest-dom'
import { StoreContainer } from './styled'

test('it works', () => {
  const { container } = render(<StoreContainer />)
  expect(container).toMatchSnapshot()
})

And It will return

    TypeError: Cannot read properties of undefined (reading 'section')
}
       6 |
    >  7 | export const StoreContainer = styled.section`
         |                                      ^
       8 |   ${tw`relative w-screen h-[110vh] overflow-hidden sm:h-[130vh] lg:h-[120vh] `}
       9 | `

I try to replace

import tw, {styled} from 'twin.macro'

to

import styled from 'styled-components'
import tw from 'twin.macro'

Then return another error

MacroError: The macro you imported from "undefined" is being executed outside the context of compilation with 
babel-plugin-macros. This indicates that you don't have the babel plugin "babel-plugin-macros" configured 
correctly. Please see the documentation for how to configure babel-plugin-macros properly: 
https://github.com/kentcdodds/babel-plugin-macros/blob/master/other/docs/user.md

      14 |
      15 | const StoreContainer = styled.div`
    > 16 |   ${tw`block`}
         |       ^
      17 | `
      18 |

finally, when I replace code to

import styled from 'styled-components'

const StoreContainer = styled.div`
  display: block
`

...it works. But I can't do this because that means the styled components of the whole project have to be rewritten.

Configs check

I checked all configs and try to fix them, but no method works. My configs below

Related packages

    "next": "^12.0.10",
    "react": "17.0.2",

    "@babel/plugin-transform-react-jsx": "^7.17.12",
    "@babel/preset-env": "^7.16.11",
    "@types/enzyme": "^3.10.11",
    "@types/jest": "^28.1.1",
    "@types/node": "16.11.12",
    "@types/react": "17.0.2",
    "@types/styled-components": "^5.1.24",
    "@wojtekmaj/enzyme-adapter-react-17": "^0.6.6",
    "babel-jest": "^28.1.0",
    "babel-plugin-macros": "^3.1.0",
    "babel-plugin-styled-components": "^2.0.7",
    "babel-plugin-twin": "^1.0.2",

    "enzyme": "^3.11.0",
    "enzyme-to-json": "^3.6.2",
    "jest": "^28.1.1",
    "jest-specific-snapshot": "^5.0.0",
    "jest-styled-components": "^7.0.8",

    "styled-components": "^5.3.3",
    "stylelint-processor-styled-components": "^1.10.0",
    "tailwindcss": "^3.0.12",
    "ts-jest": "^28.0.4",
    "ts-node": "^10.8.1",
    "twin.macro": "^2.8.2",
    "typescript": "4.5.3"
  }

tsconfig.json

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "src/*": ["src/*"]
    },
    "target": "es6",
    "module": "esnext",
    "strict": false,
    "allowJs": true,
    "jsx": "preserve",
    "noEmit": true,
    "esModuleInterop": true,
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "isolatedModules": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "noImplicitAny": false,
    "incremental": true,
    "lib": ["es6", "dom", "esnext"]
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules", ".next", "dist"]
}

jest.config

import NextJest from 'next/jest'

const createJestConfig = NextJest({
  dir: './',
})

const customJestConfig = {
  preset: 'ts-jest',
  globals: {
    'ts-jest': {
      tsconfig: '<rootDir>/tsconfig.json',
      babelConfig: '.babelrc.js',
      useESM: true,
    },
  },
  setupFiles: ['raf/polyfill'],
  setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
  modulePaths: ['<rootDir>/src'],
  testPathIgnorePatterns: [
    '<rootDir>/dist/',
    '<rootDir>/node_modules/',
    '<rootDir>/.next/',
    'node_modules/(?!swiper|ssr-window|dom7).*/',
    '<rootDir>/tailwind.config.*/',
  ],
  transformIgnorePatterns: ['<rootDir>/node_modules/?!(swiper)'],
  moduleNameMapper: {
    '~/(.*)': '<rootDir>/src/',
    '\\.(css|less)$': 'identity-obj-proxy',
    '^config(.*)$': '<rootDir>/config$1',
    '^src(.*)$': '<rootDir>/src$1',
    '^pages(.*)$': '<rootDir>/pages$1',
    '^server(.*)$': '<rootDir>/server$1',
    '^@hook(.*)$': '<rootDir>/src/hook$1',
    '^@components(.*)$': '<rootDir>/src/components$1',
  },
  snapshotSerializers: ['enzyme-to-json/serializer'],
  transform: {
    '^.+\\.(js|jsx|ts|tsx)$': [
      'babel-jest',
      'babel-plugin-styled-components',
      { presets: ['next/babel'] },
    ],
  },
  collectCoverageFrom: [
    './{src,pages,server}/**/*.{ts,tsx,js,jsx}',
    '!**/node_modules/**',
    '!**/dist/**',
    '!**/coverage/**',
    '!**/{config,constants,styles,types,__fixtures__}/**',
  ],
  watchPathIgnorePatterns: ['dist'],
}
module.exports = createJestConfig(customJestConfig)

jest.setup.ts

import Enzyme from 'enzyme'
import Adapter from '@wojtekmaj/enzyme-adapter-react-17'
import '@testing-library/jest-dom/extend-expect'

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

.babelrc.js

module.exports = {
  presets: [['next/babel', { 'preset-react': { runtime: 'automatic' } }]],
  plugins: [
    'babel-plugin-macros',
    [
      'babel-plugin-styled-components',
      {
        ssr: true,
        minify: true,
        transpileTemplateLiterals: true,
        pure: true,
        displayName: true,
        preprocess: false,
      },
    ],
  ],
}

I tried to change all the settings that might cause the error but in vain. Can anyone help me, please?

ben-rogerson commented 2 years ago

Hey there

This looks like a duplicate of #697, could you please confirm if that fixes the issue here?

dianyuyi commented 2 years ago

Thank you very much. The error was successfully fixed.

The origin setup is transform: { '^.+\\.(js|jsx|ts|tsx)$': [ 'babel-jest', 'babel-plugin-styled-components', { presets: ['next/babel'] }, ], }

Then replace to below will work transform: { '^.+\\.(js|jsx|ts|tsx|mjs)$': 'babel-jest', }

Not sure why the original project was set up like this, but I think it tells us that sometimes simplicity is best...