Skip to content

Commit dee1712

Browse files
committed
test: ShareSheet mouse interactivity — hover, click propagation and touch events
1 parent 41222eb commit dee1712

1 file changed

Lines changed: 118 additions & 0 deletions

File tree

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest';
2+
import { render, screen, fireEvent } from '@testing-library/react';
3+
import ShareSheet from './ShareSheet';
4+
import type { DashboardExportData } from '@/types/dashboard';
5+
6+
vi.mock('framer-motion', () => ({
7+
motion: {
8+
div: ({ children, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
9+
<div {...props}>{children}</div>
10+
),
11+
},
12+
AnimatePresence: ({ children }: { children: React.ReactNode }) => <>{children}</>,
13+
}));
14+
15+
vi.mock('react-qr-code', () => ({
16+
default: ({ value }: { value: string }) => <svg data-testid="qr-code" data-value={value} />,
17+
}));
18+
19+
const mockHandleTwitter = vi.fn();
20+
const mockHandleLinkedIn = vi.fn();
21+
const mockHandleReddit = vi.fn();
22+
const mockHandleNativeShare = vi.fn();
23+
24+
vi.mock('@/hooks/useShareActions', () => ({
25+
useShareActions: () => ({
26+
states: {},
27+
handleTwitter: mockHandleTwitter,
28+
handleLinkedIn: mockHandleLinkedIn,
29+
handleReddit: mockHandleReddit,
30+
handleDownloadPNG: vi.fn(),
31+
handleDownloadWEBP: vi.fn(),
32+
handleDownloadSVG: vi.fn(),
33+
handleCopyMarkdown: vi.fn(),
34+
handleDownloadJSON: vi.fn(),
35+
handleNativeShare: mockHandleNativeShare,
36+
}),
37+
}));
38+
39+
const mockExportData: DashboardExportData = {
40+
stats: { currentStreak: 5, peakStreak: 10, totalContributions: 100 },
41+
languages: [],
42+
activity: [],
43+
};
44+
45+
const onClose = vi.fn();
46+
47+
const defaultProps = {
48+
username: 'octocat',
49+
isOpen: true,
50+
onClose,
51+
exportData: mockExportData,
52+
};
53+
54+
describe('ShareSheet — Interactive Tooltips, Cursor Hovers & Touch Event Propagation', () => {
55+
beforeEach(() => {
56+
vi.clearAllMocks();
57+
});
58+
59+
it('clicking the backdrop overlay propagates the click event to onClose', () => {
60+
const { container } = render(<ShareSheet {...defaultProps} />);
61+
62+
const overlay = container.querySelector('.fixed');
63+
expect(overlay).not.toBeNull();
64+
65+
fireEvent.click(overlay!);
66+
67+
expect(onClose).toHaveBeenCalledTimes(1);
68+
});
69+
70+
it('clicking inside the modal panel stops propagation and does not call onClose', () => {
71+
const { container } = render(<ShareSheet {...defaultProps} />);
72+
73+
const modal = container.querySelector('[class*="max-w-"]');
74+
expect(modal).not.toBeNull();
75+
76+
fireEvent.click(modal!);
77+
78+
expect(onClose).not.toHaveBeenCalled();
79+
});
80+
81+
it('mouseEnter on the QR group renders hover overlay action buttons in the DOM', () => {
82+
const { container } = render(<ShareSheet {...defaultProps} />);
83+
84+
const qrGroup = container.querySelector('.group');
85+
expect(qrGroup).not.toBeNull();
86+
87+
fireEvent.mouseEnter(qrGroup!);
88+
89+
expect(screen.getByText('Copy Image')).toBeTruthy();
90+
expect(screen.getByText('Save File')).toBeTruthy();
91+
});
92+
93+
it('mouseleave on the QR group leaves the hover overlay in its opacity-0 hidden state', () => {
94+
const { container } = render(<ShareSheet {...defaultProps} />);
95+
96+
const qrGroup = container.querySelector('.group');
97+
expect(qrGroup).not.toBeNull();
98+
99+
const hoverOverlay = qrGroup!.querySelector('.opacity-0');
100+
expect(hoverOverlay).not.toBeNull();
101+
102+
fireEvent.mouseLeave(qrGroup!);
103+
104+
expect(hoverOverlay!.classList.contains('opacity-0')).toBe(true);
105+
});
106+
107+
it('touch events on the Share on X button do not block the click handler from firing', () => {
108+
render(<ShareSheet {...defaultProps} />);
109+
110+
const xButton = screen.getByRole('button', { name: /share on x/i });
111+
112+
fireEvent.touchStart(xButton);
113+
fireEvent.touchEnd(xButton);
114+
fireEvent.click(xButton);
115+
116+
expect(mockHandleTwitter).toHaveBeenCalledTimes(1);
117+
});
118+
});

0 commit comments

Comments
 (0)