Jest: Jest cannot understand token "?" from @babel/plugin-proposal-optional-chaining

Created on 24 Sep 2019  Â·  14Comments  Â·  Source: facebook/jest

💬 Questions and Help

My project is Vue 2.x. I am using @babel/plugin-proposal-optional-chaining and I have a line of code in my component.vue file that uses "?"

this.$refs.refreshBtn?.$el.addEventListener("animationend", _ => { ... });

which works well when run the app. However, my unit tests are failing because it cannot recognize the token "?"

  unknown: Unexpected token (149:26)

      Jest encountered an unexpected token
      ......
      ......
      ......
Details:
        147 |
        148 |    
      > 149 |     this.$refs.refreshBtn?.$el.addEventListener("animationend", _ => {
            |                           ^
        150 |       this.refreshClicked = false;
        151 |     });
        152 |

my babel.config.js

module.exports = {
  presets: [
    [
      '@vue/app',
      {
        useBuiltIns: "entry",
      }
    ],

  ],

  plugins: [
    '@babel/plugin-proposal-optional-chaining'
  ],

  sourceType: 'unambiguous'
};

my partial package.json:

    "scripts": {    
        "test:unit": "vue-cli-service test:unit --no-cache"
    },
    "dependencies": {
        "@casl/ability": "^2.5.1",
        "@casl/vue": "^0.2.0"
        "axios": "^0.18.1",
        "babel-polyfill": "^6.26.0",
        "base64-js": "^1.3.0",
        "commonjs": "0.0.1",
        "echarts": "^4.2.0-rc.2",
        "element-ui": "^2.4.4",
        "eureka-js-client": "^4.4.1",
        "express": "^4.16.3",
        "express-actuator": "^1.1.0",
        "google-protobuf": "3.6.1",
        "grpc": "^1.22.2",
        "gsap": "^1.20.4",
        "jasmine-core": "^3.2.1",
        "jest-junit": "^4.0.0",
        "js-cookie": "^2.2.0",
        "lodash": "^4.17.15",
        "material-design-icons": "^3.0.1",
        "moment": "^2.22.2",
        "moment-timezone": "^0.5.21",
        "normalize.css": "^8.0.0",
        "npm": "^6.4.1",
        "nprogress": "^0.2.0",
        "platform": "^1.3.5",
        "requirejs": "^2.3.5",
        "sjcl": "^1.0.8",
        "uuid": "^3.3.2",
        "vue": "^2.5.21",
        "vue-acl": "^3.0.4",
        "vue-animate-number": "^0.4.2",
        "vue-content-loader": "^0.2.1",
        "vue-echarts": "^3.1.3",
        "vue-echarts-v3": "^1.0.19",
        "vue-material": "^1.0.0-beta-10.1",
        "vue-router": "^3.0.1",
        "vue-toasted": "^1.1.24",
        "vue2-dropzone": "^3.5.2",
        "vuex": "^3.0.1",
        "vuex-persistedstate": "^2.5.2"
    },
    "devDependencies": {
        "@babel/plugin-proposal-optional-chaining": "^7.2.0",
        "@vue/cli-plugin-babel": "^3.9.2",
        "@vue/cli-plugin-e2e-nightwatch": "^3.9.2",
        "@vue/cli-plugin-eslint": "^3.9.2",
        "@vue/cli-plugin-unit-jest": "^3.9.0",
        "@vue/cli-service": "^3.9.3",
        "@vue/test-utils": "^1.0.0-beta.25",
        "autoprefixer": "^7.1.2",
        "babel-core": "7.0.0-bridge.0",
        "babel-eslint": "^10.0.1",
        "babel-helper-vue-jsx-merge-props": "^2.0.3",
        "babel-jest": "^23.6.0",
        "babel-loader": "^7.1.1",
        "babel-plugin-add-module-exports": "^0.2.1",
        "babel-plugin-dynamic-import-node": "^1.2.0",
        "babel-plugin-syntax-jsx": "^6.18.0",
        "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
        "babel-plugin-transform-runtime": "^6.22.0",
        "babel-plugin-transform-vue-jsx": "^3.5.0",
        "babel-preset-env": "^1.3.2",
        "babel-preset-es2015": "^6.24.1",
        "babel-preset-stage-2": "^6.22.0",
        "babel-register": "^6.22.0",
        "chalk": "^2.0.1",
        "chromedriver": "^76.0.0",
        "copy-webpack-plugin": "^4.0.1",
        "cross-spawn": "^5.0.1",
        "csp-html-webpack-plugin": "^2.4.0",
        "css-loader": "^0.28.0",
        "detect-browser": "^2.5.1",
        "eslint": "^5.8.0",
        "eslint-config-google": "^0.9.1",
        "eslint-config-standard": "^10.2.1",
        "eslint-friendly-formatter": "^3.0.0",
        "eslint-loader": "^1.7.1",
        "eslint-plugin-import": "^2.7.0",
        "eslint-plugin-node": "^5.2.0",
        "eslint-plugin-promise": "^3.4.0",
        "eslint-plugin-standard": "^3.0.1",
        "eslint-plugin-vue": "^5.0.0",
        "extract-text-webpack-plugin": "^3.0.0",
        "file-loader": "^1.1.4",
        "friendly-errors-webpack-plugin": "^1.6.1",
        "html-webpack-plugin": "^2.30.1",
        "jest": "^23.5.0",
        "jest-localstorage-mock": "^2.2.0",
        "jest-serializer-vue": "^0.3.0",
        "jest-transform-stub": "^1.0.0",
        "jsdom": "^12.0.0",
        "jsdom-global": "^3.0.2",
        "karma": "^3.0.0",
        "karma-chrome-launcher": "^2.2.0",
        "karma-firefox-launcher": "^1.1.0",
        "karma-ie-launcher": "^1.0.0",
        "karma-jasmine": "^1.1.2",
        "karma-webpack": "^3.0.0",
        "nightwatch": "^0.9.21",
        "node-notifier": "^5.1.2",
        "node-sass": "^4.12.0",
        "optimize-css-assets-webpack-plugin": "^3.2.0",
        "ora": "^1.2.0",
        "portfinder": "^1.0.13",
        "postcss-import": "^11.0.0",
        "postcss-loader": "^2.0.8",
        "postcss-url": "^7.2.1",
        "rimraf": "^2.6.0",
        "sass-loader": "^7.0.1",
        "selenium-server": "^3.12.0",
        "semver": "^5.3.0",
        "shelljs": "^0.7.6",
        "uglifyjs-webpack-plugin": "^1.1.1",
        "url-loader": "^0.5.8",
        "vue-jest": "^1.0.2",
        "vue-loader": "^15.5.1",
        "vue-style-loader": "^3.0.1",
        "vue-template-compiler": "^2.5.21",
        "webpack": "^4.28.1",
        "webpack-bundle-analyzer": "^2.9.0",
        "webpack-dev-server": "^2.11.1",
        "webpack-merge": "^4.1.0"
    },
   "eslintConfig": {
        "root": true,
        "env": {
            "node": true
        },
        "extends": [
            "plugin:vue/essential",
            "eslint:recommended"
        ],
        "rules": {},
        "parserOptions": {
            "parser": "babel-eslint"
        }
    },
    "postcss": {
        "plugins": {
            "autoprefixer": {}
        }
    },
    "browserslist": [
        "> 1%",
        "last 2 versions",
        "not ie <= 8"
    ]

