Skip to content

Commit 00b86f7

Browse files
authored
test(contributors): add error resilience coverage (JhaSourav07#3091)
## Description Fixes JhaSourav07#2858 Added isolated unit and integration tests for `app/contributors/page.tsx` focusing on hydration stability, exception safety, and error fallback handling. ### Changes - Added `app/contributors/page.error-resilience.test.tsx` - Mocked `ContributorsClient` to isolate page-level behavior - Verified successful rendering under normal conditions - Tested graceful handling of contributor fetch failures - Verified GitHub API rate-limit responses do not crash rendering - Confirmed empty contributor collections are propagated during fallback scenarios - Ensured unexpected service exceptions are handled without breaking page rendering - Improved coverage around resilience, recovery paths, and server-side error handling ## Pillar - [ ] 🎨 Pillar 1 — New Theme Design - [ ] 📐 Pillar 2 — Geometric SVG Improvement - [ ] 🕐 Pillar 3 — Timezone Logic Optimization - [x] 🛠️ Other (Bug fix, refactoring, docs) ## Visual Preview N/A (test-only changes) ## Checklist before requesting a review: - [x] I have read the `CONTRIBUTING.md` file. - [x] I have tested these changes locally. - [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): ...`). - [ ] I have updated `README.md` if I added a new theme or URL parameter. - [x] I have starred 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 SVG changes made). - [ ] (Recommended) I joined the CommitPulse Discord community for contributor discussions, mentorship, and faster PR support.
2 parents a14e9ad + d05d95a commit 00b86f7

1 file changed

Lines changed: 114 additions & 0 deletions

File tree

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { beforeEach, describe, expect, it, vi } from 'vitest';
2+
import { render, screen } from '@testing-library/react';
3+
import '@testing-library/jest-dom';
4+
5+
type ContributorsClientProps = {
6+
contributors: unknown[];
7+
totalContributions: number;
8+
topContributors: unknown[];
9+
};
10+
11+
const mockContributorsClient = vi.fn((_props?: ContributorsClientProps) => (
12+
<div data-testid="contributors-client">Contributors Client</div>
13+
));
14+
15+
vi.mock('./ContributorsClient', () => ({
16+
default: (props: ContributorsClientProps) => mockContributorsClient(props),
17+
}));
18+
19+
describe('ContributorsPage Error Resilience', () => {
20+
beforeEach(() => {
21+
vi.clearAllMocks();
22+
});
23+
24+
it('renders successfully under normal conditions', async () => {
25+
const { default: ContributorsPage } = await import('./page');
26+
27+
const page = await ContributorsPage();
28+
29+
render(page);
30+
31+
expect(screen.getByTestId('contributors-client')).toBeInTheDocument();
32+
});
33+
34+
it('handles failed contributor fetches without crashing', async () => {
35+
const originalFetch = global.fetch;
36+
37+
global.fetch = vi.fn().mockResolvedValue({
38+
ok: false,
39+
status: 500,
40+
headers: {
41+
get: () => null,
42+
},
43+
} as unknown as Response);
44+
45+
const { default: ContributorsPage } = await import('./page');
46+
47+
const page = await ContributorsPage();
48+
49+
render(page);
50+
51+
expect(screen.getByTestId('contributors-client')).toBeInTheDocument();
52+
53+
global.fetch = originalFetch;
54+
});
55+
56+
it('handles rate limit responses gracefully', async () => {
57+
const originalFetch = global.fetch;
58+
59+
global.fetch = vi.fn().mockResolvedValue({
60+
ok: false,
61+
status: 429,
62+
headers: {
63+
get: (name: string) => (name === 'x-ratelimit-remaining' ? '0' : null),
64+
},
65+
} as unknown as Response);
66+
67+
const { default: ContributorsPage } = await import('./page');
68+
69+
const page = await ContributorsPage();
70+
71+
render(page);
72+
73+
expect(screen.getByTestId('contributors-client')).toBeInTheDocument();
74+
75+
global.fetch = originalFetch;
76+
});
77+
78+
it('passes empty contributor collections after fetch failures', async () => {
79+
const originalFetch = global.fetch;
80+
81+
global.fetch = vi.fn().mockRejectedValue(new Error('Database unavailable'));
82+
83+
const { default: ContributorsPage } = await import('./page');
84+
85+
const page = await ContributorsPage();
86+
87+
render(page);
88+
89+
expect(mockContributorsClient).toHaveBeenCalled();
90+
91+
const props = mockContributorsClient.mock.calls[0][0] as ContributorsClientProps;
92+
93+
expect(props.contributors).toEqual([]);
94+
expect(props.topContributors).toEqual([]);
95+
96+
global.fetch = originalFetch;
97+
});
98+
99+
it('continues rendering when unexpected service exceptions occur', async () => {
100+
const originalFetch = global.fetch;
101+
102+
global.fetch = vi.fn().mockRejectedValue(new Error('Unexpected runtime error'));
103+
104+
const { default: ContributorsPage } = await import('./page');
105+
106+
const page = await ContributorsPage();
107+
108+
render(page);
109+
110+
expect(screen.getByTestId('contributors-client')).toBeInTheDocument();
111+
112+
global.fetch = originalFetch;
113+
});
114+
});

0 commit comments

Comments
 (0)