Testing a Jest unit on a function that invokes another function which in turn returns a Promise

I have a function that triggers another function which returns a Promise. Below is the code snippet for reference:

export const func1 = ({
  contentRef,
  onShareFile,
  t,
  trackOnShareFile,
}) => e => {
  trackOnShareFile()
  try {
    func2(contentRef).then(url => {
      onShareFile({
        title: t('shareFileTitle'),
        type: 'application/pdf',
        url,
      })
    }).catch(e => {
      if (process.env.NODE_ENV === 'development') {
        console.error(e)
      }
    })
    e.preventDefault()
  } catch (e) {
    if (process.env.NODE_ENV === 'development') {
      console.error(e)
    }
  }
}

The function func2, called within func1, looks something like this:

const func2 = element => {
  return import('html2pdf.js').then(html2pdf => {
    return html2pdf.default().set({ margin: 12 }).from(element).toPdf().output('datauristring').then(pdfAsString => {
      return pdfAsString.split(',')[1]
    }).then(base64String => {
      return `data:application/pdf;base64,${base64String}`
    })
  })
}

I am currently working on writing unit tests for func1 but facing some challenges. So far, I've tried the following:

describe('#func1', () => {
  it('calls `trackOnShareFile`', () => {
      // given
      const props = {
        trackOnShareFile: jest.fn(),
        onShareFile: jest.fn(),
        shareFileTitle: 'foo',
        contentRef: { innerHTML: '<div>hello world</div>' },
      }
      const eventMock = {
        preventDefault: () => {},
      }
      // when
      func1(props)(eventMock)
      // then
      expect(props.trackOnShareFile).toBeCalledTimes(1)
    })
    it('calls `onShareFile` prop', () => {
      // given
      const props = {
        trackOnShareFile: jest.fn(),
        onShareFile: jest.fn(),
        shareFileTitle: 'foo',
        contentRef: { innerHTML: '<div>hello world</div>' },
      }
      const eventMock = {
        preventDefault: () => {},
      }
      // when
      func1(props)(eventMock)
      // then
      expect(props.onShareFile).toBeCalledTimes(1)
    })
  })

While the first test passes successfully, the second one throws an error stating

Expected mock function to have been called one time, but it was called zero times.
. I'm seeking guidance on how to correctly write this test. Any assistance would be greatly appreciated.

Answer №1

Sweet, I finally got it to work!

To kick things off, we need to simulate the data-url-generator (which is where we bring in func2). The html2pdf library won't function properly in the testing environment due to its use of a fake DOM that lacks complete canvas graphics support.

jest.mock('./data-url-generator', () => jest.fn())

Next, the test can be written as follows:

it('triggers the `onShareFile` prop', done => {
    // given
    const t = key => `[${key}]`
    const urlMock = 'data:application/pdf;base64,PGh0bWw+PGJvZHk+PGRpdj5oZWxsbyB3b3JsZDwvZGl2PjwvYm9keT48L2h0bWw+'
    const shareFileTitle = 'bar'
    const contentRef = document.createElement('div')
    contentRef.textContent = 'hello world'
    const trackOnShareFile = () => { }
    const eventMock = {
      preventDefault: () => { },
    }
    func2.mockResolvedValue(urlMock)
    const onShareFileMock = ({ title, type, url }) => {
      // then
      expect(func2).toHaveBeenCalledTimes(1)
      expect(func2).toHaveBeenCalledWith(contentRef)
      expect(title).toBe('[shareFileTitle]')
      expect(type).toBe('application/pdf')
      expect(url).toBe(urlMock)
      done()
    }
    // when
    func1({
      contentRef,
      onShareFile: onShareFileMock,
      shareFileTitle,
      t,
      trackOnShareFile,
    })(eventMock)
  })

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

Tips for retaining a chosen selection in a dropdown box using AngularJS

How can I store the selected color value from a dropdown box into the $scope.color variable? Index.html: <label class="item item-select" name="selectName"> <span class="input-label">Choose your favorite color:</span> <select id="colo ...

Is it possible to send alerts in GetServerSideProps while performing a redirect?

I have implemented a component that redirects users who are not logged in using getServerSideProps. I am now exploring ways to show an alert on the redirected page notifying the user of the redirection. The code snippet responsible for redirecting users ...

Ensure UseEffect is only triggered once with specific dependencies