my jest.config.js

module.exports = {
    moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
    transform: {
        '^.+\\.vue$': 'vue-jest',
        '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$':
            'jest-transform-stub',
        '^.+\\.(js|jsx)?$': 'babel-jest'
    },
    moduleNameMapper: {
        '^@/(.*)$': '<rootDir>/src/$1'
    },
    snapshotSerializers: ['jest-serializer-vue'],
    testMatch: [
        '<rootDir>/(tests/unit/**/*.test.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx))'
    ],
    transformIgnorePatterns: ['<rootDir>/node_modules/']
};

I am pretty new to Babel and Webpack..not sure what is the correct way to make Jest to be able to recognize the plugins that I use for my project. I am thinking it is because the .vue file is processed by vue-jest which does not take babel config... if that is the case I don't know how to fix it...

If someone knows please help....appreciate it...

Please note that this issue tracker is not a help forum and this issue will be closed.

For questions or help please see:

Most helpful comment

Any updates on this?

All 14 comments

Any updates on this?

+1

I recommend upgrading to jest 24, it uses babel itself to load config. If that doesn't work, please provide a minimal reproduction we can pull down and test

I actually think I found a solution from this comment: https://github.com/kulshekhar/ts-jest/issues/1283#issuecomment-552147794. Adding this to my jest.config.js seems to work for me, though it does open up a number of other issues for me.:

  globals: {
    'ts-jest': {
      babelConfig: true,
    }
  },

