This is nice article about testing Stimulus + Jest
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();
});
});
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();
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/));
});