Disable React's CSSTransitionGroup in test
Asked Answered
C

6

13

I'm using CSSTransitionGroup to animate an element when it appears in the DOM, or when it leaves the DOM. It works well.

Now - I want to unit test this component. I'm creating a temporary DOM node and I attach it to the <body>, I render my component into it, and then I perform some actions. As a result, I expect a child DOM node to disappear.

The problem: Animation classes are applied, and component is kept in the DOM until CSS animations end. This means my test should also wait for a few hundred milliseconds before I can assert for that element being gone. I cannot do that - I want my tests to be fast, as those are unit tests.

The question: Is there a way to disable CSS transitions without adding extra options to my component?

What I tried: Unit testing itself works well. I can get rid of animations by passing a parameter to my component that will make it not use CSSTransitionGroup. So - worst case scenario - I'll do just that. But I'm looking for a better solution.

I could also assert that "-enter"/"-enter-active"/"-leave"/"-leave-active" classes are present on an element in question. That seems a bit hacky though, as I could imagine a bug where those classes would be applied, but element would remain in the DOM. I'd prefer not to resort to this approach.

Chader answered 11/4, 2016 at 13:59 Comment(3)
You could mock the component. If you're using Jest you can use ReactTestUtils.mockComponent. I wouldn't bother testing the functionality of CSSTransitionGroup directly.Parted
how about moneky patching react component when running the tests? and then u disable CSSTransitionGroup there.Mile
I kind of did that in the end @AdamGoldman. You reminded me I should probably answer my own question here.Chader
I
8

http://reactcommunity.org/react-transition-group/testing/

import { config } from 'react-transition-group'

config.disabled = true

this helped me to get rid of animations with enzyme

Inelegance answered 27/5, 2021 at 17:4 Comment(2)
It also works when using it only with jest.Perreira
Very simple & way to go!Eisk
D
5

Using Jest this is the solution I came up with. I mocked the Transition component. Since my import looks like import {Transition} from 'react-transition-group' this is what I created:

Based on my jest config, anything found in my __mocks__ folder is used instead of any imports.

__mocks__/react-transition-group/index.js:

import Transition from './Transition'

export { Transition }
//more exports to come as I use other parts of react-transition-group

__mocks__/react-transition-group/Transition.js:

import React from 'react'

export default (props) => (
  <div>
    {props.in ? props.children() : null}
  </div>
)

This way, children is immediately rendered - the "Transition" is pretty much disabled.

**This works for v2.4.0 of react-transition-group

Depositor answered 2/8, 2018 at 23:58 Comment(0)
P
2

I believe there is a neater solution using proxyquire (proxyquireify in my browserify based build). Inspired by the previous answers.

./stubs/react_css_transition_group.js:

const { createElement: el, Component } = require('react')

class ReactCSSTransitionGroup extends Component {
  constructor(props) {
    super(props)
  }

  render() {
    const { children, component } = this.props

    return el(component, null, children)
  }
}

ReactCSSTransitionGroup.defaultProps = {
  component: 'div'
}

module.exports = ReactCSSTransitionGroup

./foo_test.js:

const test = require('tape')
const { mount } = require('enzyme')
const { createElement: el } = require('react')
const proxyquire = require('proxyquireify')(require)

const CSSTransitionGroup = require('./stubs/react_css_transition_group')

const Foo = proxyquire('../src/foo', {
  'react-addons-css-transition-group': CSSTransitionGroup
})

/* pseudocode */
test('something about bar', (assert) => {
  assert.plan(1)

  const foo = el(Foo)
  const wrapper = mount(foo)

  assert.equal(wrapper.find('p').first().text(), 'bar')
})

Hope that helps future readers of this question.

Potable answered 17/11, 2016 at 15:52 Comment(0)
C
1

I went with the approach that on the one hand, makes it easy to disable animations in test, and on the other - doesn't require any test-specific parameters to be supported by each of the animated components.

Since I use ClojureScript, syntax might be unfamiliar to some, but I'll comment it a bit to make it clearer:

;; By default, in non-unit-test code, animations are enabled.
(def ^:dynamic animations-enabled true)

;; Custom component that essentially wraps React.addons.CSSTransitionGroup,
;; but modifies props passed to it whenever animations-enabled
;; is set to false.
(def css-transition-group
  (let [rc-anim-cls (rc/adapt-react-class js/React.addons.CSSTransitionGroup)]
  (rc/create-class
    {:reagent-render
     (fn [anim-spec & children]
       (let [anim-spec-2 (if animations-enabled
                           ;; If animations are enabled,
                           ;; pass props through with no change.
                           anim-spec
                           ;; If animations are disabled,
                           ;; override that in props before passing
                           ;; them to CSSTransitionGroup.
                           (assoc anim-spec
                                  :transition-enter false
                                  :transition-leave false))]
       (into [rc-anim-cls anim-spec-2] children)))})))

In regular code, this class will be used in exactly the same way as standard CSSTransitionGroup would be used.

In unit tests, however, I can bind animations-enabled as false and safely assert on DOM elements being added/removed immediately:

(binding [anim/animations-enabled false]
  ;; Create component that uses animations. It will not be
  ;; animated though.
  )
Chader answered 9/5, 2016 at 8:48 Comment(2)
I love when someone downvotes my answer with no comment on why. Whatever... this approach works for us well so far.Chader
Probably because if you're a regular JS developer, the above makes no sense. What is "binding"? global variables?Patricapatrice
A
0

I had a really tough time reasoning about the ClojureScript answer provided, had a similar requirement and noted this is seemingly the only google result returned.

Here's how I solved it, using sinon:

    transitionGroupStub = sinon.stub(ReactCSSTransitionGroup.prototype, 'render', function () {
        return React.createElement('DIV', null, this.props.children)
    })

Basically stub out the entire css transition group to render inside a div passing along the children. This may not be pretty, but it seems to work pretty well for my needs.

Arsenide answered 4/11, 2016 at 19:21 Comment(1)
I hear you, Lisp is hard to read at first ;) However, your solution makes sense. It's not 100% same as mine (I wrap CSSTransitionGroup in a test-specific class, you use stubs), so I'd say mine is a bit cleaner - maybe, but yours avoid an extra class. Cheers!Chader
A
-3

According to this plans

react-addons-css-transition-group is under deprecation. So maybe use react-motion instead?

Anastigmatic answered 4/4, 2017 at 16:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.