@zeroarst issue is likely due to using Jest 23 or vue-jest transformer not respecting their Babel config.

@dandriushchenko what's your issue exactly? Please provide a detailed repro, otherwise it's really hard to help ("proper babel config with a minimal set of plugins" is not something we can measure unfortunately).

Oh, and your attacking comments won't help anybody, so please calm down.

I have the same problem. Unit Tests fail because of

    Add @babel/plugin-proposal-optional-chaining (https://git.io/vb4Sk) to the 'plugins' section of your Babel config to enable transformation.

Jest 24.9.0 (25.0.0 fails too)

.babelrc

{
    "presets": [
        [
            "@babel/preset-env",
            {
                "useBuiltIns": "usage",
                "shippedProposals": true,
                "corejs": 3
            }
        ]
    ],
    "plugins": [
        ["transform-react-jsx", {"pragma": "h"}],
        "@babel/plugin-syntax-dynamic-import",
        "@babel/plugin-proposal-optional-chaining"
    ]
}

jest.config.js

module.exports = {
    preset: 'ts-jest',
    testEnvironment: 'jsdom',
    roots: [
        "<rootDir>/src"
    ],
    collectCoverageFrom: [
        "<rootDir>/src/**/*.tsx",
        "<rootDir>/src/**/*.ts",
        "!<rootDir>/src/test/**",
        "!<rootDir>/src/types/**",
    ],
    coverageReporters: [
        "text-summary",
        "html",
    ],
    coverageDirectory: "./coverage/",
    collectCoverage: true,
    moduleDirectories: [
        "src",
        "node_modules",
    ],
    moduleFileExtensions: [
        "ts",
        "tsx",
        "js",
    ],
    moduleNameMapper: {
        "\\.(jpg|png|otf|svg|ttf|woff|woff2)$": "<rootDir>/src/test/stubs/file.js",
        "\\.(css)$": "identity-obj-proxy",
    },
    snapshotSerializers: [
        "preact-render-spy/snapshot",
    ],
    snapshotResolver: "<rootDir>/src/test/snapshot-resolver.js",
};

I will try to setup a minimal repo. The workaround does not work for me:

  globals: {
    'ts-jest': {
      babelConfig: true,
    }
  },

Then my imports don't work anymore and I got muuuch more errors.

Minimal Repo
https://github.com/hbroer/test-optional-chaining-jest

Maybe I do something else wrong. The error is different but the same spot:

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

    /home/hauke/projects/test-optionalchaining/src/optional.ts:4
        return value?.prop?.is;
                     ^

    SyntaxError: Unexpected token .
  • TypeScript compiling works. The other test is green.
  • building with webpack and executing the artifact with node works too

Does not work:

    globals: {
        'ts-jest': {
            babelConfig: true,
        }
    },

But this does:

    globals: {
        'ts-jest': {
            babelConfig: '.babelrc',
        }
    },

Duno why everyting is finding .babelrc but ts-jest. So this problem is related to ts-jest

Or if you use a babel.config.js instead:

globals: {
        'ts-jest': {
            babelConfig: 'babel.config.js',
        }
    },

Babel 7.8.0 removes the requirement for separately using the optional chaining etc plugins. It looks like jest hasn't caught up yet. Just ran into this. You resolve it by adding preset-env to the babel presets.

@mschipperheyn can you provide your babelrc/jest config files and dependencies versions please?

In jest.config.js, I solved this problem with following.
image

Another thing that works for ts-jest users, without using Babel but by changing the target of the TypeScript compiler to anything lower than ES2020 or ESNext (see https://github.com/kulshekhar/ts-jest/issues/1283#issuecomment-592711385):

globals: {
  'ts-jest': {
    tsConfig: {
      target: 'ES2019'
    }
  }
}

This isn't actually an issue in Jest (it's a configuration error), so I'll close this.

Was this page helpful?
0 / 5 - 0 ratings