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?
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

@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
Autosizerlike 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
Autosizerlike 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!
Most helpful comment
Noting the above caveats about mocking the DOM, I adapted the AutoSizer's mock, though it's a little white-boxy.
I've noticed some issues that are pretty challenging to reproduce by defaulting the value of
AutoSizerto 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.