callstack / react-native-testing-library

🦉 Simple and complete React Native testing utilities that encourage good testing practices.
https://callstack.github.io/react-native-testing-library/
MIT License
3.01k stars 264 forks source link

Unexpected token 'export' error when running Jest tests #1625

Closed vkuprin closed 1 month ago

vkuprin commented 1 month ago

Description

I am encountering an "Unexpected token 'export'" error when running Jest tests for a React Native project using React

Native Testing Library. This issue appears to be related to the transformation of ES6 module syntax in certain dependencies.

package.json

{
  "name": "help-me-here",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "@react-native-async-storage/async-storage": "^1.23.1",
    "@react-native-community/netinfo": "^11.3.1",
    "@react-native-community/slider": "^4.5.2",
    "@react-navigation/elements": "^1.3.30",
    "@react-navigation/material-top-tabs": "^6.6.13",
    "@react-navigation/native": "^6.1.17",
    "@react-navigation/stack": "^6.3.29",
    "@reduxjs/toolkit": "^2.2.4",
    "@supabase/supabase-js": "^2.43.1",
    "@types/color": "^3.0.6",
    "@types/react-native-vector-icons": "^6.4.18",
    "axios": "^1.6.8",
    "babel-plugin-styled-components": "^2.1.4",
    "color": "^4.2.3",
    "immer": "^9.0.21",
    "lodash": "^4.17.21",
    "lottie-react-native": "^6.7.2",
    "moment": "^2.30.1",
    "react": "18.2.0",
    "react-native": "0.74.1",
    "react-native-error-boundary": "^1.2.4",
    "react-native-gesture-handler": "^2.16.2",
    "react-native-pager-view": "^6.3.1",
    "react-native-reanimated": "^3.11.0",
    "react-native-safe-area-context": "^4.10.1",
    "react-native-screens": "^3.31.1",
    "react-native-svg": "^15.2.0",
    "react-native-tab-view": "^3.5.2",
    "react-native-url-polyfill": "^2.0.0",
    "react-native-uuid": "^2.0.2",
    "react-native-vector-icons": "^10.1.0",
    "react-native-webrtc": "^118.0.7",
    "react-redux": "^9.1.2",
    "reduce-reducers": "^1.0.4",
    "redux-saga": "^1.3.0",
    "reselect": "^5.1.0",
    "socket.io-client": "^4.7.5",
    "styled-components": "^6.1.10",
    "typesafe-actions": "^5.1.0"
  },
  "devDependencies": {
    "@babel/core": "^7.24.6",
    "@babel/preset-env": "^7.24.6",
    "@babel/preset-react": "^7.24.6",
    "@babel/preset-typescript": "^7.24.6",
    "@babel/runtime": "^7.20.0",
    "@react-native/babel-preset": "0.74.83",
    "@react-native/eslint-config": "0.74.83",
    "@react-native/metro-config": "0.74.83",
    "@react-native/typescript-config": "0.74.83",
    "@testing-library/react-native": "^12.5.1",
    "@types/jest": "^29.5.12",
    "@types/lodash": "^4",
    "@types/react": "^18.2.6",
    "@types/react-native": "^0.73.0",
    "@types/react-test-renderer": "^18.0.0",
    "babel-jest": "^29.7.0",
    "babel-plugin-module-resolver": "^5.0.2",
    "eslint": "^8.19.0",
    "jest": "^29.6.3",
    "jest-environment-jsdom": "^29.7.0",
    "jest-fetch-mock": "^3.0.3",
    "metro-react-native-babel-preset": "^0.77.0",
    "prettier": "2.8.8",
    "react-native-svg-transformer": "^1.3.0",
    "react-test-renderer": "18.2.0",
    "ts-jest": "^29.1.4",
    "typescript": "^5.4.5"
  },
  "engines": {
    "node": ">=18"
  },
  "packageManager": "yarn@4.2.2"
}

babel.config.js

module.exports = {
  presets: [
    'module:@react-native/babel-preset',
    '@babel/preset-react',
  ],
  plugins: [
    ["@babel/plugin-transform-private-methods", { loose: true }],
    ["@babel/plugin-transform-private-property-in-object", { loose: true }],
    ["@babel/plugin-transform-class-properties", { loose: true }],
    'react-native-reanimated/plugin',
    [
      'module-resolver',
      {
        root: ['./src'],
        alias: {},
      },
    ],
  ],
};

jest.config.js

module.exports = {
  preset: 'react-native',
  setupFiles: ['./jestSetup.ts'],
  transform: {
    '^.+\\.[tj]sx?$': 'babel-jest',
  },
  testEnvironment: 'jest-environment-jsdom',
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
  transformIgnorePatterns: [
    'node_modules/(?!(react-native|@react-native|react-native-webrtc|react-native-reanimated|@react-native-community|@testing-library|@babel|metro-react-native-babel-preset)/)',
  ],
};

jestSetup.ts

jest.mock('react-native-webrtc');
jest.mock('node-fetch', () => require('jest-fetch-mock'));

What you did:

Running Jest tests for a React Native project with the configuration and code provided above.

Unexpected token 'export'

There seems to be an issue with configuration that prevents React Native Testing Library from working correctly.

Reproduction:

A minimal reproduction can be created by setting up a new React Native project with the provided Babel and Jest configurations, and attempting to run the example test file.

Problem description:

The current behavior is problematic because Jest is unable to process the ES6 module syntax (e.g., export) in certain dependencies, causing the tests to fail.

mdjastrzebski commented 1 month ago

Looks like Jest/Babel config problem. Please compare your setup with our example app: https://github.com/callstack/react-native-testing-library/tree/main/examples/basic

You have quite complex Babel config so my advice would be to start gradually moving your Babel & Jest config to the example app and checking which rules (added or removed) start causing the error. This way you should be able to narrow down your issue and hopefully fix it. Good luck! 🍀

vkuprin commented 1 month ago

Was able to fix the problem by reusing the example from: https://github.com/callstack/react-native-testing-library/tree/main/examples/basic

jest.setup.ts

import "@testing-library/react-native/extend-expect";

jest.mock("react-native/Libraries/Animated/NativeAnimatedHelper");
jest.mock("react-native-webrtc");
jest.mock("node-fetch", () => require("jest-fetch-mock"));

jest.config.js

module.exports = {
  preset: "@testing-library/react-native",
  moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json"],
  setupFilesAfterEnv: ["<rootDir>/jestSetup.ts"],
};

babel.config.js

module.exports = {
  presets: ["module:@react-native/babel-preset"],
  plugins: [
    "react-native-reanimated/plugin",
    "babel-plugin-styled-components",
    [
      "module-resolver",
      {
        root: ["./src"],
        alias: {},
      },
    ],
  ],
};

Seems the issue was with conflicting babel presets