Search code examples
reactjsaxiosreact-testing-librarymoxios

How to get react test using moxios to update the DOM before running later part of the test


I am trying to write a test using jest and react-testing-library for a component. The test needs to wait for useEffect to update the state following an axios request. I'm using moxios to mock the api call but I can't get the test to wait for moxios to return before it fires a click event handler that again sends a delete request to the api. The test fails saying that the DOM element I'm trying to click on doesn't exist yet because it's only produced once the useEffect request updates.

I've tried using flushEffect to wait and I've also tried wrapping the click inside the initial moxios request but both don't work

I've cut out any code that is not relevant.

This is the component. Once loaded it sends a get request to an api to grab a json response of some benefits. The BenefitsTable component takes in the benefits and for each one produces a table which includes a delete button. I'm trying to first load in the benefits and then click on the delete button. No delete button exists before they are loaded.

const Benefits = props => {
  const [benefits, setBenefits] = useState([])
  const [editing, setEditing] = useState(false)
  const [editingBenefit, setEditingBenefit] = useState({id: null, name: '', category: ''})

  useEffect(() => {
    axios.get('/api/v1/benefits')
      .then(response => {
        setBenefits(response.data)
      })
  }, [])

  const deleteBenefit = benefit => {
    const id = benefit.id
    axios.delete(`/api/v1/benefits/${id}`)
      .then(response => {
        setBenefits(benefits.filter(benefit => benefit.id !== id))
        warning('Benefit Deleted')
      })
      .catch(error => {
        warning('Benefit could not be deleted. Please try again')
      })
  }

  return(
     <div>
       <Section>
         <BenefitsTable
            benefits={benefits} 
            deleteBenefit={deleteBenefit}
            editBenefit={editBenefit}
          />
       </Section>
     </div>
  )
}

My test is as follows:

it('deletes a benefit when the delete button is clicked', () => {
  const { getByTestId } = render(<Benefits />)
    moxios.wait(() => {
      const request = moxios.requests.mostRecent()
      request.respondWith({
        status: 200,
        response: benefits
      }).then(() => {
        done()
      })
    })
    fireEvent.click(getByTestId('deleteButton1'))
    moxios.wait(() => {
      const request = moxios.requests.mostRecent()
      request.respondWith({
       status: 200,
    }).then(() => {
      expect(document.querySelectorAll('tbody > tr').length).toBe(1)
      done()
    })
  })
})

The output is Unable to find an element by: [data-testid="deleteButton1"] and I get that it's because the axios request is async but I've tried wrapping the fireevent and subsequent axios request inside the then clause of the first axios request and the although the test passes, it passes with any value meaning it isn't being correctly processed.


Solution

  • Would waiting for the element to be present work?

    it('deletes a benefit when the delete button is clicked', async () => {
      const { getByTestId } = render(<Benefits />)
        moxios.wait(() => {
          const request = moxios.requests.mostRecent()
          request.respondWith({
            status: 200,
            response: benefits
          })
        })
        await waitForElement(getByTestId('deleteButton1'))
        fireEvent.click(getByTestId('deleteButton1'))
        moxios.wait(() => {
          const request = moxios.requests.mostRecent()
          request.respondWith({
           status: 200,
        }).then(() => {
          expect(document.querySelectorAll('tbody > tr').length).toBe(1)
          done()
        })
      })
    })