From 6ae5c93be3969e0a4560803ec58ff4c30d9a8302 Mon Sep 17 00:00:00 2001
From: "dominik.seeger" <dominik.seeger@stud.uni-goettingen.de>
Date: Thu, 15 Aug 2019 11:41:05 +0200
Subject: [PATCH] Resolve "Sort updated comments chronoligically"

---
 .../submission_notes/SubmissionCorrection.vue | 12 +++-
 .../submission_notes/base/FeedbackComment.vue |  6 +-
 frontend/src/models.ts                        |  2 +-
 .../pages/base/TutorReviewerBaseLayout.vue    | 11 +--
 functional_tests/test_feedback_creation.py    | 72 ++++++++++++++++++-
 functional_tests/util.py                      | 10 +++
 6 files changed, 103 insertions(+), 10 deletions(-)

diff --git a/frontend/src/components/submission_notes/SubmissionCorrection.vue b/frontend/src/components/submission_notes/SubmissionCorrection.vue
index 6b3a3bf2..d0d4a631 100644
--- a/frontend/src/components/submission_notes/SubmissionCorrection.vue
+++ b/frontend/src/components/submission_notes/SubmissionCorrection.vue
@@ -15,7 +15,7 @@
             <template v-if="showFeedback">
               <div v-if="origFeedback[lineNo]">
                 <feedback-comment
-                  v-for="(comment, index) in origFeedback[lineNo]"
+                  v-for="(comment, index) in getSortedComments(lineNo)"
                   v-bind="comment"
                   :visibleToStudent="updatedFeedback[lineNo] ? false : comment.visibleToStudent"
                   :line-no="lineNo"
@@ -170,6 +170,16 @@ export default {
         }
       }, 5e3)
     },