Whenever the component is loaded, I have a method called useEffect() that runs once. useEffect(() => { (async function fetchData() { const results = await stateIsoAPI(); setStates(results); // API results to trigger other Use effect ...

Node.js sends a request to open a new GET method, querying the specific HTML file at "what.should.I.put.here.html/getAll", with the option for

After designing the majority of my website, I decided to host it on a school-based hosting service. My files are organized into two folders: a client folder with my html pages and main.js file, and a server folder containing the API that main.js accesses. ...

Transform URLs to end with .jpg using NextJS

My API has a specific endpoint called /api/thumbnail that currently returns a JPEG image. However, I would like the endpoint to also accept .jpg, so it can be accessed as /api/thumbnail.jpg. Would I be able to achieve this using only pure NextJS code, or ...

Navigating through the nested object values of an Axios request's response can be achieved in React JS by using the proper

I am attempting to extract the category_name from my project_category object within the Axios response of my project. This is a singular record, so I do not need to map through an array, but rather access the entire object stored in my state. Here is an ex ...

The React function is encountering a situation where the action payload is not

I encountered an error stating Cannot read property 'data' of undefined switch (action.type){ case REGISTER_USER: console.log("Action ", action);// This prints {type: "REGISTER_USER", payload: undefined} return [action.payload.data, ...

Error: The method specified in $validator.methods[method] does not exist

Having trouble solving a problem, despite looking at examples and reading posts about the method. The error I'm encountering is: TypeError: $.validator.methods[method] is undefined Below that, it shows: result = $.validator.methods[method].call( t ...

Tips for creating a multi-line cell in a React data grid

Having an issue with react-data-grid where it only displays single line text instead of multi line text. Any suggestions on how to fix this? Check out the sandbox example here: https://codesandbox.io/s/5vy2q8owj4?from-embed ...

I'm curious, which ref tag should I utilize for draft.js?

I'm currently working on a form using Draft.js with Next.js and TS, but I've encountered some errors in my code. Here is what I have so far: import {Editor, EditorState} from 'draft-js'; const [editorState, setEditorState] = useState( ...

Using node.js to parse an XML file from a URL and iterate through it to retrieve all the URLs contained within

I am currently utilizing the node module xml2js. The format of my xml file looks like this: <?xml version="1.0" encoding="UTF-8" ?> <?xml-stylesheet type="text/xsl"?> <?xml-stylesheet type="text/css" media="screen" href="some url" ?> ...

What is the process by which Node can access predefined variables in clustering?

Consider the following code snippet: var running = false; var cluster = require('cluster'); if(cluster.isMaster){ cluster.fork(); running = true; } If this code is executed within multiple forks, would the value of 'running' ...

The process of utilizing RxJS for server polling is a

My goal is to constantly update client-side data by polling the server. To achieve this, I have set up a dispatcher that triggers an action labeled FRONT_PAGE. This action is initiated when the app launches and the client is supposed to send requests every ...

Next.js components do not alter the attributes of the div element

I am encountering a problem with nextjs/reactjs. I have two tsx files: index.tsx and customAlert.tsx. The issue that I am facing is that the alert does not change color even though the CSS classes are being added to the alert HTML element. Tailwind is my c ...

Retrieve information attribute within VueJS

Within a v-for loop, I am utilizing a select form in the following manner: <div class="select"> <select v-model="shippingMethod"> <option value="{{shipping.id}}" v-for="shipping in shippingMethods" data-price="{{ shipping.amount ...

Error in jQuery Ajax post request caused by a keyword in the posted data

I understand why the post is failing, but I'm at a loss on how to fix it and I haven't been able to find any solutions online. I am removing references to jEditable in an attempt to simplify things, as this issue occurs even without the jEditable ...

Creating scalable controllers in ExpressJS

Recently diving into Node, I am venturing into the realm of creating an MVC app with ExpressJS. To mimic the structure of a well-known MVC example found on GitHub, I have organized my controllers into two main folders: main and system. My goal is to establ ...

Characteristics of JSON data containing quotation marks within the property values

Is it possible to include JavaScript functions in JSON like the example below? My JSON library is struggling to process this structure because of the quotations. How can I address this issue? I specifically need to store JavaScript functions within my JSON ...

After being redirected from another page using history() in React, the state is initially set to null but later gets updated to the correct value - Firebase integration

After logging in and being redirected to the profile page, I encounter an error that says 'Unhandled Rejection (TypeError): Cannot read property 'email' of null'. How can I ensure that the state is set before proceeding with any additio ...

Create the correct structure for an AWS S3 bucket

My list consists of paths or locations that mirror the contents found in an AWS S3 bucket: const keysS3 = [ 'platform-tests/', 'platform-tests/datasets/', 'platform-tests/datasets/ra ...