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 @@
+
+
+
+ {{ $t(title) }}
+
+
+
+
+
+
+
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 @@
-
-
- {{ $t("submission.status") }}
-
- {{ Status[submission.status] }}
-
- {{ $t("submission.after_deadline") }}
-
-
- {{ $t("submission.datetime") }} {{ $d(submission.date, "long") }}
-
-
-
- {{ $t("submission.remarks") }}
-
-
- {{ submission.remarks }}
-
-
- {{ $t("submission.remarks_empty") }}
-
-
-
- {{ $t("submission.docker_test") }}
-
- Stdout: {{ submission.stdout }}
- Sterr: {{ submission.stderr }}
-
- -
-
{{ result.value }}
- {{ result.value }}
-
-
-
-
-
-
- {{ $t("submission.files") }}
-
-
- {{ $t("submission.download_info") }}
-
-
-
-
-
- downloadFile(index)"
- >
- {{ item.filename }}
-
-
-
-
-
-
- {{ $t("submission.download_all_files") }}
-
-
-
+
+
+
+
+
+ {{ $t("submission.status") }}
+
+ {{ Status[submission.status] }}
+
+ {{ $t("submission.after_deadline") }}
+
+
+ {{ $t("submission.datetime") }} {{ $d(submission.date, "long") }}
+
+
+
+ {{ $t("submission.remarks") }}
+
+
+ {{ submission.remarks }}
+
+
+ {{ $t("submission.remarks_empty") }}
+
+
+
+ {{ $t("submission.docker_test") }}
+
+ Stdout: {{ submission.stdout }}
+ Sterr: {{ submission.stderr }}
+
+ -
+
{{ result.value }}
+ {{ result.value }}
+
+
+
+
+
+
+
+
+ {{ $t("submission.files") }}
+
+
+ {{ $t("submission.download_info") }}
+
+
+
+
+
+ downloadFile(index)"
+ >
+ {{ item.filename }}
+
+
+
+
+
+
+ {{ $t("submission.download_all_files") }}
+
+
+
+
+
@@ -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 @@
-
-
-
- {{ $t("submission.submissions_title") }}
-
-
{{ $t("submission.no_submissions") }}
-
+
+
+
+
+ {{ $t("submission.submissions_title") }}
+
{{ $t("submit.new_submission") }}
-
-
-
-
+
+
+
{{ $t("submission.no_submissions") }}
+
+
+
+
+
@@ -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 }}
-
- removeStudent({ groupId: group!.id, uid: member.uid })
- "
- >
- {{ $t("group.remove") }}
-
-
-
-
- {{ $t("group.no_members_found") }}
-
-
-
-
-
-
-
-
+
+
+ {{ $t("project.group", { number: group!.num }) }}
+ {{ "Project: " + project!.name }}
+
+
+
+
+ {{ member.given_name + " " + member.surname }}
+
+
+ removeStudent({
+ groupId: group!.id,
+ uid: member.uid,
+ })
+ "
+ >
+ {{ $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)
+ });
+})