Evaluating a method that produces an object containing a function - JavaScript testing with Jest

I'm currently dealing with a problem related to unit testing a function. The function SOP3loginConfig returns an object that contains another function called isSOP3 which returns a boolean value. I need to write tests for this specific function.

The code snippet for the function sop3login.ts

export const SOP3loginConfig = (props: IVariables) => {
  const { i18n } = props;
  return {
    buttonLabel: props.user != null ? i18n('StartJourney') : i18n('logIn'),
    loginLink:"/login?redirectUrl="+window.location.href,
    isSOP3: async() => {
      let userData = await ADCServices.getUserInfo();
      if (!userData.session.tammUserInfo || userData.session.tammUserInfo.Type!="SOP3") {
        props.SOP3toggleModal(props,true);
        setTimeout(()=>props.SOP3toggleModal(props,false),5000)
        return false;
      } else {
        return true;
      }
    },
  };
};

The part of my integration in work.ts

import { SOP3loginConfig } from 'client/.../sop3login';

const start = async (props: IVariables) => {
  if (props.user) {
    if (await SOP3loginConfig(props).isSOP3()) {
      props.history.push('/adc/card-renewal/customs');
    }
  } else {
    props.history.push(SOP3loginConfig(props).loginLink);
  }
};

My unit testing implementation in work.test.ts

  describe('Testing if SOP3loginConfig should be called', () => {
    it('Should call the SOP3 function', async () => {
      props.user = true;
      let SOP3loginConfig = jest.fn(props => {
        return {
          isSOP3: jest.fn(() => {
            return true;
          }),
        };
      });
      functions.start(props);
      expect(await SOP3loginConfig(props).isSOP3).toHaveBeenCalled();
      expect(props.history.push).toHaveBeenCalled();
    });
  });

Error message I encountered

    expect(jest.fn()).toHaveBeenCalled()

    Expected number of calls: >= 1
    Received number of calls: 0

       97 |       });
       98 |       functions.start(props);
    >  99 |       expect(await SOP3loginConfig(props).isSOP3).toHaveBeenCalled();
          |                                                   ^
      100 |       expect(props.history.push).toHaveBeenCalled();
      101 |     });
      102 |   });

I just need to cover the section

if (await SOP3loginConfig(props).isSOP3())
in work.ts.

Answer №1

Below is the solution for the unit test:

sop3login.ts:

import ADCServices from './adc.service';

export interface IVariables {
  user: any;
  history: IHistory;
  i18n(name: string): any;
  SOP3toggleModal(props: IVariables, flag: boolean): void;
}

interface IHistory {
  push(router: string): any;
}

export const SOP3loginConfig = (props: IVariables) => {
  const { i18n } = props;
  return {
    buttonLabel: props.user != null ? i18n('StartJourney') : i18n('logIn'),
    loginLink: '/login?redirectUrl=' + window.location.href,
    isSOP3: async () => {
      const userData = await ADCServices.getUserInfo();
      if (!userData.session.tammUserInfo || userData.session.tammUserInfo.Type !== 'SOP3') {
        props.SOP3toggleModal(props, true);
        setTimeout(() => props.SOP3toggleModal(props, false), 5000);
        return false;
      } else {
        return true;
      }
    },
  };
};

adc.service.ts:

export default class ADCServices {
  public static async getUserInfo() {
    return {
      session: {
        tammUserInfo: {
          Type: 'real type',
        },
      },
    };
  }
}

work.ts:

import { SOP3loginConfig, IVariables } from './sop3login';

const start = async (props: IVariables) => {
  if (props.user) {
    if (await SOP3loginConfig(props).isSOP3()) {
      props.history.push('/adc/card-renewal/customs');
    }
  } else {
    props.history.push(SOP3loginConfig(props).loginLink);
  }
};

export { start };

work.test.ts:

import { start } from './work';
import { SOP3loginConfig, IVariables } from './sop3login';

jest.mock('./sop3login.ts', () => {
  const mObj = {
    isSOP3: jest.fn(),
  };
  return { SOP3loginConfig: jest.fn(() => mObj) };
});

describe('Start SOP3loginConfig should be called', () => {
  it('Should call the SOP3', async () => {
    const mProps: IVariables = {
      user: true,
      history: { push: jest.fn() },
      i18n: jest.fn(),
      SOP3toggleModal: jest.fn(),
    };
    await start(mProps);
    expect(SOP3loginConfig).toBeCalledWith(mProps);
    expect(SOP3loginConfig(mProps).isSOP3).toBeCalledTimes(1);
  });
});

Results of the unit test with coverage report:

 PASS  src/stackoverflow/59627009/work.test.ts
  Start SOP3loginConfig should be called
    ✓ Should call the SOP3 (7ms)

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |    77.78 |       50 |      100 |    71.43 |                   |
 work.ts  |    77.78 |       50 |      100 |    71.43 |               6,9 |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        4.078s, estimated 10s

Source code available at: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59627009

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

Simplify if statements by eliminating repetition

