testing-library / dom-testing-library

🐙 Simple and complete DOM testing utilities that encourage good testing practices.
https://testing-library.com/dom
MIT License
3.25k stars 463 forks source link

*ByDisplayValue is not finding an input after it changes #1232

Open stuartbrussell-intuit opened 1 year ago

stuartbrussell-intuit commented 1 year ago

Relevant code or config:

  it.only('should format typed value', () => {
    renderComponent({ maximumAmount: '100' }); // this is a custom method that renders an input with default values
    await userEvent.tripleClick(screen.getByDisplayValue('0.00'));
    await userEvent.keyboard('55.7');
    await userEvent.tab(); // on blur, the component reformats its value to '55.70'
    expect(screen.getByDisplayValue('55.7')).toBeInTheDocument();   // this works (it shouldn't)
    expect(screen.getByDisplayValue('55.70')).toBeInTheDocument(); // this doesn't work (it should)
  });

What you did:

I'm testing an internally built component that's based on a DOM input. One of it's functions is to format a number according to currency after a blur. E.g. if you type '55.7' and tab out, it will change its value to '55.70'. The field is first rendered with the value '0.00'.

What happened:

The first getByDisplayValue('0.00') works, and that allows us to send events to the element. But after we type '55.7' and tab out, neither getByDisplayValue('55.70') nor findByDisplayValue('55.70') can find the element. When they fail, here is the output:

Unable to find an element with the display value: 55.70.

Ignored nodes: comments, script, style
<body>
  <div>
    <div>
      <div
        class="idsTSTextField moneyText TextFieldWrapper"
        style="width: 12rem;"
      >
        <label
          class="TFLabelWrapper"
          for="idsTxtField1"
        >
          <span
            class="idsT body-3 intuit light"
            theme="intuit"
          />
          <div
            class="TFInputWrapper"
          >
            <input
              aria-invalid="false"
              aria-label="Money Text"
              class="idsF TFInput intuit light TFNoErrorText TFNotDisabled TFInputHideArrows"
              id="idsTxtField1"
              type="number"
              value="55.70"
              width="12rem"
            />
          </div>
        </label>
        <div
          class=""
        />
      </div>
    </div>
  </div>
</body>

You can see that the input has the value '55.70' as expected.

In a browser with real user events, the input properly shows '55.70'. And in this unit test, the input ends with the correct value, as you can see by the output above.

Instead, getByDisplayValue('55.7') and findByDisplayValue('55.7') both work, i.e. with the typed value instead of the re-formatted value after blur.

Reproduction:

I'm hoping there's an easy answer... 🙄

Problem description:

We can't unit test this part of our component's behavior.

Suggested solution:

Hope for the best?

stuartbrussell-intuit commented 1 year ago

Found a workaround

Instead of:

  it.only('should format typed value', () => {
    renderComponent({ maximumAmount: '100' });
    // ...
    expect(screen.getByDisplayValue('55.70')).toBeInTheDocument();
  });

use:

  it.only('should format typed value', () => {
    const { container } = renderComponent({ maximumAmount: '100' });
    // ...
    /* eslint-disable-next-line testing-library/no-container */
    expect(container.querySelector('[value="55.70"]')).not.toBeNull();`
  });
MatanBobi commented 1 year ago

Is there any chance that there's something async going on there? Did you try wrapping the assertion in a waitFor?