Skip to content

Commit 6e7b287

Browse files
authored
Add projects & github tests (#682)
1 parent 0b28edb commit 6e7b287

File tree

15 files changed

+1183
-467
lines changed

15 files changed

+1183
-467
lines changed

.github/workflows/playwright.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ jobs:
3333

3434
- name: Install dependencies
3535
run: yarn
36+
37+
- name: Run tests
38+
run: yarn test
3639

3740
- name: Cache Playwright Browsers
3841
uses: actions/cache@v4

__tests__/lib/github.tests.tsx

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import { getProjectContributors, getGithubRepo, getGithubRepoContributors } from "lib/github";
2+
import { GithubContributor, GithubUser, GithubRepo } from "@utils/types";
3+
import { gitAPI } from "lib/git-api-client";
4+
5+
jest.mock("@/lib/git-api-client", () => ({
6+
gitAPI: {
7+
get: jest.fn(),
8+
},
9+
}));
10+
11+
describe("GitHub API Functions", () => {
12+
beforeEach(() => {
13+
jest.clearAllMocks();
14+
});
15+
16+
describe("getProjectContributors", () => {
17+
const mockContributors: GithubContributor[] = [
18+
{
19+
login: "user1",
20+
contributions: 100,
21+
},
22+
{
23+
login: "user2",
24+
contributions: 50,
25+
},
26+
];
27+
28+
const mockUsers: GithubUser[] = [
29+
{
30+
login: "user1",
31+
name: "User One",
32+
html_url: "",
33+
avatar_url: "",
34+
},
35+
{
36+
login: "user2",
37+
name: "User Two",
38+
html_url: "",
39+
avatar_url: "",
40+
},
41+
];
42+
43+
it("should return formatted contributor data", async () => {
44+
// Mock contributors endpoint
45+
(gitAPI.get as jest.Mock)
46+
.mockResolvedValueOnce({ status: 200, data: mockContributors })
47+
// Mock individual user endpoints
48+
.mockResolvedValueOnce({ status: 200, data: mockUsers[0] })
49+
.mockResolvedValueOnce({ status: 200, data: mockUsers[1] });
50+
51+
const result = await getProjectContributors("owner", "repo", 2);
52+
53+
expect(result).toHaveLength(2);
54+
expect(result[0]).toEqual(
55+
expect.objectContaining({
56+
...mockContributors[0],
57+
...mockUsers[0],
58+
})
59+
);
60+
expect(gitAPI.get).toHaveBeenCalledWith("/repos/owner/repo/contributors");
61+
expect(gitAPI.get).toHaveBeenCalledWith("/users/user1");
62+
expect(gitAPI.get).toHaveBeenCalledWith("/users/user2");
63+
});
64+
65+
it("should handle contributor fetch error", async () => {
66+
(gitAPI.get as jest.Mock).mockResolvedValueOnce({
67+
status: 404,
68+
error: "Not Found",
69+
});
70+
71+
await expect(getProjectContributors("owner", "repo")).rejects.toThrow(
72+
"Error fetching contributor data for owner/repo"
73+
);
74+
});
75+
76+
it("should handle user fetch error", async () => {
77+
(gitAPI.get as jest.Mock)
78+
.mockResolvedValueOnce({ status: 200, data: mockContributors })
79+
.mockResolvedValueOnce({ status: 404, error: "User not found" });
80+
81+
await expect(getProjectContributors("owner", "repo")).rejects.toThrow(
82+
"Error fetching user data for user1"
83+
);
84+
});
85+
});
86+
87+
describe("getGithubRepo", () => {
88+
const mockRepo: GithubRepo = {
89+
html_url: "",
90+
stargazers_count: 100,
91+
open_issues_count: 15,
92+
forks_count: 40,
93+
subscribers_count: 300,
94+
};
95+
96+
it("should return repository data", async () => {
97+
(gitAPI.get as jest.Mock).mockResolvedValueOnce({
98+
status: 200,
99+
data: mockRepo,
100+
});
101+
102+
const result = await getGithubRepo("owner", "repo");
103+
104+
expect(result).toEqual(mockRepo);
105+
expect(gitAPI.get).toHaveBeenCalledWith("/repos/owner/repo");
106+
});
107+
108+
it("should handle error response", async () => {
109+
(gitAPI.get as jest.Mock).mockResolvedValueOnce({
110+
status: 404,
111+
error: "Repository not found",
112+
});
113+
114+
await expect(getGithubRepo("owner", "repo")).rejects.toThrow(
115+
"Error fetching repo data for owner/repo"
116+
);
117+
});
118+
119+
it("should handle error response without error message", async () => {
120+
(gitAPI.get as jest.Mock).mockResolvedValueOnce({ status: 500 });
121+
122+
await expect(getGithubRepo("owner", "repo")).rejects.toThrow(
123+
"Error fetching repo data for owner/repo\nStatus code: 500"
124+
);
125+
});
126+
});
127+
128+
describe("getGithubRepoContributors", () => {
129+
const mockContributors: GithubContributor[] = [
130+
{
131+
login: "user1",
132+
contributions: 100,
133+
},
134+
{
135+
login: "user2",
136+
contributions: 50,
137+
},
138+
{
139+
login: "user3",
140+
contributions: 25,
141+
},
142+
];
143+
144+
it("should return contributor list with specified limit", async () => {
145+
(gitAPI.get as jest.Mock).mockResolvedValueOnce({
146+
status: 200,
147+
data: mockContributors,
148+
});
149+
150+
const result = await getGithubRepoContributors("owner", "repo", 2);
151+
152+
expect(result).toHaveLength(2);
153+
expect(result[0]).toEqual(
154+
expect.objectContaining({
155+
login: "user1",
156+
contributions: 100,
157+
})
158+
);
159+
expect(gitAPI.get).toHaveBeenCalledWith("/repos/owner/repo/contributors");
160+
});
161+
162+
it("should use default limit of 4", async () => {
163+
(gitAPI.get as jest.Mock).mockResolvedValueOnce({
164+
status: 200,
165+
data: mockContributors,
166+
});
167+
168+
const result = await getGithubRepoContributors("owner", "repo");
169+
170+
expect(result).toHaveLength(3); // Only 3 mock contributors available
171+
expect(gitAPI.get).toHaveBeenCalledWith("/repos/owner/repo/contributors");
172+
});
173+
174+
it("should handle error response", async () => {
175+
(gitAPI.get as jest.Mock).mockResolvedValueOnce({
176+
status: 404,
177+
error: "Not Found",
178+
});
179+
180+
await expect(getGithubRepoContributors("owner", "repo")).rejects.toThrow(
181+
"Error fetching contributor data for owner/repo"
182+
);
183+
});
184+
});
185+
});

0 commit comments

Comments
 (0)