diff --git a/common/static/coffee/spec/discussion/view/discussion_content_view_spec.coffee b/common/static/coffee/spec/discussion/view/discussion_content_view_spec.coffee index 85ab5ec254..71495ad9c6 100644 --- a/common/static/coffee/spec/discussion/view/discussion_content_view_spec.coffee +++ b/common/static/coffee/spec/discussion/view/discussion_content_view_spec.coffee @@ -1,13 +1,12 @@ describe "DiscussionContentView", -> beforeEach -> - setFixtures - ( + setFixtures( """
- - + 0 + + 0 votes (click to vote)

Post Title

robot @@ -23,16 +22,21 @@ describe "DiscussionContentView", -> """ ) - @thread = new Thread { - id: '01234567', - user_id: '567', - course_id: 'mitX/999/test', - body: 'this is a thread', - created_at: '2013-04-03T20:08:39Z', - abuse_flaggers: ['123'] - roles: [] + @threadData = { + id: '01234567', + user_id: '567', + course_id: 'mitX/999/test', + body: 'this is a thread', + created_at: '2013-04-03T20:08:39Z', + abuse_flaggers: ['123'], + votes: {up_count: '42'}, + type: "thread", + roles: [] } + @thread = new Thread(@threadData) @view = new DiscussionContentView({ model: @thread }) + @view.setElement($('.discussion-post')) + window.user = new DiscussionUser({id: '567', upvoted_ids: []}) it 'defines the tag', -> expect($('#jasmine-fixtures')).toExist @@ -56,3 +60,15 @@ describe "DiscussionContentView", -> @thread.set("abuse_flaggers",temp_array) @thread.unflagAbuse() expect(@thread.get 'abuse_flaggers').toEqual [] + + it 'renders the vote button properly', -> + DiscussionViewSpecHelper.checkRenderVote(@view, @thread) + + it 'votes correctly', -> + DiscussionViewSpecHelper.checkVote(@view, @thread, @threadData, false) + + it 'unvotes correctly', -> + DiscussionViewSpecHelper.checkUnvote(@view, @thread, @threadData, false) + + it 'toggles the vote correctly', -> + DiscussionViewSpecHelper.checkToggleVote(@view, @thread) diff --git a/common/static/coffee/spec/discussion/view/discussion_thread_profile_view_spec.coffee b/common/static/coffee/spec/discussion/view/discussion_thread_profile_view_spec.coffee new file mode 100644 index 0000000000..f10d30d0af --- /dev/null +++ b/common/static/coffee/spec/discussion/view/discussion_thread_profile_view_spec.coffee @@ -0,0 +1,40 @@ +describe "DiscussionThreadProfileView", -> + beforeEach -> + setFixtures( + """ +

+ + 0 votes (click to vote) + +
+ """ + ) + + @threadData = { + id: "dummy", + user_id: "567", + course_id: "TestOrg/TestCourse/TestRun", + body: "this is a thread", + created_at: "2013-04-03T20:08:39Z", + abuse_flaggers: [], + votes: {up_count: "42"} + } + @thread = new Thread(@threadData) + @view = new DiscussionThreadProfileView({ model: @thread }) + @view.setElement($(".discussion-post")) + window.user = new DiscussionUser({id: "567", upvoted_ids: []}) + + it "renders the vote correctly", -> + DiscussionViewSpecHelper.checkRenderVote(@view, @thread) + + it "votes correctly", -> + DiscussionViewSpecHelper.checkVote(@view, @thread, @threadData, true) + + it "unvotes correctly", -> + DiscussionViewSpecHelper.checkUnvote(@view, @thread, @threadData, true) + + it "toggles the vote correctly", -> + DiscussionViewSpecHelper.checkToggleVote(@view, @thread) + + it "vote button activates on appropriate events", -> + DiscussionViewSpecHelper.checkVoteButtonEvents(@view) diff --git a/common/static/coffee/spec/discussion/view/discussion_thread_show_view_spec.coffee b/common/static/coffee/spec/discussion/view/discussion_thread_show_view_spec.coffee new file mode 100644 index 0000000000..69e4b231a2 --- /dev/null +++ b/common/static/coffee/spec/discussion/view/discussion_thread_show_view_spec.coffee @@ -0,0 +1,40 @@ +describe "DiscussionThreadShowView", -> + beforeEach -> + setFixtures( + """ +
+ + 0 votes (click to vote) + +
+ """ + ) + + @threadData = { + id: "dummy", + user_id: "567", + course_id: "TestOrg/TestCourse/TestRun", + body: "this is a thread", + created_at: "2013-04-03T20:08:39Z", + abuse_flaggers: [], + votes: {up_count: "42"} + } + @thread = new Thread(@threadData) + @view = new DiscussionThreadShowView({ model: @thread }) + @view.setElement($(".discussion-post")) + window.user = new DiscussionUser({id: "567", upvoted_ids: []}) + + it "renders the vote correctly", -> + DiscussionViewSpecHelper.checkRenderVote(@view, @thread) + + it "votes correctly", -> + DiscussionViewSpecHelper.checkVote(@view, @thread, @threadData, true) + + it "unvotes correctly", -> + DiscussionViewSpecHelper.checkUnvote(@view, @thread, @threadData, true) + + it 'toggles the vote correctly', -> + DiscussionViewSpecHelper.checkToggleVote(@view, @thread) + + it "vote button activates on appropriate events", -> + DiscussionViewSpecHelper.checkVoteButtonEvents(@view) diff --git a/common/static/coffee/spec/discussion/view/discussion_view_spec_helper.coffee b/common/static/coffee/spec/discussion/view/discussion_view_spec_helper.coffee new file mode 100644 index 0000000000..d5c25aa5e2 --- /dev/null +++ b/common/static/coffee/spec/discussion/view/discussion_view_spec_helper.coffee @@ -0,0 +1,113 @@ +class @DiscussionViewSpecHelper + @expectVoteRendered = (view, voted) -> + button = view.$el.find(".vote-btn") + if voted + expect(button.hasClass("is-cast")).toBe(true) + expect(button.attr("aria-pressed")).toEqual("true") + expect(button.attr("data-tooltip")).toEqual("remove vote") + expect(button.find(".votes-count-number").html()).toEqual("43") + expect(button.find(".sr").html()).toEqual("votes (click to remove your vote)") + else + expect(button.hasClass("is-cast")).toBe(false) + expect(button.attr("aria-pressed")).toEqual("false") + expect(button.attr("data-tooltip")).toEqual("vote") + expect(button.find(".votes-count-number").html()).toEqual("42") + expect(button.find(".sr").html()).toEqual("votes (click to vote)") + + @checkRenderVote = (view, model) -> + view.renderVote() + DiscussionViewSpecHelper.expectVoteRendered(view, false) + window.user.vote(model) + view.renderVote() + DiscussionViewSpecHelper.expectVoteRendered(view, true) + window.user.unvote(model) + view.renderVote() + DiscussionViewSpecHelper.expectVoteRendered(view, false) + + @checkVote = (view, model, modelData, checkRendering) -> + view.renderVote() + if checkRendering + DiscussionViewSpecHelper.expectVoteRendered(view, false) + + spyOn($, "ajax").andCallFake((params) => + newModelData = {} + $.extend(newModelData, modelData, {votes: {up_count: "43"}}) + params.success(newModelData, "success") + # Caller invokes always function on return value but it doesn't matter here + {always: ->} + ) + + view.vote() + expect(window.user.voted(model)).toBe(true) + if checkRendering + DiscussionViewSpecHelper.expectVoteRendered(view, true) + expect($.ajax).toHaveBeenCalled() + $.ajax.reset() + + # Check idempotence + view.vote() + expect(window.user.voted(model)).toBe(true) + if checkRendering + DiscussionViewSpecHelper.expectVoteRendered(view, true) + expect($.ajax).toHaveBeenCalled() + + @checkUnvote = (view, model, modelData, checkRendering) -> + window.user.vote(model) + expect(window.user.voted(model)).toBe(true) + if checkRendering + DiscussionViewSpecHelper.expectVoteRendered(view, true) + + spyOn($, "ajax").andCallFake((params) => + newModelData = {} + $.extend(newModelData, modelData, {votes: {up_count: "42"}}) + params.success(newModelData, "success") + # Caller invokes always function on return value but it doesn't matter here + {always: ->} + ) + + view.unvote() + expect(window.user.voted(model)).toBe(false) + if checkRendering + DiscussionViewSpecHelper.expectVoteRendered(view, false) + expect($.ajax).toHaveBeenCalled() + $.ajax.reset() + + # Check idempotence + view.unvote() + expect(window.user.voted(model)).toBe(false) + if checkRendering + DiscussionViewSpecHelper.expectVoteRendered(view, false) + expect($.ajax).toHaveBeenCalled() + + @checkToggleVote = (view, model) -> + event = {preventDefault: ->} + spyOn(event, "preventDefault") + spyOn(view, "vote").andCallFake(() -> window.user.vote(model)) + spyOn(view, "unvote").andCallFake(() -> window.user.unvote(model)) + + expect(window.user.voted(model)).toBe(false) + view.toggleVote(event) + expect(view.vote).toHaveBeenCalled() + expect(view.unvote).not.toHaveBeenCalled() + expect(event.preventDefault.callCount).toEqual(1) + + view.vote.reset() + view.unvote.reset() + expect(window.user.voted(model)).toBe(true) + view.toggleVote(event) + expect(view.vote).not.toHaveBeenCalled() + expect(view.unvote).toHaveBeenCalled() + expect(event.preventDefault.callCount).toEqual(2) + + @checkVoteButtonEvents = (view) -> + spyOn(view, "toggleVote") + button = view.$el.find(".vote-btn") + + button.click() + expect(view.toggleVote).toHaveBeenCalled() + view.toggleVote.reset() + button.trigger($.Event("keydown", {which: 13})) + expect(view.toggleVote).toHaveBeenCalled() + view.toggleVote.reset() + button.trigger($.Event("keydown", {which: 32})) + expect(view.toggleVote).not.toHaveBeenCalled() diff --git a/common/static/coffee/spec/discussion/view/thread_response_show_view_spec.coffee b/common/static/coffee/spec/discussion/view/thread_response_show_view_spec.coffee new file mode 100644 index 0000000000..7ba00c66d1 --- /dev/null +++ b/common/static/coffee/spec/discussion/view/thread_response_show_view_spec.coffee @@ -0,0 +1,40 @@ +describe "ThreadResponseShowView", -> + beforeEach -> + setFixtures( + """ +
+ + 0 votes (click to vote) + +
+ """ + ) + + @commentData = { + id: "dummy", + user_id: "567", + course_id: "TestOrg/TestCourse/TestRun", + body: "this is a comment", + created_at: "2013-04-03T20:08:39Z", + abuse_flaggers: [], + votes: {up_count: "42"} + } + @comment = new Comment(@commentData) + @view = new ThreadResponseShowView({ model: @comment }) + @view.setElement($(".discussion-post")) + window.user = new DiscussionUser({id: "567", upvoted_ids: []}) + + it "renders the vote correctly", -> + DiscussionViewSpecHelper.checkRenderVote(@view, @comment) + + it "votes correctly", -> + DiscussionViewSpecHelper.checkVote(@view, @comment, @commentData, true) + + it "unvotes correctly", -> + DiscussionViewSpecHelper.checkUnvote(@view, @comment, @commentData, true) + + it 'toggles the vote correctly', -> + DiscussionViewSpecHelper.checkToggleVote(@view, @comment) + + it "vote button activates on appropriate events", -> + DiscussionViewSpecHelper.checkVoteButtonEvents(@view) diff --git a/common/static/coffee/src/discussion/content.coffee b/common/static/coffee/src/discussion/content.coffee index 23a31ae7e6..5e3d4ce20b 100644 --- a/common/static/coffee/src/discussion/content.coffee +++ b/common/static/coffee/src/discussion/content.coffee @@ -99,6 +99,13 @@ if Backbone? @get("abuse_flaggers").pop(window.user.get('id')) @trigger "change", @ + vote: -> + @get("votes")["up_count"] = parseInt(@get("votes")["up_count"]) + 1 + @trigger "change", @ + + unvote: -> + @get("votes")["up_count"] = parseInt(@get("votes")["up_count"]) - 1 + @trigger "change", @ class @Thread extends @Content urlMappers: @@ -130,14 +137,6 @@ if Backbone? unfollow: -> @set('subscribed', false) - vote: -> - @get("votes")["up_count"] = parseInt(@get("votes")["up_count"]) + 1 - @trigger "change", @ - - unvote: -> - @get("votes")["up_count"] = parseInt(@get("votes")["up_count"]) - 1 - @trigger "change", @ - display_body: -> if @has("highlighted_body") String(@get("highlighted_body")).replace(//g, '').replace(/<\/highlight>/g, '') diff --git a/common/static/coffee/src/discussion/utils.coffee b/common/static/coffee/src/discussion/utils.coffee index a85e4f0eaa..0e8362472a 100644 --- a/common/static/coffee/src/discussion/utils.coffee +++ b/common/static/coffee/src/discussion/utils.coffee @@ -91,7 +91,7 @@ class @DiscussionUtil @activateOnEnter: (event, func) -> if event.which == 13 - e.preventDefault() + event.preventDefault() func(event) @makeFocusTrap: (elem) -> diff --git a/common/static/coffee/src/discussion/views/discussion_content_view.coffee b/common/static/coffee/src/discussion/views/discussion_content_view.coffee index 9c3c4a01f5..96d74df4ef 100644 --- a/common/static/coffee/src/discussion/views/discussion_content_view.coffee +++ b/common/static/coffee/src/discussion/views/discussion_content_view.coffee @@ -159,3 +159,42 @@ if Backbone? temp_array = [] @model.set('abuse_flaggers', temp_array) + + renderVote: => + button = @$el.find(".vote-btn") + voted = window.user.voted(@model) + voteNum = @model.get("votes")["up_count"] + button.toggleClass("is-cast", voted) + button.attr("aria-pressed", voted) + button.attr("data-tooltip", if voted then "remove vote" else "vote") + button.find(".votes-count-number").html(voteNum) + button.find(".sr").html(if voted then "votes (click to remove your vote)" else "votes (click to vote)") + + toggleVote: (event) => + event.preventDefault() + if window.user.voted(@model) + @unvote() + else + @vote() + + vote: => + window.user.vote(@model) + url = @model.urlFor("upvote") + DiscussionUtil.safeAjax + $elem: @$el.find(".vote-btn") + url: url + type: "POST" + success: (response, textStatus) => + if textStatus == 'success' + @model.set(response) + + unvote: => + window.user.unvote(@model) + url = @model.urlFor("unvote") + DiscussionUtil.safeAjax + $elem: @$el.find(".vote-btn") + url: url + type: "POST" + success: (response, textStatus) => + if textStatus == 'success' + @model.set(response) diff --git a/common/static/coffee/src/discussion/views/discussion_thread_profile_view.coffee b/common/static/coffee/src/discussion/views/discussion_thread_profile_view.coffee index 7130ac555c..f6a6ea8eb6 100644 --- a/common/static/coffee/src/discussion/views/discussion_thread_profile_view.coffee +++ b/common/static/coffee/src/discussion/views/discussion_thread_profile_view.coffee @@ -2,7 +2,10 @@ if Backbone? class @DiscussionThreadProfileView extends DiscussionContentView expanded = false events: - "click .discussion-vote": "toggleVote" + "click .vote-btn": + (event) -> @toggleVote(event) + "keydown .vote-btn": + (event) -> DiscussionUtil.activateOnEnter(event, @toggleVote) "click .action-follow": "toggleFollowing" "keypress .action-follow": (event) -> DiscussionUtil.activateOnEnter(event, toggleFollowing) @@ -27,7 +30,7 @@ if Backbone? @$el.html(Mustache.render(@template, params)) @initLocal() @delegateEvents() - @renderVoted() + @renderVote() @renderAttrs() @$("span.timeago").timeago() @convertMath() @@ -35,15 +38,8 @@ if Backbone? @renderResponses() @ - renderVoted: => - if window.user.voted(@model) - @$("[data-role=discussion-vote]").addClass("is-cast") - else - @$("[data-role=discussion-vote]").removeClass("is-cast") - updateModelDetails: => - @renderVoted() - @$("[data-role=discussion-vote] .votes-count-number").html(@model.get("votes")["up_count"]) + @renderVote() convertMath: -> element = @$(".post-body") @@ -71,35 +67,6 @@ if Backbone? addComment: => @model.comment() - toggleVote: (event) -> - event.preventDefault() - if window.user.voted(@model) - @unvote() - else - @vote() - - vote: -> - window.user.vote(@model) - url = @model.urlFor("upvote") - DiscussionUtil.safeAjax - $elem: @$(".discussion-vote") - url: url - type: "POST" - success: (response, textStatus) => - if textStatus == 'success' - @model.set(response) - - unvote: -> - window.user.unvote(@model) - url = @model.urlFor("unvote") - DiscussionUtil.safeAjax - $elem: @$(".discussion-vote") - url: url - type: "POST" - success: (response, textStatus) => - if textStatus == 'success' - @model.set(response) - edit: -> abbreviateBody: -> diff --git a/common/static/coffee/src/discussion/views/discussion_thread_show_view.coffee b/common/static/coffee/src/discussion/views/discussion_thread_show_view.coffee index 1a3f8929e1..14dd01e3fa 100644 --- a/common/static/coffee/src/discussion/views/discussion_thread_show_view.coffee +++ b/common/static/coffee/src/discussion/views/discussion_thread_show_view.coffee @@ -2,7 +2,10 @@ if Backbone? class @DiscussionThreadShowView extends DiscussionContentView events: - "click .discussion-vote": "toggleVote" + "click .vote-btn": + (event) -> @toggleVote(event) + "keydown .vote-btn": + (event) -> DiscussionUtil.activateOnEnter(event, @toggleVote) "click .discussion-flag-abuse": "toggleFlagAbuse" "keypress .discussion-flag-abuse": (event) -> DiscussionUtil.activateOnEnter(event, toggleFlagAbuse) @@ -28,7 +31,7 @@ if Backbone? render: -> @$el.html(@renderTemplate()) @delegateEvents() - @renderVoted() + @renderVote() @renderFlagged() @renderPinned() @renderAttrs() @@ -38,14 +41,6 @@ if Backbone? @highlight @$("h1,h3") @ - renderVoted: => - if window.user.voted(@model) - @$("[data-role=discussion-vote]").addClass("is-cast") - @$("[data-role=discussion-vote] span.sr").html("votes (click to remove your vote)") - else - @$("[data-role=discussion-vote]").removeClass("is-cast") - @$("[data-role=discussion-vote] span.sr").html("votes (click to vote)") - renderFlagged: => if window.user.id in @model.get("abuse_flaggers") or (DiscussionUtil.isFlagModerator and @model.get("abuse_flaggers").length > 0) @$("[data-role=thread-flag]").addClass("flagged") @@ -70,52 +65,15 @@ if Backbone? updateModelDetails: => - @renderVoted() + @renderVote() @renderFlagged() @renderPinned() - @$("[data-role=discussion-vote] .votes-count-number").html(@model.get("votes")["up_count"] + '') - if window.user.voted(@model) - @$("[data-role=discussion-vote] .votes-count-number span.sr").html("votes (click to remove your vote)") - else - @$("[data-role=discussion-vote] .votes-count-number span.sr").html("votes (click to vote)") - convertMath: -> element = @$(".post-body") element.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight element.text() MathJax.Hub.Queue ["Typeset", MathJax.Hub, element[0]] - toggleVote: (event) -> - event.preventDefault() - if window.user.voted(@model) - @unvote() - else - @vote() - - vote: -> - window.user.vote(@model) - url = @model.urlFor("upvote") - DiscussionUtil.safeAjax - $elem: @$(".discussion-vote") - url: url - type: "POST" - success: (response, textStatus) => - if textStatus == 'success' - @model.set(response, {silent: true}) - - - unvote: -> - window.user.unvote(@model) - url = @model.urlFor("unvote") - DiscussionUtil.safeAjax - $elem: @$(".discussion-vote") - url: url - type: "POST" - success: (response, textStatus) => - if textStatus == 'success' - @model.set(response, {silent: true}) - - edit: (event) -> @trigger "thread:edit", event diff --git a/common/static/coffee/src/discussion/views/thread_response_show_view.coffee b/common/static/coffee/src/discussion/views/thread_response_show_view.coffee index eaed0568c2..57736e789d 100644 --- a/common/static/coffee/src/discussion/views/thread_response_show_view.coffee +++ b/common/static/coffee/src/discussion/views/thread_response_show_view.coffee @@ -1,7 +1,10 @@ if Backbone? class @ThreadResponseShowView extends DiscussionContentView events: - "click .vote-btn": "toggleVote" + "click .vote-btn": + (event) -> @toggleVote(event) + "keydown .vote-btn": + (event) -> DiscussionUtil.activateOnEnter(event, @toggleVote) "click .action-endorse": "toggleEndorse" "click .action-delete": "_delete" "click .action-edit": "edit" @@ -23,9 +26,7 @@ if Backbone? render: -> @$el.html(@renderTemplate()) @delegateEvents() - if window.user.voted(@model) - @$(".vote-btn").addClass("is-cast") - @$(".vote-btn span.sr").html("votes (click to remove your vote)") + @renderVote() @renderAttrs() @renderFlagged() @$el.find(".posted-details").timeago() @@ -46,39 +47,6 @@ if Backbone? @$el.addClass("community-ta") @$el.prepend('
Community TA
') - toggleVote: (event) -> - event.preventDefault() - @$(".vote-btn").toggleClass("is-cast") - if @$(".vote-btn").hasClass("is-cast") - @vote() - @$(".vote-btn span.sr").html("votes (click to remove your vote)") - else - @unvote() - @$(".vote-btn span.sr").html("votes (click to vote)") - - vote: -> - url = @model.urlFor("upvote") - @$(".votes-count-number").html((parseInt(@$(".votes-count-number").html()) + 1) + '') - DiscussionUtil.safeAjax - $elem: @$(".discussion-vote") - url: url - type: "POST" - success: (response, textStatus) => - if textStatus == 'success' - @model.set(response) - - unvote: -> - url = @model.urlFor("unvote") - @$(".votes-count-number").html((parseInt(@$(".votes-count-number").html()) - 1)+'') - DiscussionUtil.safeAjax - $elem: @$(".discussion-vote") - url: url - type: "POST" - success: (response, textStatus) => - if textStatus == 'success' - @model.set(response) - - edit: (event) -> @trigger "response:edit", event @@ -115,4 +83,5 @@ if Backbone? @$(".discussion-flag-abuse .flag-label").html("Report Misuse") updateModelDetails: => + @renderVote() @renderFlagged() diff --git a/lms/templates/discussion/_underscore_templates.html b/lms/templates/discussion/_underscore_templates.html index e331a779a5..2ebd1465e1 100644 --- a/lms/templates/discussion/_underscore_templates.html +++ b/lms/templates/discussion/_underscore_templates.html @@ -31,8 +31,8 @@
${"<%- obj.group_string%>"}
${"<% } %>"} - - + ${'<%- votes["up_count"] %>'}votes (click to vote) + + ${'<%- votes["up_count"] %>'} votes (click to vote)

${'<%- title %>'}

${"<% if (obj.username) { %>"} @@ -123,7 +123,7 @@