diff --git a/src/e2e/java/teammates/e2e/cases/sql/BaseE2ETestCase.java b/src/e2e/java/teammates/e2e/cases/sql/BaseE2ETestCase.java index 860616c0c40..54d8f3f46ca 100644 --- a/src/e2e/java/teammates/e2e/cases/sql/BaseE2ETestCase.java +++ b/src/e2e/java/teammates/e2e/cases/sql/BaseE2ETestCase.java @@ -20,9 +20,13 @@ import teammates.e2e.util.BackDoor; import teammates.e2e.util.EmailAccount; import teammates.e2e.util.TestProperties; +import teammates.storage.sqlentity.FeedbackQuestion; +import teammates.storage.sqlentity.FeedbackResponse; import teammates.test.BaseTestCaseWithSqlDatabaseAccess; import teammates.test.FileHelper; import teammates.test.ThreadHelper; +import teammates.ui.output.FeedbackQuestionData; +import teammates.ui.output.FeedbackResponseData; /** * Base class for all browser tests. @@ -224,13 +228,30 @@ protected void verifyEmailSent(String email, String subject) { * Removes and restores the databundle using BACKDOOR. */ @Override - protected boolean doRemoveAndRestoreDataBundle(SqlDataBundle testData) { + protected SqlDataBundle doRemoveAndRestoreDataBundle(SqlDataBundle testData) { try { - BACKDOOR.removeAndRestoreSqlDataBundle(testData); - return true; + return BACKDOOR.removeAndRestoreSqlDataBundle(testData); } catch (HttpRequestFailedException e) { e.printStackTrace(); - return false; + return null; } } + + FeedbackQuestionData getFeedbackQuestion(String courseId, String feedbackSessionName, int qnNumber) { + return BACKDOOR.getFeedbackQuestionData(courseId, feedbackSessionName, qnNumber); + } + + @Override + protected FeedbackQuestionData getFeedbackQuestion(FeedbackQuestion fq) { + return getFeedbackQuestion(fq.getCourseId(), fq.getFeedbackSession().getName(), fq.getQuestionNumber()); + } + + FeedbackResponseData getFeedbackResponse(String questionId, String giver, String recipient) { + return BACKDOOR.getFeedbackResponseData(questionId, recipient, recipient); + } + + @Override + protected FeedbackResponseData getFeedbackResponse(FeedbackResponse fr) { + return getFeedbackResponse(fr.getFeedbackQuestion().getId().toString(), fr.getGiver(), fr.getRecipient()); + } } diff --git a/src/e2e/java/teammates/e2e/cases/sql/BaseFeedbackQuestionE2ETest.java b/src/e2e/java/teammates/e2e/cases/sql/BaseFeedbackQuestionE2ETest.java new file mode 100644 index 00000000000..1597a6bd1d0 --- /dev/null +++ b/src/e2e/java/teammates/e2e/cases/sql/BaseFeedbackQuestionE2ETest.java @@ -0,0 +1,58 @@ +package teammates.e2e.cases.sql; + +import teammates.common.util.AppUrl; +import teammates.common.util.Const; +import teammates.e2e.pageobjects.FeedbackSubmitPage; +import teammates.e2e.pageobjects.InstructorFeedbackEditPage; +import teammates.storage.sqlentity.Course; +import teammates.storage.sqlentity.FeedbackSession; +import teammates.storage.sqlentity.Instructor; +import teammates.storage.sqlentity.Student; + +/** + * Base class for all feedback question related browser tests. + * + *

SUT: {@link Const.WebPageURIs#INSTRUCTOR_SESSION_EDIT_PAGE}, {@link Const.WebPageURIs#SESSION_SUBMISSION_PAGE}. + * + *

Only UI-intensive operations, e.g. question creation and response submission, are tested separately. + * This is so that if any part of the testing fails (due to regression or inherent instability), only the + * specific test for the specific feedback question needs to be re-run. + * + *

For the above reason, viewing feedback responses/results is not considered to be under this test case. + * This is because viewing results is a fast action and combining all question types together under one test case + * will save some testing time. + */ +public abstract class BaseFeedbackQuestionE2ETest extends BaseE2ETestCase { + Instructor instructor; + Course course; + FeedbackSession feedbackSession; + Student student; + + abstract void testEditPage(); + + abstract void testSubmitPage(); + + InstructorFeedbackEditPage loginToFeedbackEditPage() { + AppUrl url = createFrontendUrl(Const.WebPageURIs.INSTRUCTOR_SESSION_EDIT_PAGE) + .withCourseId(course.getId()) + .withSessionName(feedbackSession.getName()); + + return loginToPage(url, InstructorFeedbackEditPage.class, instructor.getGoogleId()); + } + + FeedbackSubmitPage loginToFeedbackSubmitPage() { + AppUrl url = createFrontendUrl(Const.WebPageURIs.STUDENT_SESSION_SUBMISSION_PAGE) + .withCourseId(student.getCourse().getId()) + .withSessionName(feedbackSession.getName()); + + return loginToPage(url, FeedbackSubmitPage.class, student.getGoogleId()); + } + + FeedbackSubmitPage getFeedbackSubmitPage() { + AppUrl url = createFrontendUrl(Const.WebPageURIs.STUDENT_SESSION_SUBMISSION_PAGE) + .withCourseId(student.getCourse().getId()) + .withSessionName(feedbackSession.getName()); + + return getNewPageInstance(url, FeedbackSubmitPage.class); + } +} diff --git a/src/e2e/java/teammates/e2e/cases/sql/FeedbackTextQuestionE2ETest.java b/src/e2e/java/teammates/e2e/cases/sql/FeedbackTextQuestionE2ETest.java new file mode 100644 index 00000000000..88662c3adbb --- /dev/null +++ b/src/e2e/java/teammates/e2e/cases/sql/FeedbackTextQuestionE2ETest.java @@ -0,0 +1,111 @@ +package teammates.e2e.cases.sql; + +import org.testng.annotations.Test; + +import teammates.common.datatransfer.questions.FeedbackTextQuestionDetails; +import teammates.common.datatransfer.questions.FeedbackTextResponseDetails; +import teammates.e2e.pageobjects.FeedbackSubmitPage; +import teammates.e2e.pageobjects.InstructorFeedbackEditPage; +import teammates.storage.sqlentity.FeedbackQuestion; +import teammates.storage.sqlentity.FeedbackResponse; +import teammates.storage.sqlentity.Instructor; + +/** + * SUT: {@link Const.WebPageURIs#INSTRUCTOR_SESSION_EDIT_PAGE}, {@link Const.WebPageURIs#SESSION_SUBMISSION_PAGE} + * specifically for text questions. + */ +public class FeedbackTextQuestionE2ETest extends BaseFeedbackQuestionE2ETest { + + @Override + protected void prepareTestData() { + testData = removeAndRestoreDataBundle(loadSqlDataBundle("/FeedbackTextQuestionE2ESqlTest.json")); + + instructor = testData.instructors.get("instructor"); + course = testData.courses.get("course"); + feedbackSession = testData.feedbackSessions.get("openSession"); + student = testData.students.get("alice.tmms@FTextQn.CS2104"); + } + + @Test + @Override + public void testAll() { + testEditPage(); + logout(); + testSubmitPage(); + } + + @Override + protected void testEditPage() { + InstructorFeedbackEditPage feedbackEditPage = loginToFeedbackEditPage(); + + ______TS("verify loaded question"); + FeedbackQuestion loadedQuestion = testData.feedbackQuestions.get("qn1ForFirstSession"); + FeedbackTextQuestionDetails questionDetails = (FeedbackTextQuestionDetails) loadedQuestion.getQuestionDetailsCopy(); + feedbackEditPage.verifyTextQuestionDetails(1, questionDetails); + + ______TS("add new question"); + // add new question exactly like loaded question + loadedQuestion.setQuestionNumber(2); + feedbackEditPage.addTextQuestion(loadedQuestion); + + feedbackEditPage.verifyTextQuestionDetails(2, questionDetails); + verifyPresentInDatabase(loadedQuestion); + + ______TS("copy question"); + FeedbackQuestion copiedQuestion = testData.feedbackQuestions.get("qn1ForSecondSession"); + questionDetails = (FeedbackTextQuestionDetails) copiedQuestion.getQuestionDetailsCopy(); + feedbackEditPage.copyQuestion(copiedQuestion.getCourseId(), + copiedQuestion.getQuestionDetailsCopy().getQuestionText()); + copiedQuestion.setQuestionNumber(3); + copiedQuestion.setFeedbackSession(feedbackSession); + + feedbackEditPage.verifyTextQuestionDetails(3, questionDetails); + verifyPresentInDatabase(copiedQuestion); + + ______TS("edit question"); + questionDetails.setRecommendedLength(200); + copiedQuestion.setQuestionDetails(questionDetails); + feedbackEditPage.editTextQuestion(3, questionDetails); + + feedbackEditPage.verifyTextQuestionDetails(3, questionDetails); + verifyPresentInDatabase(copiedQuestion); + } + + @Override + protected void testSubmitPage() { + FeedbackSubmitPage feedbackSubmitPage = loginToFeedbackSubmitPage(); + + ______TS("verify loaded question"); + FeedbackQuestion question = testData.feedbackQuestions.get("qn1ForFirstSession"); + Instructor receiver = testData.instructors.get("instructor"); + question.setQuestionNumber(1); + feedbackSubmitPage.verifyTextQuestion(1, (FeedbackTextQuestionDetails) question.getQuestionDetailsCopy()); + + ______TS("submit response"); + FeedbackResponse response = getResponse(question, receiver, "

This is the response for qn 1

"); + feedbackSubmitPage.fillTextResponse(1, receiver.getName(), response); + feedbackSubmitPage.clickSubmitQuestionButton(1); + + // TODO: uncomment when SubmitFeedbackResponse is working + // verifyPresentInDatabase(response); + + // ______TS("check previous response"); + // feedbackSubmitPage = getFeedbackSubmitPage(); + // feedbackSubmitPage.verifyTextResponse(1, receiver.getName(), response); + + // ______TS("edit response"); + // FeedbackResponse editedResponse = getResponse(question, receiver, "

Edited response

"); + // feedbackSubmitPage.fillTextResponse(1, receiver.getName(), editedResponse); + // feedbackSubmitPage.clickSubmitQuestionButton(1); + + // feedbackSubmitPage = getFeedbackSubmitPage(); + // feedbackSubmitPage.verifyTextResponse(1, receiver.getName(), response); + // verifyPresentInDatabase(editedResponse); + } + + private FeedbackResponse getResponse(FeedbackQuestion feedbackQuestion, Instructor instructor, String answer) { + FeedbackTextResponseDetails details = new FeedbackTextResponseDetails(answer); + return FeedbackResponse.makeResponse( + feedbackQuestion, student.getEmail(), null, instructor.getEmail(), null, details); + } +} diff --git a/src/e2e/java/teammates/e2e/pageobjects/FeedbackSubmitPage.java b/src/e2e/java/teammates/e2e/pageobjects/FeedbackSubmitPage.java index 4cd29ce955e..33f3215d638 100644 --- a/src/e2e/java/teammates/e2e/pageobjects/FeedbackSubmitPage.java +++ b/src/e2e/java/teammates/e2e/pageobjects/FeedbackSubmitPage.java @@ -37,6 +37,7 @@ import teammates.common.datatransfer.questions.FeedbackTextQuestionDetails; import teammates.common.datatransfer.questions.FeedbackTextResponseDetails; import teammates.common.util.Const; +import teammates.storage.sqlentity.FeedbackResponse; /** * Represents the feedback submission page of the website. @@ -155,6 +156,12 @@ public void fillTextResponse(int qnNumber, String recipient, FeedbackResponseAtt writeToRichTextEditor(getTextResponseEditor(qnNumber, recipient), responseDetails.getAnswer()); } + public void fillTextResponse(int qnNumber, String recipient, FeedbackResponse response) { + FeedbackTextResponseDetails responseDetails = + (FeedbackTextResponseDetails) response.getFeedbackResponseDetailsCopy(); + writeToRichTextEditor(getTextResponseEditor(qnNumber, recipient), responseDetails.getAnswer()); + } + public void verifyTextResponse(int qnNumber, String recipient, FeedbackResponseAttributes response) { FeedbackTextResponseDetails responseDetails = (FeedbackTextResponseDetails) response.getResponseDetailsCopy(); int responseLength = responseDetails.getAnswer().split(" ").length; @@ -163,6 +170,15 @@ public void verifyTextResponse(int qnNumber, String recipient, FeedbackResponseA + " words"); } + public void verifyTextResponse(int qnNumber, String recipient, FeedbackResponse response) { + FeedbackTextResponseDetails responseDetails = + (FeedbackTextResponseDetails) response.getFeedbackResponseDetailsCopy(); + int responseLength = responseDetails.getAnswer().split(" ").length; + assertEquals(getEditorRichText(getTextResponseEditor(qnNumber, recipient)), responseDetails.getAnswer()); + assertEquals(getResponseLengthText(qnNumber, recipient), "Response length: " + responseLength + + " words"); + } + public void verifyMcqQuestion(int qnNumber, String recipient, FeedbackMcqQuestionDetails questionDetails) { List mcqChoices = questionDetails.getMcqChoices(); List optionTexts = getMcqOptions(qnNumber, recipient); diff --git a/src/e2e/java/teammates/e2e/pageobjects/InstructorFeedbackEditPage.java b/src/e2e/java/teammates/e2e/pageobjects/InstructorFeedbackEditPage.java index 4c0eda460ee..0b9c3b02b37 100644 --- a/src/e2e/java/teammates/e2e/pageobjects/InstructorFeedbackEditPage.java +++ b/src/e2e/java/teammates/e2e/pageobjects/InstructorFeedbackEditPage.java @@ -33,6 +33,7 @@ import teammates.common.datatransfer.questions.FeedbackRubricQuestionDetails; import teammates.common.datatransfer.questions.FeedbackTextQuestionDetails; import teammates.common.util.Const; +import teammates.storage.sqlentity.FeedbackQuestion; import teammates.test.ThreadHelper; /** @@ -535,6 +536,16 @@ private void inputQuestionDetails(int questionNum, FeedbackQuestionAttributes fe } } + private void inputQuestionDetails(int questionNum, FeedbackQuestion feedbackQuestion) { + setQuestionBrief(questionNum, feedbackQuestion.getQuestionDetailsCopy().getQuestionText()); + setQuestionDescription(questionNum, feedbackQuestion.getDescription()); + FeedbackQuestionType questionType = feedbackQuestion.getQuestionDetailsCopy().getQuestionType(); + if (!questionType.equals(FeedbackQuestionType.CONTRIB)) { + setFeedbackPath(questionNum, feedbackQuestion); + setQuestionVisibility(questionNum, feedbackQuestion); + } + } + public void duplicateQuestion(int questionNum) { clickAndWaitForNewQuestion(getQuestionForm(questionNum).findElement(By.id("btn-duplicate-question"))); } @@ -558,6 +569,16 @@ public void addTextQuestion(FeedbackQuestionAttributes feedbackQuestion) { clickSaveNewQuestionButton(); } + public void addTextQuestion(FeedbackQuestion feedbackQuestion) { + addNewQuestion(2); + int questionNum = getNumQuestions(); + inputQuestionDetails(questionNum, feedbackQuestion); + FeedbackTextQuestionDetails questionDetails = + (FeedbackTextQuestionDetails) feedbackQuestion.getQuestionDetailsCopy(); + fillTextBox(getRecommendedTextLengthField(questionNum), questionDetails.getRecommendedLength().toString()); + clickSaveNewQuestionButton(); + } + public void editTextQuestion(int questionNum, FeedbackTextQuestionDetails textQuestionDetails) { clickEditQuestionButton(questionNum); WebElement recommendedTextLengthField = getRecommendedTextLengthField(questionNum); @@ -1073,6 +1094,32 @@ private void setFeedbackPath(int questionNum, FeedbackQuestionAttributes feedbac getDisplayRecipientName(newRecipient)); } + private void setFeedbackPath(int questionNum, FeedbackQuestion feedbackQuestion) { + FeedbackParticipantType newGiver = feedbackQuestion.getGiverType(); + FeedbackParticipantType newRecipient = feedbackQuestion.getRecipientType(); + String feedbackPath = getFeedbackPath(questionNum); + WebElement questionForm = getQuestionForm(questionNum).findElement(By.tagName("tm-feedback-path-panel")); + if (!CUSTOM_FEEDBACK_PATH_OPTION.equals(feedbackPath)) { + selectFeedbackPathDropdownOption(questionNum, CUSTOM_FEEDBACK_PATH_OPTION + "..."); + } + // Set to type STUDENT first to adjust NumberOfEntitiesToGiveFeedbackTo + selectDropdownOptionByText(questionForm.findElement(By.id("giver-type")), + getDisplayGiverName(FeedbackParticipantType.STUDENTS)); + selectDropdownOptionByText(questionForm.findElement(By.id("receiver-type")), + getDisplayRecipientName(FeedbackParticipantType.STUDENTS_EXCLUDING_SELF)); + if (feedbackQuestion.getNumOfEntitiesToGiveFeedbackTo() == Const.MAX_POSSIBLE_RECIPIENTS) { + click(questionForm.findElement(By.id("unlimited-recipients"))); + } else { + click(questionForm.findElement(By.id("custom-recipients"))); + fillTextBox(questionForm.findElement(By.id("custom-recipients-number")), + Integer.toString(feedbackQuestion.getNumOfEntitiesToGiveFeedbackTo())); + } + + selectDropdownOptionByText(questionForm.findElement(By.id("giver-type")), getDisplayGiverName(newGiver)); + selectDropdownOptionByText(questionForm.findElement(By.id("receiver-type")), + getDisplayRecipientName(newRecipient)); + } + private void selectFeedbackPathDropdownOption(int questionNum, String text) { WebElement questionForm = getQuestionForm(questionNum); WebElement feedbackPathPanel = questionForm.findElement(By.tagName("tm-feedback-path-panel")); @@ -1113,6 +1160,22 @@ private void setQuestionVisibility(int questionNum, FeedbackQuestionAttributes f selectVisibilityBoxes(customVisibilityTable, giver, receiver, feedbackQuestion.getShowRecipientNameTo(), 3); } + private void setQuestionVisibility(int questionNum, FeedbackQuestion feedbackQuestion) { + WebElement questionForm = getQuestionForm(questionNum); + WebElement visibilityPanel = questionForm.findElement(By.tagName("tm-visibility-panel")); + String visibility = visibilityPanel.findElement(By.cssSelector("#btn-question-visibility span")).getText(); + if (!CUSTOM_VISIBILITY_OPTION.equals(visibility)) { + selectVisibilityDropdownOption(questionNum, CUSTOM_VISIBILITY_OPTION + "..."); + } + + FeedbackParticipantType giver = feedbackQuestion.getGiverType(); + FeedbackParticipantType receiver = feedbackQuestion.getRecipientType(); + WebElement customVisibilityTable = visibilityPanel.findElement(By.id("custom-visibility-table")); + selectVisibilityBoxes(customVisibilityTable, giver, receiver, feedbackQuestion.getShowResponsesTo(), 1); + selectVisibilityBoxes(customVisibilityTable, giver, receiver, feedbackQuestion.getShowGiverNameTo(), 2); + selectVisibilityBoxes(customVisibilityTable, giver, receiver, feedbackQuestion.getShowRecipientNameTo(), 3); + } + private void selectVisibilityBoxes(WebElement table, FeedbackParticipantType giver, FeedbackParticipantType receiver, List participants, int colNum) { diff --git a/src/e2e/resources/data/FeedbackTextQuestionE2ESqlTest.json b/src/e2e/resources/data/FeedbackTextQuestionE2ESqlTest.json new file mode 100644 index 00000000000..c57e08c65b2 --- /dev/null +++ b/src/e2e/resources/data/FeedbackTextQuestionE2ESqlTest.json @@ -0,0 +1,245 @@ +{ + "accounts": { + "instructorWithSessions": { + "googleId": "tm.e2e.FTextQn.instructor", + "name": "Teammates Test", + "email": "tmms.test@gmail.tmt", + "id": "00000000-0000-4000-8000-000000000001" + }, + "tm.e2e.FTextQn.alice.tmms": { + "googleId": "tm.e2e.FTextQn.alice.tmms", + "name": "Alice Betsy", + "email": "alice.b.tmms@gmail.tmt", + "id": "00000000-0000-4000-8000-000000000002" + } + }, + "accountRequests": {}, + "courses": { + "course": { + "id": "tm.e2e.FTextQn.CS2104", + "name": "Programming Language Concepts", + "institute": "TEAMMATES Test Institute 1", + "timeZone": "Africa/Johannesburg" + }, + "course2": { + "id": "tm.e2e.FTextQn.CS1101", + "name": "Programming Methodology", + "institute": "TEAMMATES Test Institute 1", + "timeZone": "Africa/Johannesburg" + } + }, + "instructors": { + "instructor": { + "account": { + "id": "00000000-0000-4000-8000-000000000001" + }, + "name": "Teammates Test", + "email": "tmms.test@gmail.tmt", + "role": "INSTRUCTOR_PERMISSION_ROLE_COOWNER", + "isDisplayedToStudents": true, + "privileges": { + "courseLevel": { + "canViewStudentInSections": true, + "canSubmitSessionInSections": true, + "canModifySessionCommentsInSections": true, + "canModifyCourse": true, + "canViewSessionInSections": true, + "canModifySession": true, + "canModifyStudent": true, + "canModifyInstructor": true + }, + "sectionLevel": {}, + "sessionLevel": {} + }, + "id": "00000000-0000-4000-8000-000000000501", + "course": { + "id": "tm.e2e.FTextQn.CS2104" + }, + "displayName": "Co-owner" + }, + "instructor2": { + "googleId": "tm.e2e.FTextQn.instructor2", + "name": "Teammates Test 2", + "email": "tmms.test2@gmail.tmt", + "role": "INSTRUCTOR_PERMISSION_ROLE_COOWNER", + "isDisplayedToStudents": true, + "privileges": { + "courseLevel": { + "canViewStudentInSections": true, + "canSubmitSessionInSections": true, + "canModifySessionCommentsInSections": true, + "canModifyCourse": true, + "canViewSessionInSections": true, + "canModifySession": true, + "canModifyStudent": true, + "canModifyInstructor": true + }, + "sectionLevel": {}, + "sessionLevel": {} + }, + "id": "00000000-0000-4000-8000-000000000502", + "course": { + "id": "tm.e2e.FTextQn.CS2104" + }, + "displayName": "Co-owner" + }, + "instructor3": { + "account": { + "id": "00000000-0000-4000-8000-000000000001" + }, + "name": "Teammates Test", + "email": "tmms.test@gmail.tmt", + "role": "INSTRUCTOR_PERMISSION_ROLE_COOWNER", + "isDisplayedToStudents": true, + "privileges": { + "courseLevel": { + "canViewStudentInSections": true, + "canSubmitSessionInSections": true, + "canModifySessionCommentsInSections": true, + "canModifyCourse": true, + "canViewSessionInSections": true, + "canModifySession": true, + "canModifyStudent": true, + "canModifyInstructor": true + }, + "sectionLevel": {}, + "sessionLevel": {} + }, + "id": "00000000-0000-4000-8000-000000000503", + "course": { + "id": "tm.e2e.FTextQn.CS1101" + }, + "displayName": "Co-owner" + } + }, + "sections": { + "ProgrammingLanguageConceptsNone": { + "id": "00000000-0000-4000-8000-000000000101", + "course": { + "id": "tm.e2e.FTextQn.CS2104" + }, + "name": "None" + } + }, + "teams": { + "ProgrammingLanguageConceptsNone": { + "id": "00000000-0000-4000-8000-000000000201", + "section": { + "id": "00000000-0000-4000-8000-000000000101" + }, + "name": "Team 1" + } + }, + "feedbackSessions": { + "openSession": { + "creatorEmail": "tmms.test@gmail.tmt", + "instructions": "

Instructions for first session

", + "createdTime": "2012-04-01T23:59:00Z", + "startTime": "2012-04-01T22:00:00Z", + "endTime": "2026-04-30T22:00:00Z", + "sessionVisibleFromTime": "2012-04-01T22:00:00Z", + "resultsVisibleFromTime": "2026-05-01T22:00:00Z", + "timeZone": "Africa/Johannesburg", + "gracePeriod": 10, + "sentOpenEmail": false, + "sentClosingEmail": false, + "sentClosedEmail": false, + "sentPublishedEmail": false, + "isOpeningEmailEnabled": true, + "isClosingEmailEnabled": true, + "isPublishedEmailEnabled": true, + "studentDeadlines": {}, + "instructorDeadlines": {}, + "id": "00000000-0000-4000-8000-000000000701", + "course": { + "id": "tm.e2e.FTextQn.CS2104" + }, + "name": "First Session" + }, + "openSession2": { + "creatorEmail": "tmms.test@gmail.tmt", + "instructions": "

Instructions for Second session

", + "createdTime": "2012-04-01T23:59:00Z", + "startTime": "2012-04-01T22:00:00Z", + "endTime": "2026-04-30T22:00:00Z", + "sessionVisibleFromTime": "2012-04-01T22:00:00Z", + "resultsVisibleFromTime": "2026-05-01T22:00:00Z", + "timeZone": "Africa/Johannesburg", + "gracePeriod": 10, + "sentOpenEmail": false, + "sentClosingEmail": false, + "sentClosedEmail": false, + "sentPublishedEmail": false, + "isOpeningEmailEnabled": true, + "isClosingEmailEnabled": true, + "isPublishedEmailEnabled": true, + "studentDeadlines": {}, + "instructorDeadlines": {}, + "id": "00000000-0000-4000-8000-000000000702", + "course": { + "id": "tm.e2e.FTextQn.CS1101" + }, + "name": "Second Session" + } + }, + "feedbackQuestions": { + "qn1ForFirstSession": { + "id": "00000000-0000-4000-8000-000000000801", + "feedbackSession": { + "id": "00000000-0000-4000-8000-000000000701" + }, + "questionDetails": { + "questionType": "TEXT", + "questionText": "What did this instructor do well?", + "recommendedLength": 1000 + }, + "description": "

Testing description for first session

", + "questionNumber": 1, + "giverType": "STUDENTS", + "recipientType": "INSTRUCTORS", + "numOfEntitiesToGiveFeedbackTo": 1, + "showResponsesTo": ["INSTRUCTORS"], + "showGiverNameTo": ["INSTRUCTORS"], + "showRecipientNameTo": ["INSTRUCTORS"] + }, + "qn1ForSecondSession": { + "id": "00000000-0000-4000-8000-000000000802", + "feedbackSession": { + "id": "00000000-0000-4000-8000-000000000702" + }, + "questionDetails": { + "questionType": "TEXT", + "questionText": "How can this instructor improve?", + "recommendedLength": 100 + }, + "description": "

Testing description for second session

", + "questionNumber": 1, + "giverType": "STUDENTS", + "recipientType": "INSTRUCTORS", + "numOfEntitiesToGiveFeedbackTo": 1, + "showResponsesTo": ["INSTRUCTORS"], + "showGiverNameTo": ["INSTRUCTORS"], + "showRecipientNameTo": ["INSTRUCTORS"] + } + }, + "notifications": {}, + "readNotifications": {}, + "feedbackResponseComments": {}, + "students": { + "alice.tmms@FTextQn.CS2104": { + "id": "00000000-0000-4000-8000-000000000604", + "account": { + "id": "00000000-0000-4000-8000-000000000002" + }, + "course": { + "id": "tm.e2e.FTextQn.CS2104" + }, + "team": { + "id": "00000000-0000-4000-8000-000000000201" + }, + "email": "alice.b.tmms@gmail.tmt", + "name": "Alice Betsy", + "comments": "This student's name is Alice Betsy" + } + } +} diff --git a/src/main/java/teammates/ui/webapi/GetFeedbackQuestionsAction.java b/src/main/java/teammates/ui/webapi/GetFeedbackQuestionsAction.java index 331641cc9f1..ff3926300c5 100644 --- a/src/main/java/teammates/ui/webapi/GetFeedbackQuestionsAction.java +++ b/src/main/java/teammates/ui/webapi/GetFeedbackQuestionsAction.java @@ -142,13 +142,13 @@ public JsonResult execute() { switch (intent) { case STUDENT_SUBMISSION: questions = sqlLogic.getFeedbackQuestionsForStudents(feedbackSession); - StudentAttributes studentAttributes = getStudentOfCourseFromRequest(courseId); + Student student = getSqlStudentOfCourseFromRequest(courseId); questions.forEach(question -> sqlLogic.populateFieldsToGenerateInQuestion(question, courseId, - studentAttributes.getEmail(), studentAttributes.getTeam())); + student.getEmail(), student.getTeamName())); break; case INSTRUCTOR_SUBMISSION: - InstructorAttributes instructor = getInstructorOfCourseFromRequest(courseId); + Instructor instructor = getSqlInstructorOfCourseFromRequest(courseId); questions = sqlLogic.getFeedbackQuestionsForInstructors(feedbackSession, instructor.getEmail()); questions.forEach(question -> sqlLogic.populateFieldsToGenerateInQuestion(question, courseId, diff --git a/src/test/java/teammates/test/AbstractBackDoor.java b/src/test/java/teammates/test/AbstractBackDoor.java index 5cbcc012808..16c62c5e3ed 100644 --- a/src/test/java/teammates/test/AbstractBackDoor.java +++ b/src/test/java/teammates/test/AbstractBackDoor.java @@ -673,10 +673,10 @@ public FeedbackSessionAttributes getSoftDeletedSession(String feedbackSessionNam } /** - * Get feedback question from database. + * Get feedback question data from database. */ - public FeedbackQuestionAttributes getFeedbackQuestion(String courseId, String feedbackSessionName, - int qnNumber) { + public FeedbackQuestionData getFeedbackQuestionData(String courseId, String feedbackSessionName, + int qnNumber) { Map params = new HashMap<>(); params.put(Const.ParamsNames.COURSE_ID, courseId); params.put(Const.ParamsNames.FEEDBACK_SESSION_NAME, feedbackSessionName); @@ -687,11 +687,19 @@ public FeedbackQuestionAttributes getFeedbackQuestion(String courseId, String fe } FeedbackQuestionsData questionsData = JsonUtils.fromJson(response.responseBody, FeedbackQuestionsData.class); - FeedbackQuestionData question = questionsData.getQuestions() + return questionsData.getQuestions() .stream() .filter(fq -> fq.getQuestionNumber() == qnNumber) .findFirst() .orElse(null); + } + + /** + * Get feedback question from database. + */ + public FeedbackQuestionAttributes getFeedbackQuestion(String courseId, String feedbackSessionName, + int qnNumber) { + FeedbackQuestionData question = getFeedbackQuestionData(courseId, feedbackSessionName, qnNumber); if (question == null) { return null; @@ -746,10 +754,10 @@ private static List convertToFeedbackParticipantType( } /** - * Get feedback response from database. + * Get feedback response data from database. */ - public FeedbackResponseAttributes getFeedbackResponse(String feedbackQuestionId, String giver, - String recipient) { + public FeedbackResponseData getFeedbackResponseData(String feedbackQuestionId, String giver, + String recipient) { Map params = new HashMap<>(); params.put(Const.ParamsNames.FEEDBACK_QUESTION_ID, feedbackQuestionId); params.put(Const.ParamsNames.INTENT, Intent.STUDENT_SUBMISSION.toString()); @@ -760,11 +768,19 @@ public FeedbackResponseAttributes getFeedbackResponse(String feedbackQuestionId, } FeedbackResponsesData responsesData = JsonUtils.fromJson(response.responseBody, FeedbackResponsesData.class); - FeedbackResponseData fr = responsesData.getResponses() + return responsesData.getResponses() .stream() .filter(r -> r.getGiverIdentifier().equals(giver) && r.getRecipientIdentifier().equals(recipient)) .findFirst() .orElse(null); + } + + /** + * Get feedback response from database. + */ + public FeedbackResponseAttributes getFeedbackResponse(String feedbackQuestionId, String giver, + String recipient) { + FeedbackResponseData fr = getFeedbackResponseData(feedbackQuestionId, giver, recipient); if (fr == null) { return null; diff --git a/src/test/java/teammates/test/BaseTestCaseWithSqlDatabaseAccess.java b/src/test/java/teammates/test/BaseTestCaseWithSqlDatabaseAccess.java index 35ad6fb3822..cdde3eb8f12 100644 --- a/src/test/java/teammates/test/BaseTestCaseWithSqlDatabaseAccess.java +++ b/src/test/java/teammates/test/BaseTestCaseWithSqlDatabaseAccess.java @@ -1,30 +1,97 @@ package teammates.test; import teammates.common.datatransfer.SqlDataBundle; +import teammates.common.datatransfer.questions.FeedbackQuestionDetails; +import teammates.common.datatransfer.questions.FeedbackResponseDetails; +import teammates.storage.sqlentity.BaseEntity; +import teammates.storage.sqlentity.FeedbackQuestion; +import teammates.storage.sqlentity.FeedbackResponse; +import teammates.ui.output.ApiOutput; +import teammates.ui.output.FeedbackQuestionData; +import teammates.ui.output.FeedbackResponseData; /** * Base class for all test cases which are allowed to access the database. */ public abstract class BaseTestCaseWithSqlDatabaseAccess extends BaseTestCase { + private static final int VERIFICATION_RETRY_COUNT = 5; + private static final int VERIFICATION_RETRY_DELAY_IN_MS = 1000; private static final int OPERATION_RETRY_COUNT = 5; private static final int OPERATION_RETRY_DELAY_IN_MS = 1000; /** * Removes and restores the databundle, with retries. */ - protected void removeAndRestoreDataBundle(SqlDataBundle testData) { + protected SqlDataBundle removeAndRestoreDataBundle(SqlDataBundle testData) { int retryLimit = OPERATION_RETRY_COUNT; - boolean isOperationSuccess = doRemoveAndRestoreDataBundle(testData); - while (!isOperationSuccess && retryLimit > 0) { + SqlDataBundle dataBundle = doRemoveAndRestoreDataBundle(testData); + while (dataBundle == null && retryLimit > 0) { retryLimit--; print("Re-trying removeAndRestoreDataBundle"); ThreadHelper.waitFor(OPERATION_RETRY_DELAY_IN_MS); - isOperationSuccess = doRemoveAndRestoreDataBundle(testData); + dataBundle = doRemoveAndRestoreDataBundle(testData); } - assertTrue(isOperationSuccess); + assertNotNull(dataBundle); + return dataBundle; } - protected abstract boolean doRemoveAndRestoreDataBundle(SqlDataBundle testData); + protected abstract SqlDataBundle doRemoveAndRestoreDataBundle(SqlDataBundle testData); + + /** + * Verifies that two entities are equal. + */ + protected void verifyEquals(BaseEntity expected, ApiOutput actual) { + if (expected instanceof FeedbackQuestion) { + FeedbackQuestion expectedQuestion = (FeedbackQuestion) expected; + FeedbackQuestionDetails expectedQuestionDetails = expectedQuestion.getQuestionDetailsCopy(); + FeedbackQuestionData actualQuestion = (FeedbackQuestionData) actual; + assertEquals(expectedQuestion.getQuestionNumber(), (Integer) actualQuestion.getQuestionNumber()); + assertEquals(expectedQuestionDetails.getQuestionText(), actualQuestion.getQuestionBrief()); + assertEquals(expectedQuestion.getDescription(), actualQuestion.getQuestionDescription()); + assertEquals(expectedQuestionDetails.getQuestionType(), actualQuestion.getQuestionType()); + assertEquals(expectedQuestion.getGiverType(), actualQuestion.getGiverType()); + assertEquals(expectedQuestion.getRecipientType(), actualQuestion.getRecipientType()); + // TODO: compare the rest of the attributes D: + } else if (expected instanceof FeedbackResponse) { + FeedbackResponse expectedResponse = (FeedbackResponse) expected; + FeedbackResponseDetails expectedResponseDetails = expectedResponse.getFeedbackResponseDetailsCopy(); + FeedbackResponseData actualResponse = (FeedbackResponseData) actual; + assertEquals(expectedResponse.getGiver(), actualResponse.getGiverIdentifier()); + assertEquals(expectedResponse.getRecipient(), actualResponse.getRecipientIdentifier()); + assertEquals(expectedResponseDetails.getAnswerString(), actualResponse.getResponseDetails().getAnswerString()); + // TODO: compare the rest of the attributes D: + } else { + fail("Unknown entity"); + } + } + + /** + * Verifies that the given entity is present in the database. + */ + protected void verifyPresentInDatabase(BaseEntity expected) { + int retryLimit = VERIFICATION_RETRY_COUNT; + ApiOutput actual = getEntity(expected); + while (actual == null && retryLimit > 0) { + retryLimit--; + ThreadHelper.waitFor(VERIFICATION_RETRY_DELAY_IN_MS); + actual = getEntity(expected); + } + verifyEquals(expected, actual); + } + + private ApiOutput getEntity(BaseEntity entity) { + if (entity instanceof FeedbackQuestion) { + return getFeedbackQuestion((FeedbackQuestion) entity); + } else if (entity instanceof FeedbackResponse) { + return getFeedbackResponse((FeedbackResponse) entity); + } else { + throw new RuntimeException("Unknown entity type"); + } + } + + protected abstract FeedbackQuestionData getFeedbackQuestion(FeedbackQuestion fq); + + protected abstract FeedbackResponseData getFeedbackResponse(FeedbackResponse fq); }