Navigation Menu

Skip to content

Commit

Permalink
fix(clear): fire onChange handlers (#286)
Browse files Browse the repository at this point in the history
  • Loading branch information
justinanastos committed May 21, 2020
1 parent 6032f14 commit bfbe92c
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 7 deletions.
20 changes: 17 additions & 3 deletions src/__tests__/clear.js
Expand Up @@ -3,7 +3,11 @@ import {render, screen} from '@testing-library/react'
import userEvent from '../../src'

test.each(['input', 'textarea'])('should clear text in <%s>', type => {
const onChange = jest.fn()
const onChange = jest.fn().mockImplementation(event => {
// Verify that `event.target`'s value is correct when the event handler is
// fired.
expect(event.target).toHaveProperty('value', '')
})
render(
React.createElement(type, {
'data-testid': 'input',
Expand All @@ -15,13 +19,18 @@ test.each(['input', 'textarea'])('should clear text in <%s>', type => {
const input = screen.getByTestId('input')
userEvent.clear(input)
expect(input.value).toBe('')
expect(onChange).toHaveBeenCalledTimes(1)
})

test.each(['input', 'textarea'])(
'should not clear when <%s> is disabled',
type => {
const text = 'Hello, world!'
const onChange = jest.fn()
const onChange = jest.fn().mockImplementation(event => {
// Verify that `event.target`'s value is correct when the event handler is
// fired.
expect(event.target).toHaveProperty('value', '')
})
render(
React.createElement(type, {
'data-testid': 'input',
Expand All @@ -34,6 +43,7 @@ test.each(['input', 'textarea'])(
const input = screen.getByTestId('input')
userEvent.clear(input)
expect(input).toHaveProperty('value', text)
expect(onChange).toHaveBeenCalledTimes(0)
},
)

Expand All @@ -60,21 +70,24 @@ test.each(['input', 'textarea'])(
userEvent.clear(input)
expect(onKeyDown).toHaveBeenCalledTimes(1)
expect(onKeyUp).toHaveBeenCalledTimes(1)
expect(onChange).toHaveBeenCalledTimes(0)
expect(input).toHaveProperty('value', text)
},
)
;['email', 'password', 'number', 'text'].forEach(type => {
test.each(['input', 'textarea'])(
`should clear when <%s> is of type="${type}"`,
inputType => {
const onChange = jest.fn()

const value = '12345'
const placeholder = 'Enter password'

const element = React.createElement(inputType, {
value,
placeholder,
type,
onChange: jest.fn(),
onChange,
})

render(element)
Expand All @@ -83,6 +96,7 @@ test.each(['input', 'textarea'])(
expect(input.value).toBe(value)
userEvent.clear(input)
expect(input.value).toBe('')
expect(onChange).toHaveBeenCalledTimes(1)
},
)
})
27 changes: 23 additions & 4 deletions src/index.js
Expand Up @@ -93,19 +93,38 @@ const Keys = {
}

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

// We need to call `fireEvent.change` _before_ we change `element.value`
// because `fireEvent.change` will use the element's native value setter
// (meaning it will avoid prototype overrides implemented by React). If we
// call `input.value = ""` first, React will swallow the change event (this
// is checked in the tests). `fireEvent.change` will only call the native
// value setter method if the event options include `{ target: { value }}`
// (https://github.com/testing-library/dom-testing-library/blob/8846eaf20972f8e41ed11f278948ac38a692c3f1/src/events.js#L29-L32).
//
// Also, we still must call `element.value = ""` after calling
// `fireEvent.change` because `fireEvent.change` will _only_ call the native
// `value` setter and not the prototype override defined by React, causing
// React's internal represetation of this state to get out of sync with the
// value set on `input.value`; calling `element.value` after will also call
// React's setter, keeping everything in sync.
//
// Comment either of these out or re-order them and see what parts of the
// tests fail for more context.
fireEvent.change(element, {target: {value: ''}})
element.value = ''
}
}

Expand Down

0 comments on commit bfbe92c

Please sign in to comment.