Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions packages/core/src/hooks/useScroll/useScroll.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { act, renderHook } from '@testing-library/react';

import { renderHookServer } from '@/tests';
import { target } from '@/utils/helpers';

import type { StateRef } from '../useRefState/useRefState';
import type { UseScrollReturn } from './useScroll';

import { useScroll } from './useScroll';

const mockScrollTo = vi.fn();
const mockScrollIntoView = vi.fn();
const mockOnScroll = vi.fn();

const targets = [
undefined,
target('#target'),
target(document.getElementById('target')!),
target(() => document.getElementById('target')!),
{ current: document.getElementById('target') },
Object.assign(() => {}, {
state: document.getElementById('target'),
current: document.getElementById('target')
})
];

const element = document.getElementById('target') as HTMLDivElement;

beforeEach(() => {
element.scrollTo = mockScrollTo;
element.scrollIntoView = mockScrollIntoView;

Object.defineProperty(element, 'scrollTop', { value: 0, writable: true });
});

targets.forEach((target) => {
describe(`${target}`, () => {
it('Should use scroll', () => {
const { result } = renderHook(() => {
if (target)
return useScroll(target) as unknown as {
ref: StateRef<HTMLDivElement>;
} & UseScrollReturn;
return useScroll<HTMLDivElement>();
});

expect(result.current.scrollTo).toBeTypeOf('function');
expect(result.current.scrollIntoView).toBeTypeOf('function');
expect(result.current.scrolling).toBeTypeOf('boolean');

if (!target) expect(result.current.ref).toBeTypeOf('function');
if (target) expect(result.current.ref).toBeUndefined();
});

it('Should use scroll to on server side', () => {
const { result } = renderHookServer(() => {
if (target)
return useScroll(target) as unknown as {
ref: StateRef<HTMLDivElement>;
} & UseScrollReturn;
return useScroll<HTMLDivElement>();
});

expect(result.current.scrollTo).toBeTypeOf('function');
expect(result.current.scrollIntoView).toBeTypeOf('function');
expect(result.current.scrolling).toBeTypeOf('boolean');

if (!target) expect(result.current.ref).toBeTypeOf('function');
if (target) expect(result.current.ref).toBeUndefined();
});

it('Should change scrolling state', () => {
const { result } = renderHook(() => {
if (target)
return useScroll(target) as unknown as {
ref: StateRef<HTMLDivElement>;
} & UseScrollReturn;
return useScroll<HTMLDivElement>();
});

if (!target) act(() => result.current.ref(element));

act(() => {
element.dispatchEvent(new Event('scroll'));
});
expect(result.current.scrolling).toBe(true);

act(() => {
element.dispatchEvent(new Event('scrollend'));
});
expect(result.current.scrolling).toBe(false);
});

it('Should updates scroll directions on scroll', () => {
const { result } = renderHook(() => {
if (target)
return useScroll(target, mockOnScroll) as unknown as {
ref: StateRef<HTMLDivElement>;
} & UseScrollReturn;
return useScroll<HTMLDivElement>(mockOnScroll);
});

if (!target) act(() => result.current.ref(element));

act(() => {
element.scrollTop = 100;
element.dispatchEvent(new Event('scroll'));
});

act(() => {
element.scrollTop = 200;
element.dispatchEvent(new Event('scroll'));
});

expect(mockOnScroll).toHaveBeenCalled();

const params = mockOnScroll.mock.calls.at(-1)?.[0];

expect(params.directions.bottom).toBe(true);
expect(params.directions.top).toBe(false);
});

it('Should update arrived on scroll', () => {
const { result } = renderHook(() => {
if (target)
return useScroll(target, mockOnScroll) as unknown as {
ref: StateRef<HTMLDivElement>;
} & UseScrollReturn;
return useScroll<HTMLDivElement>(mockOnScroll);
});

if (!target) act(() => result.current.ref(element));

Object.defineProperty(element, 'clientHeight', { value: 500 });
Object.defineProperty(element, 'scrollHeight', { value: 1000 });

const scrollTop = 0;
act(() => {
Object.defineProperty(element, 'scrollTop', { value: scrollTop });
});

act(() => {
element.scrollTop = 500;
element.dispatchEvent(new Event('scroll'));
});

expect(mockOnScroll).toHaveBeenCalled();

const params = mockOnScroll.mock.calls[0][0];

expect(params.arrived.bottom).toBe(true);
expect(params.arrived.top).toBe(false);
});
});
});