diff --git a/frontend/src/components/buttons/BackButton.vue b/frontend/src/components/buttons/BackButton.vue new file mode 100644 index 00000000..60b732f0 --- /dev/null +++ b/frontend/src/components/buttons/BackButton.vue @@ -0,0 +1,26 @@ + + + + + diff --git a/frontend/src/components/project/ProjectInfo.vue b/frontend/src/components/project/ProjectInfo.vue index 281fedb1..8818c537 100644 --- a/frontend/src/components/project/ProjectInfo.vue +++ b/frontend/src/components/project/ProjectInfo.vue @@ -34,12 +34,7 @@ - + diff --git a/frontend/src/components/submission/SubmissionCard.vue b/frontend/src/components/submission/SubmissionCard.vue index c4d5e562..0992e811 100644 --- a/frontend/src/components/submission/SubmissionCard.vue +++ b/frontend/src/components/submission/SubmissionCard.vue @@ -1,74 +1,86 @@ @@ -134,5 +146,7 @@ const downloadAll = () => { .v-card { border-color: rgb(var(--v-theme-text)); background-color: rgb(var(--v-theme-background)); + margin-top: 10px; + margin-bottom: 10px; } diff --git a/frontend/src/components/submission/SubmissionList.vue b/frontend/src/components/submission/SubmissionList.vue index 1ab087c8..453e4fe7 100644 --- a/frontend/src/components/submission/SubmissionList.vue +++ b/frontend/src/components/submission/SubmissionList.vue @@ -1,24 +1,32 @@ @@ -57,3 +65,19 @@ const refetch_timer = () => { }; refetch_timer(); + diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index 534e43e9..f1ac3da5 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -66,6 +66,7 @@ export default { not_found2: "Project not found", to_subject: "To subject", to_groups: "To groups", + to_project: "To Project", }, navigation: { home: "Home", diff --git a/frontend/src/i18n/locales/nl.ts b/frontend/src/i18n/locales/nl.ts index 9b7259c5..6b9d58ff 100644 --- a/frontend/src/i18n/locales/nl.ts +++ b/frontend/src/i18n/locales/nl.ts @@ -34,7 +34,7 @@ export default { no_submission_files: "Geen indieningen gevonden", }, submission: { - status: "Indiening status: {status}", + status: "Status indiening:", datetime: "Indiening tijdstip:", remarks: "Opmerkingen", remarks_empty: "Geen opmerkingen voor deze indiening", @@ -65,6 +65,7 @@ export default { not_found2: "Project niet teruggevonden", to_subject: "Naar vak", to_groups: "Naar groepen", + to_project: "Naar project", }, navigation: { home: "Hoofdscherm", diff --git a/frontend/src/views/CreateProjectView.vue b/frontend/src/views/CreateProjectView.vue index 84e0ece5..cabab9a4 100644 --- a/frontend/src/views/CreateProjectView.vue +++ b/frontend/src/views/CreateProjectView.vue @@ -170,7 +170,7 @@ async function submitForm() { }); } else if (selectedGroupProject.value === "random") { const groups = divideStudentsIntoGroups(studentsData.value || [], capacity.value); - const groupsToCreate = groups.map((_) => ({ + const groupsToCreate = groups.map(() => ({ project_id: createdProjectId, score: 0, })); diff --git a/frontend/src/views/GroupView.vue b/frontend/src/views/GroupView.vue index 13a719e3..f82abb6c 100644 --- a/frontend/src/views/GroupView.vue +++ b/frontend/src/views/GroupView.vue @@ -4,49 +4,63 @@

{{ $t("group.not_found") }}

-
-

{{ $t("project.group", { number: group!.num }) }}

-

{{ "Project: " + project!.name }}

- - -
- - {{ member.given_name + " " + member.surname }} - - - {{ $t("group.remove") }} - - -
-
- {{ $t("group.no_members_found") }} -
-
- - - -
- -
+ + +

{{ $t("project.group", { number: group!.num }) }}

+

{{ "Project: " + project!.name }}

+ + +
+ + {{ member.given_name + " " + member.surname }} + + + {{ $t("group.remove") }} + + +
+
+ {{ $t("group.no_members_found") }} +
+
+ + + +
+ + + +
+ + + +
@@ -57,6 +71,7 @@ import { useProjectQuery } from "@/queries/Project"; import { useCurrentUserQuery } from "@/queries/User"; import GroupButtons from "@/components/buttons/GroupButtons.vue"; import SubmissionList from "@/components/submission/SubmissionList.vue"; +import BackButton from "@/components/buttons/BackButton.vue"; import { useSubjectInstructorsQuery } from "@/queries/Subject"; const props = defineProps<{ @@ -116,4 +131,12 @@ const { mutateAsync: removeStudent } = useRemoveUserFromGroupMutation(); .members { margin-top: 15px; } + +.backbutton { + margin-top: 25px; +} + +.submissions { + background-color: rgb(var(--v-theme-background)); +} diff --git a/frontend/tests/components/buttons/BackButton.spec.ts b/frontend/tests/components/buttons/BackButton.spec.ts new file mode 100644 index 00000000..fae4e799 --- /dev/null +++ b/frontend/tests/components/buttons/BackButton.spec.ts @@ -0,0 +1,18 @@ +import {mount} from "@vue/test-utils"; +import {expect, describe, it} from "vitest"; +import BackButton from "@/components/buttons/BackButton.vue" + +describe("BackButton", () => { + const wrapper = mount(BackButton, { + props: { + title: "group.to_grouppage", + destination: "/test" + }, + global: { + stubs: ["router-link"], + }, + }) + it("render button", () => { + expect(wrapper.findComponent({name: "router-link"}).exists()).toBeTruthy() + }); +}) diff --git a/frontend/tests/components/submission/SubmissionCard.spec.ts b/frontend/tests/components/submission/SubmissionCard.spec.ts new file mode 100644 index 00000000..936b036d --- /dev/null +++ b/frontend/tests/components/submission/SubmissionCard.spec.ts @@ -0,0 +1,56 @@ +import {mount} from "@vue/test-utils"; +import {expect, describe, it, vi} from "vitest"; +import SubmissionCard from "@/components/submission/SubmissionCard.vue" +import {ref} from "vue"; + +const testFilesQuery = { + data: ref([{filename: "testfile"}]), + isLoading: ref(false), + isError: ref(true), + error: ref({message: "error message"}), + setError(value){ + this.isError.value = value; + } +} + +vi.mock("@/queries/Submission", () => ({ + useFilesQuery: vi.fn(() => testFilesQuery) +})) + +const mockSubmission = ref({id: 1, date: new Date(2024, 4, 15), status: 2, remarks: "testremarks", + testresults: []}) + +describe("SubmissionCard", () => { + it("render if error", async () => { + const wrapper = mount(SubmissionCard, { + props: { + submission: mockSubmission, + deadline: new Date(2024, 5, 12) + } + }) + expect(wrapper.text()).toContain("error message") + testFilesQuery.setError(false) + await wrapper.vm.$nextTick() + }); + it("render submissions card", () => { + const wrapper = mount(SubmissionCard, { + props: { + submission: mockSubmission, + deadline: new Date(2024, 5, 12) + } + }) + const text = wrapper.text() + expect(text).toContain("Status indiening: Accepted") + expect(text).toContain("testremarks") + expect(text).toContain("testfile") + }); + it("render submission after deadline", () => { + const wrapper = mount(SubmissionCard, { + props: { + submission: mockSubmission, + deadline: new Date(2024, 3, 12) + } + }) + expect(wrapper.text()).toContain("Na deadline") + }) +}) diff --git a/frontend/tests/components/submission/SubmissionList.spec.ts b/frontend/tests/components/submission/SubmissionList.spec.ts new file mode 100644 index 00000000..24653f6b --- /dev/null +++ b/frontend/tests/components/submission/SubmissionList.spec.ts @@ -0,0 +1,101 @@ +import {mount} from "@vue/test-utils"; +import {expect, describe, it, vi} from "vitest"; +import SubmissionList from "@/components/submission/SubmissionList.vue" +import {computed, ref} from "vue"; + +const testSubmissionsQuery = { + data: ref([]), + error: ref({message: "testerrormessage"}), + isLoading: ref(false), + isError: ref(true), + setError(value){ + this.isError.value = value + }, + refetch: vi.fn() +} + +vi.mock("@/queries/Submission", () => ({ + useSubmissionsQuery: vi.fn(() => testSubmissionsQuery) +})) + +const testGroupQuery = { + data: ref({project_id: 1}) +} + +vi.mock("@/queries/Group", () => ({ + useGroupQuery: vi.fn(() => testGroupQuery) +})) + +const testProjectQuery = { + data: ref({id: 1, name: "testproject"}), + isSuccess: ref(true) +} + +vi.mock("@/queries/Project", () => ({ + useProjectQuery: vi.fn(() => testProjectQuery) +})) + +vi.mock("@/components/submission/SubmissionCard.vue", () => ({ + default: { + template: "
" + } +})) + +describe("SubmissionsList", () => { + it("render if error", async () => { + const wrapper = mount(SubmissionList, { + props: { + groupId: 1 + }, + global: { + mocks: { + sorted: computed(() => {return []}) + } + } + }) + expect(wrapper.text()).toContain("testerrormessage") + testSubmissionsQuery.setError(false) + await wrapper.vm.$nextTick() + }); + it("render submissionslist", () => { + const wrapper = mount(SubmissionList, { + props: { + groupId: 1 + }, + global: { + mocks: { + sorted: computed(() => {return []}) + } + } + }) + expect(wrapper.text()).toContain("Indieningen voor project") + const VBtn = wrapper.findComponent({name:"VBtn"}) + expect(VBtn.text()).toContain("Nieuwe indiening") + }); + it("render no submissions", () => { + const wrapper = mount(SubmissionList, { + props: { + groupId: 1 + }, + global: { + mocks: { + sorted: computed(() => {return []}) + } + } + }) + expect(wrapper.text()).toContain("Nog geen indieningen") + }); + it("render with submissions", () => { + const wrapper = mount(SubmissionList, { + props: { + groupId: 1 + }, + global: { + mocks: { + sorted: computed(() => {return [{id: 1}]}) + } + } + }) + expect(wrapper.findComponent(".submissionCard").exists()).toBeTruthy() + }) +}) diff --git a/frontend/tests/views/GroupView.spec.ts b/frontend/tests/views/GroupView.spec.ts new file mode 100644 index 00000000..09a54491 --- /dev/null +++ b/frontend/tests/views/GroupView.spec.ts @@ -0,0 +1,125 @@ +import {mount} from "@vue/test-utils"; +import {expect, describe, it, vi} from "vitest"; +import GroupView from "@/views/GroupView.vue" +import {ref} from "vue"; + +const testGroupQuery = { + data: ref({project_id: 1, id: 1, num: 1, members: [{given_name: "test", surname: "member"}]}), + isLoading: ref(true), + isError: ref(true), + setLoading(value){ + this.isLoading.value = value + }, + setError(value){ + this.isError.value = value + }, + setMembers(value) { + this.data.value.members = value + } +} + +vi.mock("@/queries/Group", () => ({ + useGroupQuery: vi.fn(() => testGroupQuery), + useRemoveUserFromGroupMutation: vi.fn(() => vi.fn()) +})) + +const testProjectQuery = { + data: ref({subject_id: 1, name: "testproject"}), + isLoading: ref(false), + isError: ref(false) +} + +vi.mock("@/queries/Project", () => ({ + useProjectQuery: vi.fn(() => testProjectQuery) +})) + +const testCurrentUserQuery = { + isLoading: ref(false), + isError: ref(false), + data: ref({uid: "testuser"}), + setUuid(value){ + this.data.value.uid = value + } +} + +vi.mock("@/queries/User", () => ({ + useCurrentUserQuery: vi.fn(() => testCurrentUserQuery) +})) + +const testSubjectInstructorsQuery = { + data: ref([{uid: "testinstructor"}]), + isLoading: ref(false), + isError: ref(false) +} + +vi.mock("@/queries/Subject", () => ({ + useSubjectInstructorsQuery: vi.fn(() => testSubjectInstructorsQuery) +})) + +vi.mock("@/components/buttons/GroupButtons.vue", () => ({ + default: { + template: "
" + } +})) + +vi.mock("@/components/submission/SubmissionList.vue", () => ({ + default: { + template: "
" + } +})) + +vi.mock("@/components/buttons/BackButton.vue", () => ({ + default: { + template: "
" + } +})) + +describe("GroupView", () => { + const wrapper = mount(GroupView, { + props: { + groupId: 1 + } + }) + it("render if loading", async () => { + expect(wrapper.text()).toContain("Aan het laden...") + const isLoadingTrue = (wrapper.vm as any).isLoading + expect(isLoadingTrue).toBe(true) + testGroupQuery.setLoading(false) + await wrapper.vm.$nextTick() + const isLoadingFalse = (wrapper.vm as any).isLoading + expect(isLoadingFalse).toBe(false) + }); + it("render if error", async () => { + expect(wrapper.text()).toContain("Groep niet gevonden") + const isErrorTrue = (wrapper.vm as any).isError + expect(isErrorTrue).toBe(true) + testGroupQuery.setError(false) + await wrapper.vm.$nextTick() + const isErrorFalse = (wrapper.vm as any).isError + expect(isErrorFalse).toBe(false) + }); + it("render if student", () => { + const text = wrapper.text() + expect(text).toContain("Groep 1") + expect(text).toContain("Project: testproject") + expect(text).toContain("test member") + expect(wrapper.findComponent(".groupButtons").exists()).toBeTruthy() + expect(wrapper.findComponent(".submissionList").exists()).toBeTruthy() + expect(wrapper.findComponent(".backButton").exists()).toBeTruthy() + }); + it("render if teacher", async () => { + testCurrentUserQuery.setUuid("testinstructor") + await wrapper.vm.$nextTick() + const isTeacherFunction = (wrapper.vm as any).isTeacher + expect(isTeacherFunction).toBe(true) + }); + it("render if no members", async () => { + const members = (wrapper.vm as any).amountOfMembers + expect(members).toBe(1) + testGroupQuery.setMembers([]) + await wrapper.vm.$nextTick() + expect(wrapper.text()).toContain("Geen leden teruggevonden.") + const noMembers = (wrapper.vm as any).amountOfMembers + expect(noMembers).toBe(0) + }); +})