Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
163 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { renderHook, act } from '@testing-library/react-hooks'; | ||
|
||
import useTimetable, { TimetableProps } from '~/hooks/useTimetable'; | ||
|
||
describe('useTimetable hooks', () => { | ||
it('set initial timetable', () => { | ||
const timetable = { | ||
schedule: [0, 1, 2, 3, 4, 5, 6], | ||
// 06:00, 06:30, 07:10, 22:50, 23:30, 00:30 | ||
data: [7200, 9000, 11400, 67800, 70200, 73800], | ||
}; | ||
const currentTime = new Date('2020-05-28T07:09:30').getTime(); | ||
const { result } = renderHook(() => useTimetable(timetable, currentTime)); | ||
expect(result.current.index).toBe(0); | ||
expect(result.current.remaining).toBe(30); | ||
}); | ||
|
||
describe('時間経過で保持する時刻表データも進む', () => { | ||
let timetable: TimetableProps; | ||
beforeEach(() => { | ||
timetable = { | ||
schedule: [0, 1, 2, 3, 4, 5, 6], | ||
// 06:00, 06:30, 07:10, 22:50, 23:30, 00:30 | ||
data: [7200, 9000, 11400, 67800, 70200, 73800], | ||
}; | ||
}); | ||
|
||
it('発車時刻ちょうどの場合は残り時間は`0`', () => { | ||
const currentTime = new Date('2020-05-28T07:10:00').getTime(); | ||
const { result } = renderHook(() => useTimetable(timetable, currentTime)); | ||
expect(result.current.remaining).toBe(0); | ||
}); | ||
|
||
it('発車済みの時刻は削除される', () => { | ||
const currentTime = new Date('2020-05-28T06:29:59').getTime(); | ||
const elapsedTime = currentTime + 2000; // 2 seconds | ||
|
||
const { result } = renderHook(() => useTimetable(timetable, currentTime)); | ||
expect(result.current.remaining).toBe(1); | ||
|
||
act(() => { | ||
result.current.tick(elapsedTime); | ||
}); | ||
expect(result.current.remaining).toBe(2399); | ||
}); | ||
|
||
it('発車時刻を越えたとき`index`が先頭`0`であれば維持', () => { | ||
const currentTime = new Date('2020-05-28T06:29:59').getTime(); | ||
const elapsedTime = currentTime + 2000; // 2 seconds | ||
|
||
const { result } = renderHook(() => useTimetable(timetable, currentTime)); | ||
expect(result.current.index).toBe(0); | ||
|
||
act(() => { | ||
result.current.tick(elapsedTime); | ||
}); | ||
expect(result.current.index).toBe(0); | ||
}); | ||
|
||
it('発車時刻を越えたとき`index`が先頭`1`以上であれば`-1`される', () => { | ||
const currentTime = new Date('2020-05-28T06:29:59').getTime(); | ||
const elapsedTime = currentTime + 2000; // 2 seconds | ||
|
||
const { result } = renderHook(() => useTimetable(timetable, currentTime)); | ||
expect(result.current.index).toBe(0); | ||
|
||
act(() => { | ||
result.current.next(); | ||
result.current.next(); | ||
}); | ||
expect(result.current.index).toBe(2); | ||
|
||
act(() => { | ||
result.current.tick(elapsedTime); | ||
}); | ||
expect(result.current.index).toBe(1); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { useState, useCallback, useRef } from 'react'; | ||
|
||
export type TimetableProps = { | ||
schedule: number[]; | ||
data: number[]; | ||
}; | ||
|
||
export type TimetableHook = { | ||
index: number; | ||
value: number; | ||
tick: (nextTime: number) => void; | ||
next: () => void; | ||
remaining: number; | ||
}; | ||
|
||
const getSecondsSince4am: (time: number) => number = (time) => { | ||
const d = new Date(time); | ||
const hours = (d.getHours() - 4) * 60 * 60; | ||
const minutes = d.getMinutes() * 60; | ||
const seconds = d.getSeconds(); | ||
return hours + minutes + seconds; | ||
}; | ||
|
||
const getSlicedData: (data: number[], time: number) => number[] = ( | ||
data, | ||
time, | ||
) => { | ||
const currentTimeAsSeconds = getSecondsSince4am(time); | ||
return data.filter((s) => s >= currentTimeAsSeconds); | ||
}; | ||
|
||
const useTimetable = ( | ||
timetable: TimetableProps, | ||
currentTime: number = Date.now(), | ||
): TimetableHook => { | ||
const time = useRef(currentTime); | ||
const [currentIndex, setIndex] = useState(0); | ||
const [currentData, setData] = useState( | ||
getSlicedData(timetable.data, time.current), | ||
); | ||
|
||
const value = currentData[currentIndex]; | ||
const remaining = value - getSecondsSince4am(time.current); | ||
|
||
const next = useCallback(() => { | ||
setIndex((prevIndex) => { | ||
if (prevIndex >= currentData.length - 1) { | ||
return currentData.length - 1; | ||
} | ||
return prevIndex + 1; | ||
}); | ||
}, [currentData.length]); | ||
|
||
const prev = useCallback(() => { | ||
setIndex((prevIndex) => { | ||
if (prevIndex <= 0) { | ||
return 0; | ||
} | ||
return prevIndex - 1; | ||
}); | ||
}, []); | ||
|
||
const tick = useCallback<TimetableHook['tick']>( | ||
(nextTime) => { | ||
time.current = nextTime; | ||
const nextData = getSlicedData(timetable.data, time.current); | ||
if (currentData.length !== nextData.length) { | ||
prev(); | ||
} | ||
setData(nextData); | ||
}, | ||
[timetable.data, currentData.length, prev], | ||
); | ||
|
||
return { | ||
index: currentIndex, | ||
value, | ||
tick, | ||
next, | ||
remaining, | ||
}; | ||
}; | ||
|
||
export default useTimetable; |