I have been tasked with refactoring the code below and I have already done so (check the image for reference). However, my supervisor is still not satisfied with the changes, LOL. const { appTargetId, appUserTargetId, appUserId } = buildIndexKeys(input); ...

When organizing data, the key value pair automatically sorts information according to the specified key

I have created a key value pair in Angular. The key represents the questionId and the value is the baseQuestion. The baseQuestion value may be null. One issue I am facing is that after insertion, the key value pairs are automatically sorted in ascending ...

A guide on assigning a React type to a React variable within a namespace in a d.ts file

How can I properly declare a namespace named PluginApi for users to utilize this d.ts file for code recommendations? In the code snippet below, React is referencing its own const React instead of the React library. What steps should I take to resolve thi ...

Validating a field conditionally upon submission

Adding a required validation conditionally to the "imageString" field upon submission, but the expected required validation is not being set. Initializing the form. constructor(){ this.poeForm = this.fb.group({ imageString: [""], imageFileNam ...

What are the advantages of using interfaces in React?

Exploring Typescript's interface and its application in React has been an interesting journey for me. It seems that interfaces are used to define specific props that can be passed, acting as a form of protection against passing irrelevant props. My qu ...

Creating a build task in Visual Studio Code with universal TypeScript compiler settings

Our project has the following structure: +-- views +-- viewXXX +-- ts ¦ +-- controller.ts ¦ +-- helper.ts ¦ +-- ... (*.ts) +-- viewXXX.ctrl.js // this is the desired output file +-- viewXXX.c ...

Utilize a counter beyond the ngFor loop

I need to find a way to determine the number of rows in my table without using the ngFor directive. Here is an example of what I am trying to achieve: <table class="table"> <thead> <tr> <th scope="col">#</th> ...

Angular 2 error: "unknown element" issue persists despite exhausting all attempted solutions

Here's a typical scenario where I attempt to incorporate a component from another module : External component : import { Component, ViewEncapsulation, ElementRef, ViewChild, Input, Output, EventEmitter } from '@angular/core'; declare ...

ERROR Error: Uncaught (in promise): ContradictionError: The variable this.products is being incorrectly identified as non-iterable, although it

Seeking a way to extract unique values from a JSON array. The data is fetched through the fetch API, which can be iterated through easily. [please note that the product variable contains sample JSON data, I actually populate it by calling GetAllProducts( ...

Innovative Functions of HTML5 LocalStorage for JavaScript and TypeScript Operations

Step-by-Step Guide: Determine if your browser supports the use of localStorage Check if localStorage has any stored items Find out how much space is available in your localStorage Get the maximum storage capacity of localStorage View the amount of space ...

Uncovering the types of objects in TypeScript

Can TypeScript infer the type based on the content of an object? For example: type MyKeyList = | "A" | "B" | "C" ; type MyType<T extends MyKeyList> = { type: T, value: T extends "A" ...

Steps for connecting data to a react table

This is my first time working with React and I want to display the following data (id, name, status, and quantity): interface Goods { id: number; name: string; status: string; quantity?: number; } export const getGoods = (): Promise<Goods[]> ...

Convert an enum value to a String representation

I need assistance with converting my enum value to a string format export enum Roles { manager = "manager", user = "user" } console.log(" Roles.manager: ", Roles[Roles.manager]); The following is displayed in the console: Roles.manager: manage ...

Utilizing Ionic and Angular to Submit an Input Field from an Array of Fields

I'm feeling a bit perplexed about how to approach this situation. In my app, there is a "feed" feature where each post includes a comment box. Here's a snippet of the code: <ion-card class="feed" *ngFor="let post of feed"> <ion-item n ...

Tips for displaying field options after typing parentheses in TypeScript in Visual Studio Code

Whenever the letter "b" is typed, the suggestion of "bar" appears. However, I would prefer if the suggestions show up immediately after typing the brackets. https://i.stack.imgur.com/OFTO4.png ...

Using TypeScript with axios and encountering an undefined property

Currently, I am encountering an issue while attempting to type the axios response. Here is a glimpse of how the response type appears: export interface GetBreedsResponse { data: { message: Breed[] } } Within my router file, I have implemented the ...

Angular is unable to access functions or variables within a nested function

I am currently utilizing google.maps.geocoder to make location requests within my Angular app. When I provide a callback function with the results, it leads to unexpected code breaks when trying to call another function that displays map markers. It seem ...

Manipulate and send back information from Observable in Angular

Here is an example of a scenario where I have created a service to retrieve a list of properties. Within this service, I am utilizing the map function to manipulate the response and then returning the final array as an Observable. My question is: How can ...

Tips for updating dropdown value from a child component in Angular version 8

I've been attempting to update the dropdown value from a child component in Angular 8, but I'm unsure of how to do it. If anyone has a solution, please help me out. When I click on the France button, I want the dropdown value to be set to France ...

How can I combine my two ngIf conditions into an ngIf else statement?

Having trouble incorporating an *ngIf else with two large <div> elements, as the content seems overwhelming to organize properly while following the documentation. Initially believed that using the same styling for two open text boxes, with one hidd ...