2

Getting SyntaxError: Cannot use import statement outside a module while unit testing with Jest. The error is coming from the node-fetch package that is required by the jsdom-worker package. This could be an issue with the node-fetch package but not entirely sure.

I have node modules ignored in my jest.config.js so why is this an issue?

jest.config.js file:

const ignores = ['/node_modules/', '/__fixtures__/', '/fixtures/', '/__tests__/helpers/', '/__tests__/utils/', '__mocks__'];

module.exports = {
    preset: 'ts-jest',
    roots: ['<rootDir>'],
    modulePaths: [
        "<rootDir>/src"
    ],
    moduleDirectories: [
        "node_modules"
    ],
    transformIgnorePatterns: [...ignores],
    transform: {
        '^.+\\.(ts|tsx|js|jsx)?$': 'ts-jest',
        '^.+\\.(gif|svg|ico)$': '<rootDir>/svgTransform.js',
    },
    testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.js?$',
    moduleFileExtensions: ['tsx', 'js', 'json', 'node', 'ts'],
    moduleNameMapper: {
        "\\.(css|less|scss|sass)$": "identity-obj-proxy"
      },
    clearMocks: true,
    // collectCoverage: true, // todo
    // coverageDirectory: "coverage",  // todo
    testEnvironment: 'jsdom',
    setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect', 'jsdom-worker'],
}
// }

UPDATE: After downgrading node-fetch to version 2.4, that error went away, but now have the same Syntax issue with another package, why does this keep happening?

Code Trace enter image description here

babel.config.js

// Need to convert modules to commonjs format so Jest can undertstand them.
const isTest = String(process.env.NODE_ENV) === 'test'
const isProd = String(process.env.NODE_ENV) === 'production'

module.exports = {
    // For transformation of TSX and other react related bable plugins
    presets: [
        // Allows smart transpilation according to target environments
        ['@babel/preset-env', { modules: isTest ? 'commonjs' : false }],
        // Enabling Babel to understand TypeScript
        '@babel/preset-typescript',
    ],
}

Also tried this babel config from kcd-scripts:

"use strict";

const browserslist = require('browserslist');

const semver = require('semver');

const {
  ifDep,
  ifAnyDep,
  ifTypescript,
  parseEnv,
  appDirectory,
  pkg
} = require('../utils');

const {
  BABEL_ENV,
  NODE_ENV,
  BUILD_FORMAT
} = process.env;
const isTest = (BABEL_ENV || NODE_ENV) === 'test';
const isPreact = parseEnv('BUILD_PREACT', false);
const isRollup = parseEnv('BUILD_ROLLUP', false);
const isUMD = BUILD_FORMAT === 'umd';
const isCJS = BUILD_FORMAT === 'cjs';
const isWebpack = parseEnv('BUILD_WEBPACK', false);
const isMinify = parseEnv('BUILD_MINIFY', false);
const treeshake = parseEnv('BUILD_TREESHAKE', isRollup || isWebpack);
const alias = parseEnv('BUILD_ALIAS', isPreact ? {
  react: 'preact'
} : null);
const hasBabelRuntimeDep = Boolean(pkg.dependencies && pkg.dependencies['@babel/runtime']);
const RUNTIME_HELPERS_WARN = 'You should add @babel/runtime as dependency to your package. It will allow reusing "babel helpers" from node_modules rather than bundling their copies into your files.';

if (!treeshake && !hasBabelRuntimeDep && !isTest) {
  throw new Error(RUNTIME_HELPERS_WARN);
} else if (treeshake && !isUMD && !hasBabelRuntimeDep) {
  console.warn(RUNTIME_HELPERS_WARN);
}
/**
 * use the strategy declared by browserslist to load browsers configuration.
 * fallback to the default if don't found custom configuration
 * @see https://github.com/browserslist/browserslist/blob/master/node.js#L139
 */


const browsersConfig = browserslist.loadConfig({
  path: appDirectory
}) || ['defaults'];
const envTargets = isTest ? {
  node: 'current'
} : isWebpack || isRollup ? {
  browsers: browsersConfig
} : {
  node: getNodeVersion(pkg)
};
const envOptions = {
  modules: false,
  loose: true,
  targets: envTargets
};

