Skip to content

Commit 59884c9

Browse files
authored
test(CopyRepoButton): add component tests (JhaSourav07#3545)
- Test rendering and initial state - Verify clipboard API is called with correct URL - Test success state label change - Verify timeout reset behavior - Test error handling for clipboard failures All 5 test cases passing ## Description Fixes JhaSourav07#2284 ## Pillar - [ ] Pillar 1 — New Theme Design - [ ] Pillar 2 — Geometric SVG Improvement - [ ] Pillar 3 — Timezone Logic Optimization - [ ] Other (Bug fix, refactoring, docs) ## Visual Preview ## Checklist before requesting a review: - [ ] I have read the `CONTRIBUTING.md` file. - [ ] I have tested these changes locally (`localhost:3000/api/streak?user=YOUR_USERNAME`). - [ ] I have run `npm run format` and `npm run lint` locally and resolved all errors (CI will fail otherwise). - [ ] My commits follow the Conventional Commits format (e.g., `feat(themes): ...`, `fix(calculate): ...`). - [ ] I have updated `README.md` if I added a new theme or URL parameter. - [ ] I have started the repo. - [ ] I have made sure that i have only one commit to merge in this PR. - [ ] The SVG output matches the CommitPulse "premium quality" aesthetic standard (no raw elements, smooth animations, correct fonts). - [ ] (Recommended) I joined the CommitPulse Discord community for contributor discussions, mentorship, and faster PR support.
2 parents 6ea7a5b + cf92fa8 commit 59884c9

1 file changed

Lines changed: 109 additions & 0 deletions

File tree

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2+
import { render, screen, fireEvent, act } from '@testing-library/react';
3+
import CopyRepoButton, { REPO_URL } from './CopyRepoButton';
4+
5+
describe('CopyRepoButton', () => {
6+
beforeEach(() => {
7+
vi.useFakeTimers();
8+
});
9+
10+
afterEach(() => {
11+
vi.restoreAllMocks();
12+
vi.useRealTimers();
13+
});
14+
15+
// Test 1: Rendering and initial state
16+
it('renders with default copy text', () => {
17+
render(<CopyRepoButton />);
18+
const button = screen.getByRole('button');
19+
expect(button).toBeInTheDocument();
20+
expect(button).toHaveTextContent('Copy URL');
21+
});
22+
23+
// Test 2: Clipboard API is called with correct URL
24+
it('copies repository URL to clipboard when clicked', async () => {
25+
const writeText = vi.fn().mockResolvedValue(undefined);
26+
Object.assign(navigator, {
27+
clipboard: { writeText },
28+
});
29+
30+
render(<CopyRepoButton />);
31+
const button = screen.getByRole('button');
32+
33+
await act(async () => {
34+
fireEvent.click(button);
35+
});
36+
37+
expect(writeText).toHaveBeenCalledTimes(1);
38+
expect(writeText).toHaveBeenCalledWith(REPO_URL);
39+
});
40+
41+
// Test 3: Button label changes to 'Copied!'
42+
it('changes button label to Copied! upon successful copy', async () => {
43+
const writeText = vi.fn().mockResolvedValue(undefined);
44+
Object.assign(navigator, {
45+
clipboard: { writeText },
46+
});
47+
48+
render(<CopyRepoButton />);
49+
const button = screen.getByRole('button');
50+
51+
await act(async () => {
52+
fireEvent.click(button);
53+
// Wait for the promise to resolve and state to update
54+
await Promise.resolve();
55+
});
56+
57+
expect(button).toHaveTextContent('Copied!');
58+
});
59+
60+
// Test 4: Button reverts after timeout
61+
it('reverts to normal state after timeout duration', async () => {
62+
const writeText = vi.fn().mockResolvedValue(undefined);
63+
Object.assign(navigator, {
64+
clipboard: { writeText },
65+
});
66+
67+
render(<CopyRepoButton />);
68+
const button = screen.getByRole('button');
69+
70+
await act(async () => {
71+
fireEvent.click(button);
72+
await Promise.resolve();
73+
});
74+
75+
expect(button).toHaveTextContent('Copied!');
76+
77+
await act(async () => {
78+
vi.advanceTimersByTime(2000);
79+
});
80+
81+
expect(button).toHaveTextContent('Copy URL');
82+
});
83+
84+
// Test 5: Handle clipboard failure
85+
it('handles clipboard failure gracefully', async () => {
86+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
87+
const writeText = vi.fn().mockRejectedValue(new Error('Clipboard permission denied'));
88+
Object.assign(navigator, {
89+
clipboard: { writeText },
90+
});
91+
92+
render(<CopyRepoButton />);
93+
const button = screen.getByRole('button');
94+
95+
await act(async () => {
96+
fireEvent.click(button);
97+
await Promise.resolve();
98+
});
99+
100+
expect(button).toHaveTextContent('Copy failed');
101+
102+
await act(async () => {
103+
vi.advanceTimersByTime(2000);
104+
});
105+
106+
expect(button).toHaveTextContent('Copy URL');
107+
consoleSpy.mockRestore();
108+
});
109+
});

0 commit comments

Comments
 (0)