Skip to content

Commit

Permalink
feat(fireEvent): helper to assign target properties to node (#85)
Browse files Browse the repository at this point in the history
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: testing-library/react-testing-library#152
  • Loading branch information
Kent C. Dodds committed Aug 6, 2018
1 parent 2397f64 commit 19c2868
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 1 deletion.
11 changes: 11 additions & 0 deletions README.md
Expand Up @@ -509,6 +509,17 @@ fireEvent.click(getByText('Submit'), rightClick)
// default `button` property for click events is set to `0` which is a left click.
```

**target**: When an event is dispatched on an element, the event has the
subjected element on a property called `target`. As a convenience, if you
provide a `target` property in the `eventProperties` (second argument), then
those properties will be assigned to the node which is receiving the event.

This is particularly useful for a change event:

```javascript
fireEvent.change(getByLabelText(/username/i), {target: {value: 'a'}})
```

#### `getNodeText`

```typescript
Expand Down
21 changes: 20 additions & 1 deletion src/__tests__/events.js
@@ -1,4 +1,4 @@
import {fireEvent} from '../'
import {fireEvent} from '..'

const eventTypes = [
{
Expand Down Expand Up @@ -149,3 +149,22 @@ 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)
})

test('assigning a value to a target that cannot have a value throws an error', () => {
const node = document.createElement('div')
expect(() =>
fireEvent.change(node, {target: {value: 'a'}}),
).toThrowErrorMatchingInlineSnapshot(
`"The given element does not have a value setter"`,
)
})
24 changes: 24 additions & 0 deletions src/events.js
Expand Up @@ -322,13 +322,37 @@ Object.entries(eventMap).forEach(([key, {EventType = Event, defaultInit}]) => {

fireEvent[key] = (node, init) => {
const eventInit = {...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)
} /* istanbul ignore next (I don't want to bother) */ 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] */

0 comments on commit 19c2868

Please sign in to comment.