Mock jquery click event using jest

Viewed 1006

I am trying to write the unit test for following javascript function.

const $ = require("jquery");
const Toggle = (function (toggle, $) {
  toggle.clickHandler = () => {
    $(".active").not(this).removeClass("active").next().hide(300);

    $(this).toggleClass("active");

    if (false == $(this).next().is(":visible")) {
      $("#accordion > ul").slideUp(300);
    }
    $(this).next().slideToggle(300);
  };

  $("#accordion > li > div").on("click", toggle.clickHandler);
 
  return {
    toggle,
  };
})({}, $);

module.exports = Toggle.toggle;

I have written following jest test

const toggle = require("./toggle");
const $ = require("jquery");
jest.dontMock("jquery");
jest.dontMock("./acc");

describe("Toggle", () => {
  it("It should call click handler", () => {
    document.body.innerHTML = ` <ul id="accordion">
    <li>
      <div>Sports</div>
      <ul>
        <li><a href="#">Golf</a></li>
        <li><a href="#">Cricket</a></li>
        <li><a href="#">Football</a></li>
      </ul>
    </li>
    </ul>
    `;
    const spy = jest.spyOn(toggle, "clickHandler");
    $("div").trigger("click");
    $("div").trigger("click");

    expect(spy).toBeCalledTimes(2);
  });
});

But when I am running the test I am getting the following error.

Expected number of calls: 2 Received number of calls: 0

1 Answers

When you are importing from your js file at the beginning of your test file, you will have all invokable code already run by that time.

Meaning, even before your test is initialized, $("#accordion > li > div").on("click", toggle.clickHandler); executes and as there is noting in the DOM at that point, click handler are not registered.

Right approach would be to expose a method to attach click handlers and call it from your test/code. Something like:

toggle.js

const $ = require("jquery");
const Toggle = (function (toggle, $) {
  toggle.clickHandler = () => {
    $(".active").not(this).removeClass("active").next().hide(300);

    $(this).toggleClass("active");

    if (false == $(this).next().is(":visible")) {
      $("#accordion > ul").slideUp(300);
    }
    $(this).next().slideToggle(300);
    console.log("clicked!");
  };

  toggle.attach = () => { // exposing method to attach handlers
    $("#accordion > li > div").on("click", toggle.clickHandler);
  };

  return {
    toggle
  };
})({}, $);

module.exports = Toggle.toggle;

toggle.test.js

describe("Toggle", () => {
  it("It should call click handler", async () => {
    document.body.innerHTML = ` <ul id="accordion">
    <li>
      <div>Sports</div>
      <ul>
        <li><a href="#">Golf</a></li>
        <li><a href="#">Cricket</a></li>
        <li><a href="#">Football</a></li>
      </ul>
    </li>
    </ul>
    `;
    const spy = jest.spyOn(toggle, "clickHandler");
    toggle.attach(); // attach handlers
    $("div").trigger("click");
    $("div").trigger("click");

    return new Promise((res, rej) => {
      expect(spy).toBeCalledTimes(2);
      res();
    });
  });
});
Related