/* * Copyright (c) 2015 Nordic Semiconductor ASA * * SPDX-License-Identifier: LicenseRef-Nordic-4-Clause */ import React from 'react'; import { act, render } from '@testing-library/react'; import useStopwatch, { type ITimer, type Stopwatch } from './useStopwatch'; let appCallback = () => {}; const setup = (stopwatch: Stopwatch) => { const returnVal = {}; const TestComponent = () => { Object.assign(returnVal, useStopwatch(stopwatch)); return null; }; render(); return returnVal as ReturnType; }; describe('Stop Watch', () => { const mockNow = jest.fn(() => 0); const mockSetTimeout = jest.fn((callback: () => void) => { appCallback = callback; return () => {}; }); const timerMock: ITimer = { now: mockNow, setTimeout: mockSetTimeout, }; const expectZeroElapsedTime = ( stopwatch: ReturnType, ) => { expect(stopwatch.time).toBe(0); expect(stopwatch.seconds).toBe(0); expect(stopwatch.minutes).toBe(0); expect(stopwatch.hours).toBe(0); expect(stopwatch.days).toBe(0); }; beforeEach(() => { mockNow.mockReturnValue(0); jest.clearAllMocks(); }); test('Auto Start will init timer', () => { const { isRunning } = setup({ autoStart: true, resolution: 1000, timer: timerMock, }); expect(timerMock.setTimeout).toBeCalledTimes(1); expect(timerMock.setTimeout).toBeCalledWith(expect.anything(), 1000); expect(isRunning).toBeTruthy(); }); test('Pause will stop the stopwatch and report elapsed time', () => { const stopwatch = setup({ autoStart: true, resolution: 1000, timer: timerMock, }); expect(timerMock.setTimeout).toBeCalledTimes(1); expect(timerMock.setTimeout).toBeCalledWith(expect.anything(), 1000); expect(stopwatch.isRunning).toBeTruthy(); expectZeroElapsedTime(stopwatch); mockNow.mockReturnValue(433); act(() => { stopwatch.pause(); }); expect(stopwatch.time).toBe(433); expect(stopwatch.seconds).toBe(0); expect(stopwatch.minutes).toBe(0); expect(stopwatch.hours).toBe(0); expect(stopwatch.days).toBe(0); expect(stopwatch.isRunning).toBeFalsy(); }); test('Start a paused timer will continue timer from provided time', () => { const stopwatch = setup({ autoStart: true, resolution: 1000, timer: timerMock, }); expect(timerMock.setTimeout).toBeCalledTimes(1); expect(timerMock.setTimeout).toBeCalledWith(expect.anything(), 1000); expect(stopwatch.isRunning).toBeTruthy(); expectZeroElapsedTime(stopwatch); expect(timerMock.setTimeout).nthCalledWith(1, expect.anything(), 1000); mockNow.mockReturnValue(500); act(() => { stopwatch.pause(); }); expect(stopwatch.time).toBe(500); expect(stopwatch.seconds).toBe(0); expect(stopwatch.minutes).toBe(0); expect(stopwatch.hours).toBe(0); expect(stopwatch.days).toBe(0); expect(stopwatch.isRunning).toBeFalsy(); mockNow.mockReturnValue(5000); act(() => { stopwatch.start(500); }); expect(timerMock.setTimeout).toBeCalledTimes(2); expect(timerMock.setTimeout).nthCalledWith(2, expect.anything(), 500); }); test('Timer will not automatically start', () => { const { isRunning } = setup({ autoStart: false, resolution: 1000, timer: timerMock, }); expect(timerMock.setTimeout).toBeCalledTimes(0); expect(isRunning).toBeFalsy(); }); test('Start call will start the timer', () => { const stopwatch = setup({ autoStart: false, resolution: 1000, timer: timerMock, }); expect(timerMock.setTimeout).toBeCalledTimes(0); expect(stopwatch.isRunning).toBeFalsy(); mockNow.mockReturnValue(5000); act(() => { stopwatch.start(); }); expect(timerMock.setTimeout).toBeCalledTimes(1); expect(timerMock.setTimeout).toBeCalledWith(expect.anything(), 1000); expect(stopwatch.isRunning).toBeTruthy(); }); test('Reset will reset all time the stopwatch and report elapsed time', () => { const stopwatch = setup({ autoStart: true, resolution: 1000, timer: timerMock, }); expect(timerMock.setTimeout).toBeCalledTimes(1); expect(timerMock.setTimeout).nthCalledWith(1, expect.anything(), 1000); expect(stopwatch.isRunning).toBeTruthy(); expectZeroElapsedTime(stopwatch); mockNow.mockReturnValue(1200); act(() => { appCallback(); }); expect(timerMock.setTimeout).nthCalledWith(2, expect.anything(), 800); expect(stopwatch.time).toBe(1200); expect(stopwatch.seconds).toBe(1); expect(stopwatch.minutes).toBe(0); expect(stopwatch.hours).toBe(0); expect(stopwatch.days).toBe(0); expect(stopwatch.isRunning).toBeTruthy(); mockNow.mockReturnValue(6800); act(() => { stopwatch.reset(); }); expect(timerMock.setTimeout).nthCalledWith(3, expect.anything(), 1000); expectZeroElapsedTime(stopwatch); expect(stopwatch.isRunning).toBeTruthy(); }); test('Time will update output after precise interval elapsed trigger a new timeout', () => { const stopwatch = setup({ autoStart: true, resolution: 1000, timer: timerMock, }); expectZeroElapsedTime(stopwatch); // trigger timeout with precise time elapsed mockNow.mockReturnValue(1000); act(() => { appCallback(); }); expect(timerMock.setTimeout).toBeCalledTimes(2); expect(timerMock.setTimeout).toBeCalledWith(expect.anything(), 1000); expect(stopwatch.time).toBe(1000); expect(stopwatch.seconds).toBe(1); expect(stopwatch.minutes).toBe(0); expect(stopwatch.hours).toBe(0); expect(stopwatch.days).toBe(0); }); test('Time will update trigger a new timeout and add positive correction', () => { const stopwatch = setup({ autoStart: true, resolution: 1000, timer: timerMock, }); expectZeroElapsedTime(stopwatch); // trigger timeout with error of -200ms mockNow.mockReturnValue(800); act(() => { appCallback(); }); expect(timerMock.setTimeout).toBeCalledTimes(2); expect(timerMock.setTimeout).nthCalledWith(1, expect.anything(), 1000); expect(timerMock.setTimeout).nthCalledWith(2, expect.anything(), 200); expect(stopwatch.time).toBe(800); expect(stopwatch.seconds).toBe(0); expect(stopwatch.minutes).toBe(0); expect(stopwatch.hours).toBe(0); expect(stopwatch.days).toBe(0); }); test('Time will update trigger a new timeout and add negative correction', () => { const stopwatch = setup({ autoStart: true, resolution: 1000, timer: timerMock, }); expectZeroElapsedTime(stopwatch); // trigger timeout with error of +200ms mockNow.mockReturnValue(1200); act(() => { appCallback(); }); expect(timerMock.setTimeout).toBeCalledTimes(2); expect(timerMock.setTimeout).nthCalledWith(1, expect.anything(), 1000); expect(timerMock.setTimeout).nthCalledWith(2, expect.anything(), 800); expect(stopwatch.time).toBe(1200); expect(stopwatch.seconds).toBe(1); expect(stopwatch.minutes).toBe(0); expect(stopwatch.hours).toBe(0); expect(stopwatch.days).toBe(0); }); test('Test time part splitting', () => { const stopwatch = setup({ autoStart: true, resolution: 1000, timer: timerMock, }); expectZeroElapsedTime(stopwatch); // 2days 1hr 1 min 3 seconds 500ms mockNow.mockReturnValue(176588500); act(() => { appCallback(); }); expect(stopwatch.time).toBe(176588500); expect(stopwatch.seconds).toBe(8); expect(stopwatch.minutes).toBe(3); expect(stopwatch.hours).toBe(1); expect(stopwatch.days).toBe(2); }); });