svelte-testing-library icon indicating copy to clipboard operation
svelte-testing-library copied to clipboard

Unable to test a non-bound change in the DOM

Open unknownterritory opened this issue 4 years ago • 2 comments

I am new to testing-library, so I hope I'm not missing anything obvious.

I have a helper class whose methods make changes in a component's DOM, some upon interaction by the user. I am unable to test those changes with fireEvent or userEvent. It always fails.

I simplified my use case to a very simple example:

  • My svelte component has only a button that modifies the content of a paragraph. The paragraph's content is not bound to a variable intentionally.
  • And then I have a test for it. The test expect(info).toHaveTextContent("Modified by click") fails but, curiously enough, a console.log(paragraph.innerText) shows the change.
  • I also have a codesandbox replicating this example.

Code for the component:

<script>
  // The text content of the paragraph is purposely not bound
  // with any svelte variable. The purpose of this exercise
  // is to reduce to the simplest form my use-case which is
  // to test changes done in html elements of a svelte component
  // by external helper class methods, which do not allow for
  // bindings to exist.
  const modify = () => {
    const par = document.querySelector(".info");
    par.innerText = "Modified by click";
  };
</script>

<div>
  <p class="info" data-testid="info">Click to modify</p>
  <button on:click={modify} label="button">Modify</button>
</div>

The code for the test.

import { render, screen, waitFor } from "@testing-library/svelte";
import "@testing-library/jest-dom/extend-expect";
import userEvent from "@testing-library/user-event";
import Component from "./Component.svelte";

it("should modify the text after clicking the button", async () => {
  render(Component);
  const button = screen.getByRole("button");
  userEvent.click(button);
  const info = screen.getByTestId("info");
  // console.log actually outputs the modified content?
  console.log(info.innerText);
  // The test fails independently of using waitFor or not.
  await waitFor(() => {
    expect(info).toHaveTextContent("Modified by click");
  });
});

Is this a bug of the library or should I be approaching this test differently?

P.S.: I asked on Discord, both in the testing-library and the svelte communities. I've also researched the documentation and previous issues. While I'm not entirely sure this is a bug, I would very much appreciate an indication of where to seek a solution if the fault resides in my approach.

unknownterritory avatar Jan 29 '21 00:01 unknownterritory

I have similar problem when testing button clicks that will change the DOM. Sometimes it works, sometimes it doesn't.

airedwin avatar Oct 26 '21 15:10 airedwin

I'm nearly sure i'm running into the same issue.

It doesn't seem to re-render the dom after a click event.

PClmnt avatar Jan 26 '22 17:01 PClmnt

I'm experiencing the same symptoms when testing a modal component. An initial test interaction triggers a change in the component (the menu content goes from hidden to rendered); but a subsequent interaction in the same test - that should close the menu - fails to update the 'rendered' output (e.g. as seen in debug()) and the test fails. Inspecting the property that controls visibility shows it has updated :confused: I tried wrapping my expect() in a waitFor() thinking it might simply be a timing issue; but the update render never happens...

edit: in my case it appears that the problem is the use of a transition in the tested component. See #206

blindfish3 avatar Sep 28 '23 13:09 blindfish3

It seems that using happy-dom instead of jsdom fixes the problem!

yanick avatar Jan 23 '24 19:01 yanick

The cause of this, specifically, is that jsdom does not support innerText. There are two available solutions to work around this, neither of which involve svelte-testing-library:

  1. Switch test runner to Vitest + happy-dom
    • happy-dom supports innerText
    • In my experience, happy-dom is not a simple drop-in replacement for jsdom, depending on your test suite
    • Vitest is great, but also not necessarily a drop-in replacement for Jest
  2. Switch implementation to use .textContent or .innerHTML instead of .innerText
    • Note: both of these have important differences from innerText that should be considered before changing your implementation

@yanick I think this ticket should be considered outside the scope of svelte-testing-library closed. I don't think switching our tests to happy-dom is the right move at this time (see my comment in #286 for reasoning)

mcous avatar Jan 24 '24 20:01 mcous

@yanick I think this ticket should be considered outside the scope of svelte-testing-library closed. I don't think switching our tests to happy-dom is the right move at this time (see my comment in #286 for reasoning)

Agreed. And having the tests run both under jsdom and happy-dom strikes me as better than flopping around. But that's a talk for #286 :-)

yanick avatar Jan 24 '24 21:01 yanick