Git Product home page Git Product logo

Comments (4)

tannerlyons avatar tannerlyons commented on May 30, 2024 1

Thanks! That all looks amazing. I appreciate your thoughtful response. We continue to love this lib here at Smartsheet.

from jsdom-testing-mocks.

trurl-master avatar trurl-master commented on May 30, 2024

Hello! Thank you for the issue!

You are correct, I missed this, the problem is though, that when the react observer instance is first initialised and we begin to observe some element, it is quite often inside the first render and at that point, there's no easy way (that I'm aware of) to set element's dimensions and if the callback shouldn't fire if the size of the element is 0 then nothing will happen anyway.

The advantage of the current situation is that I can test the state before the ro callback and then call it manually and test the state after that. I can potentially add some sort of method that will call the callback with all elements that just subscribed, but there's not much value there. The best would be to find some way to assign dimensions to elements during render. But I'm not sure that this is necessary, when we can set dimensions after render and manually call resize.

How do you use it? Do you have any case in mind when that is necessary?

I think I will still implement that though for the case when you are able to set dimensions before observe is called

from jsdom-testing-mocks.

tannerlyons avatar tannerlyons commented on May 30, 2024

Good point. Without some trickery it's challenging to get all the things in the right order. Maybe these examples will spawn some ideas. In short, I don't have any great ideas. I think the current situation will work just fine but perhaps the docs could be expanded to show the caveat?

In the React case below, you'd have to either do that weird ref hack, or:

  1. render your component
  2. call mockElementBoundingClientRect
  3. call rerender to rerun whatever effect the ResizeObserver is hooked up in.

When testing a hook

We have a hook useResizeObserver that returns a RefCallback to attach to the element being observed and when attached, hooks up the RO in an effect. Its test (jest, @testing-library/react-hooks) looks like this:

it("should wait on an element before attaching the resizeobserver", async () => {
    const onResize = jest.fn();
    const { result } = renderHook(
        (onResize: ResizeObserverCallback) =>
            useResizeObserver(onResize),
        {
            initialProps: onResize,
        }
    );

    /*
     result.current looks like this:
     {
         ref: RefCallback<HTMLElement>
     } 
     */

    expect(onResize).not.toHaveBeenCalled();


    // Note that in this test we can create, mock, and provide the DOM element to the ResizeObserver
    // since we have full control over it.
    const element = document.createElement("div");
    mockElementBoundingClientRect(element, { width: 100, height: 100 });
    act(() => {
        result.current.ref(element);
    });

    // Due to this issue, we have to call this manually here still.
    act(() => {
        resizeObserverMock.resize(element);
    });

    await waitFor(() => {
        expect(onResize).toHaveBeenCalledWith(...ResizeObserverEntry[]...)
    });
});

When testing a React component that uses the hook from the previous example

const TestComponent = forwardRef(
    (props: HTMLAttributes<HTMLDivElement>, ref: Ref<HTMLDivElement>) => {
        return (
            <div {...props} ref={ref}>
                something.
            </div>
        );
    }
);

it("tests resize", () => {
    const fakeRef = jest.fn().mockImplementation((elem: HTMLElement | null) => {
        mockElementBoundingClientRect(elem, {width: 100, height: 100});
    });

    render(<TestComponent ref={fakeRef} />);

    // At this point, if TestComponent uses the useResizeObserver hook from the first example, 
    // it should be hooked up.
    // Admittedly this is not optimal since it relies on the component to forward a ref.
    // I think this is JUST a tiny bit better than having to re-render the whole component, but not by much.
})

from jsdom-testing-mocks.

trurl-master avatar trurl-master commented on May 30, 2024

Hey @tannerlyons !

Finally found some time to refactor ResizeObserver a bit, check out 1.6.0@beta and the docs

There isn't much change in terms of how things are done. I made some tests, trying to implement a way to assign a size to an element before/during rendering, and found out that it's problematic. Due to the nature of how the callback is triggered (multiple calls to observe will trigger only one callback), I'm forced to trigger it outside of the main render function (inside queueMicrotask or similar) and therefore any state update that happens in the callback will be outside the render's act and will generate a "State update outside act" error.

But also it's not needed. I think it's best to trigger the callback manually, it gives more control over tests. I made, however, some minor changes to better mock the Resize Observer, like not including the element if its size is 0 (on both axes), auto-adding newly observed elements to the list of callback entries, and some other minor things. You can check it out in the updated docs.

Currently, I'm quite happy with how it works, I can't think of any case that is really difficult to test. If you can test the new version to see if it works for you, that would be great. If you know of any case that is problematic, let me know, maybe I missed something.

from jsdom-testing-mocks.

Related Issues (10)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.