diff --git a/client/src/pages/cockpit/FeedbackPage.vue b/client/src/pages/cockpit/FeedbackPage.vue index b8ece4de..0a5c8384 100644 --- a/client/src/pages/cockpit/FeedbackPage.vue +++ b/client/src/pages/cockpit/FeedbackPage.vue @@ -18,12 +18,9 @@ {{ $t("feedback.feedbackPageInfo") }}

- + @@ -36,7 +33,7 @@ import { useCurrentCourseSession } from "@/composables"; import { itGet } from "@/fetchHelpers"; import * as log from "loglevel"; import { onMounted, ref } from "vue"; -import type { FeedbackData } from "@/types"; +import type { FeedbackData, FeedbackType } from "@/types"; import FeedbackPageVV from "@/pages/cockpit/FeedbackPageVV.vue"; import FeedbackPageUK from "@/pages/cockpit/FeedbackPageUK.vue"; @@ -49,7 +46,7 @@ log.debug("FeedbackPage created", props.circleId); const courseSession = useCurrentCourseSession(); const feedbackData = ref(undefined); -const feedbackType = ref<"vv" | undefined>(undefined); +const feedbackType = ref(undefined); onMounted(async () => { log.debug("FeedbackPage mounted"); @@ -57,7 +54,10 @@ onMounted(async () => { `/api/core/feedback/${courseSession.value.id}/${props.circleId}` ); log.debug("FeedbackPage feedbackData", feedbackData.value); - if (["uk", "vv"].includes(feedbackData.value?.feedbackType ?? "")) { + if ( + feedbackData.value && + ["uk", "vv"].includes(feedbackData.value?.feedbackType ?? "") + ) { feedbackType.value = feedbackData.value.feedbackType; } }); diff --git a/client/src/pages/cockpit/FeedbackPageUK.vue b/client/src/pages/cockpit/FeedbackPageUK.vue index 460b6771..f9467de9 100644 --- a/client/src/pages/cockpit/FeedbackPageUK.vue +++ b/client/src/pages/cockpit/FeedbackPageUK.vue @@ -15,7 +15,7 @@ import type { FeedbackData } from "@/types"; import * as log from "loglevel"; import { useTranslation } from "i18next-vue"; -const props = defineProps<{ +defineProps<{ feedbackData: FeedbackData; }>(); diff --git a/client/src/pages/cockpit/FeedbackPageVV.vue b/client/src/pages/cockpit/FeedbackPageVV.vue index 8a4ac047..96ad8e98 100644 --- a/client/src/pages/cockpit/FeedbackPageVV.vue +++ b/client/src/pages/cockpit/FeedbackPageVV.vue @@ -15,7 +15,7 @@ import type { FeedbackData } from "@/types"; import * as log from "loglevel"; import { useTranslation } from "i18next-vue"; -const props = defineProps<{ +defineProps<{ feedbackData: FeedbackData; }>(); diff --git a/client/src/pages/cockpit/FeedbackResults.vue b/client/src/pages/cockpit/FeedbackResults.vue index a8bc89d9..29475fc1 100644 --- a/client/src/pages/cockpit/FeedbackResults.vue +++ b/client/src/pages/cockpit/FeedbackResults.vue @@ -63,7 +63,7 @@ interface Props { openKeys?: string[]; } -const props = withDefaults(defineProps(), { +withDefaults(defineProps(), { orderedQuestions: () => [], ratingKeys: () => [], verticalChartKeys: () => [], diff --git a/client/src/pages/learningPath/learningContentPage/feedback/FeedbackBase.vue b/client/src/pages/learningPath/learningContentPage/feedback/FeedbackBase.vue index d346edcb..c76bf24b 100644 --- a/client/src/pages/learningPath/learningContentPage/feedback/FeedbackBase.vue +++ b/client/src/pages/learningPath/learningContentPage/feedback/FeedbackBase.vue @@ -179,7 +179,11 @@ onMounted(async () => {

{{ introduction }}

-

+

{{ localStepLabels[stepNo] }}

diff --git a/cypress/e2e/dashboard/dashboardSupervisor.cy.js b/cypress/e2e/dashboard/dashboardSupervisor.cy.js index e243f3aa..aca8b793 100644 --- a/cypress/e2e/dashboard/dashboardSupervisor.cy.js +++ b/cypress/e2e/dashboard/dashboardSupervisor.cy.js @@ -74,7 +74,7 @@ describe("dashboardSupervisor.cy.js", () => { describe("feedback summary box", () => { it("contains correct numbers", () => { getDashboardStatistics("feedback.average").should("have.text", "3.3"); - getDashboardStatistics("feedback.count").should("have.text", "3"); + getDashboardStatistics("feedback.count").should("have.text", "6"); }); it("contains correct details link", () => { clickOnDetailsLink("feedback"); diff --git a/cypress/e2e/feedback/feedbackStudent.cy.js b/cypress/e2e/feedback/feedbackStudent.cy.js index d381d4db..1d9d5056 100644 --- a/cypress/e2e/feedback/feedbackStudent.cy.js +++ b/cypress/e2e/feedback/feedbackStudent.cy.js @@ -30,6 +30,10 @@ describe("feedbackStudent.cy.js", () => { // fill feedback form // step 1 cy.url().should("include", "step=1"); + cy.get('[data-cy="question-1"]').should( + "contain", + "Zufriedenheit insgesamt" + ); cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get('[data-cy="radio-4"]').click(); cy.wait(200); @@ -38,6 +42,10 @@ describe("feedbackStudent.cy.js", () => { // step 2 cy.url().should("include", "step=2"); + cy.get('[data-cy="question-2"]').should( + "contain", + "Zielerreichung insgesamt" + ); cy.get('[data-cy="next-step"]').should("be.disabled"); // the system should store after every step -> check stored data cy.loadFeedbackResponse("feedback_user_id", TEST_STUDENT1_USER_ID).then( @@ -54,6 +62,10 @@ describe("feedbackStudent.cy.js", () => { // step 3 cy.url().should("include", "step=3"); + cy.get('[data-cy="question-3"]').should( + "contain", + "Wie beurteilst du deine Sicherheit bezüglichen den Themen nach dem Kurs?" + ); cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get('[data-cy="radio-80"]').click(); cy.wait(200); @@ -62,6 +74,10 @@ describe("feedbackStudent.cy.js", () => { // step 4 cy.url().should("include", "step=4"); + cy.get('[data-cy="question-4"]').should( + "contain", + "Waren die Vorbereitungsaufträge klar und verständlich?" + ); cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get('[data-cy="radio-false"]').click(); cy.wait(200); @@ -70,6 +86,10 @@ describe("feedbackStudent.cy.js", () => { // step 5 cy.url().should("include", "step=5"); + cy.get('[data-cy="question-5"]').should( + "contain", + "Wie beurteilst du die Themensicherheit und Fachkompetenz des Kursleiters/der Kursleiterin?" + ); cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get('[data-cy="radio-2"]').click(); cy.wait(200); @@ -78,6 +98,10 @@ describe("feedbackStudent.cy.js", () => { // step 6 cy.url().should("include", "step=6"); + cy.get('[data-cy="question-6"]').should( + "contain", + "Wurden Fragen und Anregungen der Kursteilnehmenden ernst genommen und aufgegriffen?" + ); cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get('[data-cy="radio-1"]').click(); cy.wait(200); @@ -86,6 +110,10 @@ describe("feedbackStudent.cy.js", () => { // step 7 cy.url().should("include", "step=7"); + cy.get('[data-cy="question-7"]').should( + "contain", + "Was möchtest du dem Kursleiter/der Kursleiterin sonst noch sagen?" + ); cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get('[data-cy="it-textarea-instructor_open_feedback"]').type( "Der Kursleiter ist eigentlich ganz nett." @@ -96,6 +124,10 @@ describe("feedbackStudent.cy.js", () => { // step 8 cy.url().should("include", "step=8"); + cy.get('[data-cy="question-8"]').should( + "contain", + "Würdest du den Kurs weiterempfehlen?" + ); cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get('[data-cy="radio-true"]').click(); cy.wait(200); @@ -104,6 +136,11 @@ describe("feedbackStudent.cy.js", () => { // step 9 cy.url().should("include", "step=9"); + cy.get('[data-cy="question-9"]').should( + "contain", + "Was hat dir besonders gut gefallen?" + ); + cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get('[data-cy="it-textarea-course_positive_feedback"]').type( "Ich bin zufrieden mit den meisten Dingen." @@ -114,6 +151,10 @@ describe("feedbackStudent.cy.js", () => { // step 10 cy.url().should("include", "step=10"); + cy.get('[data-cy="question-10"]').should( + "contain", + "Wo siehst du Verbesserungspotential?" + ); cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get('[data-cy="it-textarea-course_negative_feedback"]').type( "Ich bin unzufrieden mit einigen Sachen." @@ -189,6 +230,10 @@ describe("feedbackStudent.cy.js", () => { // fill feedback form // step 1 cy.url().should("include", "step=1"); + cy.get('[data-cy="question-1"]').should( + "contain", + "Zufriedenheit insgesamt" + ); cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get('[data-cy="radio-4"]').click(); cy.wait(200); @@ -197,6 +242,10 @@ describe("feedbackStudent.cy.js", () => { // step 2 cy.url().should("include", "step=2"); + cy.get('[data-cy="question-2"]').should( + "contain", + "Zielerreichung insgesamt" + ); cy.get('[data-cy="next-step"]').should("be.disabled"); // the system should store after every step -> check stored data cy.loadFeedbackResponse("feedback_user_id", TEST_STUDENT1_USER_ID).then( @@ -213,6 +262,10 @@ describe("feedbackStudent.cy.js", () => { // step 3 cy.url().should("include", "step=3"); + cy.get('[data-cy="question-3"]').should( + "contain", + "Wie beurteilst du deine Sicherheit bezüglichen den Themen nach dem Circle?" + ); cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get('[data-cy="radio-80"]').click(); cy.wait(200); @@ -221,6 +274,10 @@ describe("feedbackStudent.cy.js", () => { // step 4 cy.url().should("include", "step=4"); + cy.get('[data-cy="question-4"]').should( + "contain", + "Waren die Praxisaufträge klar und verständlich?" + ); cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get('[data-cy="radio-false"]').click(); cy.wait(200); @@ -229,6 +286,10 @@ describe("feedbackStudent.cy.js", () => { // step 5 cy.url().should("include", "step=5"); + cy.get('[data-cy="question-5"]').should( + "contain", + "Würdest du den Circle weiterempfehlen?" + ); cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get('[data-cy="radio-false"]').click(); cy.wait(200); @@ -237,6 +298,11 @@ describe("feedbackStudent.cy.js", () => { // step 6 cy.url().should("include", "step=6"); + cy.get('[data-cy="question-6"]').should( + "contain", + "Was hat dir besonders gut gefallen?" + ); + cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get('[data-cy="it-textarea-course_positive_feedback"]').type( "Der Circle ist eigentlich ganz nett." @@ -247,6 +313,10 @@ describe("feedbackStudent.cy.js", () => { // step 7 cy.url().should("include", "step=7"); + cy.get('[data-cy="question-7"]').should( + "contain", + "Wo siehst du Verbesserungspotential?" + ); cy.get('[data-cy="next-step"]').should("be.disabled"); cy.get('[data-cy="it-textarea-course_negative_feedback"]').type( "Ich bin unzufrieden mit einigen Sachen." diff --git a/server/vbv_lernwelt/feedback/factories.py b/server/vbv_lernwelt/feedback/factories.py index fcd76201..edc7321b 100644 --- a/server/vbv_lernwelt/feedback/factories.py +++ b/server/vbv_lernwelt/feedback/factories.py @@ -34,6 +34,7 @@ class FeedbackResponseFactory(DjangoModelFactory): "Das Beispiel mit der Katze fand ich sehr gut veranschaulicht!", ] ), + "feedback_type": FuzzyChoice(["uk", "vv"]), } ) diff --git a/server/vbv_lernwelt/feedback/tests/test_feedback_api.py b/server/vbv_lernwelt/feedback/tests/test_feedback_api.py index 99e8c258..0fa83009 100644 --- a/server/vbv_lernwelt/feedback/tests/test_feedback_api.py +++ b/server/vbv_lernwelt/feedback/tests/test_feedback_api.py @@ -114,6 +114,7 @@ class FeedbackRestApiTestCase(FeedbackBaseTestCase): "course_negative_feedback": self.feedback_data[ "course_negative_feedback" ][i], + "feedback_type": "uk", }, feedback_user=self.feedback_users[i], submitted=True, @@ -129,6 +130,7 @@ class FeedbackRestApiTestCase(FeedbackBaseTestCase): expected = { "amount": 3, "questions": self.feedback_data, + "feedbackType": "uk", } print(response.data) diff --git a/server/vbv_lernwelt/feedback/tests/test_graphql.py b/server/vbv_lernwelt/feedback/tests/test_graphql.py new file mode 100644 index 00000000..a5516212 --- /dev/null +++ b/server/vbv_lernwelt/feedback/tests/test_graphql.py @@ -0,0 +1,91 @@ +import json + +from graphene_django.utils.testing import GraphQLTestCase + +from vbv_lernwelt.core.create_default_users import create_default_users +from vbv_lernwelt.core.models import User +from vbv_lernwelt.course.consts import COURSE_TEST_ID +from vbv_lernwelt.course.creators.test_course import create_test_course +from vbv_lernwelt.course.models import CourseSession +from vbv_lernwelt.feedback.models import FeedbackResponse +from vbv_lernwelt.learnpath.models import LearningContentFeedbackUK + + +class FeedbackMutationTestCase(GraphQLTestCase): + GRAPHQL_URL = "/server/graphql/" + + def setUp(self): + create_default_users() + create_test_course(include_vv=False, with_sessions=True) + self.course_session = CourseSession.objects.get(title="Test Bern 2022 a") + self.learning_content_feedback_page = LearningContentFeedbackUK.objects.get( + slug="test-lehrgang-lp-circle-fahrzeug-lc-feedback" + ) + self.student = User.objects.get(username="test-student1@example.com") + self.client.force_login(self.student) + + def test_creates_response(self): + data = { + "course_negative_feedback": "schlecht", + "course_positive_feedback": "gut", + "feedback_type": "uk", + "goal_attainment": 3, + "preparation_task_clarity": False, + "proficiency": 100, + "satisfaction": 3, + "would_recommend": False, + "instructor_competence": None, + "instructor_respect": None, + "instructor_open_feedback": None, + } + + response = self.query( + f""" +mutation {{ + send_feedback( + course_session_id: "{COURSE_TEST_ID}" + learning_content_page_id: "{self.learning_content_feedback_page.id}" + learning_content_type: "learnpath.LearningContentFeedbackUK" + data: {{ + course_negative_feedback: "{data['course_negative_feedback']}", + course_positive_feedback: "{data['course_positive_feedback']}", + feedback_type: null, + goal_attainment: {data['goal_attainment']}, + preparation_task_clarity: {str(data['preparation_task_clarity']).lower()}, + proficiency: {data['proficiency']}, + satisfaction: {data['satisfaction']}, + would_recommend: {str(data['would_recommend']).lower()}, + instructor_competence: null, + instructor_respect: null, + instructor_open_feedback: null, + }}, + submitted: false + ) {{ + feedback_response {{ + id + data + submitted + __typename + }} + errors {{ + field + messages + __typename + }} + __typename + }} +}} + """ + ) + + content = json.loads(response.content) + + self.assertResponseNoErrors(response) + self.assertDictEqual( + content["data"]["send_feedback"]["feedback_response"]["data"], data + ) + + feedback = FeedbackResponse.objects.first() + self.assertEqual(feedback.data, data) + self.assertEqual(feedback.submitted, False) + self.assertEqual(feedback.feedback_user, self.student)