Exploring the compatibility of Next.js with jest for utilizing third-party ESM npm packages

Caught between the proverbial rock and a hard place.

My app was built using:

  • t3-stack: v6.2.1 - T3 stack
  • Next.js: v12.3.1
  • jest: v29.3.1

Followed Next.js documentation for setting up jest with Rust Compiler at https://nextjs.org/docs/testing#setting-up-jest-with-the-rust-compiler

Introduced https://wagmi.sh/ (React Hooks for Ethereum) to my project, smooth sailing until component testing began.

In order to test, created a mocked wagmi client in a test file by importing necessary packages like this:

import { Client, createClient, WagmiConfig, useConnect } from "wagmi";

Upon running the test, encountered an issue:

.../node_modules/wagmi/dist/index.js:2 import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister"; ^^^^^^

SyntaxError: Cannot use import statement outside a module

Explored various options in jest.config.js including filters and transformIgnorePatterns but no solution for transforming wagmi esm enabled library.

Referenced jest docs and implemented ECMAScript Modules support by adding extensionsToTreatAsEsm: [".ts", ".tsx"] to jest.config.js which allowed running tests with nodejs flag:

NODE_OPTIONS=--experimental-vm-modules npx jest -- src/__tests__/Wallet.test.tsx

Success! Everything worked flawlessly!

Later needed to mock a hook and revisited jest docs on mocking modules in ESM mode, hit a roadblock as I couldn't mock hooks despite trying numerous setups detailed in the issue here.

Reverted to original setup sans ESM support where jest.mock function worked as intended.

However, faced a new challenge of not being able to import wagmi due to its ESM compatibility without ESM support activated.

The question now is how to import npm packages that are ESM modules?

Answer №1

I wanted to share my own experience and solution for a common issue:

The main problem arose from the fact that jest does not fully support node ESM mode at this time, causing issues when loading ES modules.

Additionally, there is a specific file in the Next.js distribution version, next/dist/build/jest/jest.js, which complicates overriding the testPathIgnorePatterns configuration option.

To address these challenges, you have two options:

  • Utilize
    NODE_OPTIONS=--experimental-vm-modules
    with jest, although it may limit the usage of jest.mock functionality (as discussed here).
  • Alternatively, you can modify the Next.js jest config file, as detailed in this discussion. This approach involves transpiling ESM modules before running tests, which was the path I chose.

Answer №2

Encountered the same issue when working with NextJS and Wagmi.

To address the issue, I followed the solution provided above by using

NODE_OPTIONS=--experimental-vm-modules
in the test command and adding
extensionsToTreatAsEsm: [".ts", ".tsx"]
to my jest.config.js. However, I faced a new problem:

Reference error: jest is not defined
. To resolve this, I installed @jest/globals using pnpm add @jest/globals and imported jest in every test file as follows:
import { jest } from "@jest/globals"

This is how my final jest.config.js looks like:

const nextJest = require('next/jest')

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

const customJestConfig = {
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  testEnvironment: 'jest-environment-jsdom',
  extensionsToTreatAsEsm: [".ts", ".tsx"],
  transform: {},
}

module.exports = createJestConfig(customJestConfig)

For running tests, I use the following command:

"test": "NODE_OPTIONS=--experimental-vm-modules jest --ci --silent",

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Angular2 Directive that Duplicates a Group of <tr> Elements

How can I build a component that generates HTML based on this data and HTML code? The <head> and <tbody> sections are projected, and I understand how to project multiple elements. However, I am unsure of how to repeat the projected <tr> i ...

Ways to add a string to an array as a labeled object in javascript?

Is there a way to manipulate the array in imageCollection to achieve the format of the array in carouselPhotos as shown below? export default class HomeScreen extends Component { state = { imageCollection: [ { name: "P ...

Press anywhere on the screen to conceal the AngularJS element

Attempting to create a toggle effect using 2 ng-click functions. One is bound to a button, the other to the body tag. The goal is for my content to show when the button is clicked and hide when anywhere on the body is clicked. However, it seems that Angul ...

Tips for styling a React JS component class

I am attempting to write inline CSS for a React JS component called Login, but I keep encountering an error. What could be causing this issue? Could you provide guidance on the correct way to implement in-line component CSS? import React, {Component} from ...

A more efficient method for incorporating types into props within a functional component in a TypeScript-enabled NextJS project

When dealing with multiple props, I am looking for a way to add types. For example: export default function Posts({ source, frontMatter }) { ... } I have discovered one method which involves creating a wrapper type first and then defining the parameter ty ...

Experimenting with JavaScript within an Immediately Invoked Function Expression

My team leader is requesting that I encapsulate my JavaScript code within an Immediately-Invoked Function Expression (IIFE). However, I am struggling to use spyOn in my Jasmine spec file. How can I properly use spyOn on the following: (function(){ fu ...

Issue: Incorrect hook usage. Hooks are designed to be used within the body of a function component. This error may occur due to one of the following reasons: 1

I've reviewed similar questions and attempted to apply the solutions provided, but it seems I'm missing something specific to my situation. My goal is to streamline my code by importing headers from a utils file and using them across different AP ...

Is it possible to use an Enum as a type in TypeScript?

Previously, I utilized an enum as a type because the code below is valid: enum Test { A, B, } let a: Test = Test.A However, when using it as the type for React state, my IDE displays an error: Type FetchState is not assignable to type SetStateActi ...

Removing an article from a Vue.js localStorage array using its index position

I am facing an issue while trying to remove an item from localStorage. I have created a table to store all the added items, and I would like to delete a specific article either by its index or ideally by its unique id. Your assistance is greatly apprecia ...

What is the best way to swap the values of options between two input select elements?

I am trying to create a feature where I have two select dropdowns with the same options, and when a trigger is clicked, the option values are inverted between the two selects. Here is an example: <select id="source_currency"> <option value="BRL" ...

Preventing jQuery from removing child elements while inserting data through ajax

When I try to insert data into an HTML structure using the code below, only one of the elements displays at a time. It seems like when I use Ajax to insert data, the child elements are being removed. How can I prevent this without having to change the stru ...

What could be causing this JavaScript if statement to malfunction?

I found myself with some free time and decided to create a basic API using JavaScript. What I thought would be simple turned into a frustrating mistake. Oddly enough, my if/else statement isn't working correctly - it only executes the code within the ...

clicking a table row will activate the *ngFor directive

Incorporating data from an API into a table, I have enabled the functionality for users to click on table rows in order to change the displayed data using background code: <tr [ngClass]="tablerowClass" *ngFor="let dataObject of data$ | async" (click)=" ...

Can you provide the regular expression that will successfully match this specific string of text?

Can you solve this fruit riddle? apple is 2kg apple banana mango is 2kg apple apple apple is 6kg banana banana banana is 6kg If the fruits are limited to "apple", "banana", and "mango", how can we write a regex that extracts the names of ...

Encountering a 404 error when attempting to post from a Node.js express app to a

Trying to post to a MySQL database has been giving me a 404 error. I have searched through various posts here, but none of the accepted solutions seem to work for me. I'm struggling to figure out what I am doing wrong. When utilizing a GET request, t ...

The most efficient method for retrieving data in React

Recently, I started working on a React App integrated with Riot API to display users' recent games and more. As part of this project, I'm utilizing React and NextJS (fairly new to NextJS). However, I'm contemplating the most efficient way to ...

I am struggling to set up angular-localstorage4

I have been following the instructions in the provided link: angular-localstorage4 When attempting to import WebStorageModule and LocalStorageService from angular-localstorage, I encounter an error in the console even though the compilation is successful. ...

Import JSON Data into Angular-nvD3 Chart (AngularJS)

I am seeking help to load encoded JSON Data retrieved from a database via queries into an Angular-nvD3 graph. I am unsure about the best approach to achieve this task. The encoded JSON data is fetched using API queries from a database table called PRODUCT ...

Upgrading the entire document's content using jQuery

I am dealing with an ajax response that provides the complete HTML structure of a webpage, as shown below: <!DOCTYPE> <html> <head> <!-- head content --> </head> <body> <!-- body content --> </b ...

Tips for correctly specifying the theme as a prop in the styled() function of Material UI using TypeScript

Currently, I am utilizing Material UI along with its styled function to customize components like so: const MyThemeComponent = styled("div")(({ theme }) => ` color: ${theme.palette.primary.contrastText}; background-color: ${theme.palette.primary.mai ...