Skip to content

Commit cf92fa8

Browse files
committed
test(CopyRepoButton): add component tests
- 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
1 parent 41222eb commit cf92fa8

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)