How to Test Responsive React Components with React Testing Library

Viewed 1437

I'm having issues testing React components if the rendered output relies on media queries. I'm using Jest and React Testing Library. The components use react-responsive to return different markup based on the matching media query. The window.innerWidth defaults to 1024px but the tests don't seem to "see" that. Only markup that is not wrapped in a media query will render.

Is this issue specific to RTL? Do I need to mock useMediaQuery in the test?

Here is a simple component and test to illustrate the issue:

// test component
import React from 'react'
import { useMediaQuery } from 'react-responsive'

const MOBILE_MEDIA_QUERY = 'only screen and (max-width : 480px)'
const DESKTOP_MEDIA_QUERY = 'only screen and (min-width: 1024px)'

const Test = props => {
  const isMobile = useMediaQuery({ query: MOBILE_MEDIA_QUERY })
  const isDesktop = useMediaQuery({ query: DESKTOP_MEDIA_QUERY })
  
  if (isMobile) {
    return <div>mobile</div>
  } else if(isDesktop) {
    return <div>desktop</div>
  } else {
    return <div>media queries did not match</div>
  }
}

export default Test 


// test spec
import React from 'react'
import Test from './MediaQueryTest'
import { render, screen } from '@testing-library/react'

it('should render for desktop', async () => {
  console.log(`this window.innerWidth is: ${window.innerWidth}`)

  render(<Test/>)

  expect(await screen.findByText('desktop')).toBeInTheDocument()
})

Here is a screenshot of what I get in the console when I run this test (see below). As you can see, the markup wrapped in isDesktop did not render even though the viewport is set to a width of 1024. The only markup that renders is that which is not wrapped in a media query. I'm not even trying to change the viewport width right now. I'm just trying to get it to acknowledge the default viewport width and render accordingly.

enter image description here

1 Answers

Okay, so I came up with a solution, although I'm hoping someone out there has a simpler alternative. I mocked react-responsive by placing the following file in my __mocks__ directory (see below). Please note that the media queries are imported as constants. (They're maintained by an offshore design team, not by me) You can replace these with your own media queries. I trigger the breakpoint I want in the test right before the render like so: global.innerWidth = 480

//__mocks__/react-responsive.js:

import React from 'react'
import {
  //NOTE: only variable names prefixed with 'mock' (case-insensitive) are accessible from the scope of jest.mock() factory
  SMALL_MOBILE_MEDIA_QUERY as MOCK_SMALL_MOBILE,
  MEDIUM_MOBILE_MEDIA_QUERY as MOCK_MEDIUM_MOBILE,
  MOBILE_LANDSCAPE_AND_PORTRAIT_MEDIA_QUERY as MOCK_MOBILE_LANDSCAPE_AND_PORTRAIT,
  MOBILE_MEDIA_QUERY as MOCK_MOBILE,
  CUSTOM_MOBILE_TABLET_MEDIA_QUERY as MOCK_CUSTOM_MOBILE_TABLET,
  DESKTOP_MEDIA_QUERY as MOCK_DESKTOP,
  TABLET_MEDIA_QUERY as MOCK_TABLET,
  MOBILE_AND_TABLET_MEDIA_QUERY as MOCK_MOBILE_AND_TABLET,
  CUSTOM_DESKTOP_MEDIA_QUERY as MOCK_CUSTOM_DESKTOP,
} from 'Shared/constants'

const useMediaQuery = ({ query }) => {
  const w = global.innerWidth

  switch (query) {
    case MOCK_SMALL_MOBILE:
      return w <= 350

    case MOCK_MEDIUM_MOBILE:
      return w <= 370

    case MOCK_MOBILE:
      return w <= 480

    case MOCK_MOBILE_LANDSCAPE_AND_PORTRAIT:
      return w <= 767

    case MOCK_CUSTOM_MOBILE_TABLET:
      return w <= 800

    case MOCK_DESKTOP:
      return w >= 1025

    case MOCK_CUSTOM_DESKTOP:
      return w >= 801

    case MOCK_TABLET:
      return w > 481 && w <= 1024

    case MOCK_MOBILE_AND_TABLET:
      return w <= 480 || (w >= 481 && w <= 1024)

    default:
      return false
  }
}

const MediaQuery = ({ query, children }) => {
  const w = global.innerWidth

  switch (query) {
    case MOCK_SMALL_MOBILE:
      return w <= 350 && children

    case MOCK_MEDIUM_MOBILE:
      return w <= 370 && children

    case MOCK_MOBILE:
      return w <= 480 && children

    case MOCK_CUSTOM_MOBILE_TABLET:
      return w <= 800 && children

    case MOCK_DESKTOP:
      return w >= 1025 && children

    case MOCK_CUSTOM_DESKTOP:
      return w >= 801 && children

    case MOCK_TABLET:
      return w > 481 && w <= 1024 && children

    case MOCK_MOBILE_AND_TABLET:
      return (w <= 480 || (w >= 481 && w <= 1024)) && children

    case MOCK_MOBILE_LANDSCAPE_AND_PORTRAIT:
      return w <= 767 && children

    default:
      return null
  }
}

module.exports = MediaQuery
module.exports.useMediaQuery = useMediaQuery
Related