diff --git a/src/useDragIndexCarousel/_useDragIndexCarousel.test.tsx b/src/useDragIndexCarousel/_useDragIndexCarousel.test.tsx new file mode 100644 index 0000000..33a0305 --- /dev/null +++ b/src/useDragIndexCarousel/_useDragIndexCarousel.test.tsx @@ -0,0 +1,96 @@ +import { renderHook, act } from '@testing-library/react'; +import { _useDragIndexCarousel } from './useDragIndexCarousel'; + +describe('_useDragIndexCarousel 기능 테스트', () => { + it('초기 상태 테스트 index로 시작할 수 있다.', () => { + const { result } = renderHook(() => _useDragIndexCarousel(3)); + + expect(result.current.index).toBe(0); + }); + + it('index를 next, prev로 조작할 수 있다.', () => { + const { result } = renderHook(() => _useDragIndexCarousel(2)); + + expect(result.current.index).toBe(0); + act(() => result.current.next()); + expect(result.current.index).toBe(1); + act(() => result.current.prev()); + expect(result.current.index).toBe(0); + expect(result.current.isStart).toBe(true); + act(() => result.current.next()); + act(() => result.current.next()); + expect(result.current.isEnd).toBe(true); + }); + + it('모바일 터치이벤트 테스트: 절대값으로 minMove보다 많이, 오른쪽으로 슬라이드하면 index를 증가시킬 수 있다.', () => { + const { result } = renderHook(() => _useDragIndexCarousel(3, 60)); + const touchEvent = { touches: [{ clientX: 0 }] }; + const touchMove = { touches: [{ clientX: -100 }] }; + + act(() => result.current.handleTouchStart(touchEvent as unknown as TouchEvent)); + act(() => result.current.handleTouchMove(touchMove as unknown as TouchEvent)); + act(() => result.current.handleMoveEnd()); + + expect(result.current.index).toBe(1); + }); + + it('모바일 터치이벤트 테스트: 절대값으로 minMove보다 많이, 왼쪽으로 슬라이드하면 index를 감소시킬 수 있다.', () => { + const { result } = renderHook(() => _useDragIndexCarousel(3, 60, 1)); + const touchEvent = { touches: [{ clientX: 0 }] }; + const touchMove = { touches: [{ clientX: 100 }] }; + + act(() => result.current.handleTouchStart(touchEvent as unknown as TouchEvent)); + act(() => result.current.handleTouchMove(touchMove as unknown as TouchEvent)); + act(() => result.current.handleMoveEnd()); + + expect(result.current.index).toBe(0); + }); + + it('모바일 터치이벤트 테스트: 절대값으로 minMove보다 적게, 슬라이드하면 index를 유지시킬 수 있다.', () => { + const { result } = renderHook(() => _useDragIndexCarousel(3, 100)); + const touchEvent = { touches: [{ clientX: 0 }] }; + const touchMove = { touches: [{ clientX: -90 }] }; + + act(() => result.current.handleTouchStart(touchEvent as unknown as TouchEvent)); + act(() => result.current.handleTouchMove(touchMove as unknown as TouchEvent)); + act(() => result.current.handleMoveEnd()); + + expect(result.current.index).toBe(0); + }); + + it('PC 스크롤이벤트 테스트: 절대값으로 minMove보다 많이, 오른쪽으로 슬라이드하면 index를 증가시킬 수 있다.', () => { + const { result } = renderHook(() => _useDragIndexCarousel(3, 60)); + const touchEvent = { pageX: 0 }; + const touchMove = { pageX: -100 }; + + act(() => result.current.handleScrollStart(touchEvent as MouseEvent)); + act(() => result.current.handleScrollMove(touchMove as MouseEvent)); + act(() => result.current.handleMoveEnd()); + + expect(result.current.index).toBe(1); + }); + + it('PC 스크롤이벤트 테스트: 절대값으로 minMove보다 많이, 왼쪽으로 슬라이드하면 index를 감소시킬수 있다.', () => { + const { result } = renderHook(() => _useDragIndexCarousel(3, 60, 1)); + const touchEvent = { pageX: 0 }; + const touchMove = { pageX: 100 }; + + act(() => result.current.handleScrollStart(touchEvent as MouseEvent)); + act(() => result.current.handleScrollMove(touchMove as MouseEvent)); + act(() => result.current.handleMoveEnd()); + + expect(result.current.index).toBe(0); + }); + + it('PC 스크롤이벤트 테스트: 절대값으로 minMove보다 적게 슬라이드하면 index를 유지시킬 수 있다.', () => { + const { result } = renderHook(() => _useDragIndexCarousel(3, 100, 1)); + const touchEvent = { pageX: 0 }; + const touchMove = { pageX: 60 }; + + act(() => result.current.handleScrollStart(touchEvent as MouseEvent)); + act(() => result.current.handleScrollMove(touchMove as MouseEvent)); + act(() => result.current.handleMoveEnd()); + + expect(result.current.index).toBe(1); + }); +}); diff --git a/src/useDragIndexCarousel/useDragIndexCarousel.test.tsx b/src/useDragIndexCarousel/useDragIndexCarousel.test.tsx index fbd32b3..cbde555 100644 --- a/src/useDragIndexCarousel/useDragIndexCarousel.test.tsx +++ b/src/useDragIndexCarousel/useDragIndexCarousel.test.tsx @@ -1,96 +1,236 @@ -import { renderHook, act } from '@testing-library/react'; -import { _useDragIndexCarousel } from './useDragIndexCarousel'; - -describe('useDragIndexCarousel', () => { - it('초기 상태 테스트 index로 시작할 수 있다.', () => { - const { result } = renderHook(() => _useDragIndexCarousel(3)); - - expect(result.current.index).toBe(0); +import { fireEvent, render } from '@testing-library/react'; +import useDragIndexCarousel from './useDragIndexCarousel'; +import React from 'react'; + +const DATA = [1, 2, 3, 4]; +const WRAPPER_WIDTH = 500; +const START_INDEX = 2; + +function TestComponent() { + const { CarouselWrapper, ref } = useDragIndexCarousel(DATA.length); + + return ( + + {DATA.map((index) => ( +
+
+ {index} +
+
+ ))} +
+ ); +} + +function IndexConfigureTestComponent() { + const { CarouselWrapper, ref } = useDragIndexCarousel(DATA.length, { startIndex: START_INDEX }); + + return ( + + {DATA.map((index) => ( +
+
+ {index} +
+
+ ))} +
+ ); +} + +function InfinityTestComponent() { + const { CarouselWrapper, ref } = useDragIndexCarousel(DATA.length, { + infinity: true, }); - it('index를 next, prev로 조작할 수 있다.', () => { - const { result } = renderHook(() => _useDragIndexCarousel(2)); - - expect(result.current.index).toBe(0); - act(() => result.current.next()); - expect(result.current.index).toBe(1); - act(() => result.current.prev()); - expect(result.current.index).toBe(0); - expect(result.current.isStart).toBe(true); - act(() => result.current.next()); - act(() => result.current.next()); - expect(result.current.isEnd).toBe(true); + return ( + + {DATA.map((index) => ( +
+
+ {index} +
+
+ ))} +
+ ); +} + +beforeAll(() => { + Object.defineProperty(HTMLElement.prototype, 'clientWidth', { + configurable: true, + value: WRAPPER_WIDTH, }); - it('모바일 터치이벤트 테스트: 절대값으로 minMove보다 많이, 오른쪽으로 슬라이드하면 index를 증가시킬 수 있다.', () => { - const { result } = renderHook(() => _useDragIndexCarousel(3, 60)); - const touchEvent = { touches: [{ clientX: 0 }] }; - const touchMove = { touches: [{ clientX: -100 }] }; - - act(() => result.current.handleTouchStart(touchEvent as unknown as TouchEvent)); - act(() => result.current.handleTouchMove(touchMove as unknown as TouchEvent)); - act(() => result.current.handleMoveEnd()); - - expect(result.current.index).toBe(1); + Object.defineProperties(MouseEvent.prototype, { + pageX: { + get() { + return this.clientX; + }, + }, + pageY: { + get() { + return this.clientY; + }, + }, }); +}); - it('모바일 터치이벤트 테스트: 절대값으로 minMove보다 많이, 왼쪽으로 슬라이드하면 index를 감소시킬 수 있다.', () => { - const { result } = renderHook(() => _useDragIndexCarousel(3, 60, 1)); - const touchEvent = { touches: [{ clientX: 0 }] }; - const touchMove = { touches: [{ clientX: 100 }] }; - - act(() => result.current.handleTouchStart(touchEvent as unknown as TouchEvent)); - act(() => result.current.handleTouchMove(touchMove as unknown as TouchEvent)); - act(() => result.current.handleMoveEnd()); - - expect(result.current.index).toBe(0); +describe('useDragIndexCarousel 컴포넌트 기능 테스트', () => { + it('스크롤을 통해 Carousel을 넘길 수 있다.', () => { + const { container } = render(); + const wrapper = container.querySelector('.wrapper')!; + const carousel = wrapper.querySelector('div')!; + + fireEvent.mouseDown(carousel, { clientX: 0 }); + fireEvent.mouseMove(carousel, { clientX: -100 }); + fireEvent.mouseUp(carousel); + expect(carousel.style.transform).toBe(`translateX(${-WRAPPER_WIDTH}px)`); + + fireEvent.mouseDown(carousel, { clientX: 0 }); + fireEvent.mouseMove(carousel, { clientX: -100 }); + fireEvent.mouseUp(carousel); + expect(carousel.style.transform).toBe(`translateX(${-2 * WRAPPER_WIDTH}px)`); }); - it('모바일 터치이벤트 테스트: 절대값으로 minMove보다 적게, 슬라이드하면 index를 유지시킬 수 있다.', () => { - const { result } = renderHook(() => _useDragIndexCarousel(3, 100)); - const touchEvent = { touches: [{ clientX: 0 }] }; - const touchMove = { touches: [{ clientX: -90 }] }; - - act(() => result.current.handleTouchStart(touchEvent as unknown as TouchEvent)); - act(() => result.current.handleTouchMove(touchMove as unknown as TouchEvent)); - act(() => result.current.handleMoveEnd()); - - expect(result.current.index).toBe(0); + it('스크롤을 해도 첫 인덱스나 마지막 인덱스이면 Carousel이 넘어가지 않는다.', () => { + const { container } = render(); + const wrapper = container.querySelector('.wrapper')!; + const carousel = wrapper.querySelector('div')!; + const scrollRight = () => { + fireEvent.mouseDown(carousel, { clientX: 0 }); + fireEvent.mouseMove(carousel, { clientX: -100 }); + fireEvent.mouseUp(carousel); + }; + + const scrollLeft = () => { + fireEvent.mouseDown(carousel, { clientX: 0 }); + fireEvent.mouseMove(carousel, { clientX: 100 }); + fireEvent.mouseUp(carousel); + }; + + Array.from({ length: 10 }).forEach(() => scrollRight()); + expect(carousel.style.transform).toBe(`translateX(${-3 * WRAPPER_WIDTH}px)`); + Array.from({ length: 10 }).forEach(() => scrollLeft()); + expect(carousel.style.transform).toBe(`translateX(${0}px)`); }); +}); - it('PC 스크롤이벤트 테스트: 절대값으로 minMove보다 많이, 오른쪽으로 슬라이드하면 index를 증가시킬 수 있다.', () => { - const { result } = renderHook(() => _useDragIndexCarousel(3, 60)); - const touchEvent = { pageX: 0 }; - const touchMove = { pageX: -100 }; - - act(() => result.current.handleScrollStart(touchEvent as MouseEvent)); - act(() => result.current.handleScrollMove(touchMove as MouseEvent)); - act(() => result.current.handleMoveEnd()); - - expect(result.current.index).toBe(1); +describe('useDragIndexCarousel Infinity 컴포넌트 테스트', () => { + it('Infinity option이 true인 경우, translateX는 WRAPPER_WIDTH (index=1)로 시작한다.', () => { + const { container } = render(); + const wrapper = container.querySelector('.wrapper')!; + const carousel = wrapper.querySelector('div')!; + expect(carousel.style.transform).toBe(`translateX(${-WRAPPER_WIDTH}px)`); }); - it('PC 스크롤이벤트 테스트: 절대값으로 minMove보다 많이, 왼쪽으로 슬라이드하면 index를 감소시킬수 있다.', () => { - const { result } = renderHook(() => _useDragIndexCarousel(3, 60, 1)); - const touchEvent = { pageX: 0 }; - const touchMove = { pageX: 100 }; - - act(() => result.current.handleScrollStart(touchEvent as MouseEvent)); - act(() => result.current.handleScrollMove(touchMove as MouseEvent)); - act(() => result.current.handleMoveEnd()); - - expect(result.current.index).toBe(0); + it('Infinity option이 true인 경우, 인덱스 양끝단을 오갈 수 있다.', () => { + const { container } = render(); + const wrapper = container.querySelector('.wrapper')!; + const carousel = wrapper.querySelector('div')!; + expect(carousel.style.transform).toBe(`translateX(${-WRAPPER_WIDTH}px)`); + + const scrollRight = () => { + fireEvent.mouseDown(carousel, { clientX: 0 }); + fireEvent.mouseMove(carousel, { clientX: -100 }); + fireEvent.mouseUp(carousel); + }; + + const scrollLeft = () => { + fireEvent.mouseDown(carousel, { clientX: 0 }); + fireEvent.mouseMove(carousel, { clientX: 100 }); + fireEvent.mouseUp(carousel); + }; + + scrollLeft(); + expect(carousel.style.transform).toBe(`translateX(${0}px)`); + fireEvent.transitionEnd(carousel); + expect(carousel.style.transform).toBe(`translateX(${-WRAPPER_WIDTH * DATA.length}px)`); + + Array.from({ length: 2 }).forEach(() => scrollRight()); + expect(carousel.style.transform).toBe(`translateX(${-WRAPPER_WIDTH * (DATA.length + 1)}px)`); + fireEvent.transitionEnd(carousel); + expect(carousel.style.transform).toBe(`translateX(${-WRAPPER_WIDTH}px)`); }); +}); - it('PC 스크롤이벤트 테스트: 절대값으로 minMove보다 적게 슬라이드하면 index를 유지시킬 수 있다.', () => { - const { result } = renderHook(() => _useDragIndexCarousel(3, 100, 1)); - const touchEvent = { pageX: 0 }; - const touchMove = { pageX: 60 }; - - act(() => result.current.handleScrollStart(touchEvent as MouseEvent)); - act(() => result.current.handleScrollMove(touchMove as MouseEvent)); - act(() => result.current.handleMoveEnd()); - - expect(result.current.index).toBe(1); +describe('startIndex 지정 테스트', () => { + it('startIndex를 지정할 수 있다.', () => { + const { container } = render(); + const wrapper = container.querySelector('.wrapper')!; + const carousel = wrapper.querySelector('div')!; + expect(carousel.style.transform).toBe(`translateX(${-START_INDEX * WRAPPER_WIDTH}px)`); }); }); diff --git a/src/useDragIndexCarousel/useDragIndexCarousel.tsx b/src/useDragIndexCarousel/useDragIndexCarousel.tsx index 17cf97c..49f4314 100644 --- a/src/useDragIndexCarousel/useDragIndexCarousel.tsx +++ b/src/useDragIndexCarousel/useDragIndexCarousel.tsx @@ -34,10 +34,9 @@ export function _useDragIndexCarousel(pageLimit: number, minMove = 60, startInde }; const getSliderWidth = () => { - if (ref.current) { - return ref.current.clientWidth; - } - return window.innerWidth; + // getSliderWidth는 상단의 useEffect에서 호출하는데, 여기서 ref.current를 확인하므로 + // ref.current가 반드시 존재한다. + return ref!.current!.clientWidth; }; const handleMoveEnd = () => {