Skip to content

Commit cd7f486

Browse files
committed
Add integration tests and more unit tests
1 parent f8adc57 commit cd7f486

File tree

11 files changed

+867
-97
lines changed

11 files changed

+867
-97
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import request from 'supertest';
2+
import { describe, expect, it, test } from "@jest/globals";
3+
import app from "../src/index";
4+
5+
describe('Authentication API', () => {
6+
// The unit tests below are currently commented out because they require a database connection.
7+
// They will be uncommented out once all the necessary mocks are in place.
8+
9+
// describe('POST /auth/login', () => {
10+
// it('should return 200 and a token for valid credentials', async () => {
11+
// const response = await request(app)
12+
// .post('/auth/login')
13+
// .send({ username: 'validUser', password: 'validPassword' });
14+
15+
// expect(response.status).toBe(200);
16+
// expect(response.body).toHaveProperty('token');
17+
// });
18+
19+
// it('should return 401 for invalid credentials', async () => {
20+
// const response = await request(app)
21+
// .post('/auth/login')
22+
// .send({ username: 'invalidUser', password: 'wrongPassword' });
23+
24+
// expect(response.status).toBe(401);
25+
// expect(response.body).toHaveProperty('error', 'Invalid credentials');
26+
// });
27+
28+
// it('should return 400 if username or password is missing', async () => {
29+
// const response = await request(app)
30+
// .post('/auth/login')
31+
// .send({ username: 'validUser' });
32+
33+
// expect(response.status).toBe(400);
34+
// expect(response.body).toHaveProperty('error', 'Username and password are required');
35+
// });
36+
// });
37+
38+
// describe('POST /auth/register', () => {
39+
// it('should return 201 and create a new user for valid input', async () => {
40+
// const response = await request(app)
41+
// .post('/auth/register')
42+
// .send({ username: 'newUser', password: 'newPassword' });
43+
44+
// expect(response.status).toBe(201);
45+
// expect(response.body).toHaveProperty('message', 'User registered successfully');
46+
// });
47+
48+
// it('should return 400 if username is already taken', async () => {
49+
// await request(app)
50+
// .post('/auth/register')
51+
// .send({ username: 'existingUser', password: 'password123' });
52+
53+
// const response = await request(app)
54+
// .post('/auth/register')
55+
// .send({ username: 'existingUser', password: 'password123' });
56+
57+
// expect(response.status).toBe(400);
58+
// expect(response.body).toHaveProperty('error', 'Username is already taken');
59+
// });
60+
61+
// it('should return 400 if username or password is missing', async () => {
62+
// const response = await request(app)
63+
// .post('/auth/register')
64+
// .send({ username: '' });
65+
66+
// expect(response.status).toBe(400);
67+
// expect(response.body).toHaveProperty('error', 'Username and password are required');
68+
// });
69+
// });
70+
71+
// describe('GET /auth/profile', () => {
72+
// it('should return 200 and user profile for valid token', async () => {
73+
// const loginResponse = await request(app)
74+
// .post('/auth/login')
75+
// .send({ username: 'validUser', password: 'validPassword' });
76+
77+
// const token = loginResponse.body.token;
78+
79+
// const response = await request(app)
80+
// .get('/auth/profile')
81+
// .set('Authorization', `Bearer ${token}`);
82+
83+
// expect(response.status).toBe(200);
84+
// expect(response.body).toHaveProperty('username', 'validUser');
85+
// });
86+
87+
// it('should return 401 if token is missing or invalid', async () => {
88+
// const response = await request(app)
89+
// .get('/auth/profile')
90+
// .set('Authorization', 'Bearer invalidToken');
91+
92+
// expect(response.status).toBe(401);
93+
// expect(response.body).toHaveProperty('error', 'Unauthorized');
94+
// });
95+
// });
96+
});

