jest-dom
jest-dom copied to clipboard
`fireEvent.blur(HTMLInputElement)` not working as expected: Selection on focus is not being deselected on blur
@testing-library/reactversion: 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.