From 963e9f6b41a05015abc0c8bd1b43f36d2b1030ba Mon Sep 17 00:00:00 2001 From: "Kent C. Dodds" Date: Sat, 4 Aug 2018 16:35:33 -0600 Subject: [PATCH] feat(fireEvent): helper to assign target properties to node Specifically this is so the change event can set the value of the node in a way that works for React (and all other frameworks and non-frameworks) nicely. fixes: https://github.com/kentcdodds/react-testing-library/issues/152 --- src/__tests__/events.js | 10 ++++++++++ src/events.js | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/__tests__/events.js b/src/__tests__/events.js index b57e2f21d..29e77fc76 100644 --- a/src/__tests__/events.js +++ b/src/__tests__/events.js @@ -149,3 +149,13 @@ describe(`Aliased Events`, () => { expect(spy).toHaveBeenCalledTimes(1) }) }) + +test('assigns target properties', () => { + const node = document.createElement('input') + const spy = jest.fn() + const value = 'a' + node.addEventListener('change', spy) + fireEvent.change(node, {target: {value}}) + expect(spy).toHaveBeenCalledTimes(1) + expect(node.value).toBe(value) +}) diff --git a/src/events.js b/src/events.js index c9adc34e3..002182095 100644 --- a/src/events.js +++ b/src/events.js @@ -322,13 +322,37 @@ Object.entries(eventMap).forEach(([key, {EventType = Event, defaultInit}]) => { fireEvent[key] = (node, init) => { const eventInit = Object.assign({}, defaultInit, init) + const {target: {value, ...targetProperties} = {}} = eventInit + Object.assign(node, targetProperties) + if (value !== undefined) { + setNativeValue(node, value) + } const event = new EventType(eventName, eventInit) return fireEvent(node, event) } }) +// function written after some investigation here: +// https://github.com/facebook/react/issues/10135#issuecomment-401496776 +function setNativeValue(element, value) { + const {set: valueSetter} = + Object.getOwnPropertyDescriptor(element, 'value') || {} + const prototype = Object.getPrototypeOf(element) + const {set: prototypeValueSetter} = + Object.getOwnPropertyDescriptor(prototype, 'value') || {} + if (prototypeValueSetter && valueSetter !== prototypeValueSetter) { + prototypeValueSetter.call(element, value) + } else if (valueSetter) { + valueSetter.call(element, value) + } else { + throw new Error('The given element does not have a value setter') + } +} + Object.entries(eventAliasMap).forEach(([aliasKey, key]) => { fireEvent[aliasKey] = (...args) => fireEvent[key](...args) }) export {fireEvent} + +/* eslint complexity:["error", 9] */