ZeeCoder / use-resize-observer

A React hook that allows you to use a ResizeObserver to measure an element's size.
MIT License
644 stars 42 forks source link

"ResizeObserver is not defined" error when running Jest #40

Closed remidej closed 3 years ago

remidej commented 4 years ago

Hello 👋

Using useResizeObserver crashes my app's tests. Jest uses JSDom, which apparently doesn't support the ResizeObserver API.

I know we can detect when Jest is running, but React doesn't support conditionally calling hooks, so I don't know how to prevent Jest from crashing. I think the fix has to be done inside the hook.

ZeeCoder commented 4 years ago

Hey 👋

There must be some way to patch the test environment. I'm quite certain that the answer won't lie in detecting the test environment within the hook. 😅

seloner commented 4 years ago

I am having the same issue with jest.

ZeeCoder commented 4 years ago

I'll be happy to add a section to the Readme if someone comments a solution.

2gnc commented 4 years ago

I used polyfilled version, import useResizeObserver from "use-resize-observer/polyfilled"; because of issues in IOs 12. Maybe this will help.

seloner commented 4 years ago

I managed to bypass the issue like that.

import React from 'react';

import { render, fireEvent } from '@testing-library/react';

import { AuthProvider } from '../context/AuthProvider';
import LoginPage from './LoginPage';

const pass = 'pass';
const user = 'user';
class ResizeObserver {
  observe() {}
  unobserve() {}
}
describe('loginpage ', () => {
  window.ResizeObserver = ResizeObserver;
  test('should update username and password', async () => {
    const { findByPlaceholderText } = render(
      <AuthProvider>
        <LoginPage />
      </AuthProvider>,
    );
    const username = await findByPlaceholderText('Your username...');
    const password = await findByPlaceholderText('Password...');
    fireEvent.change(username, { target: { value: user } });
    fireEvent.change(password, { target: { value: pass } });
    expect(username.value).toBe(user);
    expect(password.value).toBe(pass);
  });
});
tay1orjones commented 4 years ago

I work on a component library where we use the hook to enhance component functionality behind a prop. So if hasResize=false, we don't use the values provided from the hook. The library does not provide a polyfill for the same reasons you list in the readme.

The hook can't be placed behind a browser support conditional - so even when hasResize=false is false, the hook throws an error in environments where RO is not supported. This effectively requires our consumers to provide an RO polyfill when they may not even have configured props to use the hook.

The only solution for us is to have two components, Component and ComponentWithResize. Render one or the other depending on prop and support.

A check for RO support in the hook would be very inexpensive and fix these issues.

ZeeCoder commented 4 years ago

@tay1orjones that sounds to me like a different issue. For that I think the hook could be smart enough not to create a ResizeObserver instance when there's nothing to observe. So then when hasResize=false in your case, you would simply not pass in anything to the hook to observe, and it would do nothing.

GreenGremlin commented 4 years ago

I ran into the same problem, in that I'd like to use this hook with graceful degradation for IE. I put up https://github.com/ZeeCoder/use-resize-observer/pull/42 which does just this, though it still needs some tests added.

GreenGremlin commented 4 years ago

I added a test to #42, it should be ready to go.

tay1orjones commented 4 years ago

@ZeeCoder yep, thats fair. #42 will solve that for us, thanks @GreenGremlin!

aricallen commented 3 years ago

In case it helps someone, I found that you can mock module to use polyfilled when in tests with a few lines. This will at least get around the undefined error. It won't add any functionality tho.

jest.mock('use-resize-observer', () => {
  return jest.requireActual('use-resize-observer/polyfilled');
});
ZeeCoder commented 3 years ago

