Skip to content

Commit 16ff47f

Browse files
authored
test(compare): add CompareClient test coverage and fix contribution score assertions (JhaSourav07#3089)
## Description Adds comprehensive test coverage for CompareClient and fixes failing assertions caused by formatted numeric values being rendered with toLocaleString(). ## Changes - Added app/compare/CompareClient.test.tsx - Added test coverage for: - Page rendering - Username input updates - Comparison result rendering - Route updates via router.replace - API error handling - Mocked: - next/navigation - framer-motion - fetch - Updated contribution assertions to match rendered values (5,000 / 3,000) instead of raw values (5000 / 3000) Fixes JhaSourav07#2283 ## Pillar - [x] 📐 Pillar 2 — Geometric SVG Improvement - [x] 🛠️ Other (Bug fix, refactoring, docs) ## Checklist before requesting a review: - [x] I have read the `CONTRIBUTING.md` file. - [x] I have tested these changes locally (`localhost:3000/api/streak?user=YOUR_USERNAME`). - [x] I have run `npm run format` and `npm run lint` locally and resolved all errors (CI will fail otherwise). - [x] My commits follow the Conventional Commits format (e.g., `feat(themes): ...`, `fix(calculate): ...`). - [x] I have updated `README.md` if I added a new theme or URL parameter. - [x] I have started the repo. - [x] I have made sure that i have only one commit to merge in this PR. - [x] The SVG output matches the CommitPulse "premium quality" aesthetic standard (no raw elements, smooth animations, correct fonts). - [x] (Recommended) I joined the CommitPulse Discord community for contributor discussions, mentorship, and faster PR support.
2 parents 0ab4cc2 + 5055dd6 commit 16ff47f

1 file changed

Lines changed: 206 additions & 0 deletions

File tree

app/compare/CompareClient.test.tsx

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
2+
import '@testing-library/jest-dom/vitest';
3+
import { describe, it, expect, vi, beforeEach } from 'vitest';
4+
import CompareClient from './CompareClient';
5+
import React, { type ReactNode } from 'react';
6+
7+
const replaceMock = vi.fn();
8+
9+
vi.mock('next/navigation', () => ({
10+
useRouter: () => ({
11+
replace: replaceMock,
12+
}),
13+
useSearchParams: () => ({
14+
get: vi.fn(() => null),
15+
}),
16+
}));
17+
18+
vi.mock('framer-motion', () => ({
19+
motion: new Proxy(
20+
{},
21+
{
22+
get: (_, tag) => {
23+
return ({ children, ...props }: { children?: ReactNode; [key: string]: unknown }) =>
24+
React.createElement(tag as string, props, children);
25+
},
26+
}
27+
),
28+
AnimatePresence: ({ children }: { children?: ReactNode }) => <>{children}</>,
29+
}));
30+
31+
const mockResponse = {
32+
user1: {
33+
profile: {
34+
username: 'userA',
35+
name: 'User A',
36+
avatarUrl: 'avatar-a.png',
37+
isPro: true,
38+
bio: 'Frontend Developer',
39+
location: 'India',
40+
joinedDate: '2023',
41+
developerScore: 90,
42+
stats: {
43+
repositories: 100,
44+
followers: 200,
45+
following: 50,
46+
stars: 500,
47+
},
48+
},
49+
stats: {
50+
currentStreak: 50,
51+
peakStreak: 100,
52+
totalContributions: 5000,
53+
codingHabit: 'Night Owl',
54+
},
55+
languages: [
56+
{
57+
name: 'TypeScript',
58+
color: '#3178c6',
59+
percentage: 80,
60+
},
61+
],
62+
activity: [],
63+
},
64+
65+
user2: {
66+
profile: {
67+
username: 'userB',
68+
name: 'User B',
69+
avatarUrl: 'avatar-b.png',
70+
isPro: false,
71+
bio: 'Backend Developer',
72+
location: 'USA',
73+
joinedDate: '2022',
74+
developerScore: 80,
75+
stats: {
76+
repositories: 80,
77+
followers: 100,
78+
following: 40,
79+
stars: 300,
80+
},
81+
},
82+
stats: {
83+
currentStreak: 30,
84+
peakStreak: 70,
85+
totalContributions: 3000,
86+
codingHabit: 'Early Bird',
87+
},
88+
languages: [
89+
{
90+
name: 'JavaScript',
91+
color: '#f7df1e',
92+
percentage: 70,
93+
},
94+
],
95+
activity: [],
96+
},
97+
};
98+
99+
describe('CompareClient', () => {
100+
beforeEach(() => {
101+
vi.clearAllMocks();
102+
103+
global.fetch = vi.fn(
104+
async () =>
105+
({
106+
ok: true,
107+
json: async () => mockResponse,
108+
}) as Response
109+
);
110+
});
111+
112+
it('renders comparison page', () => {
113+
render(<CompareClient />);
114+
115+
expect(
116+
screen.getByRole('heading', {
117+
name: /compare developers/i,
118+
})
119+
).toBeInTheDocument();
120+
});
121+
122+
it('allows usernames to be modified via controls', () => {
123+
render(<CompareClient />);
124+
125+
const user1 = screen.getByPlaceholderText(/github username #1/i);
126+
const user2 = screen.getByPlaceholderText(/github username #2/i);
127+
128+
fireEvent.change(user1, {
129+
target: { value: 'userA' },
130+
});
131+
132+
fireEvent.change(user2, {
133+
target: { value: 'userB' },
134+
});
135+
136+
expect(user1).toHaveValue('userA');
137+
expect(user2).toHaveValue('userB');
138+
});
139+
140+
it('renders comparative scores correctly', async () => {
141+
render(<CompareClient />);
142+
143+
fireEvent.change(screen.getByPlaceholderText(/github username #1/i), {
144+
target: { value: 'userA' },
145+
});
146+
147+
fireEvent.change(screen.getByPlaceholderText(/github username #2/i), {
148+
target: { value: 'userB' },
149+
});
150+
151+
fireEvent.click(screen.getByRole('button', { name: /compare/i }));
152+
153+
await waitFor(() => {
154+
expect(screen.getByText(/stats showdown/i)).toBeInTheDocument();
155+
});
156+
157+
expect(screen.getByText('5,000')).toBeInTheDocument();
158+
expect(screen.getByText('3,000')).toBeInTheDocument();
159+
});
160+
161+
it('updates route when compare button is clicked', async () => {
162+
render(<CompareClient />);
163+
164+
fireEvent.change(screen.getByPlaceholderText(/github username #1/i), {
165+
target: { value: 'userA' },
166+
});
167+
168+
fireEvent.change(screen.getByPlaceholderText(/github username #2/i), {
169+
target: { value: 'userB' },
170+
});
171+
172+
fireEvent.click(screen.getByRole('button', { name: /compare/i }));
173+
174+
await waitFor(() => {
175+
expect(replaceMock).toHaveBeenCalledWith('/compare?user1=userA&user2=userB', {
176+
scroll: false,
177+
});
178+
});
179+
});
180+
181+
it('shows error message when api request fails', async () => {
182+
global.fetch = vi.fn(
183+
async () =>
184+
({
185+
ok: false,
186+
json: async () => ({
187+
error: 'Failed to fetch comparison data.',
188+
}),
189+
}) as Response
190+
);
191+
192+
render(<CompareClient />);
193+
194+
fireEvent.change(screen.getByPlaceholderText(/github username #1/i), {
195+
target: { value: 'userA' },
196+
});
197+
198+
fireEvent.change(screen.getByPlaceholderText(/github username #2/i), {
199+
target: { value: 'userB' },
200+
});
201+
202+
fireEvent.click(screen.getByRole('button', { name: /compare/i }));
203+
204+
expect(await screen.findByText(/failed to fetch comparison data/i)).toBeInTheDocument();
205+
});
206+
});

0 commit comments

Comments
 (0)