Testing Functions Defined on Window Object in Jest and TypeScript: A Comprehensive Guide

I have been struggling to test a function call that is defined on the global window object. Despite reviewing various examples, I am still unable to successfully execute a simple test case.

Api.ts

import "./global.d";
const verifier = window.Verifier;

export class Api {
  constructor(private readonly id: string) {}
  public fetchData() {
    return new Promise<object>((resolve, reject) => {
      verifier.request({
        request: {
          id: this.id
        },
        onSuccess: (data: object) => {
          resolve(data);
        },
        onFailure: () => {
          reject("Error!");
        }
      });
    });
  }
}

Api.test.ts

import { Api } from "./Api";

let windowSpy: any;

describe("Test Apis", () => {
  beforeEach(() => {
    windowSpy = jest.spyOn(window, "window", "get");
  });

  afterEach(() => {
    windowSpy.mockRestore();
  });
  it("should call the function", () => {
    const mockedReplace = jest.fn();
    windowSpy.mockImplementation(() => ({
      Verifier: {
        request: mockedReplace
      }
    }));
    const api = new Api("123");
    api.fetchData();
    expect(mockedReplace).toHaveBeenCalled();
  });
});

global.d.ts

import { Verifier } from "./verifier";

declare global {
  interface Window {
    Verifier: Verifier;
  }
}

verifier.d.ts

type RequestPayload = {
  request: {
    id: string;
  };
  onSuccess: (data: object) => void;
  onFailure: () => void;
};
type verifyCode = number;

export interface Verifier {
  request: (requestPayload: RequestPayload) => verifyCode;
}

In order to provide further clarity, I have created a codesandbox example which can be accessed through the following link: https://codesandbox.io/s/cranky-mccarthy-2jj622?file=/src/verifier.d.ts

Answer №1

The issue at hand lies in the order of your imports.

When you perform

import { Api } from "./api";
, it triggers
const verifier = window.Verifier;
, which, at that moment, is undefined.

To resolve this, simply rearrange the import statements and spies for smooth functioning:

import { RequestPayload } from "./verifier";

const mockRequest = jest
  .fn()
  .mockImplementation((requestPayload: RequestPayload) => 1);

jest.spyOn(window, "window", "get").mockImplementation(() => {
  return {
    Verifier: {
      request: mockRequest,
    },
  } as unknown as Window & typeof globalThis;
});

// // // // //

import { Api } from "./api";

// // // // //

describe("Test Apis", () => {
  let api: Api;

  beforeEach(() => {
    jest.clearAllMocks();
  });

  beforeEach(() => {
    api = new Api("123");
  });

  it("should have valid function", () => {
    expect(typeof api.fetchData).toBe("function");
  });

  it("should call the function", () => {
    api.fetchData();

    expect(mockRequest).toHaveBeenCalled();
  });
});

An alternative approach could be to directly utilize window.Verifier within the code for cleaner tests:

export class Api {
  constructor(private readonly id: string) {}
  public fetchData() {
    return new Promise<object>((resolve, reject) => {
      window.Verifier.request({
        request: {
          id: this.id,
        },
        onSuccess: (data: object) => {
          resolve(data);
        },
        onFailure: () => {
          reject("Error!");
        },
      });
    });
  }
}
describe("Test Apis", () => {
  let api: Api, mockRequest: jest.Mock;

  beforeEach(() => {
    jest.clearAllMocks();

    mockRequest = jest
      .fn()
      .mockImplementation((requestPayload: RequestPayload) => 1);

    jest.spyOn(global, "window", "get").mockImplementation(() => {
      return {
        Verifier: {
          request: mockRequest,
        },
      } as unknown as Window & typeof globalThis;
    });
  });

  beforeEach(() => {
    api = new Api("123");
  });

  it("should have valid function", () => {
    expect(typeof api.fetchData).toBe("function");
  });

  it("should call the function", () => {
    api.fetchData();

    expect(mockRequest).toHaveBeenCalled();
  });
});

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

Ways to incorporate a promise within a for loop code block

