framer / motion

Open source, production-ready animation and gesture library for React
https://framer.com/motion
MIT License
23.6k stars 793 forks source link

[BUG] Enzyme mount() throws a "No `ref` found." error #410

Closed tims-j closed 4 years ago

tims-j commented 4 years ago

Description

Testing mounted motion elements using enzyme mount() throws a ref error that pertains to forwardingRefs:

Error: Uncaught [Error: No 'ref' found. Ensure components created withmotion.customforward refs using 'React.forwardRef']

CodeSandbox: https://codesandbox.io/s/mounting-motion-elements-dz7oh

Steps to reproduce

Steps to reproduce the behavior:

  1. Open codesandbox link.
  2. Run the tests.
  3. Check the console.
  4. The test that mounts the motion component will throw an exeption, while the test that shallows works as expected.

Expected behavior Enzyme should be able to successfully mount the component without throwing an exception.

Additional Information In addition to the codesandbox I have also tested the issue using the following dependency versions in my own codebase using the following:

gc-dan commented 4 years ago

I'm having the same issue when using react-test-renderer's create() method directly:

CodeSandbox: https://codesandbox.io/s/mounting-motion-elements-n2elg

Any ideas?

sospedra commented 4 years ago

+1

Popmotion stumbled upon the same issue -- I reckon. Here's the fix PR they conducted: https://github.com/Popmotion/popmotion/pull/474/files

jonnyom commented 4 years ago

We've been running into the same issue. Any workarounds out there?

tims-j commented 4 years ago

@jonnyom we’ve actually started shifting to JEST and react-testing-library (for unrelated reasons) and have found no issues while using those with framer-motion.

Not sure how much of a workaround that classified as though 😅

jonnyom commented 4 years ago

@tims-j thanks for the heads up! We were hoping to use enzyme's mount as it's so straightforward, might need to dig into jest instead, thanks!

nicmosc commented 4 years ago

Getting the same warning but using Jest, and only in one case. Note that the component throwing this error is also using React.portal, though as I mention below, only 1 of the 2 tests fails in the suite, so i doubt it is related. Here's the details: Versions:

react: 16.10.1
jest: 24.9.0
framer-motion: 1.10.3
react-test-renderer: 16.10.1

And the test in question:

describe('matches snapshot when', () => {
    it('is visible without text', () => {
      const tree = create(<SplashScreen />).toJSON();
      expect(tree).toMatchSnapshot();
    });

    it('is visible with some text', () => {
      const tree = create(<SplashScreen text="Loading..." />).toJSON();
      expect(tree).toMatchSnapshot();
    });
  });

Surprisingly, only the second test fails, where the component has to render an element if the text prop is given (simplified code):

return ReactDOM.createPortal(
    <div className={themeStyles.root}>
      <motion.div
        className={styles.root}
        transition={{ duration: fsv.defaultTransitionTime, ease: 'easeInOut' }}
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}>
        <div className={styles.animation} />
        {text != null ? <div className={styles.text}>{text}</div> : null}
      </motion.div>
    </div>,
    document.getElementById('splash-outlet'),
  );

The error:

 FAIL  src/components/__tests__/SplashScreen.test.tsx (15.732s)
  ● Console

    console.error jest.setup.js:36
      Error: Uncaught [Error: No `ref` found. Ensure components created with `motion.custom` forward refs using `React.forwardRef`]
          at reportException (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:24)
          at invokeEventListeners (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:209:9)
          at HTMLUnknownElementImpl._dispatch (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
          at HTMLUnknownElementImpl.dispatchEvent (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
          at HTMLUnknownElementImpl.dispatchEvent (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
          at HTMLUnknownElement.dispatchEvent (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:157:21)
          at Object.invokeGuardedCallbackDev (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11807:16)
          at invokeGuardedCallback (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11860:31)
          at flushPassiveEffectsImpl (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15402:7)
          at unstable_runWithPriority (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/react-test-renderer/node_modules/scheduler/cjs/scheduler.development.js:697:12)
    console.error jest.setup.js:36
      Error: Uncaught [Error: No `ref` found. Ensure components created with `motion.custom` forward refs using `React.forwardRef`]
          at reportException (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:24)
          at invokeEventListeners (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:209:9)
          at HTMLUnknownElementImpl._dispatch (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
          at HTMLUnknownElementImpl.dispatchEvent (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
          at HTMLUnknownElementImpl.dispatchEvent (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
          at HTMLUnknownElement.dispatchEvent (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:157:21)
          at Object.invokeGuardedCallbackDev (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11807:16)
          at invokeGuardedCallback (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11860:31)
          at flushPassiveEffectsImpl (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15402:7)
          at unstable_runWithPriority (/Users/nick/Documents/Drawbotics/repos/stack/drylus/node_modules/react-test-renderer/node_modules/scheduler/cjs/scheduler.development.js:697:12)
    console.error jest.setup.js:36
      The above error occurred in the <MountComponent> component:
          in MountComponent (created by ForwardRef(MotionComponent))
          in ForwardRef(MotionComponent)
          in div
          in Unknown

      Consider adding an error boundary to your tree to customize error handling behavior.
      Visit https://fb.me/react-error-boundaries to learn more about error boundaries.

  ● SplashScreen › matches snapshot when › is visible with some text

    No `ref` found. Ensure components created with `motion.custom` forward refs using `React.forwardRef`

      23 |
      24 |     it('is visible with some text', () => {
    > 25 |       const tree = create(<SplashScreen text="Loading..." />).toJSON();
         |                    ^
      26 |       expect(tree).toMatchSnapshot();
emiloberg commented 4 years ago

We're experiencing the same problem, using Jest. For now, we've solved it by mocking motion, but let us know if we can help out trying to pinpoint the bug.

andrefox333 commented 4 years ago

@emiloberg Can you share how you mocked motion in Jest?

emiloberg commented 4 years ago

@andrefox333 Just a super simple mock where motion.div becomes a simple div, etc.

// __mocks__/framer-motion.tsx
import React from 'react';

export const AnimatePresence = ({ children }: { children: JSX.Element }) => children;

export const motion = {
  div: ({ children, ...rest }: { children: JSX.Element }) => <div {...rest}>{children}</div>,
  img: (props: any) => <img {...props} />,
};
//something.test.tsx
jest.mock('framer-motion');

it(() => { ... })

I just mocked div and img, as those are the only two "elements" we're currently using. If you're using a lot of motion elements (e.g. motion.ul, motion.li, motion.whatever) then you could return a proxy with a generic get method that returns an object of the same type as requested, e.g. motion.ul > ul

mattgperry commented 4 years ago

Closing this as the ref check was softened in an earlier version of Motion and if this is still broken then something will need to be mocked (depends on your usecase) as Motion needs ref to function correctly.