From 415911db0e2eafd2fac2a9afaaf6135491fc2a93 Mon Sep 17 00:00:00 2001 From: Francis Vauterin <159532420+francisvaut@users.noreply.github.com> Date: Fri, 12 Apr 2024 11:55:47 +0200 Subject: [PATCH] Services fix, tests (#297) * test: create course * fix: send id to backend when creating faculty * test: create faculty * test: create group * test: cleanup create tests * test: assistant type * test: services and types folder * test: start with create project * test: cleanup setup file * test: cleanup request handlers * test: reset services + fix mistakes in teacher/assistant service * chore: fix linting * test: create project * chore: linting autofix * chore: bring back multipart/from-data * Better academic year selector, filtering and sorting (#292) * feat: academic year selector, better pagination and filtering composables * chore: added course pagination * Run submission checks (#268) * chore: celery #206 * chore: Support partial update for project * chore: support patch for project 2 * refactor!: rework checks and submissions * chore: allow models passed to tasks * feat: added -k to test.sh * fix: re-add translations * chore: display faculty in course edit * fix: courses to create projects filtered on selected calendar date * fix: make teacher command * fix: linting * test: admin delete * test: fix service mistake, assistant delete --------- Co-authored-by: Ewout Verlinde Co-authored-by: Vincent Vallaeys Co-authored-by: Bram Meir --- .../composables/services/admins.service.ts | 1 + .../composables/services/assistant.service.ts | 4 +- .../composables/services/courses.service.ts | 2 +- .../composables/services/faculties.service.ts | 2 +- .../composables/services/groups.service.ts | 1 + .../composables/services/teachers.service.ts | 2 +- .../src/test/unit/faculty_service.test.ts | 24 - .../unit/{ => services}/admin_service.test.ts | 59 +- .../{ => services}/assistant_service.test.ts | 69 ++- .../{ => services}/course_service.test.ts | 70 ++- .../unit/services/faculty_service.test.ts | 67 +++ .../unit/{ => services}/group_service.test.ts | 69 ++- .../{ => services}/project_service.test.ts | 77 ++- frontend/src/test/unit/services/setup/data.ts | 351 +++++++++++ .../unit/services/setup/delete_handlers.ts | 32 + .../test/unit/services/setup/get_handlers.ts | 142 +++++ .../test/unit/services/setup/post_handlers.ts | 71 +++ .../unit/services/setup/request_handlers.ts | 5 + .../src/test/unit/services/setup/setup.ts | 34 ++ .../{ => services}/structure_check.test.ts | 19 +- .../{ => services}/student_service.test.ts | 47 +- .../{ => services}/submission_service.test.ts | 22 +- .../submission_status_service.test.ts | 6 + .../{ => services}/teacher_service.test.ts | 17 + frontend/src/test/unit/setup.ts | 547 ------------------ .../src/test/unit/types/assistant.test.ts | 90 +++ frontend/src/test/unit/types/setup.ts | 7 + frontend/src/types/Course.ts | 3 +- frontend/vite.config.ts | 4 +- 29 files changed, 1196 insertions(+), 648 deletions(-) delete mode 100644 frontend/src/test/unit/faculty_service.test.ts rename frontend/src/test/unit/{ => services}/admin_service.test.ts (70%) rename frontend/src/test/unit/{ => services}/assistant_service.test.ts (76%) rename frontend/src/test/unit/{ => services}/course_service.test.ts (80%) create mode 100644 frontend/src/test/unit/services/faculty_service.test.ts rename frontend/src/test/unit/{ => services}/group_service.test.ts (56%) rename frontend/src/test/unit/{ => services}/project_service.test.ts (68%) create mode 100644 frontend/src/test/unit/services/setup/data.ts create mode 100644 frontend/src/test/unit/services/setup/delete_handlers.ts create mode 100644 frontend/src/test/unit/services/setup/get_handlers.ts create mode 100644 frontend/src/test/unit/services/setup/post_handlers.ts create mode 100644 frontend/src/test/unit/services/setup/request_handlers.ts create mode 100644 frontend/src/test/unit/services/setup/setup.ts rename frontend/src/test/unit/{ => services}/structure_check.test.ts (86%) rename frontend/src/test/unit/{ => services}/student_service.test.ts (90%) rename frontend/src/test/unit/{ => services}/submission_service.test.ts (88%) rename frontend/src/test/unit/{ => services}/submission_status_service.test.ts (88%) rename frontend/src/test/unit/{ => services}/teacher_service.test.ts (94%) delete mode 100644 frontend/src/test/unit/setup.ts create mode 100644 frontend/src/test/unit/types/assistant.test.ts create mode 100644 frontend/src/test/unit/types/setup.ts diff --git a/frontend/src/composables/services/admins.service.ts b/frontend/src/composables/services/admins.service.ts index 45288d95..88bfa163 100644 --- a/frontend/src/composables/services/admins.service.ts +++ b/frontend/src/composables/services/admins.service.ts @@ -48,6 +48,7 @@ export function useAdmin(): AdminState { return { admins, admin, + getAdminByID, getAdmins, diff --git a/frontend/src/composables/services/assistant.service.ts b/frontend/src/composables/services/assistant.service.ts index e30c15ee..31f6964e 100644 --- a/frontend/src/composables/services/assistant.service.ts +++ b/frontend/src/composables/services/assistant.service.ts @@ -38,7 +38,7 @@ export function useAssistant(): AssistantState { async function getAssistantByCourse(courseId: string): Promise { const endpoint = endpoints.assistants.byCourse.replace('{courseId}', courseId); - await get(endpoint, assistant, Assistant.fromJSON); + await getList(endpoint, assistants, Assistant.fromJSON); } async function getAssistants(): Promise { @@ -71,7 +71,7 @@ export function useAssistant(): AssistantState { } async function deleteAssistant(id: string): Promise { - const endpoint = endpoints.admins.retrieve.replace('{id}', id); + const endpoint = endpoints.assistants.retrieve.replace('{id}', id); await deleteId(endpoint, assistant, Assistant.fromJSON); } diff --git a/frontend/src/composables/services/courses.service.ts b/frontend/src/composables/services/courses.service.ts index 65757faf..9a873cf3 100644 --- a/frontend/src/composables/services/courses.service.ts +++ b/frontend/src/composables/services/courses.service.ts @@ -60,10 +60,10 @@ export function useCourses(): CoursesState { await create( endpoint, { + id: courseData.id, name: courseData.name, description: courseData.description, academic_startyear: courseData.academic_startyear, - faculty: courseData.faculty?.id, }, course, Course.fromJSON, diff --git a/frontend/src/composables/services/faculties.service.ts b/frontend/src/composables/services/faculties.service.ts index 23fc274c..00e01f37 100644 --- a/frontend/src/composables/services/faculties.service.ts +++ b/frontend/src/composables/services/faculties.service.ts @@ -28,7 +28,7 @@ export function useFaculty(): FacultyState { async function createFaculty(facultyData: Faculty): Promise { const endpoint = endpoints.faculties.index; - await create(endpoint, { name: facultyData.name }, faculty, Faculty.fromJSON); + await create(endpoint, { id: facultyData.id, name: facultyData.name }, faculty, Faculty.fromJSON); } async function deleteFaculty(id: string): Promise { diff --git a/frontend/src/composables/services/groups.service.ts b/frontend/src/composables/services/groups.service.ts index e56b5dc1..50ad2e7a 100644 --- a/frontend/src/composables/services/groups.service.ts +++ b/frontend/src/composables/services/groups.service.ts @@ -38,6 +38,7 @@ export function useGroup(): GroupState { endpoint, { score: groupData.score, + project: groupData.project?.id, }, group, Group.fromJSON, diff --git a/frontend/src/composables/services/teachers.service.ts b/frontend/src/composables/services/teachers.service.ts index 474b9ec6..4c7d7ada 100644 --- a/frontend/src/composables/services/teachers.service.ts +++ b/frontend/src/composables/services/teachers.service.ts @@ -39,7 +39,7 @@ export function useTeacher(): TeacherState { async function getTeacherByCourse(courseId: string): Promise { const endpoint = endpoints.teachers.byCourse.replace('{courseId}', courseId); - await get(endpoint, teacher, Teacher.fromJSON); + await getList(endpoint, teachers, Teacher.fromJSON); } async function getTeachers(): Promise { diff --git a/frontend/src/test/unit/faculty_service.test.ts b/frontend/src/test/unit/faculty_service.test.ts deleted file mode 100644 index 411e2d2b..00000000 --- a/frontend/src/test/unit/faculty_service.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import { describe, it, expect } from 'vitest'; -import { useFaculty } from '@/composables/services/faculties.service.ts'; - -const { faculties, faculty, getFacultyByID, getFaculties } = useFaculty(); - -describe('faculty', (): void => { - it('gets faculty data by id', async () => { - await getFacultyByID('sciences'); - expect(faculty.value).not.toBeNull(); - expect(faculty.value?.name).toBe('wetenschappen'); - }); - - it('gets faculties data', async () => { - await getFaculties(); - expect(faculties).not.toBeNull(); - expect(Array.isArray(faculties.value)).toBe(true); - expect(faculties.value?.length).toBe(2); - expect(faculties.value?.[0]).not.toBeNull(); - expect(faculties.value?.[0].name).toBe('wetenschappen'); - expect(faculties.value?.[1]).not.toBeNull(); - expect(faculties.value?.[1].name).toBe('voetbal'); - }); -}); diff --git a/frontend/src/test/unit/admin_service.test.ts b/frontend/src/test/unit/services/admin_service.test.ts similarity index 70% rename from frontend/src/test/unit/admin_service.test.ts rename to frontend/src/test/unit/services/admin_service.test.ts index 630394ae..a7e9281f 100644 --- a/frontend/src/test/unit/admin_service.test.ts +++ b/frontend/src/test/unit/services/admin_service.test.ts @@ -6,14 +6,23 @@ import { User } from '@/types/users/User.ts'; const { admins, admin, + getAdminByID, getAdmins, createAdmin, + deleteAdmin, } = useAdmin(); +function resetService(): void { + admin.value = null; + admins.value = null; +} + describe('admin', (): void => { it('gets assistant data by id', async () => { + resetService(); + await getAdminByID('300201547011'); expect(admin.value).not.toBeNull(); expect(admin.value?.username).toBe('tverslyp'); @@ -28,6 +37,8 @@ describe('admin', (): void => { }); it('gets admins data', async () => { + resetService(); + await getAdmins(); expect(admins).not.toBeNull(); expect(Array.isArray(admins.value)).toBe(true); @@ -55,22 +66,24 @@ describe('admin', (): void => { }); it('create admin', async () => { + resetService(); + const exampleAdmin = new User( - '101', // id - 'sample_admin', // username + '', // id + '', // username 'sample.admin@UGent.be', // email - 'Sample', // first_name - 'Admin', // last_name - 2024, // last_enrolled + 'admin_first_name', // first_name + 'admin_last_name', // last_name + -1, // last_enrolled true, // is_staff - ['user'], // roles + [], // roles [], // faculties - new Date('April 2, 2023 01:15:00'), // create_time - new Date('April 2, 2024 01:15:00'), // last_login + new Date(), // create_time + null, // last_login ); await getAdmins(); - expect(admins).not.toBeNull(); + expect(admins.value).not.toBeNull(); expect(Array.isArray(admins.value)).toBe(true); const prevLength = admins.value?.length ?? 0; @@ -81,9 +94,31 @@ describe('admin', (): void => { expect(Array.isArray(admins.value)).toBe(true); expect(admins.value?.length).toBe(prevLength + 1); - expect(admins.value?.[prevLength]?.first_name).toBe('Sample'); - expect(admins.value?.[prevLength]?.last_name).toBe('Admin'); + // Only check for fields that are sent to the backend + expect(admins.value?.[prevLength]?.first_name).toBe('admin_first_name'); + expect(admins.value?.[prevLength]?.last_name).toBe('admin_last_name'); expect(admins.value?.[prevLength]?.email).toBe('sample.admin@UGent.be'); - expect(admins.value?.[prevLength]?.is_staff).toBe(true); + }); + + it('delete admin', async () => { + resetService(); + + await getAdmins(); + expect(admins.value).not.toBeNull(); + expect(Array.isArray(admins.value)).toBe(true); + const prevLength = admins.value?.length ?? 0; + + let adminId = '0'; + if (admins.value?.[0]?.id !== undefined && admins.value?.[0].id !== null) { + adminId = admins.value?.[0]?.id; + } + + await deleteAdmin(adminId); + await getAdmins(); + + expect(admins).not.toBeNull(); + expect(Array.isArray(admins.value)).toBe(true); + expect(admins.value?.length).toBe(prevLength - 1); + expect(admins.value?.[0]?.id).not.toBe(adminId); }); }); diff --git a/frontend/src/test/unit/assistant_service.test.ts b/frontend/src/test/unit/services/assistant_service.test.ts similarity index 76% rename from frontend/src/test/unit/assistant_service.test.ts rename to frontend/src/test/unit/services/assistant_service.test.ts index 9a165a5a..72884db0 100644 --- a/frontend/src/test/unit/assistant_service.test.ts +++ b/frontend/src/test/unit/services/assistant_service.test.ts @@ -12,10 +12,21 @@ const { getAssistants, createAssistant, + deleteAssistant, + + assistantJoinCourse, + assistantLeaveCourse, } = useAssistant(); +function resetService(): void { + assistant.value = null; + assistants.value = null; +} + describe('assistant', (): void => { it('gets assistant data by id', async () => { + resetService(); + await getAssistantByID('235'); expect(assistant.value).not.toBeNull(); expect(assistant.value?.username).toBe('bsimpson'); @@ -31,6 +42,8 @@ describe('assistant', (): void => { }); it('gets assistants data', async () => { + resetService(); + await getAssistants(); expect(assistants).not.toBeNull(); expect(Array.isArray(assistants.value)).toBe(true); @@ -60,6 +73,8 @@ describe('assistant', (): void => { }); it('gets assistants data by course', async () => { + resetService(); + await getAssistantByCourse('1'); expect(assistants).not.toBeNull(); expect(Array.isArray(assistants.value)).toBe(true); @@ -89,19 +104,21 @@ describe('assistant', (): void => { }); it('create assistant', async () => { + resetService(); + const exampleAssistant = new Assistant( - '102', - 'sample_assistant', - 'sample.assistant@UGent.be', - 'Sample', - 'Assistant', - 2023, - true, - [], - [], - [], - new Date('April 2, 2023 01:15:00'), - new Date('April 2, 2024 01:15:00'), + '', // id + '', // username + 'sample.assistant@UGent.be', // email + 'assistant_first_name', // first_name + 'assistant_last_name', // last name + 2023, // last enrolled + true, // is_staff + [], // roles + [], // faculties + [], // courses + new Date(), // create_time + null, // last_login ); await getAssistants(); @@ -116,9 +133,31 @@ describe('assistant', (): void => { expect(Array.isArray(assistants.value)).toBe(true); expect(assistants.value?.length).toBe(prevLength + 1); - expect(assistants.value?.[prevLength]?.first_name).toBe('Sample'); - expect(assistants.value?.[prevLength]?.last_name).toBe('Assistant'); + // Only check for fields that are sent to the backend + expect(assistants.value?.[prevLength]?.first_name).toBe('assistant_first_name'); + expect(assistants.value?.[prevLength]?.last_name).toBe('assistant_last_name'); expect(assistants.value?.[prevLength]?.email).toBe('sample.assistant@UGent.be'); - expect(assistants.value?.[prevLength]?.roles).toContain('assistant'); + }); + + it('delete assistant', async () => { + resetService(); + + await getAssistants(); + expect(assistants.value).not.toBeNull(); + expect(Array.isArray(assistants.value)).toBe(true); + const prevLength = assistants.value?.length ?? 0; + + let assistantId = '0'; + if (assistants.value?.[0]?.id !== undefined && assistants.value?.[0].id !== null) { + assistantId = assistants.value?.[0]?.id; + } + + await deleteAssistant(assistantId); + await getAssistants(); + + expect(assistants).not.toBeNull(); + expect(Array.isArray(assistants.value)).toBe(true); + expect(assistants.value?.length).toBe(prevLength - 1); + expect(assistants.value?.[0]?.id).not.toBe(assistantId); }); }); diff --git a/frontend/src/test/unit/course_service.test.ts b/frontend/src/test/unit/services/course_service.test.ts similarity index 80% rename from frontend/src/test/unit/course_service.test.ts rename to frontend/src/test/unit/services/course_service.test.ts index 27466f67..23e50373 100644 --- a/frontend/src/test/unit/course_service.test.ts +++ b/frontend/src/test/unit/services/course_service.test.ts @@ -5,32 +5,31 @@ import { Course } from '@/types/Course.ts'; import { useCourses } from '@/composables/services/courses.service.ts'; const { + pagination, courses, course, getCourseByID, + searchCourses, getCourses, getCoursesByStudent, + getCoursesByTeacher, + getCourseByAssistant, + + createCourse, + cloneCourse, + deleteCourse, } = useCourses(); -// "describe" bundles tests about 1 specific thing; here we're testing course -// aka a test suite -describe('course', (): void => { - // "it" is used to specify tests - // you can also import "test" instead of "it", because it's the exact same - // but with "it", it's easy to read => it (referring to the course) returns correct course year - it('returns correct course year', (): void => { - const course: Course = new Course('1', 'course', 'description', 2003); - // use expect for assertions - // after expect, there are a multitude of possible functions such as: - // toBe, toEqual, toContain - // check https://vitest.dev/api/expect.html for all possibilities - expect(course.getCourseYear()).toBe('2003 - 2004'); - // assert can also be used instead, if you like its syntax more - // check out https://vitest.dev/api/assert.html for more info - }); +function resetService(): void { + course.value = null; + courses.value = null; +} +describe('course', (): void => { it('gets course data by id', async () => { + resetService(); + await getCourseByID('1'); expect(course.value).not.toBeNull(); expect(course.value?.name).toBe('Math'); @@ -44,6 +43,8 @@ describe('course', (): void => { }); it('gets courses data', async () => { + resetService(); + await getCourses(); expect(courses.value).not.toBeNull(); expect(courses.value?.[0]?.name).toBe('Math'); @@ -111,6 +112,8 @@ describe('course', (): void => { }); it('gets courses data by student', async () => { + resetService(); + await getCoursesByStudent('1'); expect(courses).not.toBeNull(); expect(Array.isArray(courses.value)).toBe(true); @@ -143,4 +146,39 @@ describe('course', (): void => { expect(courses.value?.[2]?.assistants).toEqual([]); expect(courses.value?.[2]?.projects).toEqual([]); }); + + it('create course', async () => { + resetService(); + + const exampleCourse = new Course( + 'course_id', // id + 'course_name', // name + 'course_description', // description + 2024, // acedemic_startyear, + null, // parent_course + null, // faculty + [], // teachers + [], // assistants + [], // students + [], // projects + ); + + await getCourses(); + expect(courses).not.toBeNull(); + expect(Array.isArray(courses.value)).toBe(true); + const prevLength = courses.value?.length ?? 0; + + await createCourse(exampleCourse); + await getCourses(); + + expect(courses).not.toBeNull(); + expect(Array.isArray(courses.value)).toBe(true); + expect(courses.value?.length).toBe(prevLength + 1); + + // Only check for fields that are sent to the backend + expect(courses.value?.[prevLength]?.id).toBe('course_id'); + expect(courses.value?.[prevLength]?.name).toBe('course_name'); + expect(courses.value?.[prevLength]?.description).toBe('course_description'); + expect(courses.value?.[prevLength]?.academic_startyear).toBe(2024); + }); }); diff --git a/frontend/src/test/unit/services/faculty_service.test.ts b/frontend/src/test/unit/services/faculty_service.test.ts new file mode 100644 index 00000000..b6fe3903 --- /dev/null +++ b/frontend/src/test/unit/services/faculty_service.test.ts @@ -0,0 +1,67 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { describe, it, expect } from 'vitest'; +import { useFaculty } from '@/composables/services/faculties.service.ts'; +import { Faculty } from '@/types/Faculty'; + +const { + faculties, + faculty, + getFacultyByID, + getFaculties, + + createFaculty, + deleteFaculty, +} = useFaculty(); + +function resetService(): void { + faculty.value = null; + faculties.value = null; +} + +describe('faculty', (): void => { + it('gets faculty data by id', async () => { + resetService(); + + await getFacultyByID('sciences'); + expect(faculty.value).not.toBeNull(); + expect(faculty.value?.name).toBe('wetenschappen'); + }); + + it('gets faculties data', async () => { + resetService(); + + await getFaculties(); + expect(faculties).not.toBeNull(); + expect(Array.isArray(faculties.value)).toBe(true); + expect(faculties.value?.length).toBe(2); + expect(faculties.value?.[0]).not.toBeNull(); + expect(faculties.value?.[0].name).toBe('wetenschappen'); + expect(faculties.value?.[1]).not.toBeNull(); + expect(faculties.value?.[1].name).toBe('voetbal'); + }); + + it('create faculty', async () => { + resetService(); + + const exampleFaculty = new Faculty( + 'faculty_id', // id + 'faculty_name', // name + ); + + await getFaculties(); + expect(faculties).not.toBeNull(); + expect(Array.isArray(faculties.value)).toBe(true); + const prevLength = faculties.value?.length ?? 0; + + await createFaculty(exampleFaculty); + await getFaculties(); + + expect(faculties).not.toBeNull(); + expect(Array.isArray(faculties.value)).toBe(true); + expect(faculties.value?.length).toBe(prevLength + 1); + + // Only check for fields that are sent to the backend + expect(faculties.value?.[prevLength]?.id).toBe('faculty_id'); + expect(faculties.value?.[prevLength]?.name).toBe('faculty_name'); + }); +}); diff --git a/frontend/src/test/unit/group_service.test.ts b/frontend/src/test/unit/services/group_service.test.ts similarity index 56% rename from frontend/src/test/unit/group_service.test.ts rename to frontend/src/test/unit/services/group_service.test.ts index 782f757a..31a654ac 100644 --- a/frontend/src/test/unit/group_service.test.ts +++ b/frontend/src/test/unit/services/group_service.test.ts @@ -1,11 +1,32 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { describe, it, expect } from 'vitest'; import { useGroup } from '@/composables/services/groups.service.ts'; +import { Group } from '@/types/Group'; +import { useProject } from '@/composables/services/project.service'; +import { type Project } from '@/types/Projects'; -const { groups, group, getGroupByID, getGroupsByProject, getGroupsByStudent } = useGroup(); +const { + groups, + group, + getGroupByID, + getGroupsByProject, + getGroupsByStudent, + + createGroup, + deleteGroup, +} = useGroup(); + +function resetService(): void { + group.value = null; + groups.value = null; +} + +const { project, getProjectByID } = useProject(); describe('group', (): void => { it('gets group data by id', async () => { + resetService(); + await getGroupByID('0'); expect(group.value).not.toBeNull(); expect(group.value?.score).toBe(20); @@ -16,9 +37,9 @@ describe('group', (): void => { }); it('gets groups data by project', async () => { + resetService(); + await getGroupsByProject('0'); - // console.log(groups.value) - // Ensure group data is not null expect(groups.value).not.toBeNull(); expect(Array.isArray(groups.value)).toBe(true); expect(groups.value?.length).toBe(2); @@ -39,6 +60,8 @@ describe('group', (): void => { }); it('gets groups data by student', async () => { + resetService(); + await getGroupsByStudent('1'); expect(groups.value).not.toBeNull(); expect(Array.isArray(groups.value)).toBe(true); @@ -52,11 +75,37 @@ describe('group', (): void => { expect(groups.value?.[0]?.submissions).toEqual([]); }); - /* - it("create group", async () => { - let gr = new Group("3",10) - await createGroup(gr, "0") - console.log(group.value) - }) - */ + it('create group', async () => { + resetService(); + + const projectId = '0'; + await getProjectByID(projectId); + const exampleProject: Project | null = project.value; + expect(exampleProject).not.toBeNull(); + + const exampleGroup = new Group( + '', // id + 10, // score + exampleProject, // project + [], // students + [], // submissions + ); + + await getGroupsByProject(projectId); + + expect(groups).not.toBeNull(); + expect(Array.isArray(groups.value)).toBe(true); + const prevLength = groups.value?.length ?? 0; + + await createGroup(exampleGroup, projectId); + await getGroupsByProject(projectId); + + expect(groups).not.toBeNull(); + expect(Array.isArray(groups.value)).toBe(true); + expect(groups.value?.length).toBe(prevLength + 1); + + // Only check for fields that are sent to the backend + expect(groups.value?.[prevLength]?.score).toBe(10); + expect(groups.value?.[prevLength]?.project).toBeNull(); + }); }); diff --git a/frontend/src/test/unit/project_service.test.ts b/frontend/src/test/unit/services/project_service.test.ts similarity index 68% rename from frontend/src/test/unit/project_service.test.ts rename to frontend/src/test/unit/services/project_service.test.ts index aac8a074..9dc87393 100644 --- a/frontend/src/test/unit/project_service.test.ts +++ b/frontend/src/test/unit/services/project_service.test.ts @@ -1,11 +1,33 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { describe, it, expect } from 'vitest'; import { useProject } from '@/composables/services/project.service.ts'; +import { useCourses } from '@/composables/services/courses.service'; +import { Project } from '@/types/Projects'; +import { type Course } from '@/types/Course'; -const { projects, project, getProjectByID, getProjectsByCourse, getProjectsByStudent } = useProject(); +const { + projects, + project, + getProjectByID, + getProjectsByCourse, + getProjectsByCourseAndDeadline, + getProjectsByStudent, + + createProject, + deleteProject, +} = useProject(); + +const { course, getCourseByID } = useCourses(); + +function resetService(): void { + project.value = null; + projects.value = null; +} describe('project', (): void => { it('gets project data by id', async () => { + resetService(); + await getProjectByID('0'); expect(project.value).not.toBeNull(); expect(project.value?.name).toBe('sel2'); @@ -27,6 +49,8 @@ describe('project', (): void => { }); it('gets projects data', async () => { + resetService(); + await getProjectsByCourse('1'); expect(projects).not.toBeNull(); expect(Array.isArray(projects.value)).toBe(true); @@ -68,6 +92,8 @@ describe('project', (): void => { }); it('gets projects data', async () => { + resetService(); + await getProjectsByStudent('1'); expect(projects).not.toBeNull(); expect(Array.isArray(projects.value)).toBe(true); @@ -107,4 +133,53 @@ describe('project', (): void => { expect(projects.value?.[1]?.groups).toEqual([]); expect(projects.value?.[1]?.submissions).toEqual([]); }); + + it('create project', async () => { + resetService(); + + const courseId = '1'; + await getCourseByID(courseId); + + const exampleProject = new Project( + '', // id + 'project_name', // name + 'project_description', // description + true, // visible + false, // archived + false, // locked_groups + new Date('November 1, 2024 04:20:00'), // start_data + new Date('November 2, 2024 04:20:00'), // deadline + 20, // max_score + false, // score_visible + 5, // group_size + null, // structure_file + ); + + await getProjectsByCourse(courseId); + + expect(projects).not.toBeNull(); + expect(Array.isArray(projects.value)).toBe(true); + const prevLength = projects.value?.length ?? 0; + + // await createProject(JSON.stringify(exampleProject), courseId); + // await getProjectsByCourse(courseId); + + // expect(projects).not.toBeNull(); + // expect(Array.isArray(projects.value)).toBe(true); + // expect(projects.value?.length).toBe(prevLength + 1); + + // // Only check for fields that are sent to the backend + // expect(projects.value?.[prevLength]?.name).toBe('project_name'); + // expect(projects.value?.[prevLength]?.description).toBe('project_description'); + // expect(projects.value?.[prevLength]?.visible).toBe(true); + // expect(projects.value?.[prevLength]?.archived).toBe(false); + // expect(projects.value?.[prevLength]?.locked_groups).toBe(false); + // expect(projects.value?.[prevLength]?.start_date).toStrictEqual(new Date('November 1, 2024 04:20:00')); + // expect(projects.value?.[prevLength]?.deadline).toStrictEqual(new Date('November 2, 2024 04:20:00')); + // expect(projects.value?.[prevLength]?.max_score).toBe(20); + // expect(projects.value?.[prevLength]?.score_visible).toBe(false); + // expect(projects.value?.[prevLength]?.group_size).toBe(5); + // expect(projects.value?.[prevLength]?.structure_file).toBe(null); + // expect(projects.value?.[prevLength]?.course).toBe(null); + }); }); diff --git a/frontend/src/test/unit/services/setup/data.ts b/frontend/src/test/unit/services/setup/data.ts new file mode 100644 index 00000000..5ae688ba --- /dev/null +++ b/frontend/src/test/unit/services/setup/data.ts @@ -0,0 +1,351 @@ +export const groups = [ + { + id: '0', + score: 20, + project: '0', + students: ['1', '2', '3', '000201247011'], + }, + { + id: '1', + score: 18, + project: '0', + students: ['1', '2', '3', '000201247011'], + }, +]; + +export const projects = [ + { + id: '0', + course: '1', + name: 'sel2', + description: 'this is a test', + visible: true, + archived: false, + locked_groups: false, + start_date: new Date('July 21, 2024 01:15:00'), + deadline: new Date('July 23, 2024 01:15:00'), + max_score: 100, + score_visible: true, + group_size: 8, + submissions: ['1', '2'], + groups: ['0', '1'], + }, + { + id: 1, + course: '1', + name: 'sel3', + description: 'make a project', + visible: true, + archived: false, + locked_groups: false, + start_date: new Date('July 21, 2024 01:15:00'), + deadline: new Date('July 23, 2024 01:15:00'), + max_score: 20, + score_visible: false, + group_size: 3, + submissions: [], + groups: ['0', '1'], + }, +]; + +export const courses = [ + { + id: '1', + teachers: ['123', '124'], + assistants: ['235', '236'], + students: ['1', '2', '3', '000201247011'], + projects: ['0', '1'], + parent_course: null, + faculty: null, + name: 'Math', + academic_startyear: 2023, + description: 'Math course', + }, + { + id: '2', + teachers: [], + assistants: [], + students: [], + projects: [], + parent_course: '3', + faculty: null, + name: 'Sel2', + academic_startyear: 2023, + description: 'Software course', + }, + { + id: '3', + teachers: [], + assistants: [], + students: [], + projects: [], + parent_course: null, + faculty: null, + name: 'Sel1', + academic_startyear: 2022, + description: 'Software course', + }, + { + id: '12', + teachers: [], + assistants: [], + students: [], + projects: [], + parent_course: '1', + faculty: null, + name: 'Math', + academic_startyear: 2024, + description: 'Math course', + }, + { + id: '13', + teachers: [], + assistants: [], + students: [], + projects: [], + parent_course: '12', + faculty: null, + name: 'Math', + academic_startyear: 2025, + description: 'Math course', + }, + { + id: '14', + teachers: [], + assistants: [], + students: [], + projects: [], + parent_course: null, + faculty: null, + name: 'Club brugge', + academic_startyear: 2023, + description: null, + }, + { + id: '15', + teachers: [], + assistants: [], + students: [], + projects: [], + parent_course: null, + faculty: null, + name: 'vergeet barbara', + academic_startyear: 2023, + description: null, + }, +]; + +export const faculties = [ + { id: 'sciences', name: 'wetenschappen' }, + { id: 'football', name: 'voetbal' }, +]; + +export const students = [ + { + id: '1', + last_login: null, + username: 'jdoe', + is_staff: false, + email: 'John.Doe@hotmail.com', + first_name: 'John', + last_name: 'Doe', + last_enrolled: 2023, + create_time: new Date('July 21, 2024 01:15:00'), + studentId: null, + roles: ['student'], + courses: ['1', '2', '3'], + groups: ['0'], + faculties: [], + }, + { + id: '2', + last_login: null, + username: 'bverhae', + is_staff: false, + email: 'Bartje.Verhaege@gmail.com', + first_name: 'Bartje', + last_name: 'Verhaege', + last_enrolled: 2023, + create_time: new Date('July 21, 2024 01:15:00'), + studentId: null, + roles: ['student'], + courses: [], + groups: [], + faculties: [], + }, + { + id: '000201247011', + last_login: new Date('July 30, 2024 01:15:00'), + username: 'tverslyp', + is_staff: true, + email: 'Tybo.Verslype@UGent.be', + first_name: 'Tybo', + last_name: 'Verslype', + last_enrolled: 2023, + create_time: new Date('July 21, 2024 01:15:00'), + studentId: '02012470', + roles: ['student'], + courses: [], + groups: [], + faculties: [], + }, + { + id: '3', + last_login: null, + username: 'somtin', + is_staff: false, + email: 'somtin.somtin@gmail.com', + first_name: 'somtin', + last_name: 'somtin', + last_enrolled: 2023, + create_time: new Date('July 21, 2024 01:15:00'), + studentId: null, + roles: ['student'], + courses: [], + groups: [], + faculties: [], + }, +]; + +export const teachers = [ + { + id: '123', + last_login: null, + username: 'tboonen', + is_staff: false, + email: 'Tom.Boonen@gmail.be', + first_name: 'Tom', + last_name: 'Boonen', + last_enrolled: 2023, + create_time: new Date('July 21, 2024 01:15:00'), + }, + { + id: '124', + last_login: null, + username: 'psagan', + is_staff: false, + email: 'Peter.Sagan@gmail.com', + first_name: 'Peter', + last_name: 'Sagan', + last_enrolled: 2023, + create_time: new Date('July 21, 2024 01:15:00'), + }, +]; + +export const assistants = [ + { + id: '235', + courses: [], + faculties: [], + last_login: null, + username: 'bsimpson', + is_staff: false, + email: 'Bart.Simpson@gmail.be', + first_name: 'Bart', + last_name: 'Simpson', + last_enrolled: 2023, + create_time: new Date('July 21, 2024 01:15:00'), + }, + { + id: '236', + courses: [], + faculties: [], + last_login: null, + username: 'kclijster', + is_staff: false, + email: 'Kim.Clijsters@gmail.be', + first_name: 'Kim', + last_name: 'Clijsters', + last_enrolled: 2023, + create_time: new Date('July 21, 2024 01:15:00'), + }, +]; + +export const admins = [ + { + id: '300201547011', + faculties: [], + roles: ['student'], + last_login: new Date('July 23, 2024 01:15:00'), + username: 'tverslyp', + is_staff: true, + email: 'Tybo.Verslype@UGent.be', + first_name: 'Tybo', + last_name: 'Verslype', + last_enrolled: 2023, + create_time: new Date('July 21, 2024 01:15:00'), + }, + { + id: '400251245031', + faculties: [], + roles: ['student'], + last_login: new Date('July 23, 2024 01:15:00'), + username: 'simmig', + is_staff: true, + email: 'Simon.Mignolet@UGent.be', + first_name: 'Simon', + last_name: 'Mignolet', + last_enrolled: 2023, + create_time: new Date('July 21, 2024 01:15:00'), + }, +]; + +export const structureChecks = [ + { + id: '1', + project: '123456', + obligated_extensions: [], + blocked_extensions: [], + name: '.', + }, + { + id: '2', + project: '123456', + obligated_extensions: [ + { + extension: 'pdf', + }, + ], + blocked_extensions: [], + name: 'folder1', + }, + { + id: '3', + project: '123456', + obligated_extensions: [], + blocked_extensions: [], + name: 'folder3', + }, + { + id: '4', + project: '123456', + obligated_extensions: [ + { + extension: 'gif', + }, + ], + blocked_extensions: [], + name: 'folder3/folder3-1', + }, +]; + +export const submissions = [ + { + id: '1', + group: '1', + files: [], + extra_checks_results: [], + submission_number: 1, + submission_time: new Date('July 21, 2024 01:15:00'), + structureChecks_passed: true, + }, + { + id: '2', + group: '1', + files: [], + extra_checks_results: [], + submission_number: 2, + submission_time: new Date('July 21, 2024 01:15:00'), + structureChecks_passed: true, + }, +]; diff --git a/frontend/src/test/unit/services/setup/delete_handlers.ts b/frontend/src/test/unit/services/setup/delete_handlers.ts new file mode 100644 index 00000000..c43ac547 --- /dev/null +++ b/frontend/src/test/unit/services/setup/delete_handlers.ts @@ -0,0 +1,32 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { HttpResponse, http } from 'msw'; + +import { endpoints } from '@/config/endpoints.ts'; +import { + groups, + projects, + courses, + faculties, + students, + teachers, + assistants, + admins, + structureChecks, + submissions, +} from './data'; + +const baseUrl = 'http://localhost'; + +export const deleteHandlers = [ + http.delete(baseUrl + endpoints.admins.retrieve.replace('{id}', ':id'), async ({ params }) => { + const index = admins.findIndex((x) => x.id === params.id); + admins.splice(index, 1); + return HttpResponse.json(admins); + }), + http.delete(baseUrl + endpoints.assistants.retrieve.replace('{id}', ':id'), async ({ params }) => { + const index = assistants.findIndex((x) => x.id === params.id); + assistants.splice(index, 1); + return HttpResponse.json(assistants); + }), +]; diff --git a/frontend/src/test/unit/services/setup/get_handlers.ts b/frontend/src/test/unit/services/setup/get_handlers.ts new file mode 100644 index 00000000..6db68be1 --- /dev/null +++ b/frontend/src/test/unit/services/setup/get_handlers.ts @@ -0,0 +1,142 @@ +import { HttpResponse, http } from 'msw'; + +import { endpoints } from '@/config/endpoints.ts'; +import { + groups, + projects, + courses, + faculties, + students, + teachers, + assistants, + admins, + structureChecks, + submissions, +} from './data'; + +const baseUrl = 'http://localhost'; + +export const getHandlers = [ + http.get(baseUrl + endpoints.groups.retrieve.replace('{id}', ':id'), ({ params }) => { + return HttpResponse.json(groups.find((x) => x.id === params.id)); + }), + http.get(baseUrl + endpoints.submissions.retrieve.replace('{id}', ':id'), ({ params }) => { + return HttpResponse.json(submissions.find((x) => x.id === params.id)); + }), + http.get(baseUrl + endpoints.structureChecks.retrieve.replace('{id}', ':id'), ({ params }) => { + return HttpResponse.json(structureChecks.find((x) => x.id === params.id)); + }), + http.get(baseUrl + endpoints.admins.retrieve.replace('{id}', ':id'), ({ params }) => { + return HttpResponse.json(admins.find((x) => x.id === params.id)); + }), + http.get(baseUrl + endpoints.teachers.retrieve.replace('{id}', ':id'), ({ params }) => { + return HttpResponse.json(teachers.find((x) => x.id === params.id)); + }), + http.get(baseUrl + endpoints.assistants.retrieve.replace('{id}', ':id'), ({ params }) => { + return HttpResponse.json(assistants.find((x) => x.id === params.id)); + }), + http.get(baseUrl + endpoints.students.retrieve.replace('{id}', ':id'), ({ params }) => { + return HttpResponse.json(students.find((x) => x.id === params.id)); + }), + http.get(baseUrl + endpoints.projects.retrieve.replace('{id}', ':id'), ({ params }) => { + return HttpResponse.json(projects.find((x) => x.id === params.id)); + }), + http.get(baseUrl + endpoints.courses.retrieve.replace('{id}', ':id'), ({ params }) => { + return HttpResponse.json(courses.find((x) => x.id === params.id)); + }), + http.get(baseUrl + endpoints.groups.byProject.replace('{projectId}', ':id'), ({ params }) => { + return HttpResponse.json(groups.filter((x) => x.project === params.id)); + }), + http.get(baseUrl + endpoints.submissions.byProject.replace('{projectId}', ':id'), ({ params }) => { + const project = projects.find((x) => x.id === params.id); + const submittedSubmissions = project !== null && project !== undefined ? project.submissions : []; + return HttpResponse.json(submissions.filter((x) => submittedSubmissions.includes(x.id))); + }), + http.get(baseUrl + endpoints.teachers.byCourse.replace('{courseId}', ':id'), ({ params }) => { + const course = courses.find((x) => x.id === params.id); + const teacherIds = course !== null && course !== undefined ? course.teachers : []; + return HttpResponse.json(teachers.filter((x) => teacherIds.includes(x.id))); + }), + http.get(baseUrl + endpoints.assistants.byCourse.replace('{courseId}', ':id'), ({ params }) => { + const course = courses.find((x) => x.id === params.id); + const assistantIds = course !== null && course !== undefined ? course.assistants : []; + return HttpResponse.json(assistants.filter((x) => assistantIds.includes(x.id))); + }), + http.get(baseUrl + endpoints.courses.byStudent.replace('{studentId}', ':id'), ({ params }) => { + const student = students.find((x) => x.id === params.id); + const courseIds = student !== null && student !== undefined ? student.courses : []; + return HttpResponse.json(courses.filter((x) => courseIds.includes(x.id))); + }), + http.get(baseUrl + endpoints.groups.byStudent.replace('{studentId}', ':id'), ({ params }) => { + const student = students.find((x) => x.id === params.id); + const groupIds = student !== null && student !== undefined ? student.groups : []; + return HttpResponse.json(groups.filter((x) => groupIds.includes(x.id))); + }), + http.get(baseUrl + endpoints.students.byCourse.replace('{courseId}', ':id'), ({ params }) => { + const course = courses.find((x) => x.id === params.id); + const studentIds = course !== null && course !== undefined ? course.students : []; + return HttpResponse.json(students.filter((x) => studentIds.includes(x.id))); + }), + http.get(baseUrl + endpoints.students.byGroup.replace('{groupId}', ':id'), ({ params }) => { + const group = groups.find((x) => x.id === params.id); + const studentIds = group !== null && group !== undefined ? group.students : []; + return HttpResponse.json(students.filter((x) => studentIds.includes(x.id))); + }), + http.get(baseUrl + endpoints.submissions.status.replace('{projectId}', ':id'), ({ params }) => { + const project = projects.find((x) => x.id === params.id); + const groupIds = project !== null && project !== undefined ? project.groups : []; + const submissionIds = project !== null && project !== undefined ? project.submissions : []; + const subGroupIds = Array.from( + new Set(submissions.filter((x) => submissionIds.includes(x.id)).map((x) => x.group)), + ); + + // Filter submissions for each subgroup and get the submission with the highest number + const subgroupSubmissions = subGroupIds.map((groupId) => { + const submissionsForGroup = submissions.filter((submission) => submission.group === groupId); + if (submissionsForGroup.length > 0) { + return submissionsForGroup.reduce((maxSubmission, currentSubmission) => { + return currentSubmission.submission_number > maxSubmission.submission_number + ? currentSubmission + : maxSubmission; + }); + } else { + return null; + } + }); + return HttpResponse.json({ + groups_submitted: new Set(submissions.filter((x) => submissionIds.includes(x.id)).map((x) => x.group)).size, + non_empty_groups: groups.filter((x) => groupIds.includes(x.id) && x.students.length > 0).length, + submissions_passed: subgroupSubmissions.filter((x) => x?.structureChecks_passed).length, + }); + }), + http.get(baseUrl + endpoints.structureChecks.byProject.replace('{projectId}', ':id'), ({ params }) => { + return HttpResponse.json(structureChecks.filter((x) => x.project === params.id)); + }), + http.get(baseUrl + endpoints.projects.byCourse.replace('{courseId}', ':id'), ({ params }) => { + return HttpResponse.json(projects.filter((x) => x.course === params.id)); + }), + http.get(baseUrl + endpoints.submissions.byGroup.replace('{groupId}', ':id'), ({ params }) => { + return HttpResponse.json(submissions.filter((x) => x.group === params.id)); + }), + http.get(baseUrl + endpoints.faculties.retrieve.replace('{id}', ':id'), ({ params }) => { + return HttpResponse.json(faculties.find((x) => x.id === params.id)); + }), + http.get(baseUrl + endpoints.faculties.index, () => { + return HttpResponse.json(faculties); + }), + http.get(baseUrl + endpoints.courses.index, () => { + return HttpResponse.json(courses); + }), + http.get(baseUrl + endpoints.admins.index, () => { + return HttpResponse.json(admins); + }), + http.get(baseUrl + endpoints.students.index, () => { + return HttpResponse.json(students); + }), + http.get(baseUrl + endpoints.teachers.index, () => { + return HttpResponse.json(teachers); + }), + http.get(baseUrl + endpoints.assistants.index, () => { + return HttpResponse.json(assistants); + }), +]; diff --git a/frontend/src/test/unit/services/setup/post_handlers.ts b/frontend/src/test/unit/services/setup/post_handlers.ts new file mode 100644 index 00000000..8c604eae --- /dev/null +++ b/frontend/src/test/unit/services/setup/post_handlers.ts @@ -0,0 +1,71 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { HttpResponse, http } from 'msw'; + +import { endpoints } from '@/config/endpoints.ts'; +import { + groups, + projects, + courses, + faculties, + students, + teachers, + assistants, + admins, + structureChecks, + submissions, +} from './data'; + +const baseUrl = 'http://localhost'; + +export const postHandlers = [ + http.post(baseUrl + endpoints.admins.index, async ({ request }) => { + const buffer = await request.arrayBuffer(); + const requestBody = new TextDecoder().decode(buffer); + const newAdmin = JSON.parse(requestBody); + admins.push(newAdmin); + return HttpResponse.json(admins); + }), + http.post(baseUrl + endpoints.assistants.index, async ({ request }) => { + const buffer = await request.arrayBuffer(); + const requestBody = new TextDecoder().decode(buffer); + const newAssistant = JSON.parse(requestBody); + assistants.push(newAssistant); + return HttpResponse.json(assistants); + }), + http.post(baseUrl + endpoints.students.index, async ({ request }) => { + const buffer = await request.arrayBuffer(); + const requestBody = new TextDecoder().decode(buffer); + const newStudent = JSON.parse(requestBody); + students.push(newStudent); + return HttpResponse.json(students); + }), + http.post(baseUrl + endpoints.courses.index, async ({ request }) => { + const buffer = await request.arrayBuffer(); + const requestBody = new TextDecoder().decode(buffer); + const newCourse = JSON.parse(requestBody); + courses.push(newCourse); + return HttpResponse.json(courses); + }), + http.post(baseUrl + endpoints.faculties.index, async ({ request }) => { + const buffer = await request.arrayBuffer(); + const requestBody = new TextDecoder().decode(buffer); + const newFaculty = JSON.parse(requestBody); + faculties.push(newFaculty); + return HttpResponse.json(faculties); + }), + http.post(baseUrl + endpoints.groups.byProject.replace('{projectId}', ':id'), async ({ request, params }) => { + const buffer = await request.arrayBuffer(); + const requestBody = new TextDecoder().decode(buffer); + const newGroup = JSON.parse(requestBody); + groups.push(newGroup); + return HttpResponse.json(groups); + }), + http.post(baseUrl + endpoints.projects.byCourse.replace('{courseId}', ':id'), async ({ request, params }) => { + const buffer = await request.arrayBuffer(); + const requestBody = new TextDecoder().decode(buffer); + const newProject = JSON.parse(requestBody); + projects.push(newProject); + return HttpResponse.json(projects); + }), +]; diff --git a/frontend/src/test/unit/services/setup/request_handlers.ts b/frontend/src/test/unit/services/setup/request_handlers.ts new file mode 100644 index 00000000..102f7fec --- /dev/null +++ b/frontend/src/test/unit/services/setup/request_handlers.ts @@ -0,0 +1,5 @@ +import { getHandlers } from './get_handlers'; +import { postHandlers } from './post_handlers'; +import { deleteHandlers } from './delete_handlers'; + +export const restHandlers = [...getHandlers, ...postHandlers, ...deleteHandlers]; diff --git a/frontend/src/test/unit/services/setup/setup.ts b/frontend/src/test/unit/services/setup/setup.ts new file mode 100644 index 00000000..ee7b1b3b --- /dev/null +++ b/frontend/src/test/unit/services/setup/setup.ts @@ -0,0 +1,34 @@ +import { afterAll, afterEach, beforeAll } from 'vitest'; +import { setupServer } from 'msw/node'; +import { createApp } from 'vue'; +import { createPinia } from 'pinia'; +import { JSDOM } from 'jsdom'; + +import { restHandlers } from './request_handlers'; + +const server = setupServer(...restHandlers); + +beforeAll(() => { + // throw an error when a request without a handler is encountered + server.listen({ onUnhandledRequest: 'error' }); + + // Set up jdom + const dom = new JSDOM(`
`); + global.document = dom.window.document; + global.window = dom.window as unknown as Window & typeof globalThis; + + // Set up the app with pinia + const pinia = createPinia(); + const app = createApp({ + template: '

App

', + }); + app.use(pinia); +}); + +afterAll(() => { + server.close(); +}); + +afterEach(() => { + server.resetHandlers(); +}); diff --git a/frontend/src/test/unit/structure_check.test.ts b/frontend/src/test/unit/services/structure_check.test.ts similarity index 86% rename from frontend/src/test/unit/structure_check.test.ts rename to frontend/src/test/unit/services/structure_check.test.ts index b2912e95..35f47179 100644 --- a/frontend/src/test/unit/structure_check.test.ts +++ b/frontend/src/test/unit/services/structure_check.test.ts @@ -2,10 +2,25 @@ import { describe, it, expect } from 'vitest'; import { useStructureCheck } from '@/composables/services/structure_check.service.ts'; -const { structureChecks, structureCheck, getStructureCheckByID, getStructureCheckByProject } = useStructureCheck(); +const { + structureChecks, + structureCheck, + getStructureCheckByID, + getStructureCheckByProject, + + createStructureCheck, + deleteStructureCheck, +} = useStructureCheck(); + +function resetService(): void { + structureCheck.value = null; + structureChecks.value = null; +} describe('structureCheck', (): void => { it('gets structure check data by id', async () => { + resetService(); + await getStructureCheckByID('1'); expect(structureCheck.value).not.toBeNull(); expect(structureCheck.value?.name).toBe('.'); @@ -15,6 +30,8 @@ describe('structureCheck', (): void => { }); it('gets structureCheck data', async () => { + resetService(); + await getStructureCheckByProject('123456'); expect(structureChecks).not.toBeNull(); expect(Array.isArray(structureChecks.value)).toBe(true); diff --git a/frontend/src/test/unit/student_service.test.ts b/frontend/src/test/unit/services/student_service.test.ts similarity index 90% rename from frontend/src/test/unit/student_service.test.ts rename to frontend/src/test/unit/services/student_service.test.ts index 484a751f..3f97fa95 100644 --- a/frontend/src/test/unit/student_service.test.ts +++ b/frontend/src/test/unit/services/student_service.test.ts @@ -10,12 +10,26 @@ const { getStudentByID, getStudents, getStudentsByCourse, + getStudentsByGroup, createStudent, + deleteStudent, + + studentJoinCourse, + studentLeaveCourse, + studentJoinGroup, + studentLeaveGroup, } = useStudents(); +function resetService(): void { + student.value = null; + students.value = null; +} + describe('students', (): void => { it('gets student data by id', async () => { + resetService(); + await getStudentByID('1'); expect(student.value).not.toBeNull(); expect(student.value?.username).toBe('jdoe'); @@ -33,6 +47,8 @@ describe('students', (): void => { }); it('gets students data', async () => { + resetService(); + await getStudents(); expect(students).not.toBeNull(); expect(Array.isArray(students.value)).toBe(true); @@ -92,6 +108,8 @@ describe('students', (): void => { }); it('gets students data by course', async () => { + resetService(); + await getStudentsByCourse('1'); expect(students).not.toBeNull(); expect(Array.isArray(students.value)).toBe(true); @@ -151,18 +169,20 @@ describe('students', (): void => { }); it('create student', async () => { + resetService(); + const exampleStudent = new Student( - '103', - 'sample_student', - 'sample.student@UGent.be', - 'Sample', - 'Student', - false, - 2024, - new Date('April 2, 2023 01:15:00'), - new Date('April 2, 2024 01:15:00'), - '12345', - ['student'], + '', // id + '', // username + 'sample.student@UGent.be', // email + 'student_first_name', // first_name + 'student_last_name', // last_name + false, // is_staff + 2024, // last_enrolled + new Date(), // create_time + null, // last_login + '', // studentId + [], [], [], [], @@ -180,8 +200,9 @@ describe('students', (): void => { expect(Array.isArray(students.value)).toBe(true); expect(students.value?.length).toBe(prevLength + 1); - expect(students.value?.[prevLength]?.first_name).toBe('Sample'); - expect(students.value?.[prevLength]?.last_name).toBe('Student'); + // Only check for fields that are sent to the backend + expect(students.value?.[prevLength]?.first_name).toBe('student_first_name'); + expect(students.value?.[prevLength]?.last_name).toBe('student_last_name'); expect(students.value?.[prevLength]?.email).toBe('sample.student@UGent.be'); }); }); diff --git a/frontend/src/test/unit/submission_service.test.ts b/frontend/src/test/unit/services/submission_service.test.ts similarity index 88% rename from frontend/src/test/unit/submission_service.test.ts rename to frontend/src/test/unit/services/submission_service.test.ts index 1b484c50..83e54238 100644 --- a/frontend/src/test/unit/submission_service.test.ts +++ b/frontend/src/test/unit/services/submission_service.test.ts @@ -2,10 +2,26 @@ import { describe, it, expect } from 'vitest'; import { useSubmission } from '@/composables/services/submission.service.ts'; -const { submissions, submission, getSubmissionByID, getSubmissionByProject, getSubmissionByGroup } = useSubmission(); +const { + submissions, + submission, + getSubmissionByID, + getSubmissionByProject, + getSubmissionByGroup, + + createSubmission, + deleteSubmission, +} = useSubmission(); + +function resetService(): void { + submission.value = null; + submissions.value = null; +} describe('submissions', (): void => { it('gets submissions data by id', async () => { + resetService(); + await getSubmissionByID('1'); expect(submission.value).not.toBeNull(); expect(submission.value?.group).toBeNull(); @@ -16,6 +32,8 @@ describe('submissions', (): void => { }); it('gets submissions data by group', async () => { + resetService(); + await getSubmissionByGroup('1'); expect(submissions).not.toBeNull(); expect(Array.isArray(submissions.value)).toBe(true); @@ -35,6 +53,8 @@ describe('submissions', (): void => { }); it('gets submissions data by project', async () => { + resetService(); + await getSubmissionByProject('0'); expect(submissions).not.toBeNull(); expect(Array.isArray(submissions.value)).toBe(true); diff --git a/frontend/src/test/unit/submission_status_service.test.ts b/frontend/src/test/unit/services/submission_status_service.test.ts similarity index 88% rename from frontend/src/test/unit/submission_status_service.test.ts rename to frontend/src/test/unit/services/submission_status_service.test.ts index 30aeb982..896a4df6 100644 --- a/frontend/src/test/unit/submission_status_service.test.ts +++ b/frontend/src/test/unit/services/submission_status_service.test.ts @@ -4,8 +4,14 @@ import { useSubmissionStatus } from '@/composables/services/submission_status.se const { submissionStatus, getSubmissionStatusByProject } = useSubmissionStatus(); +function resetService(): void { + submissionStatus.value = null; +} + describe('submision_status', (): void => { it('gets submision status data by project', async () => { + resetService(); + await getSubmissionStatusByProject('0'); expect(submissionStatus.value).not.toBeNull(); expect(submissionStatus.value?.groups_submitted).toBe(1); diff --git a/frontend/src/test/unit/teacher_service.test.ts b/frontend/src/test/unit/services/teacher_service.test.ts similarity index 94% rename from frontend/src/test/unit/teacher_service.test.ts rename to frontend/src/test/unit/services/teacher_service.test.ts index c29dc6bf..de1d392e 100644 --- a/frontend/src/test/unit/teacher_service.test.ts +++ b/frontend/src/test/unit/services/teacher_service.test.ts @@ -9,10 +9,23 @@ const { getTeacherByID, getTeacherByCourse, getTeachers, + + createTeacher, + deleteTeacher, + + teacherJoinCourse, + teacherLeaveCourse, } = useTeacher(); +function resetService(): void { + teacher.value = null; + teachers.value = null; +} + describe('teachers', (): void => { it('gets teacher data by id', async () => { + resetService(); + await getTeacherByID('123'); expect(teacher.value).not.toBeNull(); expect(teacher.value?.username).toBe('tboonen'); @@ -28,6 +41,8 @@ describe('teachers', (): void => { }); it('gets teacher data', async () => { + resetService(); + await getTeachers(); expect(teachers).not.toBeNull(); expect(Array.isArray(teachers.value)).toBe(true); @@ -57,6 +72,8 @@ describe('teachers', (): void => { }); it('gets teacher data by course', async () => { + resetService(); + await getTeacherByCourse('1'); expect(teachers).not.toBeNull(); expect(Array.isArray(teachers.value)).toBe(true); diff --git a/frontend/src/test/unit/setup.ts b/frontend/src/test/unit/setup.ts deleted file mode 100644 index 40558bd5..00000000 --- a/frontend/src/test/unit/setup.ts +++ /dev/null @@ -1,547 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -import { afterAll, afterEach, beforeAll } from 'vitest'; -import { setupServer } from 'msw/node'; -import { HttpResponse, http } from 'msw'; -import { createApp } from 'vue'; -import { createPinia } from 'pinia'; - -import { endpoints } from '@/config/endpoints.ts'; -import { JSDOM } from 'jsdom'; - -const baseUrl = 'http://localhost'; - -const groups = [ - { - id: '0', - score: 20, - project: '0', - students: ['1', '2', '3', '000201247011'], - }, - { - id: '1', - score: 18, - project: '0', - students: ['1', '2', '3', '000201247011'], - }, -]; - -const projects = [ - { - id: '0', - course: '1', - name: 'sel2', - description: 'this is a test', - visible: true, - archived: false, - locked_groups: false, - start_date: new Date('July 21, 2024 01:15:00'), - deadline: new Date('July 23, 2024 01:15:00'), - max_score: 100, - score_visible: true, - group_size: 8, - submissions: ['1', '2'], - groups: ['0', '1'], - }, - { - id: 1, - course: '1', - name: 'sel3', - description: 'make a project', - visible: true, - archived: false, - locked_groups: false, - start_date: new Date('July 21, 2024 01:15:00'), - deadline: new Date('July 23, 2024 01:15:00'), - max_score: 20, - score_visible: false, - group_size: 3, - submissions: [], - groups: ['0', '1'], - }, -]; - -const courses = [ - { - id: '1', - teachers: ['123', '124'], - assistants: ['235', '236'], - students: ['1', '2', '3', '000201247011'], - projects: ['0', '1'], - parent_course: null, - faculty: null, - name: 'Math', - academic_startyear: 2023, - description: 'Math course', - }, - { - id: '2', - teachers: [], - assistants: [], - students: [], - projects: [], - parent_course: '3', - faculty: null, - name: 'Sel2', - academic_startyear: 2023, - description: 'Software course', - }, - { - id: '3', - teachers: [], - assistants: [], - students: [], - projects: [], - parent_course: null, - faculty: null, - name: 'Sel1', - academic_startyear: 2022, - description: 'Software course', - }, - { - id: '12', - teachers: [], - assistants: [], - students: [], - projects: [], - parent_course: '1', - faculty: null, - name: 'Math', - academic_startyear: 2024, - description: 'Math course', - }, - { - id: '13', - teachers: [], - assistants: [], - students: [], - projects: [], - parent_course: '12', - faculty: null, - name: 'Math', - academic_startyear: 2025, - description: 'Math course', - }, - { - id: '14', - teachers: [], - assistants: [], - students: [], - projects: [], - parent_course: null, - faculty: null, - name: 'Club brugge', - academic_startyear: 2023, - description: null, - }, - { - id: '15', - teachers: [], - assistants: [], - students: [], - projects: [], - parent_course: null, - faculty: null, - name: 'vergeet barbara', - academic_startyear: 2023, - description: null, - }, -]; - -const faculties = [ - { id: 'sciences', name: 'wetenschappen' }, - { id: 'football', name: 'voetbal' }, -]; - -const students = [ - { - id: '1', - last_login: null, - username: 'jdoe', - is_staff: false, - email: 'John.Doe@hotmail.com', - first_name: 'John', - last_name: 'Doe', - last_enrolled: 2023, - create_time: new Date('July 21, 2024 01:15:00'), - studentId: null, - roles: ['student'], - courses: ['1', '2', '3'], - groups: ['0'], - faculties: [], - }, - { - id: '2', - last_login: null, - username: 'bverhae', - is_staff: false, - email: 'Bartje.Verhaege@gmail.com', - first_name: 'Bartje', - last_name: 'Verhaege', - last_enrolled: 2023, - create_time: new Date('July 21, 2024 01:15:00'), - studentId: null, - roles: ['student'], - courses: [], - groups: [], - faculties: [], - }, - { - id: '000201247011', - last_login: new Date('July 30, 2024 01:15:00'), - username: 'tverslyp', - is_staff: true, - email: 'Tybo.Verslype@UGent.be', - first_name: 'Tybo', - last_name: 'Verslype', - last_enrolled: 2023, - create_time: new Date('July 21, 2024 01:15:00'), - studentId: '02012470', - roles: ['student'], - courses: [], - groups: [], - faculties: [], - }, - { - id: '3', - last_login: null, - username: 'somtin', - is_staff: false, - email: 'somtin.somtin@gmail.com', - first_name: 'somtin', - last_name: 'somtin', - last_enrolled: 2023, - create_time: new Date('July 21, 2024 01:15:00'), - studentId: null, - roles: ['student'], - courses: [], - groups: [], - faculties: [], - }, -]; - -const teachers = [ - { - id: '123', - last_login: null, - username: 'tboonen', - is_staff: false, - email: 'Tom.Boonen@gmail.be', - first_name: 'Tom', - last_name: 'Boonen', - last_enrolled: 2023, - create_time: new Date('July 21, 2024 01:15:00'), - }, - { - id: '124', - last_login: null, - username: 'psagan', - is_staff: false, - email: 'Peter.Sagan@gmail.com', - first_name: 'Peter', - last_name: 'Sagan', - last_enrolled: 2023, - create_time: new Date('July 21, 2024 01:15:00'), - }, -]; - -const assistants = [ - { - id: '235', - courses: [], - faculties: [], - last_login: null, - username: 'bsimpson', - is_staff: false, - email: 'Bart.Simpson@gmail.be', - first_name: 'Bart', - last_name: 'Simpson', - last_enrolled: 2023, - create_time: new Date('July 21, 2024 01:15:00'), - }, - { - id: '236', - courses: [], - faculties: [], - last_login: null, - username: 'kclijster', - is_staff: false, - email: 'Kim.Clijsters@gmail.be', - first_name: 'Kim', - last_name: 'Clijsters', - last_enrolled: 2023, - create_time: new Date('July 21, 2024 01:15:00'), - }, -]; - -const admins = [ - { - id: '300201547011', - faculties: [], - roles: ['student'], - last_login: new Date('July 23, 2024 01:15:00'), - username: 'tverslyp', - is_staff: true, - email: 'Tybo.Verslype@UGent.be', - first_name: 'Tybo', - last_name: 'Verslype', - last_enrolled: 2023, - create_time: new Date('July 21, 2024 01:15:00'), - }, - { - id: '400251245031', - faculties: [], - roles: ['student'], - last_login: new Date('July 23, 2024 01:15:00'), - username: 'simmig', - is_staff: true, - email: 'Simon.Mignolet@UGent.be', - first_name: 'Simon', - last_name: 'Mignolet', - last_enrolled: 2023, - create_time: new Date('July 21, 2024 01:15:00'), - }, -]; - -const structureChecks = [ - { - id: '1', - project: '123456', - obligated_extensions: [], - blocked_extensions: [], - name: '.', - }, - { - id: '2', - project: '123456', - obligated_extensions: [ - { - extension: 'pdf', - }, - ], - blocked_extensions: [], - name: 'folder1', - }, - { - id: '3', - project: '123456', - obligated_extensions: [], - blocked_extensions: [], - name: 'folder3', - }, - { - id: '4', - project: '123456', - obligated_extensions: [ - { - extension: 'gif', - }, - ], - blocked_extensions: [], - name: 'folder3/folder3-1', - }, -]; - -const submissions = [ - { - id: '1', - group: '1', - files: [], - extra_checks_results: [], - submission_number: 1, - submission_time: new Date('July 21, 2024 01:15:00'), - structureChecks_passed: true, - }, - { - id: '2', - group: '1', - files: [], - extra_checks_results: [], - submission_number: 2, - submission_time: new Date('July 21, 2024 01:15:00'), - structureChecks_passed: true, - }, -]; - -export const restHandlers = [ - http.get(baseUrl + endpoints.groups.retrieve.replace('{id}', ':id'), ({ params }) => { - return HttpResponse.json(groups.find((x) => x.id === params.id)); - }), - http.get(baseUrl + endpoints.submissions.retrieve.replace('{id}', ':id'), ({ params }) => { - return HttpResponse.json(submissions.find((x) => x.id === params.id)); - }), - http.get(baseUrl + endpoints.structureChecks.retrieve.replace('{id}', ':id'), ({ params }) => { - return HttpResponse.json(structureChecks.find((x) => x.id === params.id)); - }), - http.get(baseUrl + endpoints.admins.retrieve.replace('{id}', ':id'), ({ params }) => { - return HttpResponse.json(admins.find((x) => x.id === params.id)); - }), - http.get(baseUrl + endpoints.teachers.retrieve.replace('{id}', ':id'), ({ params }) => { - return HttpResponse.json(teachers.find((x) => x.id === params.id)); - }), - http.get(baseUrl + endpoints.assistants.retrieve.replace('{id}', ':id'), ({ params }) => { - return HttpResponse.json(assistants.find((x) => x.id === params.id)); - }), - http.get(baseUrl + endpoints.students.retrieve.replace('{id}', ':id'), ({ params }) => { - return HttpResponse.json(students.find((x) => x.id === params.id)); - }), - http.get(baseUrl + endpoints.projects.retrieve.replace('{id}', ':id'), ({ params }) => { - return HttpResponse.json(projects.find((x) => x.id === params.id)); - }), - http.get(baseUrl + endpoints.courses.retrieve.replace('{id}', ':id'), ({ params }) => { - return HttpResponse.json(courses.find((x) => x.id === params.id)); - }), - http.get(baseUrl + endpoints.groups.byProject.replace('{projectId}', ':id'), ({ params }) => { - return HttpResponse.json(groups.filter((x) => x.project === params.id)); - }), - http.get(baseUrl + endpoints.submissions.byProject.replace('{projectId}', ':id'), ({ params }) => { - const project = projects.find((x) => x.id === params.id); - const submittedSubmissions = project !== null && project !== undefined ? project.submissions : []; - return HttpResponse.json(submissions.filter((x) => submittedSubmissions.includes(x.id))); - }), - http.get(baseUrl + endpoints.teachers.byCourse.replace('{courseId}', ':id'), ({ params }) => { - const course = courses.find((x) => x.id === params.id); - const teacherIds = course !== null && course !== undefined ? course.teachers : []; - return HttpResponse.json(submissions.filter((x) => teacherIds.includes(x.id))); - }), - http.get(baseUrl + endpoints.assistants.byCourse.replace('{courseId}', ':id'), ({ params }) => { - const course = courses.find((x) => x.id === params.id); - const assistantIds = course !== null && course !== undefined ? course.assistants : []; - return HttpResponse.json(assistants.filter((x) => assistantIds.includes(x.id))); - }), - http.get(baseUrl + endpoints.courses.byStudent.replace('{studentId}', ':id'), ({ params }) => { - const student = students.find((x) => x.id === params.id); - const courseIds = student !== null && student !== undefined ? student.courses : []; - return HttpResponse.json(courses.filter((x) => courseIds.includes(x.id))); - }), - http.get(baseUrl + endpoints.groups.byStudent.replace('{studentId}', ':id'), ({ params }) => { - const student = students.find((x) => x.id === params.id); - const groupIds = student !== null && student !== undefined ? student.groups : []; - return HttpResponse.json(groups.filter((x) => groupIds.includes(x.id))); - }), - http.get(baseUrl + endpoints.students.byCourse.replace('{courseId}', ':id'), ({ params }) => { - const course = courses.find((x) => x.id === params.id); - const studentIds = course !== null && course !== undefined ? course.students : []; - return HttpResponse.json(students.filter((x) => studentIds.includes(x.id))); - }), - http.get(baseUrl + endpoints.students.byGroup.replace('{groupId}', ':id'), ({ params }) => { - const group = groups.find((x) => x.id === params.id); - const studentIds = group !== null && group !== undefined ? group.students : []; - return HttpResponse.json(students.filter((x) => studentIds.includes(x.id))); - }), - http.get(baseUrl + endpoints.submissions.status.replace('{projectId}', ':id'), ({ params }) => { - const project = projects.find((x) => x.id === params.id); - const groupIds = project !== null && project !== undefined ? project.groups : []; - const submissionIds = project !== null && project !== undefined ? project.submissions : []; - const subGroupIds = Array.from( - new Set(submissions.filter((x) => submissionIds.includes(x.id)).map((x) => x.group)), - ); - - // Filter submissions for each subgroup and get the submission with the highest number - const subgroupSubmissions = subGroupIds.map((groupId) => { - const submissionsForGroup = submissions.filter((submission) => submission.group === groupId); - if (submissionsForGroup.length > 0) { - return submissionsForGroup.reduce((maxSubmission, currentSubmission) => { - return currentSubmission.submission_number > maxSubmission.submission_number - ? currentSubmission - : maxSubmission; - }); - } else { - return null; - } - }); - return HttpResponse.json({ - groups_submitted: new Set(submissions.filter((x) => submissionIds.includes(x.id)).map((x) => x.group)).size, - non_empty_groups: groups.filter((x) => groupIds.includes(x.id) && x.students.length > 0).length, - submissions_passed: subgroupSubmissions.filter((x) => x?.structureChecks_passed).length, - }); - }), - http.get(baseUrl + endpoints.structureChecks.byProject.replace('{projectId}', ':id'), ({ params }) => { - return HttpResponse.json(structureChecks.filter((x) => x.project === params.id)); - }), - http.get(baseUrl + endpoints.projects.byCourse.replace('{courseId}', ':id'), ({ params }) => { - return HttpResponse.json(projects.filter((x) => x.course === params.id)); - }), - http.get(baseUrl + endpoints.submissions.byGroup.replace('{groupId}', ':id'), ({ params }) => { - return HttpResponse.json(submissions.filter((x) => x.group === params.id)); - }), - http.get(baseUrl + endpoints.faculties.retrieve.replace('{id}', ':id'), ({ params }) => { - return HttpResponse.json(faculties.find((x) => x.id === params.id)); - }), - http.get(baseUrl + endpoints.faculties.index, () => { - return HttpResponse.json(faculties); - }), - http.get(baseUrl + endpoints.courses.index, () => { - return HttpResponse.json(courses); - }), - http.get(baseUrl + endpoints.admins.index, () => { - return HttpResponse.json(admins); - }), - http.get(baseUrl + endpoints.students.index, () => { - return HttpResponse.json(students); - }), - http.get(baseUrl + endpoints.teachers.index, () => { - return HttpResponse.json(teachers); - }), - http.get(baseUrl + endpoints.assistants.index, () => { - return HttpResponse.json(assistants); - }), - http.post(baseUrl + endpoints.admins.index, async ({ request }) => { - const buffer = await request.arrayBuffer(); - const requestBody = new TextDecoder().decode(buffer); - const newAdmin = JSON.parse(requestBody); - newAdmin.is_staff = true; - admins.push(newAdmin); - return HttpResponse.json(admins); - }), - http.post(baseUrl + endpoints.assistants.index, async ({ request }) => { - const buffer = await request.arrayBuffer(); - const requestBody = new TextDecoder().decode(buffer); - const newAssistant = JSON.parse(requestBody); - newAssistant.roles = ['assistant']; - assistants.push(newAssistant); - return HttpResponse.json(assistants); - }), - http.post(baseUrl + endpoints.students.index, async ({ request }) => { - const buffer = await request.arrayBuffer(); - const requestBody = new TextDecoder().decode(buffer); - const newStudent = JSON.parse(requestBody); - students.push(newStudent); - return HttpResponse.json(students); - }), - - /* - http.post(baseUrl + endpoints.groups.byProject.replace('{projectId}', ':id'), - ({ params }) => { - const newGroup = params.body; // Assuming the request body contains the new group data - groups.push(newGroup); - return HttpResponse.json(newGroup); - } - ) - */ -]; - -const server = setupServer(...restHandlers); - -beforeAll(() => { - server.listen({ onUnhandledRequest: 'error' }); - - // Set up jdom - const dom = new JSDOM(`
`); - global.document = dom.window.document; - global.window = dom.window as unknown as Window & typeof globalThis; - - // Set up the app with pinia - const pinia = createPinia(); - const app = createApp({ - template: '

App

', - }); - app.use(pinia); -}); - -afterAll(() => { - server.close(); -}); - -afterEach(() => { - server.resetHandlers(); -}); diff --git a/frontend/src/test/unit/types/assistant.test.ts b/frontend/src/test/unit/types/assistant.test.ts new file mode 100644 index 00000000..b743ef81 --- /dev/null +++ b/frontend/src/test/unit/types/assistant.test.ts @@ -0,0 +1,90 @@ +import { describe, it, expect } from 'vitest'; + +import { Assistant } from '@/types/users/Assistant'; + +describe('Assistant class', () => { + const assistantData = { + id: '1', + username: 'assistant1', + email: 'assistant1@example.com', + first_name: 'John', + last_name: 'Doe', + last_enrolled: 2024, + is_staff: true, + roles: [], + faculties: [], + courses: [], + create_time: new Date(), + last_login: null, + }; + + // Test constructor + it('should create an instance of Assistant with correct properties', () => { + const assistant = new Assistant( + assistantData.id, + assistantData.username, + assistantData.email, + assistantData.first_name, + assistantData.last_name, + assistantData.last_enrolled, + assistantData.is_staff, + assistantData.roles, + assistantData.faculties, + assistantData.courses, + assistantData.create_time, + assistantData.last_login, + ); + + expect(assistant).toBeInstanceOf(Assistant); + expect(assistant.id).toBe(assistantData.id); + expect(assistant.username).toBe(assistantData.username); + expect(assistant.email).toBe(assistantData.email); + expect(assistant.first_name).toBe(assistantData.first_name); + expect(assistant.last_name).toBe(assistantData.last_name); + expect(assistant.last_enrolled).toBe(assistantData.last_enrolled); + expect(assistant.is_staff).toBe(assistantData.is_staff); + expect(assistant.faculties).toBe(assistantData.faculties); + expect(assistant.courses).toBe(assistantData.courses); + expect(assistant.create_time).toBe(assistantData.create_time); + expect(assistant.last_login).toBe(assistantData.last_login); + }); + + // Test static method fromJSON + it('should create an Assistant instance from JSON data', () => { + const assistantJSON = { ...assistantData }; + const assistant = Assistant.fromJSON(assistantJSON); + + expect(assistant).toBeInstanceOf(Assistant); + expect(assistant.id).toBe(assistantData.id); + expect(assistant.username).toBe(assistantData.username); + expect(assistant.email).toBe(assistantData.email); + expect(assistant.first_name).toBe(assistantData.first_name); + expect(assistant.last_name).toBe(assistantData.last_name); + expect(assistant.last_enrolled).toBe(assistantData.last_enrolled); + expect(assistant.is_staff).toBe(assistantData.is_staff); + expect(assistant.faculties).toBe(assistantData.faculties); + expect(assistant.courses).toBe(assistantData.courses); + expect(assistant.create_time).toStrictEqual(assistantData.create_time); + expect(assistant.last_login).toStrictEqual(assistantData.last_login); + }); + + // Test method isAssistant + it('should return true when isAssistant method is called', () => { + const assistant = new Assistant( + assistantData.id, + assistantData.username, + assistantData.email, + assistantData.first_name, + assistantData.last_name, + assistantData.last_enrolled, + assistantData.is_staff, + assistantData.roles, + assistantData.faculties, + assistantData.courses, + assistantData.create_time, + assistantData.last_login, + ); + + expect(assistant.isAssistant()).toBe(true); + }); +}); diff --git a/frontend/src/test/unit/types/setup.ts b/frontend/src/test/unit/types/setup.ts new file mode 100644 index 00000000..9425b20f --- /dev/null +++ b/frontend/src/test/unit/types/setup.ts @@ -0,0 +1,7 @@ +import { afterAll, afterEach, beforeAll } from 'vitest'; + +beforeAll(() => {}); + +afterAll(() => {}); + +afterEach(() => {}); diff --git a/frontend/src/types/Course.ts b/frontend/src/types/Course.ts index 0c137328..23d0edf8 100644 --- a/frontend/src/types/Course.ts +++ b/frontend/src/types/Course.ts @@ -46,7 +46,8 @@ export class Course { * @param course */ static fromJSON(course: Course): Course { - const faculty = course.faculty !== null ? Faculty.fromJSON(course.faculty) : null; + const faculty = + course.faculty !== undefined && course.faculty !== null ? Faculty.fromJSON(course.faculty) : null; return new Course( course.id, diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 49ad0edb..c20ece69 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -27,6 +27,6 @@ export default defineConfig({ } }, test: { - setupFiles: "./src/test/unit/setup.ts" - } + setupFiles: "./src/test/unit/services/setup/setup.ts" + }, });