diff --git a/common/static/coffee/src/discussion/views/thread_response_view.coffee b/common/static/coffee/src/discussion/views/thread_response_view.coffee index da37313d52..7418f80e28 100644 --- a/common/static/coffee/src/discussion/views/thread_response_view.coffee +++ b/common/static/coffee/src/discussion/views/thread_response_view.coffee @@ -133,13 +133,14 @@ if Backbone? createEditView: () -> if @showView? - @showView.undelegateEvents() @showView.$el.empty() - @showView = null - @editView = new ThreadResponseEditView(model: @model) - @editView.bind "response:update", @update - @editView.bind "response:cancel_edit", @cancelEdit + if @editView? + @editView.model = @model + else + @editView = new ThreadResponseEditView(model: @model) + @editView.bind "response:update", @update + @editView.bind "response:cancel_edit", @cancelEdit renderSubView: (view) -> view.setElement(@$('.discussion-response')) @@ -161,14 +162,15 @@ if Backbone? createShowView: () -> if @editView? - @editView.undelegateEvents() @editView.$el.empty() - @editView = null - @showView = new ThreadResponseShowView(model: @model) - @showView.bind "response:_delete", @_delete - @showView.bind "response:edit", @edit - @showView.on "comment:endorse", => @trigger("comment:endorse") + if @showView? + @showView.model = @model + else + @showView = new ThreadResponseShowView(model: @model) + @showView.bind "response:_delete", @_delete + @showView.bind "response:edit", @edit + @showView.on "comment:endorse", => @trigger("comment:endorse") renderShowView: () -> @renderSubView(@showView) diff --git a/common/test/acceptance/pages/lms/discussion.py b/common/test/acceptance/pages/lms/discussion.py index e923334ddb..d097fd76b5 100644 --- a/common/test/acceptance/pages/lms/discussion.py +++ b/common/test/acceptance/pages/lms/discussion.py @@ -107,13 +107,22 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin): "Response field received focus" ).fulfill() + @wait_for_js def is_response_editor_visible(self, response_id): """Returns true if the response editor is present, false otherwise""" return self._is_element_visible(".response_{} .edit-post-body".format(response_id)) + def is_response_visible(self, comment_id): + """Returns true if the response is viewable onscreen""" + return self._is_element_visible(".response_{} .response-body".format(comment_id)) + def is_response_editable(self, response_id): """Returns true if the edit response button is present, false otherwise""" - return self._is_element_visible(".response_{} .discussion-response .action-edit".format(response_id)) + with self._secondary_action_menu_open(".response_{} .discussion-response".format(response_id)): + return self._is_element_visible(".response_{} .discussion-response .action-edit".format(response_id)) + + def get_response_body(self, response_id): + return self._get_element_text(".response_{} .response-body".format(response_id)) def start_response_edit(self, response_id): """Click the edit button for the response, loading the editing view""" @@ -124,6 +133,57 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin): "Response edit started" ).fulfill() + def get_response_vote_count(self, response_id): + return self._get_element_text(".response_{} .discussion-response .action-vote .vote-count".format(response_id)) + + def vote_response(self, response_id): + current_count = self._get_element_text(".response_{} .discussion-response .action-vote .vote-count".format(response_id)) + self._find_within(".response_{} .discussion-response .action-vote".format(response_id)).first.click() + self.wait_for_ajax() + EmptyPromise( + lambda: current_count != self.get_response_vote_count(response_id), + "Response is voted" + ).fulfill() + + def is_response_reported(self, response_id): + return self._is_element_visible(".response_{} .discussion-response .post-label-reported".format(response_id)) + + def report_response(self, response_id): + with self._secondary_action_menu_open(".response_{} .discussion-response".format(response_id)): + self._find_within(".response_{} .discussion-response .action-report".format(response_id)).first.click() + self.wait_for_ajax() + EmptyPromise( + lambda: self.is_response_reported(response_id), + "Response is reported" + ).fulfill() + + def is_response_endorsed(self, response_id): + return "endorsed" in self._get_element_text(".response_{} .discussion-response .posted-details".format(response_id)) + + def endorse_response(self, response_id): + self._find_within(".response_{} .discussion-response .action-endorse".format(response_id)).first.click() + self.wait_for_ajax() + EmptyPromise( + lambda: self.is_response_endorsed(response_id), + "Response edit started" + ).fulfill() + + def set_response_editor_value(self, response_id, new_body): + """Replace the contents of the response editor""" + self._find_within(".response_{} .discussion-response .wmd-input".format(response_id)).fill(new_body) + + def submit_response_edit(self, response_id, new_response_body): + """Click the submit button on the response editor""" + self._find_within(".response_{} .discussion-response .post-update".format(response_id)).first.click() + EmptyPromise( + lambda: ( + not self.is_response_editor_visible(response_id) and + self.is_response_visible(response_id) and + self.get_response_body(response_id) == new_response_body + ), + "Comment edit succeeded" + ).fulfill() + def is_show_comments_visible(self, response_id): """Returns true if the "show comments" link is visible for a response""" return self._is_element_visible(".response_{} .action-show-comments".format(response_id)) diff --git a/common/test/acceptance/tests/discussion/test_discussion.py b/common/test/acceptance/tests/discussion/test_discussion.py index 9a62f97253..63b461586f 100644 --- a/common/test/acceptance/tests/discussion/test_discussion.py +++ b/common/test/acceptance/tests/discussion/test_discussion.py @@ -246,6 +246,107 @@ class DiscussionCommentDeletionTest(UniqueCourseTest): page.delete_comment("comment_other_author") +@attr('shard_1') +class DiscussionResponseEditTest(UniqueCourseTest): + """ + Tests for editing responses displayed beneath thread in the single thread view. + """ + + def setUp(self): + super(DiscussionResponseEditTest, self).setUp() + + # Create a course to register for + CourseFixture(**self.course_info).install() + + def setup_user(self, roles=[]): + roles_str = ','.join(roles) + self.user_id = AutoAuthPage(self.browser, course_id=self.course_id, roles=roles_str).visit().get_user_id() + + def setup_view(self): + view = SingleThreadViewFixture(Thread(id="response_edit_test_thread")) + view.addResponse( + Response(id="response_other_author", user_id="other", thread_id="response_edit_test_thread"), + ) + view.addResponse( + Response(id="response_self_author", user_id=self.user_id, thread_id="response_edit_test_thread"), + ) + view.push() + + def edit_response(self, page, response_id): + self.assertTrue(page.is_response_editable(response_id)) + page.start_response_edit(response_id) + new_response = "edited body" + page.set_response_editor_value(response_id, new_response) + page.submit_response_edit(response_id, new_response) + + def test_edit_response_as_student(self): + """ + Scenario: Students should be able to edit the response they created not responses of other users + Given that I am on discussion page with student logged in + When I try to edit the response created by student + Then the response should be edited and rendered successfully + And responses from other users should be shown over there + And the student should be able to edit the response of other people + """ + self.setup_user() + self.setup_view() + page = DiscussionTabSingleThreadPage(self.browser, self.course_id, "response_edit_test_thread") + page.visit() + self.assertTrue(page.is_response_visible("response_other_author")) + self.assertFalse(page.is_response_editable("response_other_author")) + self.edit_response(page, "response_self_author") + + def test_edit_response_as_moderator(self): + """ + Scenario: Moderator should be able to edit the response they created and responses of other users + Given that I am on discussion page with moderator logged in + When I try to edit the response created by moderator + Then the response should be edited and rendered successfully + And I try to edit the response created by other users + Then the response should be edited and rendered successfully + """ + self.setup_user(roles=["Moderator"]) + self.setup_view() + page = DiscussionTabSingleThreadPage(self.browser, self.course_id, "response_edit_test_thread") + page.visit() + self.edit_response(page, "response_self_author") + self.edit_response(page, "response_other_author") + + def test_vote_report_endorse_after_edit(self): + """ + Scenario: Moderator should be able to vote, report or endorse after editing the response. + Given that I am on discussion page with moderator logged in + When I try to edit the response created by moderator + Then the response should be edited and rendered successfully + And I try to edit the response created by other users + Then the response should be edited and rendered successfully + And I try to vote the response created by moderator + Then the response should be voted successfully + And I try to vote the response created by other users + Then the response should be voted successfully + And I try to report the response created by moderator + Then the response should be reported successfully + And I try to report the response created by other users + Then the response should be reported successfully + And I try to endorse the response created by moderator + Then the response should be endorsed successfully + And I try to endorse the response created by other users + Then the response should be endorsed successfully + """ + self.setup_user(roles=["Moderator"]) + self.setup_view() + page = DiscussionTabSingleThreadPage(self.browser, self.course_id, "response_edit_test_thread") + page.visit() + self.edit_response(page, "response_self_author") + self.edit_response(page, "response_other_author") + page.vote_response('response_self_author') + page.vote_response('response_other_author') + page.report_response('response_self_author') + page.report_response('response_other_author') + page.endorse_response('response_self_author') + page.endorse_response('response_other_author') + + @attr('shard_1') class DiscussionCommentEditTest(UniqueCourseTest): """