PaulLeCam / react-leaflet

React components for Leaflet maps
https://react-leaflet.js.org
Other
5.1k stars 884 forks source link

Jest can't parse v4.0.0-beta.0 #977

Closed SwedishBarbarossa closed 2 years ago

SwedishBarbarossa commented 2 years ago

The beta works fine in the browser, but Jest does not like it currently.

Bug report

Before opening an issue, make sure to read the contributing guide and understand this is a bug tracker, not a support platform.

Please make sure to check the following boxes before submitting an issue.\ Issues opened without using this template will be closed unless they have a good reason not to follow this template.

Expected behavior

Running Jest works out of the box.

Actual behavior

Jest encounters an unexpected token.

project_folder/node_modules/react-leaflet/lib/index.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){export { useMap, useMapEvent, useMapEvents } from './hooks.js';
                                                                                      ^^^^^^

    SyntaxError: Unexpected token 'export'

      3 | import React from 'react';
      4 | import { Button } from 'react-bootstrap';
    > 5 | import {
        | ^
      6 |     MapContainer, Marker, Popup, TileLayer, useMapEvent
      7 | } from 'react-leaflet';
      8 | import { MapPin } from 'Assets';

      at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1728:14)
      at Object.<anonymous> (src/Profile/Map.tsx:5:1)

Steps to reproduce

Import react-leaflet in file. Import file in test. Run Jest on test.

My setup:

# package.json
{
  "name": "proj",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@material-ui/core": "^4.12.4",
    "axios": "^0.26.1",
    "bootstrap": "^5.1.3",
    "jquery": "^3.6.0",
    "js-cookie": "^3.0.1",
    "react": "^18.0.0",
    "react-bootstrap": "^2.2.3",
    "react-calendar": "^3.7.0",
    "react-dom": "^18.0.0",
    "react-leaflet": "^4.0.0-beta.0",
    "react-scripts": "^5.0.0",
    "web-vitals": "^2.1.4",
    "whatwg-fetch": "^3.6.2"
  },
  "scripts": {
    "start": "vite",
    "build": "vite build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "lint": "eslint"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "@testing-library/jest-dom": "^5.16.4",
    "@testing-library/react": "^13.0.0",
    "@testing-library/user-event": "^14.0.4",
    "@types/jest": "^27.4.1",
    "@types/js-cookie": "^3.0.1",
    "@types/node": "^17.0.23",
    "@types/react": "^17.0.43",
    "@types/react-dom": "^17.0.14",
    "@typescript-eslint/eslint-plugin": "^5.18.0",
    "@typescript-eslint/parser": "^5.18.0",
    "@vitejs/plugin-react-refresh": "^1.3.6",
    "eslint": "^8.12.0",
    "eslint-config-airbnb": "^19.0.4",
    "eslint-config-airbnb-typescript": "^17.0.0",
    "eslint-import-resolver-typescript": "^2.7.1",
    "eslint-plugin-import": "^2.26.0",
    "eslint-plugin-jsx-a11y": "^6.5.1",
    "eslint-plugin-react": "^7.29.4",
    "eslint-plugin-react-hooks": "^4.4.0",
    "typescript": "^4.6.3",
    "vite": "^2.9.1",
    "vite-plugin-svgr": "^1.1.0"
  }
}
# tsconfig.json
{
    "compilerOptions": {
        "noImplicitAny": false /* Raise error on expressions and declarations with an implied 'any' type. */,
        "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,
        "sourceMap": true /* Generates corresponding '.map' file. */,
        "target": "es2021" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */,
        "jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
        "types": [
            "react",
            "node"
        ] /* Type declaration files to be included in compilation. */,
        "module": "esNext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
        "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
        "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
        "declaration": false /* Generates corresponding '.d.ts' file. */,
        "removeComments": true /* Do not emit comments to output. */,
        "noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
        "noUnusedLocals": false /* Report errors on unused locals. */,
        "strict": true /* Enable all strict type-checking options. */,
        "outDir": "dist" /* Redirect output structure to the directory. */,
        "baseUrl": "./src" /* Base directory to resolve non-absolute module names. */,
        "typeRoots": [
            "src/@types",
            "node_modules/@types"
        ] /* List of folders to include type definitions from. */,
        "strictNullChecks": true /* Enable strict null checks. */,
        "allowJs": true /* Allow javascript files to be compiled. *//* ,
        "composite": true */,
        "lib": ["DOM", "DOM.Iterable", "ESNext"],
        "useDefineForClassFields": true,
        "skipLibCheck": false,
        "esModuleInterop": false,
        "forceConsistentCasingInFileNames": true,
        "resolveJsonModule": true,
        "isolatedModules": true,
        "noEmit": true,
    },
    "exclude": [
        "*/build/**",
        "*/dist/**",
        "*/node_modules/**"
    ],
    "include": [
        "src",
        "vite.config.ts"
    ],
}
# vite.config.ts
import reactRefresh from '@vitejs/plugin-react-refresh';
import path from 'path';
import { defineConfig } from 'vite';
import svgr from 'vite-plugin-svgr';

export default defineConfig({
    resolve: {
        alias: {
            Assets: path.resolve(__dirname, "src/Assets"),
            CustomDropdown: path.resolve(__dirname, "src/CustomDropdown"),
            Profile: path.resolve(__dirname, "src/Profile"),
            Utils: path.resolve(__dirname, "src/Utils"),
            CustomComponents: path.resolve(__dirname, "src/CustomComponents/")
        }
    },
    plugins: [svgr(), reactRefresh()]
});
PaulLeCam commented 2 years ago

v4 exports ES modules, you need to configure Jest accordingly, see https://jestjs.io/docs/ecmascript-modules

slutske22 commented 2 years ago

@SwedishBarbarossa did you figure out a solution for this? I am struggling to get this working

