Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions Client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
"jsdom": "^26.1.0",
"msw": "^2.8.5",
Copy link

Copilot AI May 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move msw into devDependencies instead of dependencies to prevent including test utilities in production bundles.

Suggested change
"msw": "^2.8.5",

Copilot uses AI. Check for mistakes.
"postcss": "^8.4.38",
"rollup-plugin-visualizer": "^5.12.0",
"tailwindcss": "^3.4.3",
Expand Down
67 changes: 67 additions & 0 deletions Client/src/__tests__/redux/classSearchSlice.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { describe, it, expect, beforeAll, afterEach, afterAll } from "vitest";
import { renderWithProviders } from "@/utils/test-utils";
import { setupServer } from "msw/node";
import { http, HttpResponse } from "msw";
import {
fetchSectionsAsync,
setFilters,
setPage,
} from "@/redux/classSearch/classSearchSlice";
import { getInitialFilterValues } from "@/components/classSearch/courseFilters/helpers/constants";
import { mockSections } from "@/utils/mockData";
import { AppDispatch } from "@/redux/store";

// Setup MSW server
const handlers = [
http.get("http://localhost:4000/classSearch", () => {
return HttpResponse.json({ data: mockSections, page: 1, totalPages: 1 });
}),
];

const server = setupServer(...handlers);

// Setup and teardown
beforeAll(() => server.listen({ onUnhandledRequest: "warn" }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

describe("classSearchSlice", () => {
it("sets filters and page via reducers", () => {
const { store } = renderWithProviders(<div />);
const newFilters = { ...getInitialFilterValues(), subject: "TEST" };

store.dispatch(setFilters(newFilters));
store.dispatch(setPage(3));

const state = store.getState().classSearch;
expect(state.filters.subject).toBe("TEST");
expect(state.page).toBe(3);
});

it("fetches sections and updates state via fetchSectionsAsync", async () => {
const { store } = renderWithProviders(<div />);
await (store.dispatch as AppDispatch)(fetchSectionsAsync());

const state = store.getState().classSearch;
expect(state.loading).toBe(false);
expect(state.sections.length).toBe(1);

Check failure on line 47 in Client/src/__tests__/redux/classSearchSlice.test.tsx

View workflow job for this annotation

GitHub Actions / client-build-and-lint

src/__tests__/redux/classSearchSlice.test.tsx > classSearchSlice > fetches sections and updates state via fetchSectionsAsync

AssertionError: expected +0 to be 1 // Object.is equality - Expected + Received - 1 + 0 ❯ src/__tests__/redux/classSearchSlice.test.tsx:47:35

Check failure on line 47 in Client/src/__tests__/redux/classSearchSlice.test.tsx

View workflow job for this annotation

GitHub Actions / client-build-and-lint

src/__tests__/redux/classSearchSlice.test.tsx > classSearchSlice > fetches sections and updates state via fetchSectionsAsync

AssertionError: expected +0 to be 1 // Object.is equality - Expected + Received - 1 + 0 ❯ src/__tests__/redux/classSearchSlice.test.tsx:47:35
expect(state.sections[0].classNumber).toBe(1234);
expect(state.page).toBe(1);
expect(state.totalPages).toBe(1);
});

it("handles fetchSectionsAsync errors", async () => {
server.use(
http.get("http://localhost:4000/classSearch", () => {
return new HttpResponse(null, { status: 500 });
})
);

const { store } = renderWithProviders(<div />);
await (store.dispatch as AppDispatch)(fetchSectionsAsync());

const state = store.getState().classSearch;
expect(state.loading).toBe(false);
expect(state.error).toBeTruthy();
});
});
37 changes: 33 additions & 4 deletions Client/src/utils/mockData.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { MessageObjType } from "@polylink/shared/types";
import { CourseTerm, MessageObjType, Section } from "@polylink/shared/types";

export const mockMessages: MessageObjType[] = [
{
id: "msg1",
sender: "user",
text: "Hey! Can you help me understand how machine learning works?",
model: "GPT-4",
userReaction: null,
},
{
Expand All @@ -19,7 +18,6 @@ export const mockMessages: MessageObjType[] = [
id: "msg3",
sender: "user",
text: "Yes, please! Can you give me a simple example?",
model: "GPT-4",
userReaction: null,
},
{
Expand All @@ -33,7 +31,6 @@ export const mockMessages: MessageObjType[] = [
id: "msg5",
sender: "user",
text: "That makes sense! What about neural networks?",
model: "GPT-4",
userReaction: null,
},
{
Expand All @@ -45,3 +42,35 @@ export const mockMessages: MessageObjType[] = [
thinkingState: false,
},
];

export const mockSections: Section[] = [
{
classNumber: 1234,
courseName: "Test Class",
term: "spring2025" as CourseTerm,
courseId: "COURSE1",
subject: "TEST",
catalogNumber: "101",
component: "LEC",
description: "Test Description",
prerequisites: null,
units: "3",
enrollmentStatus: "O",
enrollment: {
waitTotal: 0,
waitCap: 0,
classCapacity: 100,
enrollmentTotal: 50,
enrollmentAvailable: 50,
enrollmentStatusDescription: "Open",
},
instructionMode: "PA",
courseAttributes: [],
meetings: [],
instructors: [],
instructorsWithRatings: null,
techElectives: [],
classPair: null,
isCreditNoCredit: false,
},
] as Section[];
Copy link

Copilot AI May 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Remove the redundant as Section[] cast—since you already declared mockSections with type Section[], the explicit cast is unnecessary.

Suggested change
] as Section[];
];

Copilot uses AI. Check for mistakes.
Loading
Loading