Testing Stimulus, Jest

This is nice article about testing Stimulus + Jest

3 Likes

I was able to get going with your examples very well. Thank you for writing this up. Where I am now having trouble is when I have an element

<a id="anchor" href="#" data-action="foo.bar">dosomething</a>

and the foo controller#bar function uses fetch to get a response from the server and replace an item in the dom with the response

bar(event) {
  event.preventDefault();
  fetch(this.data.get("url"))
   .then(response => response.text())
   .then(data => this.itemTarget.innerHTML = data)
   .catch(error => console.log('Error ',error));
}

a jest test looks something like

it("fetches the url and calls #replaceItem with the response", () => {
  fetchMock.get(editAction, new Promise(res => res(formItem)));
  let anchor = document.getElementById("anchor");
  anchor.click();
  expect(fetchMock).toHaveBeenCalledWith(editAction);

  let item = document.getElementById("test-item");
  expect(item.innerHTML).toEqual(formItem);
  fetchMock.reset();
});

I can see that the foo method is being called after the click, but I cannot figure out the incantation to use either done, or async/await, etc. to get the item.innerHTML test to work (it comes back unchanged).

I was able to solve my own problem. Here is how I did it for posterity, using process.nextTick

it("fetches the url and calls #replaceItem with the response", () => {
  fetchMock.get(editAction, new Promise(res => res(formItem)));
  let anchor = document.getElementById("anchor");
  anchor.click();
  expect(fetchMock).toHaveBeenCalledWith(editAction);

  process.nextTick(() => {
    let item = document.getElementById("test-item");
    expect(item.innerHTML).toEqual(formItem);
    fetchMock.reset();
  });
});
1 Like

I might be a little bit late to the game, but I was having similar issues that process.NextTick didn’t resolve.

At the top of the file, I had to do the following:

import { setImmediate } from "timers";
const flushPromises = () => new Promise(setImmediate);

Then whenever I needed to flush any pending promises, I would just call the flushPromises before the expectation.

button.click();
// Once the button is clicked an AJAX request is made in the controller and adds an element to the DOM
flushPromises();
expect(element).toBeTruthy();
1 Like

Oh, not just me then…I tried all the examples above, but not luck.

I ended up using waitFor from testing-library.

import { waitFor } from '@testing-library/dom';

...

it("does something", async () => {
  const button = document.querySelector('button');
  const target = document.querySelector('#target');

  button.click(); // Triggers fetch and updates target

  await waitFor(() => expect(target.innerHTML).toMatch(/something/));
});