I am using autosuggest with [email protected] as inspired from https://material-ui-next.com/demos/autocomplete/#react-autosuggest.
react-autosuggest is awesome but I have a small ask. Would it be possible to share how to test <Autosuggest> using jest + enzyme as it is default for create-react-app? I have now been bashing my head against it for a whole day without getting it to work.
I have tried simulating a key press to start the machinery using enzymes simulate but it doesn't work.
If anyone have something to share I would be very happy.
I'd love to see a demo on how to do this. Just about to try to write some specs in Storybook to test out my form.
One problem I had was finding DOM elements corresponding to the suggestions I expected to render. My issue was I failed to simulate a focus event after simulating an input event. In my case:
const inputField = wrapper.find('input.react-autosuggest__input')
inputField.simulate('change', { target: {value: "_something_to_trigger_suggestions_"}});
inputField.simulate('focus');
Was enough for me to be able to find my suggestion list elements using wrapper.find('li.react-autosuggest__suggestion')
Here's my whole test file that selects a value in the suggestions and checks that the event fires.
Done in TypeScript
import * as React from 'react'
import AutoCompleteProfileField, { State, Props } from './autoCompleteProfileField'
import { mount, ReactWrapper, configure } from 'enzyme'
import * as Adapter from 'enzyme-adapter-react-16'
// import { Suggestion as SuggestionType } from '../../types/suggestion'
configure({ adapter: new Adapter() })
export const autoCompleteProfileFieldTests = describe('Auto selecting Persona values to create a Profile Mapping', () => {
let props: Props
let mountedAutoCompleteProfileField: ReactWrapper
const autoCompleteProfileField = () => {
if (!mountedAutoCompleteProfileField) {
mountedAutoCompleteProfileField = mount(
}
return mountedAutoCompleteProfileField
}
beforeEach(() => {
mountedAutoCompleteProfileField = undefined
})
const mockFn = jest.fn()
it('Selecting a Persona value ', () => {
props = {
suggestions: [
{ label: 'Alpha Go' },
{ label: 'Boris Johnson' },
{ label: 'Carl Marks' },
{ label: 'Delphi' },
{ label: 'Estonia' },
{ label: 'Estonia' },
{ label: 'Estonia' },
{ label: 'Franklin, VA' },
{ label: 'Gomez' },
{ label: 'Homer' },
{ label: 'Inglewood, CO' },
{ label: 'Jefferies LTD' }
],
handleSelectionChange: mockFn
}
autoCompleteProfileField().find('input[name="name"]').simulate('change', { target: { value: 'e' } })
autoCompleteProfileField().find('input[name="name"]').simulate('focus')
autoCompleteProfileField().find('MenuItem').first().simulate('click')
autoCompleteProfileField().find('input[name="name"]').first().simulate('blur')
expect(props.handleSelectionChange).toBeCalled()
})
it('Typing an @ filters the drop down to 1 value', () => {
expect(3).toEqual(3)
})
})
and for completeness here's the component
import * as React from 'react'
import { StyleRulesCallback, TextField } from '@material-ui/core/'
import MenuItem from '@material-ui/core/MenuItem'
import Paper from '@material-ui/core/Paper'
import { withStyles } from '@material-ui/core/styles'
import * as Autosuggest from 'react-autosuggest'
const match = require('autosuggest-highlight/match')
const parse = require('autosuggest-highlight/parse')
import { Part as PartType } from '../../types/part'
import { Suggestion as SuggestionType } from '../../types/suggestion'
const styles: StyleRulesCallback = theme => ({
container: {
flexGrow: 1,
position: 'relative'
},
formControl: {
background: 'red'
},
input: {
fontSize: 14,
maxWidth: 368,
width: 112
},
inputLabel: {
color: '#8d97a5',
fontSize: 12
},
menuItem: {
fontFamily: 'Roboto',
fontSize: 14,
height: 16
},
selectEmpty: {
background: '#fff',
marginTop: theme.spacing.unit * 2
},
suggestion: {
display: 'block'
},
suggestionsContainerOpen: {
left: 0,
marginTop: theme.spacing.unit,
position: 'absolute',
right: 0,
zIndex: 1
},
suggestionsList: {
listStyleType: 'none',
margin: 0,
padding: 0
}
})
let allSuggestions: Array
function renderSuggestion (suggestion: SuggestionType, { query, isHighlighted }: { query: any; isHighlighted: boolean }) {
const matches = match(suggestion.label, query)
const parts: Array
return (
)
}
function getSuggestionValue (suggestion: SuggestionType) {
return suggestion.label
}
function renderSuggestionsContainer (options: any) {
const { children } = options
return (
)
}
function getSuggestions (value: string) {
const inputValue = value.trim().toLowerCase()
const inputLength = inputValue.length
let count = 0
return inputLength === 0
? []
: allSuggestions.filter(suggestion => {
const keep = count < 5 && suggestion.label.toLowerCase().slice(0, inputLength) === inputValue
if (keep) {
count += 1
}
return keep
})
}
export interface OwnProps {
classes?: any
}
export interface StateProps {
suggestions: Array
handleSelectionChange: any
}
// export interface DispatchProps {
// select: Function
// }
export type Props = OwnProps & StateProps
export interface State {
suggestions: Array
value: string
}
class AutoCompleteProfileField extends React.Component
constructor (props: Props) {
super(props)
this.state = {
suggestions: [],
value: ''
}
}
componentDidMount () {
allSuggestions = this.props.suggestions
}
public renderInput = (inputProps: any) => {
const { classes, ref, ...other } = inputProps
return (
<div>
<TextField
fullWidth={true}
id='name'
name='name'
onChange={this.props.handleSelectionChange}
label='Search'
InputProps={{
classes: {
input: classes.textfield
},
disableUnderline: false,
inputRef: ref,
...other
}}
/>
</div>
)
}
public handleChange = (event: any, newVal: any) => {
const newValue = newVal.newValue
this.setState({
value: newValue
})
}
public handleSuggestionsFetchRequested = ({ value }: { value: any }) => {
this.setState({
suggestions: getSuggestions(value)
})
}
public handleSuggestionsClearRequested = () => {
this.setState({
suggestions: []
})
}
public render () {
const { classes } = this.props
return (
<Autosuggest
id='selectedPersonaFieldValue'
theme={{
container: classes.container,
suggestion: classes.suggestion,
suggestionsContainerOpen: classes.suggestionsContainerOpen,
suggestionsList: classes.suggestionsList
}}
renderInputComponent={this.renderInput}
suggestions={this.state.suggestions}
onSuggestionsFetchRequested={this.handleSuggestionsFetchRequested}
onSuggestionsClearRequested={this.handleSuggestionsClearRequested}
renderSuggestionsContainer={renderSuggestionsContainer}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={{
classes,
onBlur: this.props.handleSelectionChange,
onChange: this.handleChange,
value: this.state.value
}}
/>
)
}
}
// export withStyles
export default withStyles(styles)(AutoCompleteProfileField)
Alright ladies and gents, this is how I got it to work.
I'm using react-testing-library, but I'm sure this solution will work with Enzyme. I had to use the npm package called user-event to 'type' the character's I wanted, and then focus on the input afterward.
For whatever reason though, I had to use that user-event package, it wouldn't work for me by using react-testing-library's fireEvent method.
const { getByTestId, getAllByTestId } = render(
<TestContainer />,
);
const input = getByTestId('cdd-text-input')
await userEvent.type(input, "a");
input.focus()
await wait(() => getAllByTestId('cdd-suggestion'))
hope this helps 馃憤
Most helpful comment
One problem I had was finding DOM elements corresponding to the suggestions I expected to render. My issue was I failed to simulate a focus event after simulating an input event. In my case:
Was enough for me to be able to find my suggestion list elements using
wrapper.find('li.react-autosuggest__suggestion')