whisper-money/resources/js/hooks/use-scroll-progress.test.ts

63 lines
2.0 KiB
TypeScript

import { act, renderHook } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { useScrollProgress } from './use-scroll-progress';
/**
* Place the element's box so that the hook sees it at a known position, then
* fire the scroll listener the hook attached.
*/
function positionElement(
element: HTMLElement | null,
{ top, height }: { top: number; height: number },
) {
vi.spyOn(element as HTMLElement, 'getBoundingClientRect').mockReturnValue({
top,
height,
} as DOMRect);
act(() => {
window.dispatchEvent(new Event('scroll'));
});
}
describe('useScrollProgress', () => {
beforeEach(() => {
window.innerHeight = 800;
});
it('stays at 0 until the element enters from the bottom', () => {
const { result } = renderHook(() =>
useScrollProgress<HTMLDivElement>(),
);
result.current.ref.current = document.createElement('div');
// Element still below the fold: top beyond the viewport height.
positionElement(result.current.ref.current, { top: 900, height: 200 });
expect(result.current.progress).toBe(0);
});
it('reaches 1 once the element has passed above the viewport', () => {
const { result } = renderHook(() =>
useScrollProgress<HTMLDivElement>(),
);
result.current.ref.current = document.createElement('div');
positionElement(result.current.ref.current, { top: -400, height: 200 });
expect(result.current.progress).toBe(1);
});
it('reports how far through the viewport the element has travelled', () => {
const { result } = renderHook(() =>
useScrollProgress<HTMLDivElement>(),
);
result.current.ref.current = document.createElement('div');
// (800 - 400) / (800 + 200) = 0.4
positionElement(result.current.ref.current, { top: 400, height: 200 });
expect(result.current.progress).toBeCloseTo(0.4);
});
});