it('should call onLeave callback with proper dom node for each new item', () => {
        const spy = sinon.spy();

        mountSync(
            domNode,
            elem(
                AnimationGroup,
                null,
                null,
                [
                    elem('div', 'a', { id : 'id1' }),
                    elem('div', 'b', { id : 'id2' }),
                    elem('div', 'c', { id : 'id3' }),
                    elem('div', 'd', { id : 'id4' })
                ]));

        mountSync(
            domNode,
            elem(
                AnimationGroup,
                null,
                { onLeave : spy },
                [
                    elem('div', 'a', { id : 'id1' }),
                    elem('div', 'c', { id : 'id3' })
                ]));

        expect(spy.calledTwice).to.be.ok();
        expect(spy.firstCall.calledWith(document.getElementById('id2'))).to.be.ok();
        expect(spy.secondCall.calledWith(document.getElementById('id4'))).to.be.ok();
    });
    it('should remove item after animation', done => {
        mountSync(
            domNode,
            elem(
                AnimationGroup,
                null,
                null,
                [
                    elem('div', 'a', { id : 'id1' }),
                    elem('div', 'b', { id : 'id2' }),
                    elem('div', 'c', { id : 'id3' })
                ]));

        mountSync(
            domNode,
            elem(
                AnimationGroup,
                null,
                { onLeave : (_, onLeft) => { setTimeout(onLeft, 30); } },
                [
                    elem('div', 'a', { id : 'id1' }),
                    elem('div', 'c', { id : 'id3' })
                ]));

        const domNodeToRemove = document.getElementById('id2');

        expect(domNodeToRemove.parentNode).to.be.equal(domNode);
        setTimeout(() => {
            expect(domNodeToRemove.parentNode).to.be.equal(null);
            done();
        }, 60);
    });
    it('should call stop callback if entering causes during leaving', done => {
        const spy = sinon.spy();

        mountSync(
            domNode,
            elem(AnimationGroup, null, null, elem('div', 'a')));

        mountSync(
            domNode,
            elem(
                AnimationGroup,
                null,
                {
                    onLeave : (_, onLeft) => {
                        setTimeout(onLeft, 30);
                        return spy;
                    }
                }));

        mountSync(
            domNode,
            elem(
                AnimationGroup,
                null,
                null,
                elem('div', 'a')));

        setTimeout(() => {
            expect(spy.called).to.be.ok();
            done();
        }, 60);
    });
    it('should remove "leave" classes on stop', done => {
        mountSync(
            domNode,
            elem(
                CssAnimationGroup,
                null,
                null,
                elem('div', 'a', { id : 'id1' })));

        mountSync(
            domNode,
            elem(CssAnimationGroup));

        setTimeout(() => {
            mountSync(
                domNode,
                elem(
                    CssAnimationGroup,
                    null,
                    null,
                    elem('div', 'a', { id : 'id1' })));

            expect(hasClass('id1', 'leavel')).not.to.be.ok();

            done();
        }, 30);
    });
    it('should not remove "leave" class, call callback on animation end', done => {
        sinon.spy(AnimationGroup.prototype, '_onLeft');

        mountSync(
            domNode,
            elem(
                CssAnimationGroup,
                null,
                null,
                elem('div', 'a', { id : 'id1' })));

        mountSync(
            domNode,
            elem(CssAnimationGroup, null, attrs));

        addAnimationEndListener('id1', () => {
            expect(hasClass('id1', 'leave')).to.be.ok();
            expect(AnimationGroup.prototype._onLeft.called).to.be.ok();

            AnimationGroup.prototype._onLeft.restore();
            done();
        });

        setTimeout(() => {
            dispatchAnimationEndEvent('id1');
        }, 30);
    });
    it('should not call onLeave callback if item is already leaving', () => {
        const spy = sinon.spy();

        mountSync(
            domNode,
            elem(AnimationGroup, null, null, elem('div', 'a', { id : 'id1' })));

        mountSync(
            domNode,
            elem(AnimationGroup, null, { onLeave : spy }));

        mountSync(
            domNode,
            elem(AnimationGroup, null, { onLeave : spy }));

        expect(spy.calledOnce).to.be.ok();
    });
        setTimeout(() => {
            mountSync(
                domNode,
                elem(CssAnimationGroup, null, attrs));

            expect(hasClass('id1', 'appear')).not.to.be.ok();

            done();
        }, 30);
    it('should add "enter" class for each item', () => {
        mountSync(
            domNode,
            elem(CssAnimationGroup));

        mountSync(
            domNode,
            elem(
                CssAnimationGroup,
                null,
                attrs,
                [
                    elem('div', 'a', { id : 'id1' }),
                    elem('div', 'b', { id : 'id2' })
                ]));

        expect(hasClass('id1', 'enter')).to.be.ok();
        expect(hasClass('id2', 'enter')).to.be.ok();
    });
        setTimeout(() => {
            mountSync(
                domNode,
                elem(
                    CssAnimationGroup,
                    null,
                    null,
                    elem('div', 'a', { id : 'id1' })));

            expect(hasClass('id1', 'leavel')).not.to.be.ok();

            done();
        }, 30);
    it('should call stop callback if leaving causes during appearance', done => {
        const spy = sinon.spy();

        mountSync(
            domNode,
            elem(
                AnimationGroup,
                null,
                {
                    onAppear : (_, onAppeared) => {
                        setTimeout(onAppeared, 30);
                        return spy;
                    }
                },
                elem('div', 'a')));

        mountSync(domNode, elem(AnimationGroup));

        setTimeout(() => {
            expect(spy.called).to.be.ok();
            done();
        }, 60);
    });
    it('should call onAppear callback with proper dom node for each item after initial render', () => {
        const spy = sinon.spy();

        mountSync(
            domNode,
            elem(
                AnimationGroup,
                null,
                { onAppear : spy },
                [
                    elem('div', 'a', { id : 'id1' }),
                    elem('div', 'b', { id : 'id2' })
                ]));

        expect(spy.calledTwice).to.be.ok();
        expect(spy.firstCall.calledWith(document.getElementById('id1'))).to.be.ok();
        expect(spy.secondCall.calledWith(document.getElementById('id2'))).to.be.ok();
    });