module.exports = () => ({
  presets: [[require.resolve('@babel/preset-env'), envOptions], ifAnyDep(['react', 'preact'], [require.resolve('@babel/preset-react'), {
    pragma: isPreact ? ifDep('react', 'React.h', 'h') : undefined
  }]), ifTypescript([require.resolve('@babel/preset-typescript')])].filter(Boolean),
  plugins: [[require.resolve('@babel/plugin-transform-runtime'), {
    useESModules: treeshake && !isCJS
  }], require.resolve('babel-plugin-macros'), alias ? [require.resolve('babel-plugin-module-resolver'), {
    root: ['./src'],
    alias
  }] : null, ifAnyDep(['react', 'preact'], [require.resolve('babel-plugin-transform-react-remove-prop-types'), isPreact ? {
    removeImport: true
  } : {
    mode: 'unsafe-wrap'
  }]), isUMD ? require.resolve('babel-plugin-transform-inline-environment-variables') : null, [require.resolve('@babel/plugin-proposal-class-properties'), {
    loose: true
  }], isMinify ? require.resolve('babel-plugin-minify-dead-code-elimination') : null, treeshake ? null : require.resolve('@babel/plugin-transform-modules-commonjs')].filter(Boolean)
});

function getNodeVersion({
  engines: {
    node: nodeVersion = '10.13'
  } = {}
}) {
  const oldestVersion = semver.validRange(nodeVersion).replace(/[>=<|]/g, ' ').split(' ').filter(Boolean).sort(semver.compare)[0];

  if (!oldestVersion) {
    throw new Error(`Unable to determine the oldest version in the range in your package.json at engines.node: "${nodeVersion}". Please attempt to make it less ambiguous.`);
  }

  return oldestVersion;
}
11
  • can you post your typescript and/or babel configs? the issue is probably there.
    – Derek
    Commented Jul 9, 2022 at 0:48
  • just posted @Derek
    – AAMCODE
    Commented Jul 9, 2022 at 0:57
  • 1
    The second image shows a browser build being used (index.browser.js). JS bundles meant for the browser do not need to adhere to Node's file naming conventions, so it can use import in a .js file in a non-"type": "module" package. However, this does not work in Node. You need to ensure Jest isn't consuming browser bundles as they may not adhere to Node's ESM conventions. Commented Jul 10, 2022 at 0:17
  • 1
    Wrote up github.com/juanelas/bigint-conversion/issues/10, though that might only fix your immediate issue once a patch is released. Commented Jul 11, 2022 at 8:11
  • 1
    @rschristian Yep. I solved the next syntax error I got by downgrading the nanoid package I was using to the next lower version and that fixed it .. keep getting weird stuff like that
    – AAMCODE
    Commented Jul 11, 2022 at 23:06

2 Answers 2

1

Solved this by installing the package jest-webpack-resolver and adding

 "jestWebpackResolver": {
        "webpackConfig": "./webpack.config.js"
    },

to my package.json.

3
  • Why does this solve it though? Commented Oct 13, 2022 at 15:25
  • Ah, this post needs to be updated.
    – AAMCODE
    Commented Oct 17, 2022 at 13:04
  • Hi Anthony, I'm struggling with similar problems currently. I tried so many thing. Stumbled upon your 'Question' here and wondering if I should try jestWebpackResolver, although I'm using ts/webpack, no Babel.? But I'm a little bit in doubt what really solved it for you as you seem to have two solutions here, one from oct and one from july. Is it possible you could indicate what really solved it in your case. Is jestWebpackResolver necessary? Commented Oct 18, 2023 at 6:24
1

I reconfigured my jest config to this and also needed to mock the packages that were throwing errors which was causing most of the errors:

module.exports = {
    roots: ['<rootDir>'],
    modulePaths: ['<rootDir>/src'],
    moduleDirectories: ['node_modules'],
    transformIgnorePatterns: ['/node_modules/'],
    transform: {
        '^.+\\.(ts|tsx|js|jsx)?$': 'babel-jest',
        '^.+\\.(jpg|jpeg|png|gif|svg|ico|url)$':
            '<rootDir>/src/__tests__/__utils__/imageTransform.js',
    },
    testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.js?$',

    testPathIgnorePatterns: ['/__utils__/'],
    moduleFileExtensions: ['tsx', 'js', 'ts'],
    moduleNameMapper: {
        '\\.(css|less|scss|sass)$': 'identity-obj-proxy',
        '\\.(png|jpg|webp|ttf|woff|woff2|svg|mp4)$':
            '<rootDir>/src/__mocks__/fileMock.js',
    },
    clearMocks: true,
    setupFilesAfterEnv: [
        '@testing-library/jest-dom/extend-expect',
        './src/__tests__/__utils__/setup.js',
    ],
    testEnvironment: 'jsdom',
    collectCoverageFrom: ['src/**/*.{ts,tsx}', '!src/tests'],
}

The mocks were as simple as:

module.exports = {
    nanoid: () =>
        Array(100)
            .fill()
            .map(() => Math.random())
            .join(''),
}

for nanoid in a root level __mocks__ directory.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.