diff --git a/react/src/hooks/useIntervalValue.test.tsx b/react/src/hooks/useIntervalValue.test.tsx new file mode 100644 index 0000000000..f9bb8ff342 --- /dev/null +++ b/react/src/hooks/useIntervalValue.test.tsx @@ -0,0 +1,207 @@ +import { useInterval, useIntervalValue } from './useIntervalValue'; +import { render, screen, act } from '@testing-library/react'; +import React from 'react'; + +// Test component for useInterval +const TestIntervalComponent: React.FC<{ + delay: number | null; + pauseWhenHidden?: boolean; +}> = ({ delay, pauseWhenHidden = true }) => { + const [count, setCount] = React.useState(0); + + useInterval( + () => { + setCount((prev) => prev + 1); + }, + delay, + pauseWhenHidden, + ); + + return
{count}
; +}; + +// Test component for useIntervalValue +const TestIntervalValueComponent: React.FC<{ + delay: number | null; + pauseWhenHidden?: boolean; +}> = ({ delay, pauseWhenHidden = true }) => { + const value = useIntervalValue( + () => Date.now(), + delay, + undefined, + pauseWhenHidden, + ); + + return
{value}
; +}; + +describe('useInterval and useIntervalValue hooks', () => { + beforeEach(() => { + jest.useFakeTimers(); + // Mock document.hidden as false initially (page is visible) + Object.defineProperty(document, 'hidden', { + writable: true, + value: false, + }); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + describe('useInterval', () => { + it('should increment count at specified interval', () => { + render(); + + expect(screen.getByTestId('interval-count')).toHaveTextContent('0'); + + act(() => { + jest.advanceTimersByTime(1000); + }); + + expect(screen.getByTestId('interval-count')).toHaveTextContent('1'); + + act(() => { + jest.advanceTimersByTime(2000); + }); + + expect(screen.getByTestId('interval-count')).toHaveTextContent('3'); + }); + + it('should pause when pauseWhenHidden is true and page becomes hidden', () => { + render(); + + expect(screen.getByTestId('interval-count')).toHaveTextContent('0'); + + // Page becomes hidden + act(() => { + Object.defineProperty(document, 'hidden', { value: true }); + document.dispatchEvent(new Event('visibilitychange')); + }); + + // Timer should not increment + act(() => { + jest.advanceTimersByTime(2000); + }); + + expect(screen.getByTestId('interval-count')).toHaveTextContent('0'); + + // Page becomes visible again + act(() => { + Object.defineProperty(document, 'hidden', { value: false }); + document.dispatchEvent(new Event('visibilitychange')); + }); + + // Should execute immediately when visible again + expect(screen.getByTestId('interval-count')).toHaveTextContent('1'); + + // Continue normal interval + act(() => { + jest.advanceTimersByTime(1000); + }); + + expect(screen.getByTestId('interval-count')).toHaveTextContent('2'); + }); + + it('should continue running when pauseWhenHidden is false and page becomes hidden', () => { + render(); + + expect(screen.getByTestId('interval-count')).toHaveTextContent('0'); + + // Page becomes hidden + act(() => { + Object.defineProperty(document, 'hidden', { value: true }); + document.dispatchEvent(new Event('visibilitychange')); + }); + + // Timer should continue to increment even when hidden + act(() => { + jest.advanceTimersByTime(2000); + }); + + expect(screen.getByTestId('interval-count')).toHaveTextContent('2'); + }); + + it('should not run when delay is null', () => { + render(); + + expect(screen.getByTestId('interval-count')).toHaveTextContent('0'); + + act(() => { + jest.advanceTimersByTime(5000); + }); + + expect(screen.getByTestId('interval-count')).toHaveTextContent('0'); + }); + }); + + describe('useIntervalValue', () => { + it('should update value at specified interval', () => { + render(); + + const initialValue = screen.getByTestId('interval-value').textContent; + + act(() => { + jest.advanceTimersByTime(1000); + }); + + const updatedValue = screen.getByTestId('interval-value').textContent; + expect(updatedValue).not.toBe(initialValue); + }); + + it('should pause value updates when page becomes hidden and pauseWhenHidden is true', () => { + render( + , + ); + + const initialValue = screen.getByTestId('interval-value').textContent; + + // Page becomes hidden + act(() => { + Object.defineProperty(document, 'hidden', { value: true }); + document.dispatchEvent(new Event('visibilitychange')); + }); + + act(() => { + jest.advanceTimersByTime(2000); + }); + + // Value should not change when hidden + expect(screen.getByTestId('interval-value')).toHaveTextContent( + initialValue!, + ); + + // Page becomes visible again + act(() => { + Object.defineProperty(document, 'hidden', { value: false }); + document.dispatchEvent(new Event('visibilitychange')); + }); + + // Value should update immediately when visible again + const newValue = screen.getByTestId('interval-value').textContent; + expect(newValue).not.toBe(initialValue); + }); + + it('should continue updating when pauseWhenHidden is false and page becomes hidden', () => { + render( + , + ); + + const initialValue = screen.getByTestId('interval-value').textContent; + + // Page becomes hidden + act(() => { + Object.defineProperty(document, 'hidden', { value: true }); + document.dispatchEvent(new Event('visibilitychange')); + }); + + act(() => { + jest.advanceTimersByTime(1000); + }); + + // Value should continue to update even when hidden + const updatedValue = screen.getByTestId('interval-value').textContent; + expect(updatedValue).not.toBe(initialValue); + }); + }); +});