Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Usage with react-test-renderer #493

Closed
arbesfeld opened this issue Dec 5, 2016 · 20 comments
Closed

Usage with react-test-renderer #493

arbesfeld opened this issue Dec 5, 2016 · 20 comments
Labels

Comments

@arbesfeld
Copy link

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

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

@bvaughn
Copy link
Owner

bvaughn commented Dec 6, 2016

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.

@silvenon
Copy link
Contributor

silvenon commented Dec 21, 2017

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.

@silvenon
Copy link
Contributor

silvenon commented Dec 22, 2017

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.

@izakfilmalter
Copy link

Even easier solution:

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

@rickarubio
Copy link

@izakfilmalter nice solution, thanks for sharing!

@tomasstankovic
Copy link

tomasstankovic commented Aug 23, 2018

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

@thielium
Copy link

thielium commented Dec 13, 2018

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.

@mzedeler
Copy link

mzedeler commented May 19, 2020

This works for me:

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

@AnnyCaroline
Copy link

@mzedeler

I'm having this error

image

@lokeshpathrabe
Copy link

@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 }), );

@AnnyCaroline
Copy link

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)

@ldavidpace
Copy link

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}),
  };
});

@IgorKharkiv
Copy link

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.

@yangfei4913438
Copy link

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)

It is ok! thanks a lot!

@gap777
Copy link

gap777 commented Apr 9, 2021

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)

This kinda works, but ends up making every HTML element think it's 1500px tall... including Menus (and Popover), which complain bitterly:

Material-UI: The popover component is too tall.
Some part of it can not be seen on the screen (748px).
Please consider adding a max-height to improve the user-experience.

@keeganbrown
Copy link

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)

This kinda works, but ends up making every HTML element think it's 1500px tall... including Menus (and Popover), which complain bitterly:

Material-UI: The popover component is too tall.
Some part of it can not be seen on the screen (748px).
Please consider adding a max-height to improve the user-experience.

In my case 500px for each dimension was unnecessary. I was able to use a value of 2 and it worked just as well:

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

@julia-loggi
Copy link

julia-loggi commented May 25, 2021

@ldavidpace thank you, your code works for me. And I just improved it and made a module

//  __mocks__/react-virtualized.js

import React from 'react';

const ReactVirtualized = jest.requireActual('react-virtualized');

module.exports = {
  ...ReactVirtualized, 
  AutoSizer: ({children}) => children({height: 1000, width: 1000})
};

And all you need just add the line jest.mock('react-virtualized'); at the top of a test file

@mjangir
Copy link

mjangir commented Aug 4, 2021

@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 }), );

What if I import it using import AutoSizer from 'react-virtualized-auto-sizer';?

@MoSattler
Copy link

@mjangir see this #493 (comment)

@john-wego
Copy link

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.

Thanks for this!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests