import React from 'react';
import { render, fireEvent, act } from '@testing-library/react';
import { BackToTop } from '@/src/components/BackToTop';
// Mock window properties
global.scrollTo = jest.fn();
Object.defineProperty(window, 'pageYOffset', {
writable: true,
configurable: true,
value: 0,
});
describe('BackToTop', () => {
beforeEach(() => {
jest.clearAllMocks();
window.pageYOffset = 0;
});
it('does not render button when at top of page', () => {
const { container } = render();
const button = container.querySelector('button');
expect(button).not.toBeInTheDocument();
});
it('renders button when scrolled down', () => {
const { container } = render();
// Simulate scroll down
act(() => {
window.pageYOffset = 400;
fireEvent.scroll(window);
});
const button = container.querySelector('button');
expect(button).toBeInTheDocument();
});
it('hides button when scrolled back up', () => {
const { container } = render();
// Scroll down first
act(() => {
window.pageYOffset = 400;
fireEvent.scroll(window);
});
expect(container.querySelector('button')).toBeInTheDocument();
// Scroll back up
act(() => {
window.pageYOffset = 100;
fireEvent.scroll(window);
});
expect(container.querySelector('button')).not.toBeInTheDocument();
});
it('scrolls to top when clicked', () => {
const { container } = render();
// Make button visible
act(() => {
window.pageYOffset = 400;
fireEvent.scroll(window);
});
const button = container.querySelector('button');
fireEvent.click(button!);
expect(global.scrollTo).toHaveBeenCalledWith({
top: 0,
behavior: 'smooth'
});
});
it('has correct styling when visible', () => {
const { container } = render();
// Make button visible
act(() => {
window.pageYOffset = 400;
fireEvent.scroll(window);
});
const button = container.querySelector('button');
expect(button).toHaveClass('fixed');
expect(button).toHaveClass('bottom-8');
expect(button).toHaveClass('right-8');
expect(button).toHaveClass('bg-blue-600');
expect(button).toHaveClass('text-white');
expect(button).toHaveClass('rounded-full');
expect(button).toHaveClass('shadow-lg');
});
it('has hover effect', () => {
const { container } = render();
// Make button visible
act(() => {
window.pageYOffset = 400;
fireEvent.scroll(window);
});
const button = container.querySelector('button');
expect(button).toHaveClass('hover:bg-blue-700');
expect(button).toHaveClass('hover:scale-110');
});
it('contains arrow icon', () => {
const { container } = render();
// Make button visible
act(() => {
window.pageYOffset = 400;
fireEvent.scroll(window);
});
const svg = container.querySelector('svg');
expect(svg).toBeInTheDocument();
expect(svg).toHaveClass('w-6');
expect(svg).toHaveClass('h-6');
});
it('has aria-label for accessibility', () => {
const { container } = render();
// Make button visible
act(() => {
window.pageYOffset = 400;
fireEvent.scroll(window);
});
const button = container.querySelector('button');
expect(button).toHaveAttribute('aria-label', 'Back to top');
});
it('cleans up scroll listener on unmount', () => {
const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener');
const { unmount } = render();
unmount();
expect(removeEventListenerSpy).toHaveBeenCalledWith('scroll', expect.any(Function));
removeEventListenerSpy.mockRestore();
});
});