@tay1orjones the latest alpha release (https://github.com/ZeeCoder/use-resize-observer/releases/tag/v6.2.0-alpha.1) will not instantiate the ResizeObserver, until there's an actual element it receives, so it should handle your case.

I still think the original issue with Jest reported here is not something the library needs to adapt to, but rather the environment needs to be mocked or polyfilled. Also see the documentation for other options to opt out of RO instantiation altogether if needed.: https://github.com/ZeeCoder/use-resize-observer#opting-out-of-or-delaying-resizeobserver-instantiation

peterkrieg commented 3 years ago

Another possible mock solution - create a minimal mock of useResizeObserver which at least returns an object with width

// JSDom (which is used by jest) does not implement layout/rendering.
// we create this mock to simply simulate a desktop view with a width of 1000
function useResizeObserverMock() {
  return {
    width: 1000,
  };
}

jest.mock('use-resize-observer', () => useResizeObserverMock);
sawsanCS commented 2 years ago

adding this line of code in the testing file fixed the error for me global.ResizeObserver = require('resize-observer-polyfill')

Francois-Esquire commented 1 year ago

For those looking for a more configurable solution, the below worked for me:

jest.config.js

module.exports = {
  moduleNameMapper: {
    'use-resize-observer': 'use-resize-observer/polyfilled'
  }
};
VladislavVB commented 1 year ago

Solution for vitest write in the file with the test at the highest level of nesting

const ResizeObserverMock = vi.fn(() => ({
  observe: vi.fn(),
  unobserve: vi.fn(),
  disconnect: vi.fn()
}))

vi.stubGlobal('ResizeObserver', ResizeObserverMock)
BhagyashreeCH commented 1 year ago

I managed to bypass the issue like that.

import React from 'react';

import { render, fireEvent } from '@testing-library/react';

import { AuthProvider } from '../context/AuthProvider';
import LoginPage from './LoginPage';

const pass = 'pass';
const user = 'user';
class ResizeObserver {
  observe() {}
  unobserve() {}
}
describe('loginpage ', () => {
  window.ResizeObserver = ResizeObserver;
  test('should update username and password', async () => {
    const { findByPlaceholderText } = render(
      <AuthProvider>
        <LoginPage />
      </AuthProvider>,
    );
    const username = await findByPlaceholderText('Your username...');
    const password = await findByPlaceholderText('Password...');
    fireEvent.change(username, { target: { value: user } });
    fireEvent.change(password, { target: { value: pass } });
    expect(username.value).toBe(user);
    expect(password.value).toBe(pass);
  });
});

Hi, I was getting "please install a polyfill for ResizeObserver" after migrating jest26 to jest28 version in angular application. We are not using ResizeObserver in our application but I am not sure whether any dependency with NGXCHARTS, So I was getting this error suddenly.

I have tried above solution of bypassing, it worked thank you.

class ResizeObserver { observe() {} unobserve() {} disconnect() {} }

describe('Component', () => { window.ResizeObserver = ResizeObserver; })

rodentskie commented 1 year ago

adding this line of code in the testing file fixed the error for me global.ResizeObserver = require('resize-observer-polyfill')

import ResizeObserver from 'resize-observer-polyfill';
global.ResizeObserver = ResizeObserver;
kiranlpatil commented 1 year ago

adding this line of code in the testing file fixed the error for me global.ResizeObserver = require('resize-observer-polyfill')

import ResizeObserver from 'resize-observer-polyfill';
global.ResizeObserver = ResizeObserver;

Thanks, It really works. I am rendering ReactFlow in JEST. Surprisingly it works.

abuzain432432 commented 9 months ago

global.ResizeObserver = class { observe() {} unobserve() {} disconnect() {} }; This code solved my issue

lohnsonok commented 4 months ago

adding this line of code in the testing file fixed the error for me global.ResizeObserver = require('resize-observer-polyfill')

import ResizeObserver from 'resize-observer-polyfill';
global.ResizeObserver = ResizeObserver;

directy in jest.setup.ts works well :+1:

sanchitos commented 3 months ago

npm i -D resize-observer-polyfill

In setupTests.ts

import ResizeObserver from 'resize-observer-polyfill';
global.ResizeObserver = ResizeObserver;