Skip to content

Commit 547a2a1

Browse files
committed
unit testing for question api functions
1 parent 62a72a9 commit 547a2a1

File tree

6 files changed

+357
-6
lines changed

6 files changed

+357
-6
lines changed

apps/frontend/__tests__/Datetime.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { describe, expect, it } from "@jest/globals"
21
import { formatTime } from "@/utils/DateTime"
32

43
describe("datetime module", () => {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { GetQuestions } from "@/app/services/question"
2+
import { getToken } from "@/app/services/login-store";
3+
4+
const TOKEN = 'mocktoken';
5+
6+
jest.mock("@/app/services/login-store", () => {
7+
return {
8+
__esModule: true,
9+
getToken: jest.fn(() => TOKEN)
10+
};
11+
})
12+
13+
beforeEach(() => {
14+
global.fetch = jest.fn().mockResolvedValue({
15+
async json() {
16+
return {}
17+
}
18+
});
19+
})
20+
21+
describe("mock", () => {
22+
23+
it("mocks correctly", async () => {
24+
await GetQuestions()
25+
expect(jest.mocked(getToken).mock.calls).toEqual([[]])
26+
expect(jest.mocked(fetch).mock.calls[0][1]).toEqual({
27+
"headers": {
28+
"Authorization": `Bearer ${TOKEN}`,
29+
},
30+
"method": "GET",
31+
})
32+
});
33+
34+
})
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
import { CreateQuestion, DeleteQuestion, GetQuestions, GetSingleQuestion, Question } from "@/app/services/question"
2+
3+
const NEXT_PUBLIC_QUESTION_SERVICE_URL = process.env.NEXT_PUBLIC_QUESTION_SERVICE_URL
4+
5+
const QUESTIONS = [
6+
{
7+
"id": 12345,
8+
"docRefId": "asdfDocRef",
9+
"title": "Asdf",
10+
"description": "Asdf description",
11+
"categories": ["Arrays", "Algorithms"],
12+
"complexity": "hard"
13+
},
14+
{
15+
"id": 12346,
16+
"docRefId": "qwerDocRef",
17+
"title": "Qwer",
18+
"description": "Qwer description",
19+
"categories": ["Strings", "Data Structures"],
20+
"complexity": "medium"
21+
},
22+
{
23+
"id": 12347,
24+
"docRefId": "zxcvDocRef",
25+
"title": "Zxcv",
26+
"description": "Zxcv description",
27+
"categories": ["Graphs", "Algorithms"],
28+
"complexity": "easy"
29+
},
30+
{
31+
"id": 12348,
32+
"docRefId": "tyuiDocRef",
33+
"title": "Tyui",
34+
"description": "Tyui description",
35+
"categories": ["Trees", "Recursion"],
36+
"complexity": "hard"
37+
},
38+
{
39+
"id": 12349,
40+
"docRefId": "ghjkDocRef",
41+
"title": "Ghjk",
42+
"description": "Ghjk description",
43+
"categories": ["Dynamic Programming", "Math"],
44+
"complexity": "medium"
45+
},
46+
{
47+
"id": 12350,
48+
"docRefId": "bnmlDocRef",
49+
"title": "Bnml",
50+
"description": "Bnml description",
51+
"categories": ["Sorting", "Searching"],
52+
"complexity": "easy"
53+
},
54+
{
55+
"id": 12351,
56+
"docRefId": "poiuDocRef",
57+
"title": "Poiu",
58+
"description": "Poiu description",
59+
"categories": ["Bit Manipulation", "Algorithms"],
60+
"complexity": "hard"
61+
},
62+
{
63+
"id": 12352,
64+
"docRefId": "lkjhDocRef",
65+
"title": "Lkjh",
66+
"description": "Lkjh description",
67+
"categories": ["Greedy", "Data Structures"],
68+
"complexity": "medium"
69+
},
70+
{
71+
"id": 12353,
72+
"docRefId": "mnbvDocRef",
73+
"title": "Mnbv",
74+
"description": "Mnbv description",
75+
"categories": ["Backtracking", "Recursion"],
76+
"complexity": "easy"
77+
},
78+
{
79+
"id": 12354,
80+
"docRefId": "vcxzDocRef",
81+
"title": "Vcxz",
82+
"description": "Vcxz description",
83+
"categories": ["Graphs", "Dynamic Programming"],
84+
"complexity": "hard"
85+
}
86+
]
87+
88+
89+
function createMockResponse(obj: any): Response {
90+
// @ts-ignore don't need the whole response
91+
return {
92+
json: () => Promise.resolve(obj),
93+
ok: true,
94+
status: 200
95+
}
96+
}
97+
98+
const TOKEN = "mockjwttoken"
99+
100+
beforeEach(() => {
101+
window.localStorage["TOKEN"] = TOKEN
102+
})
103+
104+
describe("GetQuestions", () => {
105+
beforeEach(() => {
106+
global.fetch = jest.fn().mockResolvedValue({
107+
async json() {
108+
return QUESTIONS
109+
}
110+
});
111+
})
112+
113+
it("gets all questions on the first page with () call", async () => {
114+
115+
const res = await GetQuestions()
116+
117+
expect(jest.mocked(fetch).mock.calls).toStrictEqual([[`${NEXT_PUBLIC_QUESTION_SERVICE_URL}questions`, {
118+
headers: {
119+
"Authorization": `Bearer ${TOKEN}`,
120+
},
121+
method: "GET",
122+
}]])
123+
expect(res).toStrictEqual(QUESTIONS)
124+
125+
});
126+
127+
it("gets all questions on the 2nd page with (2) call", async () => {
128+
129+
const res = await GetQuestions(2)
130+
131+
expect(jest.mocked(fetch).mock.calls).toStrictEqual([[`${NEXT_PUBLIC_QUESTION_SERVICE_URL}questions?offset=10`, {
132+
headers: {
133+
"Authorization": `Bearer ${TOKEN}`,
134+
},
135+
method: "GET",
136+
}]])
137+
});
138+
139+
it("gets all questions on the 2nd page with (limit=3) call", async () => {
140+
141+
await GetQuestions(undefined, 3)
142+
143+
expect(jest.mocked(fetch).mock.calls).toStrictEqual([[`${NEXT_PUBLIC_QUESTION_SERVICE_URL}questions?limit=3`, {
144+
headers: {
145+
"Authorization": `Bearer ${TOKEN}`,
146+
},
147+
method: "GET",
148+
}]])
149+
});
150+
151+
it("gets all questions on the 2nd page with (limit=3) call", async () => {
152+
153+
await GetQuestions(undefined, undefined, "difficulty asc")
154+
155+
expect(jest.mocked(fetch).mock.calls).toStrictEqual([[`${NEXT_PUBLIC_QUESTION_SERVICE_URL}questions?sortField=difficulty&sortValue=asc`, {
156+
headers: {
157+
"Authorization": `Bearer ${TOKEN}`,
158+
},
159+
method: "GET",
160+
}]])
161+
});
162+
163+
it("gets all questions on the 2nd page with (limit=3) call", async () => {
164+
165+
await GetQuestions(undefined, undefined, undefined, ["easy", "hard"])
166+
167+
expect(jest.mocked(fetch).mock.calls).toStrictEqual([[`${NEXT_PUBLIC_QUESTION_SERVICE_URL}questions?complexity=easy,hard`, {
168+
headers: {
169+
"Authorization": `Bearer ${TOKEN}`,
170+
},
171+
method: "GET",
172+
}]])
173+
});
174+
175+
it("formats urls for categories", async () => {
176+
177+
await GetQuestions(undefined, undefined, undefined, undefined, ["CatA", "CatB"])
178+
179+
expect(jest.mocked(fetch).mock.calls).toStrictEqual([[
180+
`${NEXT_PUBLIC_QUESTION_SERVICE_URL}questions?categories=CatA,CatB`,
181+
{
182+
headers: {
183+
"Authorization": `Bearer ${TOKEN}`,
184+
},
185+
method: "GET",
186+
}
187+
]])
188+
});
189+
190+
it("formats url for title", async () => {
191+
192+
await GetQuestions(undefined, undefined, undefined, undefined, undefined, "The Title Name")
193+
194+
expect(jest.mocked(fetch).mock.calls).toStrictEqual([[
195+
`${NEXT_PUBLIC_QUESTION_SERVICE_URL}questions?title=The%20Title%20Name`,
196+
{
197+
headers: {
198+
"Authorization": `Bearer ${TOKEN}`,
199+
},
200+
method: "GET",
201+
}
202+
]])
203+
});
204+
})
205+
206+
207+
describe("GetSingleQuestion", () => {
208+
const DOCREF = "mockdocref";
209+
beforeEach(() => {
210+
global.fetch = jest.fn().mockResolvedValue({
211+
async json() {
212+
return QUESTIONS[0]
213+
}
214+
});
215+
});
216+
217+
it("gets a question by docref", async () => {
218+
const res = await GetSingleQuestion(DOCREF);
219+
220+
expect(jest.mocked(fetch).mock.calls).toStrictEqual([[`${NEXT_PUBLIC_QUESTION_SERVICE_URL}questions/${DOCREF}`, {
221+
headers: {
222+
"Authorization": `Bearer ${TOKEN}`,
223+
},
224+
method: "GET",
225+
}]])
226+
expect(res).toStrictEqual(QUESTIONS[0]);
227+
})
228+
})
229+
230+
describe("CreateQuestion", () => {
231+
it("uploads a question", async () => {
232+
// grabs a subset of QUESTIONS[0]
233+
const newQuestion = (({title, description, categories, complexity}) => ({title, description, categories, complexity}))(QUESTIONS[0])
234+
const createdQuestion = QUESTIONS[0];
235+
236+
global.fetch = jest.fn().mockResolvedValue({
237+
status: 200,
238+
statusText: "OK",
239+
async json() {
240+
return createdQuestion
241+
}
242+
});
243+
244+
const res = await CreateQuestion(newQuestion);
245+
246+
expect(jest.mocked(fetch).mock.calls).toStrictEqual([[`${NEXT_PUBLIC_QUESTION_SERVICE_URL}questions`, {
247+
headers: {
248+
"Content-Type": "application/json",
249+
"Authorization": `Bearer ${TOKEN}`,
250+
},
251+
method: "POST",
252+
body: JSON.stringify(newQuestion)
253+
}]])
254+
expect(res).toStrictEqual(createdQuestion);
255+
})
256+
257+
it("fails uploading question", async () => {
258+
// grabs a subset of QUESTIONS[0]
259+
const newQuestion = (({title, description, categories, complexity}) => ({title, description, categories, complexity}))(QUESTIONS[0])
260+
261+
global.fetch = jest.fn().mockResolvedValue({
262+
status: 400,
263+
statusText: "Not Found",
264+
data: "Question title already exists"
265+
})
266+
267+
const res = CreateQuestion(newQuestion);
268+
269+
expect(jest.mocked(fetch).mock.calls).toStrictEqual([[`${NEXT_PUBLIC_QUESTION_SERVICE_URL}questions`, {
270+
headers: {
271+
"Content-Type": "application/json",
272+
"Authorization": `Bearer ${TOKEN}`,
273+
},
274+
method: "POST",
275+
body: JSON.stringify(newQuestion)
276+
}]])
277+
await expect(res).rejects.toThrow("Error creating question: 400 Not Found")
278+
})
279+
280+
281+
})
282+
283+
284+
describe("DeleteQuestion", () => {
285+
const DOCREF = "mockdocref";
286+
beforeEach(() => {
287+
global.fetch = jest.fn().mockResolvedValue({
288+
status: 200,
289+
statusText: "OK",
290+
data: `Question with ID ${DOCREF} deleted successfully`
291+
});
292+
});
293+
294+
it("deletes successfully", async () => {
295+
const shouldbeNothing = await DeleteQuestion(DOCREF);
296+
297+
expect(jest.mocked(fetch).mock.calls).toStrictEqual([[
298+
`${NEXT_PUBLIC_QUESTION_SERVICE_URL}questions/${DOCREF}`,
299+
{
300+
headers: {
301+
"Authorization": `Bearer ${TOKEN}`,
302+
},
303+
method: "DELETE",
304+
}
305+
]])
306+
expect(shouldbeNothing).toBeUndefined();
307+
})
308+
})

apps/frontend/jest.config.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ const config: Config = {
9696
// ],
9797

9898
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
99-
// moduleNameMapper: {},
99+
moduleNameMapper: {
100+
'^@/(.*)$': '<rootDir>/src/$1',
101+
},
100102

101103
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
102104
// modulePathIgnorePatterns: [],

apps/frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@
3333
"yjs": "^13.6.20"
3434
},
3535
"devDependencies": {
36-
"@jest/globals": "^29.7.0",
3736
"@testing-library/dom": "^10.4.0",
3837
"@testing-library/jest-dom": "^6.6.3",
3938
"@testing-library/react": "^16.0.1",
39+
"@types/jest": "^29.5.14",
4040
"@types/node": "^20",
4141
"@types/react": "^18.3.8",
4242
"@types/react-dom": "^18.3.0",

0 commit comments

Comments
 (0)