In this code snippet, my goal is to create a ranking based on the prices and nutrient content of various foods in a Wix database. The process involves calculating an index for each food item: function calculate() { var array = []; wixData.query('Food& ...

What is the best way to make a click handler respond to any click on an "a" tag, regardless of its surrounding elements?

If I have the following HTML code: <a href="/foo">bar</a> <a href="/"> <div></div> </a> And I am looking to create a jQuery handler that will respond when ANY "a" tag is clicked: $(document).click((e) => { ...

Utilize JavaScript API for generating and retrieving XSD schema and XML documents

Are there any stable JavaScript APIs that can be used to generate and read XSD schemas and XML documents? My specific requirement is: Allow one user to define/generate an XSD schema through a UI. Later, allow another user to provide appropriate data ...

Implementing the Applet Param Tag using JavaScript or jQuery

<applet id="applet" code="myclass.class"> <param name="Access_ID" value="newaccessid"> <param name="App_ID" value="newappid"> <param name="Current_Url" value="newticketurl"> <param name="Bug_ID" value="newbugid"&g ...

How to retrieve data obtained from parsing readline and fs in node.js beyond the scope of the callback function

This specific question diverges from the one mentioned with an existing answer. It pertains to a snippet of code taken from node.js documentation involving the use of fs and readfile, with a focus on detecting an end-of-file flag, which appears to be the r ...

Is it possible that adding html tables together could result in the numbers being concatenated instead of summed?

When attempting to calculate the total sum of values in an html table column, my variable seems to be returning concatenated strings instead of the actual sum. For example, instead of 1 + 2 + 3 = 6, I am getting 1 + 2 + 3 = 123. The values in the "votes" ...

Tips for transforming numerical date data into a string format

Is there a method to convert the numeric month in a table into a string format? <table style="width: 100%;"> <tr> <th>Date</th> <th>Total</th> </tr> <tr> <td id="date ...

Why is ng-click not triggering within ng-repeat?

I currently have the code snippet below: scope.monthpickerclick = function(scope){ $window.alert('test'); console.log(scope); }; scope.monthpicker = function(alldatesdumparray){ var alldatesdump = booking.getalldates(); var ...

Is it possible to send an AJAX request to a Django view that might result in a redirect?

Query I encountered an issue while attempting to access a specific Django view through AJAX. This particular view redirects users if they haven't authorized the site with Google. I suspect the problem arises from redirecting "within" a view requested ...

Creating a JavaScript array with each element being a pair of objects

Hey there! I'm trying to create an array where each element is a pair of objects. Something like this: var Shelves = new arr[][] var books = new Books[] ; Shelves[book[i], book[j=i+1]], [book[i+1], book[j=i+1]] and so on......; I know how to use a f ...

Combining storybook and stencil: A comprehensive guide

I'm currently working on integrating storybook with stencil, but the stencil project was initially set up with the app starter using npm init stencil. Here's how it looks I've been researching how to use stencil with storybook, but most res ...

The operation of accessing a property of an undefined element fails due to unavailability, hence the error "Cannot

Hey everyone, I'm encountering an issue with opening a project developed in Angular 5.2.0 with Angular CLI version 1.7.4, while my Angular CLI version is 14.0.7... I ran "npm install" without any errors, but when I run "ng version" to check the local ...

conceal the present element using jQuery while dealing with numerous identical elements

When clicking on an element, I am attempting to hide that specific element. However, all elements share the same id and I need each one to hide individually upon click. Typically, I would use PHP in a foreach loop to add an incremental value to each ID. U ...

experiencing difficulties in retrieving the outcome from a sweetalert2 popup

function confirmation() { swal.fire({ title: "Are you absolutely certain?", text: "You are about to permanently delete important files", type: "warning", showCancelButton: true, show ...

In what ways can elements of the general concept be inferred from incorporating TypeScript?

I'm facing a challenge in figuring out how to achieve a specific task. We have a function called createActions that requires an object type and a string as a key. function createActions<T, Tkey extends string>(key: Tkey) { return function(o ...

Deleting items from an array in ReactJS

When retrieving a list of users from AWS Cognito, everything works flawlessly. However, the task of iterating over this array and removing users that do not match a specific Client ID is where I'm facing difficulties. What am I doing wrong in this sc ...

The Node express GET route experiences intermittent failures when using wildcard match for every other request

I have configured my router to accept any GET request that does not include the word "API" in it. This is specifically for my single page application to handle bookmarked URLs that are not recognized by the app's routes. However, I am encountering an ...

"Debating Angular: Comparing the Use of Getters and Methods in

To prevent cluttering the *ngIf directive with logic directly in the template. <div *ngIf="a === 3 && b === 'foo'"></div> I typically create a custom method like this: public isOk(): boolean { return a === 3 & ...

Issues with react-router-dom's <Routes> and <BrowserRouter> components

Encountering an issue with <Routes> while using react-router-dom. The code in my index.tsx file is as follows: import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './Ap ...

Retrieve the URL of the image from an XML document

Figuring out how to extract the image URL from XML files in Android Studio can be challenging. After analyzing 30 RSS Feed XMLs, I discovered that 95% of them contain ".jpg" images with links starting with "http," not "www." Therefore, I am working on a co ...