Jest: A guide on mocking esModule methods

In my code, I have a function that utilizes the library jszip to zip folders and files:

// app.ts
const runJszip = async (): Promise<void> => {
  const zip = new Jszip();

  zip.folder('folder')?.file('file.txt', 'just some text');
  zip.file('file.txt', 'just some text');

  await zip.generateAsync({ type: 'blob' });
};

To test this function, I decided to spy on the methods folder and file. I used the mocking partial strategy along with Jest to handle the default export of the jszip library:

// app.test.ts
import { runJszip } from './app';

const mockFile = jest.fn();
const mockFolder = jest.fn();

const mockJszip = jest.fn().mockImplementation(() => {
  return {
    folder: mockFolder,
    file: mockFile,
  };
});

jest.mock('jszip', () => {
  return jest.fn().mockImplementation(() => ({
    __esModule: true,
    default: mockJszip,
  }));
});

test('jszip', async () => {
  await runJszip();

  expect(mockFile).toHaveBeenCalledTimes(2);
  expect(mockFolder).toHaveBeenCalledTimes(1);
});

However, I encountered an issue where I could not properly mock the folder method, resulting in the error message:

    Message:
      zip.folder is not a function

      4 |   const zip = new Jszip();
      5 |
    > 6 |   zip.folder('folder')?.file('file.txt', 'just some text');

If anyone has any ideas on how to successfully mock and spy on this method, I would greatly appreciate it.

You can check out a minimal reproducible example here.

Answer ā„–1

Successfully implemented the solution by creating a mockFolder in @bln's answer, which returned a mocked Jszip instance:

import { runJszip } from './app';

const mockFile = jest.fn();
let mockFolder: jest.Mock;

function mockJszip() {
  mockFolder = mockFolder ?? jest.fn(mockJszip);
  return {
    folder: mockFolder,
    file: mockFile,
    generateAsync: jest.fn(),
  };
}

jest.mock('jszip', () => {
  return {
    __esModule: true,
    default: mockJszip,
  };
});

test('jszip', async () => {
  await runJszip();

  expect(mockFile).toHaveBeenCalledTimes(2);
  expect(mockFolder).toHaveBeenCalledTimes(1);
});

https://stackblitz.com/edit/webpack-5-react-starter-ofxcmy?file=src/app.test.ts

Answer ā„–2

import { runJszip } from './app';

const fakeFile = jest.fn();
const fakeFolder = jest.fn();

function fakeJszip() {
  return {
    folder: fakeFolder,
    file: fakeFile,
    generateAsync: jest.fn(),
  };
}

jest.mock('jszip', () => {
  return {
    __esModule: true,
    default: fakeJszip,
  };
});

test('jszip', async () => {
  await runJszip();

  expect(fakeFile).toHaveBeenCalledTimes(2);
  expect(fakeFolder).toHaveBeenCalledTimes(1);
});

For the ?.file() chaining, consider implementing it with some use of mockImplementation around fakeJszip

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

The persistent issue of window.history.pushstate repeatedly pushing the identical value

I need some assistance with my Vue application. I am trying to update the URL when a user clicks on an element: const updateURL = (id: string) => { window.history.pushState({}, '', `email/${id}`); }; The issue I'm facing is th ...

Changing colors in the rows of a table

Here is a fiddle I created to demonstrate my issue. https://jsfiddle.net/7w3c384f/8/ In the fiddle, you can see that my numbered list has alternating colors achieved through the following jQuery code: $(document).ready(function(){ $("tr:even").css("ba ...

My customized mat-error seems to be malfunctioning. Does anyone have any insight as to why?

Encountering an issue where the mat-error is not functioning as intended. A custom component was created to manage errors, but it is not behaving correctly upon rendering. Here is the relevant page code: <mat-form-field appearance="outline"> < ...

What steps should I take to ensure that this countdown is continuously updating every second?

I created a function that's functioning properly, but I'm looking to update the message every second. Previously, I attempted using setInterval(countdownTimer(), 1000), however, it was unsuccessful. Below is my code snippet! let x = await msg.c ...

Key within square brackets in a JSON array

