diff --git a/src/index.js b/src/index.js index d67ad18c..35a61edd 100644 --- a/src/index.js +++ b/src/index.js @@ -115,20 +115,54 @@ const Keys = { Backspace: { keyCode: 8, code: "Backspace", key: "Backspace" } }; +/** + * Method to abstract setting the value of an element + * + * We need to override the logic here because React will wrap an `input`'s + * `.value` setter and prevent us from dispatching `onChange` events after + * setting the value with `value = something`. Here, we'll call the prototype + * value setter method so the native `.value` will be correct, fire the event + * (the event.target will have the correct value, as will the original `input`), + * and then use the prototype override of `value` if there is one for React's + * internals. + * + * @param {Element} element Element we want to set the `value` on + * @param {String} value Value we want to set on `element` + */ +function setElementValue(element, value) { + const valueSetter = Object.getOwnPropertyDescriptor(element, "value").set; + const prototypeValueSetter = Object.getOwnPropertyDescriptor( + Object.getPrototypeOf(element), + "value" + ).set; + + // Call the native prototype method + prototypeValueSetter.call(element, value); + + fireEvent.change(element, { bubbles: true }); + + // If the prototype method is not the same as the method on the element, then + // there's an override we need to also call. + if (valueSetter !== prototypeValueSetter) { + element.value = value; + } +} + function backspace(element) { - const eventOptions = { + const keyboardEventOptions = { key: Keys.Backspace.key, keyCode: Keys.Backspace.keyCode, which: Keys.Backspace.keyCode }; - fireEvent.keyDown(element, eventOptions); - fireEvent.keyUp(element, eventOptions); + fireEvent.keyDown(element, keyboardEventOptions); + fireEvent.keyUp(element, keyboardEventOptions); if (!element.readOnly) { fireEvent.input(element, { inputType: "deleteContentBackward" }); - element.value = ""; // when we add special keys to API, will need to respect selected range + + setElementValue(element, ""); } } @@ -225,7 +259,6 @@ const userEvent = { selectAll(element); backspace(element); - element.addEventListener("blur", fireChangeEvent); }, async type(element, text, userOpts = {}) {