import { fireEvent, render, screen } from '@testing-library/react'
import { BackToTop } from '@/components/ui/back-to-top'
// Mock window.scrollTo
const mockScrollTo = jest.fn()
Object.defineProperty(window, 'scrollTo', {
value: mockScrollTo,
writable: true
})
/**
* Helper to trigger scroll events and mock document/window properties
* @param scrollY - Current scroll position
* @param pageHeight - Total page height
* @param viewportHeight - Viewport height
*/
const mockScrollEvent = (scrollY: number, pageHeight: number, viewportHeight: number) => {
Object.defineProperty(window, 'scrollY', { value: scrollY, configurable: true })
Object.defineProperty(document.documentElement, 'scrollHeight', {
value: pageHeight,
configurable: true
})
Object.defineProperty(window, 'innerHeight', { value: viewportHeight, configurable: true })
fireEvent.scroll(window)
}
describe('BackToTop', () => {
beforeEach(() => {
jest.clearAllMocks()
// Reset scroll position
Object.defineProperty(window, 'scrollY', { value: 0, configurable: true })
})
it('should not render when page is short', () => {
render()
// Simulate short page (not 1.5x viewport height)
mockScrollEvent(500, 800, 800)
expect(screen.queryByRole('button')).not.toBeInTheDocument()
})
it('should not render when user has not scrolled enough', () => {
render()
// Simulate long page but low scroll position
mockScrollEvent(200, 2000, 800)
expect(screen.queryByRole('button')).not.toBeInTheDocument()
})
it('should render when page is long and user has scrolled past threshold', () => {
render()
// Simulate long page and sufficient scroll
mockScrollEvent(500, 2000, 800)
expect(screen.getByRole('button')).toBeInTheDocument()
expect(screen.getByLabelText('Back to top')).toBeInTheDocument()
})
it('should scroll to top when clicked', () => {
render()
// Make button visible
mockScrollEvent(500, 2000, 800)
const button = screen.getByRole('button')
fireEvent.click(button)
expect(mockScrollTo).toHaveBeenCalledWith({
top: 0,
behavior: 'smooth'
})
})
it('should hide when scrolling back up', () => {
render()
// Make button visible
mockScrollEvent(500, 2000, 800)
expect(screen.getByRole('button')).toBeInTheDocument()
// Scroll back up
mockScrollEvent(200, 2000, 800)
expect(screen.queryByRole('button')).not.toBeInTheDocument()
})
it('should have correct accessibility attributes', () => {
render()
// Make button visible
mockScrollEvent(500, 2000, 800)
const button = screen.getByRole('button')
expect(button).toHaveAttribute('aria-label', 'Back to top')
})
})