From 3bc5b673c6f34122583814279956d25c10c9d2b2 Mon Sep 17 00:00:00 2001 From: Steffen van Bergerem Date: Sat, 29 Aug 2015 13:12:02 +0200 Subject: [PATCH] Refactor mobile comment js code and add tests --- .../javascripts/mobile/mobile_comments.js | 310 +++++++++--------- .../jasmine_fixtures/aspects_spec.rb | 7 + .../mobile/mobile_comments_spec.js | 74 +++++ 3 files changed, 242 insertions(+), 149 deletions(-) create mode 100644 spec/javascripts/mobile/mobile_comments_spec.js diff --git a/app/assets/javascripts/mobile/mobile_comments.js b/app/assets/javascripts/mobile/mobile_comments.js index fbe557102..35a7138ed 100644 --- a/app/assets/javascripts/mobile/mobile_comments.js +++ b/app/assets/javascripts/mobile/mobile_comments.js @@ -4,167 +4,179 @@ * the COPYRIGHT file. */ -$(document).ready(function() { +(function() { + Diaspora.Mobile = {}; + Diaspora.Mobile.Comments = { + initialize: function() { + var self = this; + $(".stream").on("tap click", "a.back_to_stream_element_top", function() { + var bottomBar = $(this).closest(".bottom_bar").first(); + var streamElement = bottomBar.parent(); + $("html, body").animate({ + scrollTop: streamElement.offset().top - 54 + }, 1000); + }); - $(".stream").on("tap click", "a.back_to_stream_element_top", function() { - var bottomBar = $(this).closest(".bottom_bar").first(); - var streamElement = bottomBar.parent(); - $("html, body").animate({ - scrollTop: streamElement.offset().top - 54 - }, 1000); - }); + $(".stream").on("tap click", "a.show_comments", function(evt){ + evt.preventDefault(); + self.toggleComments($(this)); + }); - $(".stream").on("tap click", "a.show_comments", function(evt){ - evt.preventDefault(); - toggleComments($(this)); - }); + $(".stream").on("tap click", "a.comment-action", function(evt) { + evt.preventDefault(); + self.showCommentBox(this); + var bottomBar = $(this).closest(".bottom_bar").first(); + var commentContainer = bottomBar.find(".comment_container").first(); + self.scrollToOffset(commentContainer); + }); - function toggleComments(toggleReactionsLink) { - if(toggleReactionsLink.hasClass("loading")) { return; } - if (toggleReactionsLink.hasClass("active")) { - hideComments(toggleReactionsLink); - } else { - showComments(toggleReactionsLink); - } - } + $(".stream").on("submit", ".new_comment", function(evt) { + evt.preventDefault(); + var form = $(this); + $.post(form.attr("action")+"?format=mobile", form.serialize(), function(data) { + self.updateStream(form, data); + }, "html"); + }); + }, - function hideComments(toggleReactionsLink) { - var bottomBar = toggleReactionsLink.closest(".bottom_bar").first(), - commentsContainer = commentsContainerLazy(bottomBar), - existingCommentsContainer = commentsContainer(); - existingCommentsContainer.hide(); - toggleReactionsLink.removeClass("active"); - } - - function showComments(toggleReactionsLink) { - var bottomBar = toggleReactionsLink.closest(".bottom_bar").first(), - commentsContainer = commentsContainerLazy(bottomBar), - existingCommentsContainer = commentsContainer(), - commentActionLink = bottomBar.find("a.comment-action"); - if (existingCommentsContainer.length > 0) { - showLoadedComments(toggleReactionsLink, existingCommentsContainer, commentActionLink); - } else { - showUnloadedComments(toggleReactionsLink, bottomBar, commentActionLink); - } - } - - function showLoadedComments(toggleReactionsLink, existingCommentsContainer, commentActionLink) { - toggleReactionsLink.addClass("active"); - existingCommentsContainer.show(); - showCommentBox(commentActionLink); - existingCommentsContainer.find("time.timeago").timeago(); - } - - function showUnloadedComments(toggleReactionsLink, bottomBar, commentActionLink) { - toggleReactionsLink.addClass("loading"); - var commentsContainer = commentsContainerLazy(bottomBar); - $.ajax({ - url: toggleReactionsLink.attr("href"), - success: function (data) { - toggleReactionsLink.addClass("active").removeClass("loading"); - $(data).insertAfter(bottomBar.children(".show_comments").first()); - showCommentBox(commentActionLink); - commentsContainer().find("time.timeago").timeago(); - }, - error: function() { - toggleReactionsLink.removeClass("loading"); + toggleComments: function(toggleReactionsLink) { + if(toggleReactionsLink.hasClass("loading")) { return; } + if (toggleReactionsLink.hasClass("active")) { + this.hideComments(toggleReactionsLink); + } else { + this.showComments(toggleReactionsLink); } - }); - } + }, - function commentsContainerLazy(bottomBar) { - return function() { - return bottomBar.find(".comment_container").first(); - }; - } + hideComments: function(toggleReactionsLink) { + var bottomBar = toggleReactionsLink.closest(".bottom_bar").first(), + commentsContainer = this.commentsContainerLazy(bottomBar), + existingCommentsContainer = commentsContainer(); + existingCommentsContainer.hide(); + toggleReactionsLink.removeClass("active"); + }, - $(".stream").on("tap click", "a.comment-action", function(evt) { - evt.preventDefault(); - showCommentBox(this); - var bottomBar = $(this).closest(".bottom_bar").first(); - var commentContainer = bottomBar.find(".comment_container").first(); - scrollToOffset(commentContainer); - }); - var scrollToOffset = function(commentsContainer){ - var commentCount = commentsContainer.find("li.comment").length; - if ( commentCount > 3 ) { - var lastComment = commentsContainer.find("li:nth-child("+(commentCount-3)+")"); - $("html,body").animate({ - scrollTop: lastComment.offset().top - }, 1000); - } - }; + showComments: function(toggleReactionsLink) { + var bottomBar = toggleReactionsLink.closest(".bottom_bar").first(), + commentsContainer = this.commentsContainerLazy(bottomBar), + existingCommentsContainer = commentsContainer(), + commentActionLink = bottomBar.find("a.comment-action"); + if (existingCommentsContainer.length > 0) { + this.showLoadedComments(toggleReactionsLink, existingCommentsContainer, commentActionLink); + } else { + this.showUnloadedComments(toggleReactionsLink, bottomBar, commentActionLink); + } + }, - function showCommentBox(link){ - var commentActionLink = $(link); - if(commentActionLink.hasClass("inactive")) { + showLoadedComments: function(toggleReactionsLink, existingCommentsContainer, commentActionLink) { + toggleReactionsLink.addClass("active"); + existingCommentsContainer.show(); + this.showCommentBox(commentActionLink); + existingCommentsContainer.find("time.timeago").timeago(); + }, + + showUnloadedComments: function(toggleReactionsLink, bottomBar, commentActionLink) { + toggleReactionsLink.addClass("loading"); + var commentsContainer = this.commentsContainerLazy(bottomBar); + var self = this; $.ajax({ - url: commentActionLink.attr("href"), - beforeSend: function(){ - commentActionLink.addClass("loading"); + url: toggleReactionsLink.attr("href"), + success: function (data) { + toggleReactionsLink.addClass("active").removeClass("loading"); + $(data).insertAfter(bottomBar.children(".show_comments").first()); + self.showCommentBox(commentActionLink); + commentsContainer().find("time.timeago").timeago(); }, - context: commentActionLink, - success: function(data){ - appendCommentBox.call(this, commentActionLink, data); + error: function() { + toggleReactionsLink.removeClass("loading"); } }); + }, + + commentsContainerLazy: function(bottomBar) { + return function() { + return bottomBar.find(".comment_container").first(); + }; + }, + + scrollToOffset: function(commentsContainer){ + var commentCount = commentsContainer.find("li.comment").length; + if ( commentCount > 3 ) { + var lastComment = commentsContainer.find("li:nth-child("+(commentCount-3)+")"); + $("html,body").animate({ + scrollTop: lastComment.offset().top + }, 1000); + } + }, + + showCommentBox: function(link){ + var commentActionLink = $(link); + var self = this; + if(commentActionLink.hasClass("inactive")) { + $.ajax({ + url: commentActionLink.attr("href"), + beforeSend: function(){ + commentActionLink.addClass("loading"); + }, + context: commentActionLink, + success: function(data){ + self.appendCommentBox.call(this, commentActionLink, data); + } + }); + } + }, + + appendCommentBox: function(link, data) { + link.removeClass("loading"); + link.removeClass("inactive"); + var bottomBar = link.closest(".bottom_bar").first(); + bottomBar.append(data); + var textArea = bottomBar.find("textarea.comment_box").first()[0]; + autosize(textArea); + }, + + updateStream: function(form, data) { + var bottomBar = form.closest(".bottom_bar").first(); + this.addNewComments(bottomBar, data); + this.updateCommentCount(bottomBar); + this.updateReactionCount(bottomBar); + this.handleCommentShowing(form, bottomBar); + bottomBar.find("time.timeago").timeago(); + }, + + addNewComments: function(bottomBar, data) { + var commentsContainer = bottomBar.find(".comment_container").first(); + var comments = commentsContainer.find(".comments").first(); + comments.append(data); + }, + + // Fix for no comments + updateCommentCount: function(bottomBar) { + var commentCount = bottomBar.find(".comment_count"); + commentCount.text(commentCount.text().replace(/(\d+)/, function (match) { + return parseInt(match) + 1; + })); + }, + + // Fix for no reactions + updateReactionCount: function(bottomBar) { + var toggleReactionsLink = bottomBar.find(".show_comments").first(); + toggleReactionsLink.text(toggleReactionsLink.text().replace(/(\d+)/, function (match) { + return parseInt(match) + 1; + })); + }, + + handleCommentShowing: function(form, bottomBar) { + var formContainer = form.parent(); + formContainer.remove(); + var commentActionLink = bottomBar.find("a.comment-action").first(); + commentActionLink.addClass("inactive"); + var toggleReactionsLink = bottomBar.find(".show_comments").first(); + this.showComments(toggleReactionsLink); } - } + }; +})(); - function appendCommentBox(link, data) { - link.removeClass("loading"); - link.removeClass("inactive"); - var bottomBar = link.closest(".bottom_bar").first(); - bottomBar.append(data); - var textArea = bottomBar.find("textarea.comment_box").first()[0]; - autosize(textArea); - } - - $(".stream").on("submit", ".new_comment", function(evt) { - evt.preventDefault(); - var form = $(this); - $.post(form.attr("action")+"?format=mobile", form.serialize(), function(data) { - updateStream(form, data); - }, "html"); - }); - - function updateStream(form, data) { - var bottomBar = form.closest(".bottom_bar").first(); - addNewComments(bottomBar, data); - updateCommentCount(bottomBar); - updateReactionCount(bottomBar); - handleCommentShowing(form, bottomBar); - bottomBar.find("time.timeago").timeago(); - } - - function addNewComments(bottomBar, data) { - var commentsContainer = bottomBar.find(".comment_container").first(); - var comments = commentsContainer.find(".comments").first(); - comments.append(data); - } - - // Fix for no comments - function updateCommentCount(bottomBar) { - var commentCount = bottomBar.find(".comment_count"); - commentCount.text(commentCount.text().replace(/(\d+)/, function (match) { - return parseInt(match) + 1; - })); - } - - // Fix for no reactions - function updateReactionCount(bottomBar) { - var toggleReactionsLink = bottomBar.find(".show_comments").first(); - toggleReactionsLink.text(toggleReactionsLink.text().replace(/(\d+)/, function (match) { - return parseInt(match) + 1; - })); - } - - function handleCommentShowing(form, bottomBar) { - var formContainer = form.parent(); - formContainer.remove(); - var commentActionLink = bottomBar.find("a.comment-action").first(); - commentActionLink.addClass("inactive"); - var toggleReactionsLink = bottomBar.find(".show_comments").first(); - showComments(toggleReactionsLink); - } +$(document).ready(function() { + Diaspora.Mobile.Comments.initialize(); }); diff --git a/spec/controllers/jasmine_fixtures/aspects_spec.rb b/spec/controllers/jasmine_fixtures/aspects_spec.rb index bdbf026ba..e61faba7b 100644 --- a/spec/controllers/jasmine_fixtures/aspects_spec.rb +++ b/spec/controllers/jasmine_fixtures/aspects_spec.rb @@ -58,6 +58,13 @@ describe StreamsController, :type => :controller do save_fixture(html_for("body"), "aspects_index_post_with_comments") end + it "generates a mobile jasmine fixture with a post with comments", fixture: true do + message = bob.post(:status_message, text: "HALO WHIRLED", to: @bob.aspects.where(name: "generic").first.id) + 5.times { bob.comment!(message, "what") } + get :aspects, format: :mobile + save_fixture(html_for("body"), "aspects_index_mobile_post_with_comments") + end + it 'generates a jasmine fixture with a followed tag', :fixture => true do @tag = ActsAsTaggableOn::Tag.create!(:name => "partytimeexcellent") TagFollowing.create!(:tag => @tag, :user => alice) diff --git a/spec/javascripts/mobile/mobile_comments_spec.js b/spec/javascripts/mobile/mobile_comments_spec.js new file mode 100644 index 000000000..041023ba3 --- /dev/null +++ b/spec/javascripts/mobile/mobile_comments_spec.js @@ -0,0 +1,74 @@ +describe("Diaspora.Mobile.Comments", function(){ + describe("toggleComments", function() { + beforeEach(function() { + spec.loadFixture("aspects_index_mobile_post_with_comments"); + this.link = $(".stream .show_comments").first(); + spyOn(Diaspora.Mobile.Comments, "showComments"); + spyOn(Diaspora.Mobile.Comments, "hideComments"); + }); + + it("calls showComments", function() { + Diaspora.Mobile.Comments.toggleComments(this.link); + expect(Diaspora.Mobile.Comments.showComments).toHaveBeenCalled(); + expect(Diaspora.Mobile.Comments.hideComments).not.toHaveBeenCalled(); + }); + + it("calls hideComments if the link class is 'active'", function() { + this.link.addClass("active"); + Diaspora.Mobile.Comments.toggleComments(this.link); + expect(Diaspora.Mobile.Comments.showComments).not.toHaveBeenCalled(); + expect(Diaspora.Mobile.Comments.hideComments).toHaveBeenCalled(); + }); + + it("doesn't call any function if the link class is 'loading'", function() { + this.link.addClass("loading"); + Diaspora.Mobile.Comments.toggleComments(this.link); + expect(Diaspora.Mobile.Comments.showComments).not.toHaveBeenCalled(); + expect(Diaspora.Mobile.Comments.hideComments).not.toHaveBeenCalled(); + }); + }); + + describe("showUnloadedComments", function() { + beforeEach(function() { + spec.loadFixture("aspects_index_mobile_post_with_comments"); + this.link = $(".stream .show_comments").first(); + this.bottomBar = this.link.closest(".bottom_bar").first(); + this.commentActionLink = this.bottomBar.find("a.comment-action"); + }); + + it("adds the 'loading' class to the link", function() { + Diaspora.Mobile.Comments.showUnloadedComments(this.link, this.bottomBar, this.commentActionLink); + expect($(".show_comments").first()).toHaveClass("loading"); + }); + + it("removes the 'loading' class if the request failed", function() { + Diaspora.Mobile.Comments.showUnloadedComments(this.link, this.bottomBar, this.commentActionLink); + jasmine.Ajax.requests.mostRecent().respondWith({status: 400}); + expect($(".show_comments").first()).not.toHaveClass("loading"); + }); + + it("adds the 'active' class if the request succeeded", function() { + Diaspora.Mobile.Comments.showUnloadedComments(this.link, this.bottomBar, this.commentActionLink); + jasmine.Ajax.requests.mostRecent().respondWith({status: 200, contentType: "text/plain", responseText: "test"}); + expect($(".show_comments").first()).toHaveClass("active"); + expect($(".show_comments").first()).not.toHaveClass("loading"); + }); + + it("calls showCommentBox", function() { + spyOn(Diaspora.Mobile.Comments, "showCommentBox"); + Diaspora.Mobile.Comments.showUnloadedComments(this.link, this.bottomBar, this.commentActionLink); + jasmine.Ajax.requests.mostRecent().respondWith({status: 200, contentType: "text/plain", responseText: "test"}); + expect(Diaspora.Mobile.Comments.showCommentBox).toHaveBeenCalledWith(this.commentActionLink); + }); + + it("adds the response text to the comments list", function() { + Diaspora.Mobile.Comments.showUnloadedComments(this.link, this.bottomBar, this.commentActionLink); + jasmine.Ajax.requests.mostRecent().respondWith({ + status: 200, + contentType: "text/plain", + responseText: "
new comments
" + }); + expect($(".stream .stream_element").first()).toContainElement(".commentContainerForTest"); + }); + }); +});