I am dealing with a JSON array that includes a square bracket in the key, like this: { "msg":"OK", "data":[ { "nls!type":"NCR", "current":"Assign", "physicalid":"0FFE0001000009FC5BD6805C00001352", "attribute[custTitle]":"Tit ...

Bookshelfjs fails to return a promise when updating multiple data

I am facing an issue when trying to update multiple rows in my database. The problem lies in the fact that the promise does not return anything, even though the data is successfully saved. Below is a snippet of my code: doUpdate: Promise.method(function ...

Is there a way to modify the text of image URLs using JavaScript?

My method of replacing a specific word in text works like this: document.body.innerHTML = document.body.innerHTML.replace(/katt/g, "smurf"); However, when I try to replace an image URL in HTML using the same line of code, it doesn't seem to work. H ...

Automatically refreshing the page when the back button is clicked

Within my ASP.NET application, I have two pages - Page A and Page B. When a user clicks on a link on Page A, they are redirected to Page B. However, if the user then clicks the browser's back button while on Page B, I need to force a refresh of Page A ...

ReadOnly types in Inheritance

Currently, I am working on creating an unchangeable, nested data structure that also incorporates inheritance. To achieve this, I am using the Readonly generic type. In order to create different types within this structure, one of these Readonly types need ...

Is there a more efficient method to select all the rows containing '1's in a specific cell within a large range of data?

As a beginner, I've developed a script that scans through a large table and extracts rows containing the value '1'. The table consists of approximately 2000 rows, so it's taking quite some time to process all of them. Is there a more e ...

Node: permit the event loop to run during lengthy tasks and then return

Currently, I am facing an issue with a function that works like this: function longFunc(par1,par2) { var retVal = [], stopReq = false; function evtLs() { stopReq = true; } something.on("event", evtLs); for(var i=0; ...

hyperlink to choose a specific option from a drop-down menu

The challenge I am currently working on involves page1.html <a href="/foo"></a> foo.html <select ng-model="ctrl.listvalues"> <option id="{{item.value}}" ng-repeat="item" in ctrl.availableValues" value="{{item.value}}">item.di ...

Error: Unexpected character U found at the beginning of the JSON data when using JSON.parse in Angular 8

Lately, I came across an issue while making changes to some parts of my previous code. The error caught my attention as it occurred when trying to pass a specific object or a part of that object in Angular 8, NodeJS with express, and mongoose. Upon checki ...

Using npm webpack-mix to execute a JavaScript function stored in a separate file

Currently, I am facing a challenge with accessing a function that is defined in the table.js file from within my index.html. Here is a snippet of the code: table.js let table; function set_table(table) { this.table = table; consol ...

Is there a way to switch the main image by clicking on different thumbnails in a sidebar using javascript/jQuery

Currently on my page, I have a large plot created with jqplot along with a sidebar containing several smaller plots. I am trying to find a way to dynamically change the large plot based on which of the smaller plots the user clicks on, without needing to ...

Building React applications with server-side rendering using custom HTML structures

I recently started using Suspense in my React app and decided to implement SSR. However, as I was going through the documentation at https://reactjs.org/docs/react-dom-server.html#rendertopipeablestream, I couldn't find a clear explanation on how to u ...

How do I utilize the file handler to execute the flush method within the Deno log module using Typescript?

I'm having trouble accessing the fileHandler object from my logger in order to flush the buffer to the file. This is the program I am working with: import * as log from "https://deno.land/<a href="/cdn-cgi/l/email-protection" class="__cf_emai ...

Ember application experiencing trouble displaying Facebook Like Box

Iā€™m currently facing an issue with integrating the like box into our ember app, specifically in a template named about. The problem arises when users enter the ember app from a different route, instead of directly accessing the about route. In such cases ...

How to iterate through two arrays using AngularJS ng-repeat

I have been attempting to create a specific layout by iterating through two arrays However, the output I am receiving from the ng-repeats does not match my desired view Below is the current code that I am working with: $scope.properties = ["First name", ...

In relation to User Interface: Analyzing target tracking and studying the flow of time

Is there a way to track mouse cursor movements, button clicks, and click times to an external database using only CSS, HTML5, and Javascript? I am curious about the possibility of conducting usability studies in this manner. ...