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

*ByDisplayValue is not finding an input after it changes

Open stuartbrussell-intuit opened this issue 2 years ago • 2 comments

  • @testing-library/jest-dom version: 5.16.5
  • react and react-dom version: 16.13.1
  • jest version: 27.0.1
  • node version: 14.21.1

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 avatar May 09 '23 22:05 stuartbrussell-intuit

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();`
  });

stuartbrussell-intuit avatar May 11 '23 06:05 stuartbrussell-intuit

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

MatanBobi avatar May 11 '23 06:05 MatanBobi