React-virtualized: Usage with react-test-renderer

Created on 6 Dec 2016  路  14Comments  路  Source: bvaughn/react-virtualized

I'm trying to unit test components that render RV components with the react-test-renderer, and running into this error: https://github.com/facebook/react/issues/7371.

It seems like an issue with the usage of findDOMNode. What is the recommended way to unit test RV components?

question

Most helpful comment

Noting the above caveats about mocking the DOM, I adapted the AutoSizer's mock, though it's a little white-boxy.

describe("My Test", () => {
  const originalOffsetHeight = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetHeight');
  const originalOffsetWidth = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetWidth');

  beforeAll(() => {
    Object.defineProperty(HTMLElement.prototype, 'offsetHeight', { configurable: true, value: 50 });
    Object.defineProperty(HTMLElement.prototype, 'offsetWidth', { configurable: true, value: 50 });
  });

  afterAll(() => {
    Object.defineProperty(HTMLElement.prototype, 'offsetHeight', originalOffsetHeight);
    Object.defineProperty(HTMLElement.prototype, 'offsetWidth', originalOffsetWidth);
  });
  // Your tests
})

I've noticed some issues that are pretty challenging to reproduce by defaulting the value of AutoSizer to the grid (column sizes aren't recalculated though the width has changed). That's the only real reason to mock rather than to default. If I can repro, I'll make a bug.

All 14 comments

Some components (eg AutoSizer, CellMeasurer) require a real (or at least reasonable) DOM. You can try to mock things out but I think it would be particularly difficult for CellMeasurer

Other components (eg WindowScroller, CellMeasurer, Table) depend on findDOMNode. You could try to mock it, as Dan suggests here. Or you could just do what I do to test react-virtualized itself (eg example test).

Most react-virtualized components don't need a real DOM and so can be tested with Jest. However AutoSizer, CellMeasurer, and WindowScroller are not Jest-compatible b'c they require DOM methods that JSdom doesn't implement. I haven't had much luck mocking them out either; it ends up being too complex and brittle.

A very awkward way to mock AutoSizer: 馃ぃ

// MyComp.js
import React from 'react';
import { Grid } from 'react-virtualized';
let { AutoSizer } = require('react-virtualized');

if (process.env.NODE_ENV === "test") {
  AutoSizer = require('react-virtualized/dist/commonjs/AutoSizer');
}

class MyComp extends React.Component {
  render() {
    return (
      <AutoSizer>
        {({ width, height }) => (
          <Grid
            width={width}
            height={height}
            {/* ... */}
          />
        )}
      </AutoSizer>
    );
  }
}
// MyComp.test.js
import React from 'react';
import MyComp from './MyComp';

jest.mock('react-virtualized/dist/commonjs/AutoSizer', () => {
  const width = 1024;
  const height = 768;
  return ({ children }) =>
    <div>{children({ width, height })}</div>;
});

// ...

I couldn't find a way to mock only AutoSizer without polluting my application code.

Actually, that's completely unnecessary, you can do this instead in your application code:

<AutoSizer>
  {({ width, height }) => {
    let actualWidth = width;
    let actualHeight = height;
    if (process.env.NODE_ENV === 'test') {
      actualWidth = window.innerWidth;
      actualHeight = window.innerHeight;
    }
    // do something with these dimensions
  }}
</AutoSizer>

This will prevent width and height from being 0 on subsequent renders.

The contents of AutoSizer will not re-render on resize, but that's E2E territory anyway.

Even easier solution:

<AutoSizer>
  {({ width, height }) => (
    <List
      height={height || 100}
      width={width || 100}
    />
  )}
</AutoSizer>

@izakfilmalter nice solution, thanks for sharing!

<AutoSizer defaultWidth={100} defaultHeight={100}></AutoSizer>

Noting the above caveats about mocking the DOM, I adapted the AutoSizer's mock, though it's a little white-boxy.

describe("My Test", () => {
  const originalOffsetHeight = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetHeight');
  const originalOffsetWidth = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetWidth');

  beforeAll(() => {
    Object.defineProperty(HTMLElement.prototype, 'offsetHeight', { configurable: true, value: 50 });
    Object.defineProperty(HTMLElement.prototype, 'offsetWidth', { configurable: true, value: 50 });
  });

  afterAll(() => {
    Object.defineProperty(HTMLElement.prototype, 'offsetHeight', originalOffsetHeight);
    Object.defineProperty(HTMLElement.prototype, 'offsetWidth', originalOffsetWidth);
  });
  // Your tests
})

I've noticed some issues that are pretty challenging to reproduce by defaulting the value of AutoSizer to the grid (column sizes aren't recalculated though the width has changed). That's the only real reason to mock rather than to default. If I can repro, I'll make a bug.

This works for me:

jest.mock(
  'react-virtualized-auto-sizer',
  () => ({ children }) => children({ height: 600, width: 600})
)

@mzedeler

I'm having this error

image

@mzedeler Thanks for the workaround. It worked for me.
@AnnyCaroline I guess you need to find from where you are importing AutoSizer. This is what worked for me:

jest.mock('react-virtualized/dist/es/AutoSizer', () => ({ children }) => children({ height: 600, width: 600 }), );

Thanks @lokeshpathrabe . Your code solved the errors but my virtualized list don't seem to render its items.

Instead, I ended up using this code, based on @thielium answer:

jest.spyOn(HTMLElement.prototype, 'offsetHeight', 'get').mockReturnValue(1500)
jest.spyOn(HTMLElement.prototype, 'offsetWidth', 'get').mockReturnValue(1500)

For the next person who struggles to get it importing. I am using typescript and import Autosizer like this:

import {
    AutoSizer,
} from 'react-virtualized';

To get the mock to work I had to re-export the rest of react-virtualized like so:

jest.mock('react-virtualized', () => {
  const ReactVirtualized = jest.requireActual('react-virtualized');
  return {
    ...ReactVirtualized,
    AutoSizer: ({
      children,
    }) => children({height: 1000, width: 1000}),
  };
});

For the next person who struggles to get it importing. I am using typescript and import Autosizer like this:

import {
    AutoSizer,
} from 'react-virtualized';

To get the mock to work I had to re-export the rest of react-virtualized like so:

jest.mock('react-virtualized', () => {
  const ReactVirtualized = jest.requireActual('react-virtualized');
  return {
    ...ReactVirtualized,
    AutoSizer: ({
      children,
    }) => children({height: 1000, width: 1000}),
  };
});

@ldavidpace, thanks a lot! You saved my day.

For the next person who struggles to get it importing. I am using typescript and import Autosizer like this:

import {
    AutoSizer,
} from 'react-virtualized';

To get the mock to work I had to re-export the rest of react-virtualized like so:

jest.mock('react-virtualized', () => {
  const ReactVirtualized = jest.requireActual('react-virtualized');
  return {
    ...ReactVirtualized,
    AutoSizer: ({
      children,
    }) => children({height: 1000, width: 1000}),
  };
});

it's ok. thanks very much!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

wnz99 picture wnz99  路  3Comments

davidychow87 picture davidychow87  路  3Comments

ms007 picture ms007  路  4Comments

hyeminHwang picture hyeminHwang  路  3Comments

zllc picture zllc  路  3Comments