SwedishBarbarossa commented 2 years ago

@slutske22 nah, just disabled the tests of modules importing the map for now. In last stretch before publishing a product, so these tests take a back seat.

gustavoalvesleao commented 2 years ago

@slutske22 did you figure out?

slutske22 commented 2 years ago

@gustavoalvesleao I screwed around alot with various versions of jest and babel, as well as with a million different ways to configure babel, typescript, and jest. Ultimately I got something working. I don't remember exactly what I did, but you can take a look at my setup for the repo I was testing in here: https://github.com/slutske22/react-esri-leaflet

It was sort of a whirlwind, and what I think I came up with in the end was adding this to package.json:

  "jest": {
    "transform": {
      "\\.js$": [
        "ts-jest"
      ]
    },
    "preset": "ts-jest",
    "testEnvironment": "jsdom",
    "transformIgnorePatterns": [
      "node_modules/(?!react-leaflet)/"
    ]
  }

And in my tsconfig:

{
  "compilerOptions": {
    "moduleResolution": "node",
    "target": "ES2020",
    "allowJs": true,
    "module": "esnext",
    "declaration": true,
    "jsx": "react",
    "allowSyntheticDefaultImports": true,
    "outDir": "build",
    "lib": ["dom", "esnext"],
    "esModuleInterop": true
  },
  "exclude": ["node_modules", "examples"]
}

I can't remember what other changes I kept or ditched, but check out that repo and mind the package versions, and .babelrc (Which has a lot in there for other reasons).

gustavoalvesleao commented 2 years ago

@slutske22 thank you. I got different errors, but I'll try to continue from here.

Update**: In the end all I had to do was to add in my package.json:

"jest": {
    "transformIgnorePatterns": [
      "node_modules/(?!react-leaflet)/"
    ]
  }

My app was built with the default create-react-app.

AurinoJunior commented 2 years ago

@slutske22 thank you. I got different errors, but I'll try to continue from here.

Update**: In the end all I had to do was to add in my package.json:

"jest": {
    "transformIgnorePatterns": [
      "node_modules/(?!react-leaflet)/"
    ]
  }

My app was built with the default create-react-app.

I have the same problem but this solution didn't work for me, does anyone have another tip?

albjeremias commented 1 year ago

this bug is ridiculous.. doesnt make sense.. any cool fork?

gsehrlich commented 1 year ago

@slutske22 thank you. I got different errors, but I'll try to continue from here. Update**: In the end all I had to do was to add in my package.json:

"jest": {
    "transformIgnorePatterns": [
      "node_modules/(?!react-leaflet)/"
    ]
  }

My app was built with the default create-react-app.

I have the same problem but this solution didn't work for me, does anyone have another tip?

It didn't work for me either (also create-react-app). I needed to add @react-leaflet as well (thanks to this answer):

  "jest": {
    "transformIgnorePatterns": ["/node_modules/(?!react-leaflet|@react-leaflet)"]
  }

@slutske22 Thanks, your answer got me going in the right direction.

doougui commented 1 year ago

I tried this but still got the same error. My jest.config file looks like this:

// eslint-disable-next-line @typescript-eslint/no-var-requires
const nextJest = require('next/jest')

const createJestConfig = nextJest({
  // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
  dir: './'
})

const customJestConfig = {
  // Add more setup options before each test is run
  // setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  // if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work
  testEnvironment: 'jest-environment-jsdom',
  testPathIgnorePatterns: ['/node_modules/', '/.next/'],
  collectCoverage: true,
  collectCoverageFrom: [
    'src/**/*.ts(x)?',
    '!src/pages/**/*.tsx',
    '!src/styles/**/*.ts',
    '!src/types/**/*.d.ts',
    '!src/**/mock.ts',
    '!src/**/types.ts'
  ],
  setupFiles: ['<rootDir>/.jest/setEnvVars.ts'],
  setupFilesAfterEnv: ['<rootDir>/.jest/setup.ts'],
  modulePaths: ['<rootDir>/src/', '<rootDir>/.jest'],
  moduleNameMapper: {
    '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$':
      'identity-obj-proxy'
  },
  transformIgnorePatterns: ['/node_modules/(?!react-leaflet|@react-leaflet)']
}

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig)

Does anybody have a solution?

doougui commented 1 year ago

I managed to make it work by passing the --transformIgnorePatterns flag directly to my test script. Still unsure why this works, but setting this configuration on my jest.config.js file doesn't.

"scripts": {
  ...
  "test": "jest --transformIgnorePatterns \"node_modules/(?!react-leaflet|@react-leaflet)\"",
  "test:watch": "yarn test --watch",
  ...
},

My final jest.config.js looks like this:

// eslint-disable-next-line @typescript-eslint/no-var-requires
const nextJest = require('next/jest')

const createJestConfig = nextJest({
  // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
  dir: './'
})

const customJestConfig = {
  // Add more setup options before each test is run
  // setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  // if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work
  testEnvironment: 'jest-environment-jsdom',
  testPathIgnorePatterns: ['/node_modules/', '/.next/'],
  collectCoverage: true,
  collectCoverageFrom: [
    'src/**/*.ts(x)?',
    '!src/pages/**/*.tsx',
    '!src/styles/**/*.ts',
    '!src/types/**/*.d.ts',
    '!src/**/mock.ts',
    '!src/**/types.ts'
  ],
  setupFiles: ['<rootDir>/.jest/setEnvVars.ts'],
  setupFilesAfterEnv: ['<rootDir>/.jest/setup.ts'],
  modulePaths: ['<rootDir>/src/', '<rootDir>/.jest'],
  moduleNameMapper: {
    '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$':
      'identity-obj-proxy'
  }
}

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig)

If anyone knows the reason, please let me know.