// @vitest-environment jsdom
import { render, fireEvent, act } from '@testing-library/react';
import { FancyCursor } from './FancyCursor';
import { describe, it, expect, vi, beforeAll, afterAll, beforeEach } from 'vitest';
import React from 'react';
describe('FancyCursor', () => {
beforeAll(() => {
// Mock elementFromPoint for JSDOM
// @ts-ignore
document.elementFromPoint = vi.fn((x, y) => null);
// Mock requestAnimationFrame
vi.useFakeTimers();
});
afterAll(() => {
vi.useRealTimers();
// @ts-ignore
delete document.elementFromPoint;
});
beforeEach(() => {
vi.clearAllMocks();
// Ensure we are not seen as a touch device
Object.defineProperty(navigator, 'maxTouchPoints', { value: 0, writable: true });
// @ts-ignore
delete window.ontouchstart;
// console.log('Test Setup:', { ontouchstart: 'ontouchstart' in window, maxTouchPoints: navigator.maxTouchPoints });
});
it('renders and updates position on mousemove', () => {
const { container } = render();
// Using firstElementChild because querySelector seems flaky in this test environment
const cursor = container.firstElementChild as HTMLElement;
expect(cursor).toBeTruthy();
expect(cursor.id).toBe('fancy-cursor');
fireEvent.mouseMove(window, { clientX: 100, clientY: 100 });
act(() => {
vi.runAllTimers();
});
// Check transform
expect(cursor.style.transform).toContain('translate3d(100px, 100px, 0)');
});
it('toggles link-hover class when hovering a link', () => {
// We need to attach the link to the document body because FancyCursor uses global event listeners
// and checks the document structure via events or elementFromPoint.
const link = document.createElement('a');
link.href = '#';
link.id = 'test-link';
link.textContent = 'Link';
document.body.appendChild(link);
const { container } = render();
const cursor = container.firstElementChild as HTMLElement;
expect(cursor).toBeTruthy();
// Simulate mouse over the link.
// Important: in our optimized code, we rely on 'mouseover' event to set 'hoveredElement'.
fireEvent.mouseOver(link);
// Then mouseMove triggers the update loop
fireEvent.mouseMove(window, { clientX: 50, clientY: 50 });
act(() => {
vi.runAllTimers();
});
expect(cursor.classList.contains('link-hover')).toBe(true);
// Move away to body
fireEvent.mouseOver(document.body);
fireEvent.mouseMove(window, { clientX: 200, clientY: 200 });
act(() => {
vi.runAllTimers();
});
expect(cursor.classList.contains('link-hover')).toBe(false);
document.body.removeChild(link);
});
it('does not call elementFromPoint on mousemove', () => {
// Reset elementFromPoint mock
const mockElementFromPoint = vi.fn();
// @ts-ignore
document.elementFromPoint = mockElementFromPoint;
render();
// Trigger mousemove
fireEvent.mouseMove(window, { clientX: 10, clientY: 10 });
act(() => {
vi.runAllTimers();
});
// Should NOT be called in the optimized version
expect(mockElementFromPoint).not.toHaveBeenCalled();
});
});