testing-library/react-hooks-testing-library

Configuring PrettyDOM when testing React hook

oscaralanpierce opened this issue · 4 comments

What is your question:

Hi folks, I'm trying to test a library with a React hook that adds a script tag to the document. (This is a script supporting Google identity services that unfortunately is not available as a package, so adding it to the document is the only way.) I need to test that the script's onload callback is run when the script loads successfully (and that its onerror callback is run otherwise). The core functionality of the hook is contained in this callback. Unfortunately, the callback is not running during the test. Because the output includes Ignored nodes: comments, <script />, <style />, I believe the reason for the issue is that PrettyDOM filters script nodes by default.

I understand that PrettyDOM can be configured by passing in a filterNode function as an option, however, since my tests don't explicitly include/use PrettyDOM, I'm not sure how to achieve this or if it's even possible.

I'm using:

  • v. 13.3.0 of @testing-library/react,
  • React >= 18.0.0
  • v. 8.0.1 of @testing-library/react-hooks
  • v. 18.2.0 of react-test-renderer

Please let me know if you need any additional information. Thanks in advance for your help!

Details

This is my test code (simplified):

import { renderHook, waitFor } from '@testing-library/react'
import useGoogleLogin from '../src/hooks/use-google-login'

describe('useGoogleLogin', () => {
  const clientId = 'somestring'
  const onLoad = jest.fn(() => {})
  const onError = jest.fn(() => {})

  describe('when the script loads successfully', () => {
    it('calls the onload callback', async () => {
      renderHook(() => useGoogleLogin({ clientId, onLoad, onError })

      await waitFor(() => expect(onLoad).toHaveBeenCalled())
    })
  })
})

This is the test output:

yarn run v1.22.19
$ jest
 PASS  __tests__/login-component-test.js
 FAIL  __tests__/use-google-login-test.js
   useGoogleLogin  when the script loads successfully  calls the onload callback

    expect(jest.fn()).toHaveBeenCalled()

    Expected number of calls: >= 1
    Received number of calls:    0

    Ignored nodes: comments, <script />, <style />
    <html>
      <head />
      <body>
        <div />
      </body>
    </html>

      30 |               renderHook(() => useGoogleLogin({ clientId, onLoad, onError }))
      31 |
    > 32 |               await waitFor(() => { expect(onLoad).toHaveBeenCalled() })
         |                                                       ^
      33 |             })
      34 |           })
      35 |         })

      at toHaveBeenCalled (__tests__/use-google-login-test.js:32:55)
      at runWithExpensiveErrorDiagnosticsDisabled (node_modules/@testing-library/dom/dist/config.js:50:12)
      at checkCallback (node_modules/@testing-library/dom/dist/wait-for.js:141:77)
      at checkRealTimersCallback (node_modules/@testing-library/dom/dist/wait-for.js:133:16)
      at Timeout.task [as _onTimeout] (node_modules/jsdom/lib/jsdom/browser/Window.js:514:19)

Finally, this is the code of the function that adds the script to the document (which is called from within the hook):

const loadGapiScript = (document, src, onLoad, onError) => {
  const script = document.getElementsByTagName('script')[0]

  let js = document.createElement('script')
  js.id = 'google-auth'
  js.src = src
  js.async = true
  js.defer = true

  js.onerror = onError
  js.onload = onLoad

  if (script && script.parentNode) {
    script.parentNode.insertBefore(js, script)
  } else {
    document.head.appendChild(js)
  }
}

export default loadGapiScript

We don't support react@18, does the issue still remain if you use react@17?

Oh gotcha, in that case I’ll have to figure something else out because my library is specifically replacing one that doesn’t support React 18. Sorry to bother you!

Hey @danascheider,

I noticed you are importing renderHook from @testing-library/react which is not this library, but rather the React 18 replacement from React Testing Library. You can try raising your issue with them instead.

Oh good catch, thanks!