Add useSpring tests

This commit is contained in:
Mario Beltrán Alarcón 2019-08-21 23:36:50 +02:00
parent cecd1adcfa
commit 3c43c4b05d
3 changed files with 134 additions and 9 deletions

View File

@ -1,6 +1,6 @@
import { storiesOf } from '@storybook/react';
import * as React from 'react';
import { useSpring } from '..';
import useSpring from '../useSpring';
import ShowDocs from './util/ShowDocs';
const Demo = () => {

View File

@ -0,0 +1,119 @@
import { act, renderHook } from '@testing-library/react-hooks';
import useSpring from '../useSpring';
import { Spring } from 'rebound';
// simulate Spring for testing
const mockSetCurrentValue = jest.fn();
const mockAddListener = jest.fn();
const mockSetEndValue = jest.fn();
const mockRemoveListener = jest.fn();
let triggerSpringUpdate = () => {};
let springListener: Listener = { onSpringUpdate: () => {} };
interface Listener {
onSpringUpdate: (currentSpring: Spring) => void;
}
const mockCreateSpring: Spring = jest.fn().mockImplementation(() => {
let currentValue = 0;
let endValue = 0;
const getCloserValue = (a, b) => Math.round((a + b) / 2);
const getCurrentValue = () => {
currentValue = getCloserValue(currentValue, endValue);
return currentValue;
};
triggerSpringUpdate = () => {
if (currentValue !== endValue) {
springListener.onSpringUpdate({ getCurrentValue } as any);
}
};
return {
setCurrentValue: val => {
currentValue = val;
mockSetCurrentValue(val);
},
addListener: newListener => {
springListener = newListener;
mockAddListener(newListener);
},
setEndValue: val => {
endValue = val;
mockSetEndValue(val);
},
removeListener: mockRemoveListener,
};
}) as any;
jest.mock('rebound', () => {
return {
Sprint: {},
SpringSystem: jest.fn().mockImplementation(() => {
return { createSpring: mockCreateSpring };
}),
};
});
it('should init value to provided target', () => {
const { result } = renderHook(() => useSpring(70));
expect(result.current).toBe(70);
expect(mockSetCurrentValue).toHaveBeenCalledTimes(1);
expect(mockSetCurrentValue).toHaveBeenCalledWith(70);
expect(mockCreateSpring).toHaveBeenCalledTimes(1);
expect(mockCreateSpring).toHaveBeenCalledWith(50, 3);
});
it('should create spring with custom tension and friction args provided', () => {
renderHook(() => useSpring(500, 20, 7));
expect(mockCreateSpring).toHaveBeenCalledTimes(1);
expect(mockCreateSpring).toHaveBeenCalledWith(20, 7);
});
it('should subscribe only once', () => {
const { rerender } = renderHook(() => useSpring());
expect(mockAddListener).toHaveBeenCalledTimes(1);
expect(mockAddListener).toHaveBeenCalledWith(springListener);
rerender();
expect(mockAddListener).toHaveBeenCalledTimes(1);
});
it('should handle spring update', () => {
let targetValue = 70;
let lastSpringValue = targetValue;
const { result, rerender } = renderHook(() => useSpring(targetValue));
targetValue = 100;
rerender();
expect(result.current).toBe(lastSpringValue);
act(() => {
triggerSpringUpdate(); // simulate new spring value
});
expect(result.current).toBeGreaterThan(lastSpringValue);
expect(result.current).toBeLessThanOrEqual(targetValue);
lastSpringValue = result.current;
act(() => {
triggerSpringUpdate(); // simulate another new spring value
});
expect(result.current).toBeGreaterThan(lastSpringValue);
expect(result.current).toBeLessThanOrEqual(targetValue);
});
it('should remove listener on unmount', () => {
const { unmount } = renderHook(() => useSpring());
expect(mockRemoveListener).not.toHaveBeenCalled();
unmount();
expect(mockRemoveListener).toHaveBeenCalledTimes(1);
expect(mockRemoveListener).toHaveBeenCalledWith(springListener);
});

View File

@ -1,31 +1,37 @@
import { useEffect, useState } from 'react';
import { useEffect, useState, useMemo } from 'react';
import { Spring, SpringSystem } from 'rebound';
const useSpring = (targetValue: number = 0, tension: number = 50, friction: number = 3) => {
const [spring, setSpring] = useState<Spring | null>(null);
const [value, setValue] = useState<number>(targetValue);
useEffect(() => {
const listener = {
// memoize listener to being able to unsubscribe later properly, otherwise
// listener fn will be different on each re-render and wouldn't unsubscribe properly.
const listener = useMemo(
() => ({
onSpringUpdate: currentSpring => {
const newValue = currentSpring.getCurrentValue();
setValue(newValue);
},
};
}),
[]
);
useEffect(() => {
if (!spring) {
const newSpring = new SpringSystem().createSpring(tension, friction);
newSpring.setCurrentValue(targetValue);
setSpring(newSpring);
newSpring.addListener(listener);
return;
}
return () => {
spring.removeListener(listener);
setSpring(null);
if (spring) {
spring.removeListener(listener);
setSpring(null);
}
};
}, [tension, friction]);
}, [tension, friction, spring]);
useEffect(() => {
if (spring) {