14

I have looked into similar questions (like this one), but the proposed solutions didn't work for me when using react-testing-library.

I have a component that can receive multiple children. This component will then calculate its own size and its children size, to check how many children it will be able to render. It works fine when I use it in my application.

My problem is that, when rendering this component with react-testing-library, the container of the component is rendered with a 0 height and width; so my component will understand that there are no available space to render any child.

I tried to define a custom container inside the tests; tried to force some styling to set width and height; but none of that worked.

Is there a way to "fix" that?

Component (omitted some of the code for the purposes of this question):

const ParentComponent = (props) => {
  const [visibleChildren, setVisibleChildren] = useState(0)
  const myself = useRef(null)

  useEffect(() => {
    const componentWidth = myself.current.offsetWidth;
    const childrenWidth = myself.current.children.reduce(
      // Returns the total children width
    )

    // Returns the number of children I can display
    const childrenAmount = calculateSpace()
    setVisibleChildren(childrenAmount)
  }, [myself])

  // Slice the array of children to display only the right amount
  slicedChildren = props.children.slice(0, visibleChildren)

  return (
    <div ref={myself}>
      {slicedChildren}
    </div>
  )
}

Use:

<ParentComponent>
  <Child />
  <Child />
  <Child />
</ParentComponent>

Test:

import React from 'react'
import {render} from '@testing-library/react'
import ParentComponent from '../ParentComponent'

test('Render component', () => {
  const { getAllByRole } = render(
    <ParentComponent>
      <Child />
      <Child />
      <Child />
    </ParentComponent>
  )

  expect(getAllByRole("Child").length).toEqual(3)
})

Updated:

Added this codesandbox example.

skyboyer
  • 22,209
  • 7
  • 57
  • 64
Bruno Monteiro
  • 4,153
  • 5
  • 29
  • 48

1 Answers1

14

We can mock that property right on HTMLElement.prototype:

Object.defineProperties(window.HTMLElement.prototype, {
  offsetWidth: {
    get: function() { return this.tagName === 'SPAN' ? 100: 500}
  }
});

kudos to https://github.com/jsdom/jsdom/issues/135#issuecomment-68191941

Take a look into issue, it has looooong story(since 2011!) but is still opened

Telling the truth, I see mocking offsetWidth for testing logic to be more reliable. I'd not expect from testing environment to calculate real styles(like offset sizes) all the way.

skyboyer
  • 22,209
  • 7
  • 57
  • 64
  • 2
    Hi Bruno, Please, I am facing a similar problem. Where exactly in the test code did you apply the above snippet? Thank you. – Sau001 Nov 24 '20 at 23:20
  • Bless you, thanks a lot for sharing! I had been struggling with a similar scenario involving a grid layout for which a width change would never be detected. Forcing `offsetWidth` into a specific value did the trick for me, and I agree that this makes for a reasonable approach to check the side effects of something as the aim is nowhere near testing the browser's ability to adjust widths, but instead that the component behaves accordingly provided that the resizing has already occurred in the browser. – Herick Apr 14 '21 at 00:48
  • Hi @Sau001, I added the above snipped in a `beforeAll()` in the test file. – Bruno Monteiro Apr 14 '21 at 23:53