@@ -155,6 +155,14 @@ def test_pin_thread(self):
155155 assert response .status_code == 200
156156 self ._assert_json_response_contains_group_info (response )
157157
158+ def test_vote (self ):
159+ response = self .call_view (
160+ "vote_for_thread" , "update_thread_votes" , view_args = {"value" : "up" }
161+ )
162+ self ._assert_json_response_contains_group_info (response )
163+ response = self .call_view ("undo_vote_for_thread" , "delete_thread_vote" )
164+ self ._assert_json_response_contains_group_info (response )
165+
158166
159167class ViewsTestCaseMixin :
160168
@@ -214,6 +222,22 @@ def set_up_course(self, block_count=0):
214222
215223 assert self .client .login (username = "student" , password = self .password )
216224
225+ def _setup_mock_request (self , mock_function , include_depth = False ):
226+ """
227+ Ensure that mock_request returns the data necessary to make views
228+ function correctly
229+ """
230+ data = {
231+ "user_id" : str (self .student .id ),
232+ "closed" : False ,
233+ "commentable_id" : "non_team_dummy_id" ,
234+ "thread_id" : "dummy" ,
235+ "thread_type" : "discussion" ,
236+ }
237+ if include_depth :
238+ data ["depth" ] = 0
239+ self .set_mock_return_value (mock_function , data )
240+
217241
218242@ddt .ddt
219243@disable_signal (views , "comment_flagged" )
@@ -549,6 +573,26 @@ def un_flag_comment(self, is_closed):
549573
550574 assert response .status_code == 200
551575
576+ @ddt .data (
577+ ("upvote_thread" , "update_thread_votes" , "thread_id" , "thread_voted" ),
578+ ("upvote_comment" , "update_comment_votes" , "comment_id" , "comment_voted" ),
579+ ("downvote_thread" , "update_thread_votes" , "thread_id" , "thread_voted" ),
580+ ("downvote_comment" , "update_comment_votes" , "comment_id" , "comment_voted" ),
581+ )
582+ @ddt .unpack
583+ def test_voting (self , view_name , function_name , item_id , signal ):
584+ self ._setup_mock_request ("get_thread" )
585+ self ._setup_mock_request ("get_parent_comment" )
586+ self ._setup_mock_request (function_name )
587+ with self .assert_discussion_signals (signal ):
588+ response = self .client .post (
589+ reverse (
590+ view_name ,
591+ kwargs = {item_id : "dummy" , "course_id" : str (self .course_id )},
592+ )
593+ )
594+ assert response .status_code == 200
595+
552596
553597@disable_signal (views , "comment_endorsed" )
554598class ViewPermissionsTestCase (
@@ -843,15 +887,22 @@ def test_comment_actions(self, user, commentable_id, status_code):
843887 commentable_id = getattr (self , commentable_id )
844888 self ._setup_mock (
845889 user ,
846- ["get_parent_comment" , "update_comment_flag" ],
890+ [
891+ "get_parent_comment" ,
892+ "update_comment_flag" ,
893+ "update_comment_votes" ,
894+ "delete_comment_vote" ,
895+ ],
847896 make_minimal_cs_comment (
848897 {
898+ "closed" : False ,
849899 "commentable_id" : commentable_id ,
850900 "course_id" : str (self .course .id ),
851901 }
852902 ),
853903 )
854- for action in ["un_flag_abuse_for_comment" , "flag_abuse_for_comment" ]:
904+ # "un_flag_abuse_for_comment", "flag_abuse_for_comment",
905+ for action in ["upvote_comment" , "downvote_comment" ]:
855906 response = self .client .post (
856907 reverse (
857908 action ,
@@ -873,7 +924,12 @@ def test_threads_actions(self, user, commentable_id, status_code):
873924 commentable_id = getattr (self , commentable_id )
874925 self ._setup_mock (
875926 user ,
876- ["get_thread" , "update_thread_flag" ],
927+ [
928+ "get_thread" ,
929+ "update_thread_flag" ,
930+ "update_thread_votes" ,
931+ "delete_thread_vote" ,
932+ ],
877933 make_minimal_cs_thread (
878934 {
879935 "commentable_id" : commentable_id ,
@@ -882,7 +938,12 @@ def test_threads_actions(self, user, commentable_id, status_code):
882938 ),
883939 )
884940
885- for action in ["un_flag_abuse_for_thread" , "flag_abuse_for_thread" ]:
941+ for action in [
942+ "un_flag_abuse_for_thread" ,
943+ "flag_abuse_for_thread" ,
944+ "upvote_thread" ,
945+ "downvote_thread" ,
946+ ]:
886947 response = self .client .post (
887948 reverse (
888949 action ,
@@ -893,3 +954,87 @@ def test_threads_actions(self, user, commentable_id, status_code):
893954 )
894955 )
895956 assert response .status_code == status_code
957+
958+
959+ @disable_signal (views , "comment_created" )
960+ @ddt .ddt
961+ class ForumEventTestCase (
962+ ForumsEnableMixin , SharedModuleStoreTestCase , MockForumApiMixin
963+ ):
964+ """
965+ Forum actions are expected to launch analytics events. Test these here.
966+ """
967+
968+ @classmethod
969+ def setUpClass (cls ):
970+ super ().setUpClassAndForumMock ()
971+ # pylint: disable=super-method-not-called
972+ with super ().setUpClassAndTestData ():
973+ cls .course = CourseFactory .create ()
974+
975+ @classmethod
976+ def tearDownClass (cls ):
977+ """Stop patches after tests complete."""
978+ super ().tearDownClass ()
979+ super ().disposeForumMocks ()
980+
981+ @classmethod
982+ def setUpTestData (cls ):
983+ super ().setUpTestData ()
984+
985+ seed_permissions_roles (cls .course .id )
986+
987+ cls .student = UserFactory .create ()
988+ CourseEnrollmentFactory (user = cls .student , course_id = cls .course .id )
989+ cls .student .roles .add (Role .objects .get (name = "Student" , course_id = cls .course .id ))
990+ CourseAccessRoleFactory (
991+ course_id = cls .course .id , user = cls .student , role = "Wizard"
992+ )
993+
994+ @ddt .data (
995+ ("vote_for_thread" , "update_thread_votes" , "thread_id" , "thread" ),
996+ ("undo_vote_for_thread" , "delete_thread_vote" , "thread_id" , "thread" ),
997+ ("vote_for_comment" , "update_comment_votes" , "comment_id" , "response" ),
998+ ("undo_vote_for_comment" , "delete_comment_vote" , "comment_id" , "response" ),
999+ )
1000+ @ddt .unpack
1001+ @patch ("eventtracking.tracker.emit" )
1002+ def test_thread_voted_event (
1003+ self , view_name , function_name , obj_id_name , obj_type , mock_emit
1004+ ):
1005+ undo = view_name .startswith ("undo" )
1006+ cs_thread = make_minimal_cs_thread (
1007+ {
1008+ "commentable_id" : "test_commentable_id" ,
1009+ "username" : "gumprecht" ,
1010+ }
1011+ )
1012+ cs_comment = make_minimal_cs_comment (
1013+ {
1014+ "closed" : False ,
1015+ "commentable_id" : "test_commentable_id" ,
1016+ "username" : "gumprecht" ,
1017+ }
1018+ )
1019+ self .set_mock_return_value ("get_thread" , cs_thread )
1020+ self .set_mock_return_value ("get_parent_comment" , cs_comment )
1021+ self .set_mock_return_value (
1022+ function_name , cs_thread if "thread" in view_name else cs_comment
1023+ )
1024+
1025+ request = RequestFactory ().post ("dummy_url" , {})
1026+ request .user = self .student
1027+ request .view_name = view_name
1028+ view_function = getattr (views , view_name )
1029+ kwargs = dict (course_id = str (self .course .id ))
1030+ kwargs [obj_id_name ] = obj_id_name
1031+ if not undo :
1032+ kwargs .update (value = "up" )
1033+ view_function (request , ** kwargs )
1034+
1035+ assert mock_emit .called
1036+ event_name , event = mock_emit .call_args [0 ]
1037+ assert event_name == f"edx.forum.{ obj_type } .voted"
1038+ assert event ["target_username" ] == "gumprecht"
1039+ assert event ["undo_vote" ] == undo
1040+ assert event ["vote_value" ] == "up"
0 commit comments