4
votes

I am struggling to understand how to update a react component's props while using cypress-react-unit-test.

Here I have a simple controlled input component.

interface MyInputProps {
    inputVal: string
    onInputChanged(val: string): void
}

export const MyInput = ({ inputVal, onInputChanged }: MyInputProps) => {
    return <input
        type='text'
        value={inputVal}
        onChange={e => onInputChanged(e.target.value)}
    />
}

I then want to test that component by giving it an initial value of '', then doing something like cy.get('input').type('a'). I would then expect that the onInputChanged callback would be called.

But this is where I get stuck. What do I do with the updated value ('a')? I can't use hooks or state inside of the tests, so how do I get my component to re-render using the updated value? It seems wrong to do mount(<MyInput inputVal='' />), then mount(<MyInput inputVal='a' />) because then I’m mounting a different component and I’m not testing how the component reacts to props updates.

// MyInput.spec.tsx
describe('My Input', () => {
    it('updates when props change', () => {
        mount(<MyInput
            inputVal=''
            onInputChanged={(newVal) => {
                /* What do I do here? How do I update the props */
                return null
            }}
        />);
        
        /* Update props */
        /* Re-render somehow */
    })
})
1
Perhaps use a local variable to hold the current inputVal, mount once, have cypress .type('a-new-value'), check the local variable is updated, finally mount a second time and check the input's value. - Ackroydd
Take a look at the re-render example, essentially a wrapper component is used (so-called mini web app). But mounting twice in the test does the same thing in a simpler way. - Richard Matsen

1 Answers

0
votes

I think the problem is not on your test, it on your MyInput component.
Put your state to MyInput or use defaultValue instead value:

export const MyInput = ({ inputVal, onInputChanged }: MyInputProps) => {
  return (
    <input
      type="text"
      defaultValue={inputVal}
      onChange={(e) => onInputChanged(e.target.value)}
    />
  );
};

as you know, set value={inputVal} will make input never change, only change when you change inputVal.