+    getSortedComments (lineNo) {
+      if (!this.origFeedback || (!this.origFeedback && !this.origFeedback[lineNo])) return new Array()
+      let feedback = [...this.origFeedback[lineNo]]
+
+      return feedback.sort((a, b) => {
+        const da = new Date(a.modified)
+        const db = new Date(b.modified)
+        return da.getTime() - db.getTime()
+      })
+    },
     init () {
       SubmissionNotes.RESET_STATE()
       SubmissionNotes.SET_SUBMISSION(this.submissionObj)
diff --git a/frontend/src/components/submission_notes/base/FeedbackComment.vue b/frontend/src/components/submission_notes/base/FeedbackComment.vue
index 8a497083..963fe4b4 100644
--- a/frontend/src/components/submission_notes/base/FeedbackComment.vue
+++ b/frontend/src/components/submission_notes/base/FeedbackComment.vue
@@ -98,7 +98,7 @@ export default {
       type: String,
       required: true
     },
-    created: {
+    modified: {
       type: String,
       required: false
     },
@@ -133,8 +133,8 @@ export default {
     },
     markedForDeletion () { return SubmissionNotes.state.commentsMarkedForDeletion },
     parsedCreated () {
-      if (this.created) {
-        return new Date(this.created).toLocaleString()
+      if (this.modified) {
+        return new Date(this.modified).toLocaleString()
       } else {
         return 'Just now'
       }
diff --git a/frontend/src/models.ts b/frontend/src/models.ts
index b24847d8..4065aa32 100644
--- a/frontend/src/models.ts
+++ b/frontend/src/models.ts
@@ -165,7 +165,7 @@ export interface FeedbackComment {
      * @type {Date}
      * @memberof FeedbackComment
      */
-    created?: string
+    modified?: string
     /**
      *
      * @type {string}
diff --git a/frontend/src/pages/base/TutorReviewerBaseLayout.vue b/frontend/src/pages/base/TutorReviewerBaseLayout.vue
index c4d47eae..beed5790 100644
--- a/frontend/src/pages/base/TutorReviewerBaseLayout.vue
+++ b/frontend/src/pages/base/TutorReviewerBaseLayout.vue
@@ -12,7 +12,7 @@
             <v-icon>{{ item.icon }}</v-icon>
           </v-list-tile-action>
           <v-list-tile-content>
-            <v-list-tile-title>
+            <v-list-tile-title :id="item.tagId">
               {{ item.name }}
             </v-list-tile-title>
           </v-list-tile-content>
@@ -45,17 +45,20 @@ export default {
         {
           name: 'Overview',
           icon: 'home',
-          route: '/home'
+          route: '/home',
+          tagId: 'overview'
         },
         {
           name: 'Feedback History',
           icon: 'feedback',
-          route: '/feedback'
+          route: '/feedback',
+          tagId: 'feedback'
         },
         {
           name: 'Statistics',
           icon: 'bar_chart',
-          route: '/statistics'
+          route: '/statistics',
+          tagId: 'statistics'
         }
       ]
     }
diff --git a/functional_tests/test_feedback_creation.py b/functional_tests/test_feedback_creation.py
index 1e1bd335..c85c84be 100644
--- a/functional_tests/test_feedback_creation.py
+++ b/functional_tests/test_feedback_creation.py
@@ -1,12 +1,14 @@
 from django.test import LiveServerTestCase
 from selenium import webdriver
+from selenium.webdriver import ActionChains
 from selenium.webdriver.support.ui import WebDriverWait
 from selenium.webdriver.support import expected_conditions as ec
+from selenium.webdriver.common.by import By
 
 from core.models import UserAccount, Submission, FeedbackComment
 from functional_tests.util import (login, create_browser, reset_browser_after_test,
                                    go_to_subscription, wait_until_code_changes,
-                                   reconstruct_submission_code)
+                                   reconstruct_submission_code, wait_until_element_count_equals)
 from util import factory_boys as fact
 
 
@@ -223,6 +225,74 @@ class UntestedParent:
             self.assertEqual(0, submission_for_code.feedback.score)
             self.assertEqual(1, submission_for_code.feedback.feedback_lines.count())
 
+        def test_comments_are_sorted_by_last_updated(self):
+            self._login()
+            go_to_subscription(self)
+
+            code = reconstruct_submission_code(self)
+            self.browser.find_element_by_id('score-full').click()
+
+            # give feedback on first line
+            self.write_comments_on_lines([(1, 'first ever comment')])
+
+            submit_btn = self.browser.find_element_by_id('submit-feedback')
+            submit_btn.click()
+
+            WebDriverWait(self.browser, 10).until(
+                wait_until_code_changes(self, code)
+            )
+
+            reset_browser_after_test(self.browser, self.live_server_url)  # logs out user
+
+            user_snd = 'tutor_snd'
+            password = 'p'
+            fact.UserAccountFactory(username=user_snd, password=password)
+
+            login(self.browser, self.live_server_url, user_snd, password)
+            go_to_subscription(self, stage='validate')
+
+            self.write_comments_on_lines([(1, 'the second comment')])
+            self.browser.find_element_by_id('score-full').click()
+            self.browser.find_element_by_class_name('final-checkbox').click()
+            self.browser.find_element_by_id('submit-feedback').click()
+
+            sub_url = 'subscription/' + str(self.sub_type.pk) + '/ended'
+            WebDriverWait(self.browser, 10).until(ec.url_contains(sub_url))
+
+            reset_browser_after_test(self.browser, self.live_server_url)  # logs out user
+            self._login()
+
+            # goto history
+            self.browser.find_element_by_id('feedback').click()
+            feedback_entry = self.browser.find_element_by_class_name('feedback-row')
+            ActionChains(self.browser).move_to_element(feedback_entry).click().perform()
+
+            # validate that second comment is under the first comment
+            comments = self.browser.find_elements_by_class_name('dialog-box')
+            first_text = comments[0].find_element_by_class_name('message')
+            second_text = comments[1].find_element_by_class_name('message')
+
+            self.assertEqual(len(comments), 2)
+            self.assertEqual(first_text.text, 'first ever comment')
+            self.assertEqual(second_text.text, 'the second comment')
+
+            # give feedback on first line
+            self.write_comments_on_lines([(1, 'first comment updated')])
+            self.browser.find_element_by_id('score-full').click()
+            self.browser.find_element_by_id('submit-feedback').click()
+
+            WebDriverWait(self.browser, 5).until(
+                wait_until_element_count_equals(self, By.CLASS_NAME, "dialog-box", 2)
+            )
+
+            # validate that the edited first comment is under the second comment
+            comments = self.browser.find_elements_by_class_name('dialog-box')
+            first_text = comments[0].find_element_by_class_name('message')
+            second_text = comments[1].find_element_by_class_name('message')
+
+            self.assertEqual(first_text.text, 'the second comment')
+            self.assertEqual(second_text.text, 'first comment updated')
+
 
 class TestFeedbackCreationTutor(UntestedParent.TestFeedbackCreationGeneric):
     def setUp(self):
diff --git a/functional_tests/util.py b/functional_tests/util.py
index 9fd0e6ed..728542e2 100644
--- a/functional_tests/util.py
+++ b/functional_tests/util.py
@@ -130,3 +130,13 @@ def wait_until_code_changes(test_class_instance, code):
             return False
         return code != new_code
     return condition
+
+
+def wait_until_element_count_equals(test_class_instance, by, selector, count):
+    def condition(*args):
+        try:
+            elements = test_class_instance.browser.find_elements(by, selector)
+        except Exception:
+            return False
+        return len(elements) == count
+    return condition
-- 
GitLab