0

I have created a react app using a boilerplate create-react-app. I'm trying to write basic tests using testing-library but they are failing. I have a Login component which uses the Alert component to show an error message.

this is my Login component looks like

function Login({ onSubmit, error }) {
    const [credentials, setCredentials] = useState({
        username: '',
        password: ''
    });

    const onFieldChange = ({ value }, field) => {
        let newCredentials = credentials;
        newCredentials[field] = value;
        setCredentials(newCredentials);
    }

    return (
        <div className="container">
            <h2 id="title">Login 👋</h2>

            <form className="items" onSubmit={(event) => onSubmit(event, credentials)}>
                <input type="text" id="username" placeholder="Username" onChange={({ target }) => onFieldChange(target, 'username')} />
                <input type="password" id="password" placeholder="Password" onChange={({ target }) => onFieldChange(target, 'password')} />
                {error && <Alert message={error} />}
                <button id="button" data-testid="submit">Submit</button>
                <a id="sign" type="submit" href="#">Sign Up</a>
            </form>
        </div>
    );
}

export { Login };

Alert component

const AlertError = styled.div`
        background-color: #f8d7da;
        padding: 10px;
        border-radius: 5px;
`
const AlertMessage = styled.p`
        color: #721c24

`

const Container = styled.div(props => ({
    display: 'flex',
    flexDirection: props.column && 'column'
}))

function Alert({ message }) {
    return (
        <AlertError>
            <AlertMessage role="alert" data-testid="alert">{message}</AlertMessage>
        </AlertError>
    )
}

App.test.js

describe('Login ', () => {
  let changeUsernameInput, changePasswordInput, clickSubmit, handleSubmit, alertRender;
  const { container, getByTestId, getByText, getByPlaceholderText, getByRole } = render(<Login onSubmit={handleSubmit} error={''} />);
  const user = { username: 'michelle', password: 'smith' }
  fireEvent.change(getByPlaceholderText(/username/i), { target: { value: user.username } })
  fireEvent.change(getByPlaceholderText(/password/i), { target: { value: user.password } })
  fireEvent.click(getByText(/submit/i))
  alertRender = getByRole('alert')  // breaks on this line
  it('should call onSubmit with the username and password', () => {
    expect(true).toBeTruthy()
  })
})

The following is the error I receive Unable to find an accessible element with the role "alert"

UPDATE
As mentioned by @Christian the issue was error prop in Login component was not populated when I was trying to get its value so I had to query it only after submitting the form. Here's the full updated test file.

describe('Login ', () => {
  let changeUsernameInput, changePasswordInput, clickSubmit, handleSubmit, alertRender, error;
  handleSubmit = jest.fn()
  const { getByTestId, getByPlaceholderText, queryByTestId, rerender } = render(<Login onSubmit={handleSubmit} error={error} />);
  const user = { username: 'michelle', password: 'smith' }
  fireEvent.change(getByPlaceholderText(/username/i), { target: { value: user.username } })
  fireEvent.change(getByPlaceholderText(/password/i), { target: { value: user.password } })
  // fireEvent.click(getByText(/submit/i))
  fireEvent.submit(getByTestId(/login-form/i))

  // re-render the same component with different props
  error = 'The username and password you entered did not match our records. Please double-check and try again.'
  rerender(<Login onSubmit={handleSubmit} error={error} />)
  alertRender = queryByTestId('alert')
  console.log("alertRender:", alertRender.textContent)
  it('should call onSubmit with the username and password', () => {
    expect(alertRender.textContent).toBe(error)
  })
})

1 Answer 1

1

As far as I can tell, the <Alert /> component isn't visible because error is set to a blank string, and is therefore falsey.

When you write

error && <Alert message={error} />

The <Alert /> is only shown when error is set. From what I can see, you're not changing error.

The getBy* selectors from React Testing Library panic if the item doesn't exist. If you don't want that, you can use queryBy* instead. Or you can pass in a non-empty value for error. I'm not sure what you want.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, Christian for the reply. Ah! I forgot to add the fireEvent.click line here before querying the alert. I've updated the question please have a look. Also, I had to re-render the component with updated props because after "submission" the props get updated and now its working. Thanks again
I'm glad you got it working. I think your test name is a bit misleading, though. it('should call onSubmit...') should probably be renamed to it('shows the error if there is one'), if you want to keep the same content. To check that onSubmit is called, you can use Jest's expect(handleSubmit).toHaveBeenCalled()

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.