React-autosuggest: Question regarding testing

Created on 31 Dec 2017  路  4Comments  路  Source: moroshko/react-autosuggest

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.

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:

        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')

All 4 comments

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 | undefined

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 = parse(suggestion.label, matches)

return (


{parts.map((part: PartType, index: number) => {
return part.highlight ? (
{part.text}

) : (
{part.text}

)
})}


)
}

function getSuggestionValue (suggestion: SuggestionType) {
return suggestion.label
}

function renderSuggestionsContainer (options: any) {
const { children } = options
return (
{children}

)
}

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 馃憤

Was this page helpful?
0 / 5 - 0 ratings

Related issues

luke-unifymonitor picture luke-unifymonitor  路  3Comments

AlgoTrader picture AlgoTrader  路  3Comments

octplane picture octplane  路  4Comments

Razinsky picture Razinsky  路  3Comments

devatlant picture devatlant  路  3Comments