Skip to content

Commit 3e5394e

Browse files
fix: onchange event with preact/compat enabled (#72)
* fix: fireEvent doesnt mirror onChange behavior when using preact/compat this fix aligns this library with testing-library for React where fireEvent.change() works as expected * refactor: move onChange test to separate file onChange test requires import from preact/compat which affects other tests in the same test file so I moved it to separate file * refactor: simplify imports replace unnecessary forwardRef import with general preact/compat; improve commentary Co-authored-by: Ryan Christian <[email protected]> * fix: apply aliasing only when preact/compat is used prevent applying aliasing when compat library is not used; add change event to basic event tests to ensure both cases work --------- Co-authored-by: Ryan Christian <[email protected]>
1 parent 9fdc19b commit 3e5394e

File tree

3 files changed

+59
-5
lines changed

3 files changed

+59
-5
lines changed

Diff for: src/__tests__/events-compat.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { h } from 'preact' // required by render
2+
import { fireEvent, render } from '..'
3+
import 'preact/compat'
4+
5+
test('calling `fireEvent` with `preact/compat` and onChange works too', () => {
6+
const handler = jest.fn()
7+
8+
// Preact only matches React's aliasing of `onChange` when `preact/compat` is used
9+
// This test ensures this is supported properly with `fireEvent.change()`
10+
const {
11+
container: { firstChild: input }
12+
} = render(<input type="text" onChange={handler} />)
13+
14+
const targetProperties = { value: 'a' }
15+
const otherProperties = { isComposing: true }
16+
const init = {
17+
target: targetProperties,
18+
...otherProperties
19+
}
20+
21+
expect(fireEvent.change(input, init)).toBe(true)
22+
23+
expect(handler).toHaveBeenCalledTimes(1)
24+
expect(handler).toHaveBeenCalledWith(expect.objectContaining(otherProperties))
25+
})

Diff for: src/__tests__/events.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const eventTypes = [
3030
},
3131
{
3232
type: 'Focus',
33-
events: ['input', 'invalid'],
33+
events: ['input', 'invalid', 'change'],
3434
elementType: 'input'
3535
},
3636
{
@@ -200,8 +200,14 @@ test('calling `fireEvent` directly works too', () => {
200200
})
201201

202202
test('`fireEvent` returns false when prevented', () => {
203-
const { container: { firstChild: button } } = render(
204-
(<button onClick={(e) => { e.preventDefault() }} />)
203+
const {
204+
container: { firstChild: button }
205+
} = render(
206+
<button
207+
onClick={(e) => {
208+
e.preventDefault()
209+
}}
210+
/>
205211
)
206212

207213
expect(fireEvent.click(button)).toBe(false)

Diff for: src/fire-event.js

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
11
import { fireEvent as domFireEvent, createEvent } from '@testing-library/dom'
2+
import { options } from 'preact'
3+
4+
let isCompat = false
5+
6+
// Detects if preact/compat is used
7+
const oldHook = options.vnode
8+
options.vnode = (vnode) => {
9+
if (vnode.$$typeof) isCompat = true
10+
if (oldHook) oldHook(vnode)
11+
}
12+
13+
// Renames event to match React (preact/compat) version
14+
const renameEventCompat = (key) => {
15+
return key === 'change' ? 'input' : key
16+
}
217

318
// Similar to RTL we make are own fireEvent helper that just calls DTL's fireEvent with that
419
// we can that any specific behaviors to the helpers we need
@@ -11,8 +26,16 @@ Object.keys(domFireEvent).forEach((key) => {
1126
// we hit the Preact listeners.
1227
const eventName = `on${key.toLowerCase()}`
1328
const isInElem = eventName in elem
29+
// Preact changes all change events to input events when running 'preact/compat',
30+
// making the event name out of sync.
31+
// The problematic code is in: preact/compat/src/render.js > handleDomVNode()
32+
const keyFiltered = !isCompat ? key : renameEventCompat(key)
33+
1434
return isInElem
15-
? domFireEvent[key](elem, init)
16-
: domFireEvent(elem, createEvent(key[0].toUpperCase() + key.slice(1), elem, init))
35+
? domFireEvent[keyFiltered](elem, init)
36+
: domFireEvent(
37+
elem,
38+
createEvent(keyFiltered[0].toUpperCase() + keyFiltered.slice(1), elem, init)
39+
)
1740
}
1841
})

0 commit comments

Comments
 (0)