jest-dom icon indicating copy to clipboard operation
jest-dom copied to clipboard

`fireEvent.blur(HTMLInputElement)` not working as expected: Selection on focus is not being deselected on blur

Open jayantasamaddar opened this issue 3 years ago • 0 comments

  • @testing-library/react version: 13.4.0
  • Testing Framework and version: "jest": "^29.1.2",
  • DOM Environment: "jest-environment-jsdom": "^29.1.2", "@testing-library/jest-dom": "^5.16.5"

What you did:

I have a Input Text Component setup which selects the entire value of the input field on focus. It is expected to when it loses focus or is blurred, it automatically deselects the selection.


Relevant code or config:

it('When blurred, deselect', () => {
      const inputEl = screen.getByLabelText("Search") as HTMLInputElement;
      const inputVal = "Placeholder"; // The input value
      /** Fire an focus event */
      inputEl.focus();    // // alternatively: fireEvent.focus(inputEl)   - Tried both
      const afterFocusSelection = inputVal.slice(
        inputEl.selectionStart,
        inputEl.selectionEnd
      );
      expect(afterFocusSelection).toBe(inputVal); // Compare to input value: Selected
      console.log({ afterFocusSelection });   // "Placeholder"
      /** Fire a blur event */
      act(() => {
        fireEvent.blur(inputEl); // alternatively: fireEvent.blur(inputEl)   - Tried both
      }); 
      expect(inputEl).not.toHaveFocus(); // Passes Test
      const afterBlurSelection = inputVal.slice(
        inputEl.selectionStart,
        inputEl.selectionEnd
      );
      console.log({ afterBlurSelection });    // Still returns "Placeholder"
      expect(afterBlurSelection).not.toBe(inputVal); // Compare to input value: Not selected   | Doesn't pass Test
 });

What happened:

Getting the following error.

  • The console log of the selection, before and after Blur shows the same selection: "Placeholder"
  console.log
    { afterFocusSelection: 'Placeholder' }

      at Object.log (src/components/Textfield/Textfield.test.tsx:162:15)

  console.log
    { afterBlurSelection: 'Placeholder' }

expect(received).not.toBe(expected) // Object.is equality

    Expected: not "Placeholder"

      171 |       );
      172 |       console.log({ afterBlurSelection });
    > 173 |       expect(afterBlurSelection).not.toBe(inputVal); // Compare to input value: Not selected
          |                                      ^
      174 |     });
      175 |   });

Problem description:

Not able to test whether the functionality of select, deselect on focus and blur is possible.

This is unexpected behaviour, because the On Focus test works,

it('On focus, Select the Textfield value', () => {
      const inputEl = screen.getByLabelText("Search") as HTMLInputElement;
      const inputVal = "Placeholder"; // The input value
      /** Get selection before focus event */
      const beforeFocusSelection = inputVal.slice(
        inputEl.selectionStart,
        inputEl.selectionEnd
      );
      console.log({ beforeFocusSelection });   // Returns "" (empty string), i.e. nothing is selected
      expect(beforeFocusSelection).not.toBe(inputVal); // Compare to input value: Not selected | Passes Test
      /** Fire an focus event */
      act(() => {
        fireEvent.focus(inputEl);
      });
      expect(inputEl).toHaveFocus(); // Expect focus event to work |  Passes Test
      /** Get selection after focus event */
      const afterFocusSelection = inputVal.slice(
        inputEl.selectionStart,
        inputEl.selectionEnd
      );
      expect(afterFocusSelection).toBe(inputVal); // Compare to input value: Selected | Passes Test
});

Reproduction:

This is the component. Copy this and import it into the test. The tests are already above.

import { useState, useRef, useEffect, FocusEvent, ChangeEvent, forwardRef } from 'react;
import { TextfieldProps } from './types';

interface TextfieldProps {
  value?: string;
  onBlur?: (e?: FocusEvent<HTMLInputElement>) => void;
  onFocus?: (e?: FocusEvent<HTMLInputElement>) => void;
  onChange?: (e: ChangeEvent<HTMLInputElement>) => void
}

const Textfield = forwardRef<HTMLInputElement, TextfieldProps>(({ value = "Placeholder", onBlur, onFocus, onChange }) => {
   const inputRef = useRef<HTMLInputElement>(null);
   const [ focus, setFocus ] = useState(false)

  useEffect(() => {
     const target = inputRef.current;
     focus ? target?.focus() : target?.blur();
  }, [focus]);
   
   const handleBlur = (e: FocusEvent<HTMLInputElement>) => {
        setFocus(false);
        onBlur && onBlur(e);
   });

   const handleFocus = (e: FocusEvent<HTMLInputElement>) => {
        setFocus(true);
        inputRef.current?.select(); // Selects on Focus
        onFocus && onFocus(e);
   });
   
   return (
      <div className="TextfieldContainer">
        <label id="test-textfield-label" htmlFor="test-textfield-id">Search</label>
        <input 
            id="test-textfield-id"
            data-testid="test-textfield" 
            className="test-textfield" 
            onBlur={handleBlur} 
            onFocus={handleFocus} 
            onChange={onChange}
            value={value} 
            aria-labelledby="test-textfield-label"
            />
      </div>
   )
});

I'll try making a repo later in the day and update this...but this should be extremely simple to reproduce.


jayantasamaddar avatar Oct 20 '22 19:10 jayantasamaddar