Bartek
Bartek

Reputation: 11

Cannot use import statement outside a module - jest + nextj.js

Running jest test I faced following issue:

` SyntaxError: Cannot use import statement outside a module

      30 |   certificateEuropean: '/icons/certificate-european.svg',
      31 |   check: '/icons/check.svg',
    > 32 |   checkGold: '/icons/check-gold.svg',

`

This is the test:

` import React from 'react'; import { render } from '@testing-library/react';

import { Icon } from '@/components/UI/Icon';

describe('RegistrationForm', () => {
  test('should update and retrieve values correctly', () => {
    render(<Icon type="accreditation" />);
  });
});

`

This is the component:

` const iconMap = { accreditation: '/icons/accreditation.svg', . . }

export const getIconSrc = (type: IconType): string => iconMap[type];

export const Icon = React.forwardRef<HTMLImageElement, IconProps>(
  ({ type, size, width, alt = '', id, quantity }, ref) => {
    const _size = size ?? 24;
    const _width = width ?? _size;
    const iconSrc = getIconSrc(type);

    return (
      <>
        <img
          data-testid={id}
          className="h-full"
          ref={ref}
          src={iconSrc}
          width={_width}
          height={_size}
          alt={alt}
        />
        {typeof quantity === 'number' && quantity > 0 ? (
          <span>
            {quantity}
          </span>
        ) : null}
      </>
    );
  }
);
Icon.displayName = 'Icon';

`

and this is my jest.config.js

` // jest.config.js 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 = {

  moduleDirectories: ['node_modules', '<rootDir>/'],


  moduleNameMapper: {
    '@/(.*)$': '<rootDir>/src/$1',
    '@@/(.*)$': '<rootDir>/$1',
    '\\.(ttf|otf|eot|svg)$': '<rootDir>/__mocks__/fontMock.js',
  },
  testEnvironment: 'jsdom',
  setupFiles: ['./text-encoder.mock.ts'],
  setupFilesAfterEnv: ['@testing-library/jest-dom'],
};

module.exports = createJestConfig(customJestConfig);`

I tried adding following fields to jest.config.js `
preset: 'ts-jest', testEnvironment: //every possible option transform: { '^.+\.tsx?$': 'ts-jest', }, transformIgnorePatterns: ['/node_modules/'],

globals: {
  "ts-jest": {
    isolatedModules: true,
  },
},`

in package.json

"type": "module",your text run node --experimental-vm-modules node_modules/jest/bin/jest.js instead of npm test result: Must use import to load ES Module: C:\Users\USERNAME\frontend\node_modules\@sitecore-feaas\clientside\dist\browser\react.esm.js

Upvotes: 1

Views: 355

Answers (3)

Kyle Kingsbury
Kyle Kingsbury

Reputation: 11

The issue is due to how the module entrypoint is being selected based on the environment, browser vs node. @sitecore-feaas and @sitecore/byoc use esm for browsers and cjs for node.

When you are running tests within jest, you are likely running jsdom or jest-environment-jsdom environment to run your tests within a simulated browser environment. This causes the module entrypoint to select "browser" which @sitecore-feaas and @sitecore/byoc map the browser entrypoint to esm instead of cjs.

Instead of configuring jest to transpile the esm modules back to cjs, you can override the entrypoint to select the cjs version.

In jest.config.js add a moduleNameMapper property to your jest config to map the entrypoints (example using next.js and jest):

const nextJest = require('next/jest');

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

/** @type {import('jest').Config} */
const customJestConfig = {

  // Add this property if it does not exist and map the package imports to the correct entrypoint
  moduleNameMapper: {
    '@sitecore-feaas/clientside/react': '<rootDir>/node_modules/@sitecore-feaas/clientside/dist/browser/react.cjs',
    '@sitecore/byoc': '<rootDir>/node_modules/@sitecore/byoc/dist/browser/index.cjs',
  },
};

module.exports = createJestConfig(customJestConfig);

Upvotes: 1

Alex Baumgertner
Alex Baumgertner

Reputation: 144

Here is the other solution: mock @sitecore-jss in your test. For example if your component uses RichText you can mock it like this:

jest.mock('@sitecore-jss/sitecore-jss-nextjs', () => ({
  RichText: (props) => {
    return props.field?.value;
  },
}));

Upvotes: 0

Alex Baumgertner
Alex Baumgertner

Reputation: 144

It seems like something wrong with @sitecore-feaas package.json — when I deleted "exports" field in @sitecore-feaas/package.json and did the same with @sitecore/byoc it started working.

I created a jest settings workaround:

jest.config.ts:

  globalSetup: '<rootDir>/jest/sitecoreLibsExportFixing.ts',
  globalTeardown: '<rootDir>/jest/sitecoreLibsExportFixingRestore.ts',

sitecoreLibsWithWrongExport.ts:

export const sitecoreLibsWithWrongExport = ['@sitecore/byoc', '@sitecore-feaas/clientside'];

sitecoreLibsExportFixing.ts:

import fs from 'fs';
import { sitecoreLibsWithWrongExport } from './sitecoreLibsWithWrongExport';

const sitecoreLibsExportFixing = (libsToPatch: string[]) => {
  libsToPatch.forEach((lib) => {
    const packageFilePath = `node_modules/${lib}/package.json`;
    const backupFilePath = `node_modules/${lib}/package.backup.json`;

    // Store a backup
    fs.copyFileSync(packageFilePath, backupFilePath);

    // Read the file
    const rawData = fs.readFileSync(packageFilePath, 'utf-8');
    const packageData = JSON.parse(rawData);

    // Remove the 'exports' property
    if (packageData.exports) {
      delete packageData.exports;

      // Write the updated content back to the file
      const updatedData = JSON.stringify(packageData, null, 2);
      fs.writeFileSync(packageFilePath, updatedData);
    }
  });
};

export default () => sitecoreLibsExportFixing(sitecoreLibsWithWrongExport);

sitecoreLibsExportFixingRestore.ts:

import fs from 'fs';
import { sitecoreLibsWithWrongExport } from './sitecoreLibsWithWrongExport';

const sitecoreLibsExportFixingRestore = (libsToRestore: string[]) => {
  libsToRestore.forEach((lib) => {
    const packageFilePath = `node_modules/${lib}/package.json`;
    const backupFilePath = `node_modules/${lib}/package.backup.json`;

    // Restore the backup file
    if (fs.existsSync(backupFilePath)) {
      fs.copyFileSync(backupFilePath, packageFilePath);
    }
  });
};

export default () => sitecoreLibsExportFixingRestore(sitecoreLibsWithWrongExport);

Upvotes: 0

Related Questions