course-matrix/backend/__tests__/index.test.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,4 @@ describe("Sum function", () => {
66
test("Returns correct value", () => {
77
expect(2 + 3).toEqual(5);
88
});
9-
});
10-
11-
// Will finish the rest of the tests below in Sprint 3
12-
13-
// describe("GET /auth/session", () => {
14-
// test("It should respond with 200 status", async () => {
15-
// const response = await request(app).get("/auth/session");
16-
// expect(response.statusCode).toBe(200);
17-
// });
18-
// });
9+
});
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
2+
import { BrowserRouter } from "react-router-dom";
3+
import { beforeEach, describe, vi, Mock, afterEach, it, expect, MockedFunction } from "vitest";
4+
import TimetableBuilder from "../src/pages/TimetableBuilder/TimetableBuilder";
5+
import { useForm, UseFormWatch } from "react-hook-form";
6+
import { z } from "zod";
7+
import React from "react";
8+
9+
vi.mock("react-hook-form", () => ({
10+
useForm: vi.fn(),
11+
}));
12+
13+
vi.mock("@/api/coursesApiSlice", () => ({
14+
useGetCoursesQuery: vi.fn(() => ({ data: [], isLoading: false })),
15+
}));
16+
17+
vi.mock("@/api/timetableApiSlice", () => ({
18+
useGetTimetablesQuery: vi.fn(() => ({ data: [] })),
19+
}));
20+
21+
vi.mock("@/api/eventsApiSlice", () => ({
22+
useGetEventsQuery: vi.fn(() => ({ data: null })),
23+
}));
24+
25+
vi.mock("@/api/offeringsApiSlice", () => ({
26+
useGetOfferingsQuery: vi.fn(() => ({ data: [] })),
27+
}));
28+
29+
vi.mock("@/api/restrictionsApiSlice", () => ({
30+
useGetRestrictionsQuery: vi.fn(() => ({ data: [] })),
31+
}));
32+
33+
vi.mock("@/utils/useDebounce", () => ({
34+
useDebounceValue: (value: string) => value,
35+
}));
36+
37+
describe("TimetableBuilder", () => {
38+
const mockSetValue = vi.fn();
39+
const mockHandleSubmit = vi.fn((fn) => fn);
40+
41+
beforeEach(() => {
42+
(useForm as Mock).mockReturnValue({
43+
control: {},
44+
handleSubmit: mockHandleSubmit,
45+
setValue: mockSetValue,
46+
watch: vi.fn(() => {
47+
const result: { unsubscribe: () => void } & any[] = [];
48+
result.unsubscribe = () => {};
49+
return result;
50+
}) as unknown as UseFormWatch<any>,
51+
reset: vi.fn(),
52+
getValues: vi.fn(() => []),
53+
});
54+
});
55+
56+
afterEach(() => {
57+
vi.clearAllMocks();
58+
});
59+
60+
it("renders the TimetableBuilder component", () => {
61+
render(
62+
<BrowserRouter>
63+
<TimetableBuilder />
64+
</BrowserRouter>
65+
);
66+
67+
expect(screen.getByText(/New Timetable/i)).toBeInTheDocument();
68+
expect(screen.getByText(/Pick a few courses you'd like to take/i)).toBeInTheDocument();
69+
});
70+
71+
it("calls the reset function when the Reset button is clicked", () => {
72+
const mockReset = vi.fn();
73+
(useForm as MockedFunction<typeof useForm>).mockReturnValue({
74+
reset: mockReset,
75+
handleSubmit: mockHandleSubmit,
76+
setValue: mockSetValue,
77+
watch: vi.fn(() => {
78+
const result: unknown = [];
79+
result.unsubscribe = () => {};
80+
return result;
81+
}),
82+
getValues: vi.fn(() => []),
83+
});
84+
85+
render(
86+
<BrowserRouter>
87+
<TimetableBuilder />
88+
</BrowserRouter>
89+
);
90+
91+
const resetButton = screen.getByText(/Reset/i);
92+
fireEvent.click(resetButton);
93+
94+
expect(mockReset).toHaveBeenCalled();
95+
});
96+
97+
it("opens the custom settings modal when the Add new button is clicked", () => {
98+
render(
99+
<BrowserRouter>
100+
<TimetableBuilder />
101+
</BrowserRouter>
102+
);
103+
104+
const addNewButton = screen.getByText(/\+ Add new/i);
105+
fireEvent.click(addNewButton);
106+
107+
expect(screen.getByText(/Custom Settings/i)).toBeInTheDocument();
108+
});
109+
110+
it("displays selected courses when courses are added", async () => {
111+
const mockWatch = vi.fn(() => [
112+
{ id: 1, code: "CS101", name: "Introduction to Computer Science" },
113+
]);
114+
(useForm as vi.Mock).mockReturnValue({
115+
watch: mockWatch,
116+
handleSubmit: mockHandleSubmit,
117+
setValue: mockSetValue,
118+
reset: vi.fn(),
119+
getValues: vi.fn(() => []),
120+
});
121+
122+
render(
123+
<BrowserRouter>
124+
<TimetableBuilder />
125+
</BrowserRouter>
126+
);
127+
128+
expect(screen.getByText(/Selected courses: 1/i)).toBeInTheDocument();
129+
expect(screen.getByText(/CS101/i)).toBeInTheDocument();
130+
});
131+
132+
it("removes a course when the remove button is clicked", async () => {
133+
const mockWatch = vi.fn(() => [
134+
{ id: 1, code: "CS101", name: "Introduction to Computer Science" },
135+
]);
136+
const mockSetValue = vi.fn();
137+
(useForm as vi.Mock).mockReturnValue({
138+
watch: mockWatch,
139+
handleSubmit: mockHandleSubmit,
140+
setValue: mockSetValue,
141+
reset: vi.fn(),
142+
getValues: vi.fn(() => [
143+
{ id: 1, code: "CS101", name: "Introduction to Computer Science" },
144+
]),
145+
});
146+
147+
render(
148+
<BrowserRouter>
149+
<TimetableBuilder />
150+
</BrowserRouter>
151+
);
152+
153+
const removeButton = screen.getByRole("button", { name: /Remove/i });
154+
fireEvent.click(removeButton);
155+
156+
await waitFor(() => {
157+
expect(mockSetValue).toHaveBeenCalledWith("courses", []);
158+
});
159+
});
160+
161+
it("submits the form when the Generate button is clicked", () => {
162+
const mockSubmit = vi.fn();
163+
(useForm as vi.Mock).mockReturnValue({
164+
handleSubmit: vi.fn((fn) => fn),
165+
setValue: mockSetValue,
166+
watch: vi.fn(() => []),
167+
reset: vi.fn(),
168+
getValues: vi.fn(() => []),
169+
});
170+
171+
render(
172+
<BrowserRouter>
173+
<TimetableBuilder />
174+
</BrowserRouter>
175+
);
176+
177+
const generateButton = screen.getByText(/Generate/i);
178+
fireEvent.click(generateButton);
179+
180+
expect(mockHandleSubmit).toHaveBeenCalled();
181+
});
182+
});

course-matrix/frontend/__tests__/UserMenu.test.tsx

Lines changed: 0 additions & 83 deletions
This file was deleted.

0 commit comments

Comments
 (0)