diff --git a/frontend/tests/controllers/assignments-controller.test.ts b/frontend/tests/controllers/assignments-controller.test.ts new file mode 100644 index 00000000..4e765462 --- /dev/null +++ b/frontend/tests/controllers/assignments-controller.test.ts @@ -0,0 +1,65 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { AssignmentController } from '../../src/controllers/assignments'; +import { AssignmentDTO } from '@dwengo-1/common/interfaces/assignment'; + +describe('AssignmentController Tests', () => { + let controller: AssignmentController; + + beforeEach(() => { + controller = new AssignmentController('8764b861-90a6-42e5-9732-c0d9eb2f55f9'); // Example class ID + }); + + it('should fetch all assignments', async () => { + const result = await controller.getAll(true); + expect(result).toHaveProperty('assignments'); + expect(Array.isArray(result.assignments)).toBe(true); + expect(result.assignments.length).toBeGreaterThan(0); + }); + + it('should fetch an assignment by number', async () => { + const assignmentNumber = 21000; // Example assignment ID + const result = await controller.getByNumber(assignmentNumber); + expect(result).toHaveProperty('assignment'); + expect(result.assignment).toHaveProperty('id', assignmentNumber); + }); + + it('should update an existing assignment', async () => { + const assignmentNumber = 21000; + const updatedData = { title: 'Updated Assignment Title' }; + const result = await controller.updateAssignment(assignmentNumber, updatedData); + expect(result).toHaveProperty('assignment'); + expect(result.assignment).toHaveProperty('id', assignmentNumber); + expect(result.assignment).toHaveProperty('title', updatedData.title); + }); + + it('should fetch submissions for an assignment', async () => { + const assignmentNumber = 21000; + const result = await controller.getSubmissions(assignmentNumber, true); + expect(result).toHaveProperty('submissions'); + expect(Array.isArray(result.submissions)).toBe(true); + }); + + it('should fetch questions for an assignment', async () => { + const assignmentNumber = 21000; + const result = await controller.getQuestions(assignmentNumber, true); + expect(result).toHaveProperty('questions'); + expect(Array.isArray(result.questions)).toBe(true); + }); + + it('should fetch groups for an assignment', async () => { + const assignmentNumber = 21000; + const result = await controller.getGroups(assignmentNumber, true); + expect(result).toHaveProperty('groups'); + expect(Array.isArray(result.groups)).toBe(true); + }); + + it('should handle fetching a non-existent assignment', async () => { + const assignmentNumber = 99999; // Non-existent assignment ID + await expect(controller.getByNumber(assignmentNumber)).rejects.toThrow(); + }); + + it('should handle deleting a non-existent assignment', async () => { + const assignmentNumber = 99999; // Non-existent assignment ID + await expect(controller.deleteAssignment(assignmentNumber)).rejects.toThrow(); + }); +}); diff --git a/frontend/tests/controllers/classes-controller.test.ts b/frontend/tests/controllers/classes-controller.test.ts new file mode 100644 index 00000000..e8c07b12 --- /dev/null +++ b/frontend/tests/controllers/classes-controller.test.ts @@ -0,0 +1,10 @@ +import { describe, expect, it } from 'vitest'; +import { ClassController } from '../../src/controllers/classes'; + +describe('Test controller classes', () => { + it('Get classes', async () => { + const controller = new ClassController(); + const data = await controller.getAll(true); + expect(data.classes).to.have.length.greaterThan(0); + }); +}); diff --git a/frontend/tests/controllers/groups.controller.test.ts b/frontend/tests/controllers/groups.controller.test.ts new file mode 100644 index 00000000..30d90c09 --- /dev/null +++ b/frontend/tests/controllers/groups.controller.test.ts @@ -0,0 +1,13 @@ +import { describe, expect, it } from 'vitest'; +import { GroupController } from '../../src/controllers/groups'; + +describe('Test controller groups', () => { + it('Get groups', async () => { + const classId = '8764b861-90a6-42e5-9732-c0d9eb2f55f9'; + const assignmentNumber = 21000; + + const controller = new GroupController(classId, assignmentNumber); + const data = await controller.getAll(true); + expect(data.groups).to.have.length.greaterThan(0); + }); +}); diff --git a/frontend/tests/controllers/learning-paths-controller.test.ts b/frontend/tests/controllers/learning-paths-controller.test.ts new file mode 100644 index 00000000..d742d5ef --- /dev/null +++ b/frontend/tests/controllers/learning-paths-controller.test.ts @@ -0,0 +1,21 @@ +import { beforeEach, describe, expect, it } from 'vitest'; +import { LearningPathController } from '../../src/controllers/learning-paths'; +import { Language } from '../../src/data-objects/language'; + +describe("Test controller learning paths", () => { + let controller: LearningPathController; + + beforeEach(async () => { + controller = new LearningPathController(); + }); + + it("Can search for learning paths", async () => { + const data = await controller.search("kiks", Language.Dutch); + expect(data).to.have.length.greaterThan(0); + }); + + it("Can get learning path by id", async () => { + const data = await controller.getAllByTheme("kiks"); + expect(data).to.have.length.greaterThan(0); + }); +}); diff --git a/frontend/tests/controllers/student-controller.test.ts b/frontend/tests/controllers/student-controller.test.ts index 89c8224e..09688720 100644 --- a/frontend/tests/controllers/student-controller.test.ts +++ b/frontend/tests/controllers/student-controller.test.ts @@ -1,19 +1,30 @@ -import { StudentController } from "../../src/controllers/students"; -import { expect, it, describe, afterAll, beforeAll } from "vitest"; -import { setup, teardown } from "../setup-backend.js"; +import { StudentController } from '../../src/controllers/students'; +import { beforeEach, describe, expect, it } from 'vitest'; -describe("Test controller students", () => { - beforeAll(async () => { - await setup(); +describe('Test controller students', () => { + let controller: StudentController; + + beforeEach(async () => { + controller = new StudentController(); }); - afterAll(async () => { - await teardown(); - }); - - it("Get students", async () => { - const controller = new StudentController(); + it('Get students', async () => { const data = await controller.getAll(true); expect(data.students).to.have.length.greaterThan(0); }); + + it('Get student by username', async () => { + const username = 'testleerling1'; + const data = await controller.getByUsername(username); + expect(data.student.username).to.equal(username); + }); + + it('Get classes of student', async () => { + const students = await controller.getAll(true); + + for (const student of students.students) { + const data = await controller.getClasses(student.username, true); + expect(data.classes).to.have.length.greaterThan(0); + } + }); }); diff --git a/frontend/tests/controllers/submissions-controller.test.ts b/frontend/tests/controllers/submissions-controller.test.ts new file mode 100644 index 00000000..ca2799df --- /dev/null +++ b/frontend/tests/controllers/submissions-controller.test.ts @@ -0,0 +1,15 @@ +import { describe, expect, it } from 'vitest'; +import { SubmissionController } from '../../src/controllers/submissions'; +import { Language } from '../../src/data-objects/language'; + +describe("Test controller submissions", () => { + it("Get submission by number", async () => { + const hruid = "id03"; + const classId = "8764b861-90a6-42e5-9732-c0d9eb2f55f9"; + const controller = new SubmissionController(hruid); + + const data = await controller.getByNumber(Language.English, 1, classId, 1, 1, 1); + + expect(data.submission).to.have.property("submissionNumber"); + }); +}); diff --git a/frontend/tests/controllers/teacher-controller.test.ts b/frontend/tests/controllers/teacher-controller.test.ts new file mode 100644 index 00000000..b0d7ba98 --- /dev/null +++ b/frontend/tests/controllers/teacher-controller.test.ts @@ -0,0 +1,71 @@ +import { beforeEach, describe, expect, it } from 'vitest'; +import { TeacherController } from '../../src/controllers/teachers'; + +describe("Test controller teachers", () => { + let controller: TeacherController; + + beforeEach(async () => { + controller = new TeacherController(); + }); + + it("Get all teachers", async () => { + const data = await controller.getAll(true); + expect(data.teachers).to.have.length.greaterThan(0); + expect(data.teachers[0]).to.have.property("username"); + expect(data.teachers[0]).to.have.property("firstName"); + expect(data.teachers[0]).to.have.property("lastName"); + }); + + it("Get teacher by username", async () => { + const username = "testleerkracht1"; + const data = await controller.getByUsername(username); + expect(data.teacher.username).to.equal(username); + expect(data.teacher).to.have.property("firstName"); + expect(data.teacher).to.have.property("lastName"); + }); + + it("Get teacher by non-existent username", async () => { + const username = "nonexistentuser"; + await expect(controller.getByUsername(username)).rejects.toThrow(); + }); + + it("Create a new teacher", async () => { + const newTeacher = { + username: "newteacher", + firstName: "New", + lastName: "Teacher", + }; + const data = await controller.createTeacher(newTeacher); + expect(data.teacher.username).to.equal(newTeacher.username); + expect(data.teacher.firstName).to.equal(newTeacher.firstName); + expect(data.teacher.lastName).to.equal(newTeacher.lastName); + }); + + it("Delete a teacher", async () => { + const username = "newteacher"; + const data = await controller.deleteTeacher(username); + expect(data).toBeTruthy(); + }); + + it("Handle deletion of non-existent teacher", async () => { + const username = "nonexistentuser"; + await expect(controller.deleteTeacher(username)).rejects.toThrow(); + }); + + it("Get classes for a teacher", async () => { + const username = "testleerkracht1"; + const data = await controller.getClasses(username, true); + expect(data.classes).to.have.length.greaterThan(0); + expect(data.classes[0]).to.have.property("id"); + expect(data.classes[0]).to.have.property("displayName"); + }); + + it("Get students for a teacher", async () => { + const username = "testleerkracht1"; + const data = await controller.getStudents(username, true); + expect(data.students).to.have.length.greaterThan(0); + expect(data.students[0]).to.have.property("username"); + expect(data.students[0]).to.have.property("firstName"); + expect(data.students[0]).to.have.property("lastName"); + }); +}); diff --git a/frontend/tests/utils/assingment-rules.test.ts b/frontend/tests/utils/assingment-rules.test.ts new file mode 100644 index 00000000..c070aa0a --- /dev/null +++ b/frontend/tests/utils/assingment-rules.test.ts @@ -0,0 +1,82 @@ +import { describe, expect, it } from 'vitest'; +import { + assignmentTitleRules, + classRules, + deadlineRules, + descriptionRules, + learningPathRules, +} from '../../src/utils/assignment-rules'; + +describe('Validation Rules', () => { + describe('assignmentTitleRules', () => { + it('should return true for a valid title', () => { + const result = assignmentTitleRules[0]('Valid Title'); + expect(result).toBe(true); + }); + + it('should return an error message for an empty title', () => { + const result = assignmentTitleRules[0](''); + expect(result).toBe('Title cannot be empty.'); + }); + }); + + describe('learningPathRules', () => { + it('should return true for a valid learning path', () => { + const result = learningPathRules[0]({ hruid: '123', title: 'Path Title' }); + expect(result).toBe(true); + }); + + it('should return an error message for an invalid learning path', () => { + const result = learningPathRules[0]({ hruid: '', title: '' }); + expect(result).toBe('You must select a learning path.'); + }); + }); + + describe('classRules', () => { + it('should return true for a valid class', () => { + const result = classRules[0]('Class 1'); + expect(result).toBe(true); + }); + + it('should return an error message for an empty class', () => { + const result = classRules[0](''); + expect(result).toBe('You must select at least one class.'); + }); + }); + + describe('deadlineRules', () => { + it('should return true for a valid future deadline', () => { + const futureDate = new Date(Date.now() + 1000 * 60 * 60).toISOString(); + const result = deadlineRules[0](futureDate); + expect(result).toBe(true); + }); + + it('should return an error message for a past deadline', () => { + const pastDate = new Date(Date.now() - 1000 * 60 * 60).toISOString(); + const result = deadlineRules[0](pastDate); + expect(result).toBe('The deadline must be in the future.'); + }); + + it('should return an error message for an invalid date', () => { + const result = deadlineRules[0]('invalid-date'); + expect(result).toBe('Invalid date or time.'); + }); + + it('should return an error message for an empty deadline', () => { + const result = deadlineRules[0](''); + expect(result).toBe('You must set a deadline.'); + }); + }); + + describe('descriptionRules', () => { + it('should return true for a valid description', () => { + const result = descriptionRules[0]('This is a valid description.'); + expect(result).toBe(true); + }); + + it('should return an error message for an empty description', () => { + const result = descriptionRules[0](''); + expect(result).toBe('Description cannot be empty.'); + }); + }); +}); diff --git a/frontend/tests/utils/deep-equals.test.ts b/frontend/tests/utils/deep-equals.test.ts new file mode 100644 index 00000000..fcad2869 --- /dev/null +++ b/frontend/tests/utils/deep-equals.test.ts @@ -0,0 +1,68 @@ +import { describe, it, expect } from 'vitest'; +import { deepEquals } from '../../src/utils/deep-equals'; + +describe('deepEquals', () => { + it('should return true for identical primitive values', () => { + expect(deepEquals(1, 1)).toBe(true); + expect(deepEquals('test', 'test')).toBe(true); + expect(deepEquals(true, true)).toBe(true); + }); + + it('should return false for different primitive values', () => { + expect(deepEquals(1, 2)).toBe(false); + expect(deepEquals('test', 'other')).toBe(false); + expect(deepEquals(true, false)).toBe(false); + }); + + it('should return true for identical objects', () => { + const obj1 = { a: 1, b: { c: 2 } }; + const obj2 = { a: 1, b: { c: 2 } }; + expect(deepEquals(obj1, obj2)).toBe(true); + }); + + it('should return false for different objects', () => { + const obj1 = { a: 1, b: { c: 2 } }; + const obj2 = { a: 1, b: { c: 3 } }; + expect(deepEquals(obj1, obj2)).toBe(false); + }); + + it('should return true for identical arrays', () => { + const arr1 = [1, 2, [3, 4]]; + const arr2 = [1, 2, [3, 4]]; + expect(deepEquals(arr1, arr2)).toBe(true); + }); + + it('should return false for different arrays', () => { + const arr1 = [1, 2, [3, 4]]; + const arr2 = [1, 2, [3, 5]]; + expect(deepEquals(arr1, arr2)).toBe(false); + }); + + it('should return false for objects and arrays compared', () => { + expect(deepEquals({ a: 1 }, [1])).toBe(false); + }); + + it('should return true for null compared to null', () => { + expect(deepEquals(null, null)).toBe(true); + }); + + it('should return false for null compared to an object', () => { + expect(deepEquals(null, {})).toBe(false); + }); + + it('should return false for undefined compared to null', () => { + expect(deepEquals(undefined, null)).toBe(false); + }); + + it('should return true for deeply nested identical structures', () => { + const obj1 = { a: [1, { b: 2, c: [3, 4] }] }; + const obj2 = { a: [1, { b: 2, c: [3, 4] }] }; + expect(deepEquals(obj1, obj2)).toBe(true); + }); + + it('should return false for deeply nested different structures', () => { + const obj1 = { a: [1, { b: 2, c: [3, 4] }] }; + const obj2 = { a: [1, { b: 2, c: [3, 5] }] }; + expect(deepEquals(obj1, obj2)).toBe(false); + }); +}); diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts index 17c96938..a9beb299 100644 --- a/frontend/vitest.config.ts +++ b/frontend/vitest.config.ts @@ -9,6 +9,7 @@ export default mergeConfig( environment: "jsdom", exclude: [...configDefaults.exclude, "e2e/**"], root: fileURLToPath(new URL("./", import.meta.url)), + testTimeout: 100000, coverage: { reporter: ["text", "json-summary", "json"], // If you want a coverage reports even if your tests are failing, include the reportOnFailure option @@ -16,14 +17,27 @@ export default mergeConfig( exclude: [ "**/*config*", "**/tests/**", - "src/**/*.vue", - "src/**/*.d.ts", - "src/assets/**", - "src/data-objects/**", + "playwright-report/**", "**/dist/**", "**/e2e/**", "**/*config*", "**/node_modules/**", + + "src/main.ts", + "src/router/index.ts", + "src/utils/constants.ts", + + "**/*.d.ts", + + "src/**/*.vue", + "src/assets/**", + "src/i18n/**", + + "src/data-objects/**", + "src/exception/**", // TODO Might be useful to test later + "src/queries/**", // TODO Might be useful to test later + "src/views/learning-paths/gift-adapters/**", // TODO Might be useful to test later + "src/services/auth/**", // TODO Might be useful to test later ], thresholds: { lines: 50, @@ -39,6 +53,7 @@ export default mergeConfig( globalSetup: ["./tests/setup-backend.ts"], * In this project, the backend server is started for each test-file individually. */ + globalSetup: ["./tests/setup-backend.ts"], }, }), );