25

I'm using create-react-app, Jest and react-testing-library for the configuration of the chatbot project.

I have a functional component that uses useRef hook. When a new message comes useEffect hook is triggered and cause scrolling event by looking a ref's current property.

const ChatBot = () => {
  const chatBotMessagesRef = useRef(null)
  const chatBotContext = useContext(ChatBotContext)
  const { chat, typing } = chatBotContext

  useEffect(() => {
    if (typeof chatMessagesRef.current.scrollTo !== 'undefined' && chat && chat.length > 0) { 
       chatBotMessagesRef.current.scrollTo({
         top: chatMessagesRef.current.scrollHeight,
         behavior: 'smooth'
       })
    }
    // eslint-disable-next-line
  }, [chat, typing])

   return (
    <>
      <ChatBotHeader />
      <div className='chatbot' ref={chatBotMessagesRef}>
        {chat && chat.map((message, index) => {
          return <ChatBotBoard answers={message.answers} key={index} currentIndex={index + 1} />
        })}
        {typing &&
        <ServerMessage message='' typing isLiveChat={false} />
        }
      </div>
    </>
  )
}

I want to be able to test whether is scrollTo function triggered when a new chat item or typing comes, do you have any ideas? I couldn't find a way to test useRef.

5
  • You could mock the scrollTo function (using jest.fn()), add a new chat item and check if the mock is called. Commented Nov 30, 2019 at 18:01
  • But scrollTo function is instance of the current object which is the method of the ref, how can I mock scrollTo without mocking ref? Commented Dec 2, 2019 at 5:42
  • Ah yes that can be difficult, have you tried mocking it using: Element.prototype.scrollTo = jest.fn() Commented Dec 2, 2019 at 6:53
  • I tried that, but how can I react the element before render? I need to mock the ref somehow before render so that I can test it, it is a very odd case I guess, I also used jest.spyOn but that did not work either Commented Dec 2, 2019 at 8:01
  • This is a huge limitation of react testing library. The ref should just act as a ref. There is no point in writing a test if you are just mocking most of it. Commented Sep 29, 2022 at 18:35

1 Answer 1

25
+50

You can move your useEffect out of your component and pass a ref as a parameter to it. Something like

const useScrollTo = (chatMessagesRef, chat) => {
    useEffect(() => {
    if (typeof chatMessagesRef.current.scrollTo !== 'undefined' && chat && chat.length > 0) { 
       chatBotMessagesRef.current.scrollTo({
         top: chatMessagesRef.current.scrollHeight,
         behavior: 'smooth'
       })
    }
  }, [chat])
}

Now in your component

import useScrollTo from '../..'; // whatever is your path

const MyComponent = () => {
  const chatBotMessagesRef = useRef(null);
  const { chat } = useContext(ChatBotContext);

  useScrollTo(chatBotMessagesRef, chat);

  // your render..
}

Your useScrollTo test:

import useScrollTo from '../..'; // whatever is your path
import { renderHook } from '@testing-library/react-hooks'

it('should scroll', () => {
  const ref = {
    current: {
      scrollTo: jest.fn()
    }
  }
  const chat = ['message1', 'message2']

  renderHook(() => useScrollTo(ref, chat)) 

  expect(ref.current.scrollTo).toHaveBeenCalledTimes(1)
})
Sign up to request clarification or add additional context in comments.

1 Comment

If I give a ref as a prop by creating it with const ref = React.createRef() it gives me error, but if I mock it like const ref = { current: { scrollTo: jest.fn() }} and give a mock chat array rather than taking it from the real context object, the test works and also it seems that I covered all the conditions in the ChatBot component that uses useScrollTo hook, so somewhat your answer leads me to correct my test!

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.