Merge pull request #7182 from svbergerem/spv-load-initial-interactions
Single post view interaction refactorings
This commit is contained in:
commit
ce5e42c471
22 changed files with 341 additions and 132 deletions
|
|
@ -29,6 +29,7 @@ If so, please delete it since it will prevent the federation from working proper
|
|||
* Use id as fallback when sorting posts [#7523](https://github.com/diaspora/diaspora/pull/7523)
|
||||
* Remove no-posts-info when adding posts to the stream [#7523](https://github.com/diaspora/diaspora/pull/7523)
|
||||
* Upgrade to rails 5.1 [#7514](https://github.com/diaspora/diaspora/pull/7514)
|
||||
* Refactoring single post view interactions [#7182](https://github.com/diaspora/diaspora/pull/7182)
|
||||
|
||||
## Bug fixes
|
||||
|
||||
|
|
|
|||
|
|
@ -48,12 +48,6 @@ app.models.Post = Backbone.Model.extend(_.extend({}, app.models.formatDateMixin,
|
|||
var body = this.get("text").trim()
|
||||
, newlineIdx = body.indexOf("\n");
|
||||
return (newlineIdx > 0 ) ? body.substr(newlineIdx+1, body.length) : "";
|
||||
},
|
||||
|
||||
//returns a promise
|
||||
preloadOrFetch : function(){
|
||||
var action = app.hasPreload("post") ? this.set(app.parsePreload("post")) : this.fetch();
|
||||
return $.when(action);
|
||||
}
|
||||
}));
|
||||
// @license-end
|
||||
|
|
|
|||
|
|
@ -3,10 +3,6 @@
|
|||
//require ../post
|
||||
|
||||
app.models.Post.Interactions = Backbone.Model.extend({
|
||||
url : function(){
|
||||
return this.post.url() + "/interactions";
|
||||
},
|
||||
|
||||
initialize : function(options){
|
||||
this.post = options.post;
|
||||
this.comments = new app.collections.Comments(this.get("comments"), {post : this.post});
|
||||
|
|
@ -14,33 +10,16 @@ app.models.Post.Interactions = Backbone.Model.extend({
|
|||
this.reshares = new app.collections.Reshares(this.get("reshares"), {post : this.post});
|
||||
},
|
||||
|
||||
parse : function(resp){
|
||||
this.comments.reset(resp.comments);
|
||||
this.likes.reset(resp.likes);
|
||||
this.reshares.reset(resp.reshares);
|
||||
|
||||
var comments = this.comments
|
||||
, likes = this.likes
|
||||
, reshares = this.reshares;
|
||||
|
||||
return {
|
||||
comments : comments,
|
||||
likes : likes,
|
||||
reshares : reshares,
|
||||
fetched : true
|
||||
};
|
||||
},
|
||||
|
||||
likesCount : function(){
|
||||
return this.get("fetched") ? this.likes.models.length : this.get("likes_count");
|
||||
return this.get("likes_count");
|
||||
},
|
||||
|
||||
resharesCount : function(){
|
||||
return this.get("fetched") ? this.reshares.models.length : this.get("reshares_count");
|
||||
return this.get("reshares_count");
|
||||
},
|
||||
|
||||
commentsCount : function(){
|
||||
return this.get("fetched") ? this.comments.models.length : this.get("comments_count");
|
||||
return this.get("comments_count");
|
||||
},
|
||||
|
||||
userLike : function(){
|
||||
|
|
|
|||
|
|
@ -9,9 +9,8 @@ app.pages.SinglePostViewer = app.views.Base.extend({
|
|||
},
|
||||
|
||||
initialize : function() {
|
||||
this.model = new app.models.Post({ id : gon.post.id });
|
||||
this.model.preloadOrFetch().done(_.bind(this.initViews, this));
|
||||
this.model.interactions.fetch(); //async, yo, might want to throttle this later.
|
||||
this.model = new app.models.Post(gon.post);
|
||||
this.initViews();
|
||||
},
|
||||
|
||||
initViews : function() {
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ app.Router = Backbone.Router.extend({
|
|||
},
|
||||
|
||||
singlePost: function(id) {
|
||||
this.renderPage(function() { return new app.pages.SinglePostViewer({id: id}); });
|
||||
this.renderPage(function() { return new app.pages.SinglePostViewer({id: id, el: $("#container")}); });
|
||||
},
|
||||
|
||||
spotlight: function() {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ app.views.SinglePostCommentStream = app.views.CommentStream.extend({
|
|||
this.CommentView = app.views.ExpandedComment;
|
||||
$(window).on('hashchange',this.highlightPermalinkComment);
|
||||
this.setupBindings();
|
||||
this.model.comments.on("reset", this.render, this);
|
||||
this.model.comments.fetch({success: function() {
|
||||
setTimeout(this.highlightPermalinkComment, 0);
|
||||
}.bind(this)});
|
||||
},
|
||||
|
||||
highlightPermalinkComment: function() {
|
||||
|
|
@ -17,14 +19,13 @@ app.views.SinglePostCommentStream = app.views.CommentStream.extend({
|
|||
$(".highlighted").removeClass("highlighted");
|
||||
element.addClass("highlighted");
|
||||
var pos = element.offset().top - headerSize;
|
||||
window.scroll(0, pos);
|
||||
$("html,body").animate({scrollTop: pos});
|
||||
}
|
||||
},
|
||||
|
||||
postRenderTemplate: function() {
|
||||
app.views.CommentStream.prototype.postRenderTemplate.apply(this);
|
||||
this.$(".new-comment-form-wrapper").removeClass("hidden");
|
||||
_.defer(this.highlightPermalinkComment);
|
||||
},
|
||||
|
||||
presenter: function(){
|
||||
|
|
|
|||
|
|
@ -4,8 +4,15 @@ app.views.SinglePostInteractionCounts = app.views.Base.extend({
|
|||
templateName: "single-post-viewer/single-post-interaction-counts",
|
||||
tooltipSelector: ".avatar.micro",
|
||||
|
||||
events: {
|
||||
"click #show-all-likes": "showAllLikes",
|
||||
"click #show-all-reshares": "showAllReshares"
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
this.model.interactions.on("change", this.render, this);
|
||||
this.model.interactions.likes.on("change", this.render, this);
|
||||
this.model.interactions.reshares.on("change", this.render, this);
|
||||
},
|
||||
|
||||
presenter: function() {
|
||||
|
|
@ -15,8 +22,28 @@ app.views.SinglePostInteractionCounts = app.views.Base.extend({
|
|||
reshares: interactions.reshares.toJSON(),
|
||||
commentsCount: interactions.commentsCount(),
|
||||
likesCount: interactions.likesCount(),
|
||||
resharesCount: interactions.resharesCount()
|
||||
resharesCount: interactions.resharesCount(),
|
||||
showMoreLikes: interactions.likes.length < interactions.likesCount(),
|
||||
showMoreReshares: interactions.reshares.length < interactions.resharesCount()
|
||||
};
|
||||
},
|
||||
|
||||
_showAll: function(interactionType, models) {
|
||||
this.$("#show-all-" + interactionType).addClass("hidden");
|
||||
this.$("#" + interactionType + " .loader").removeClass("hidden");
|
||||
models.fetch({success: function() {
|
||||
models.trigger("change");
|
||||
}});
|
||||
},
|
||||
|
||||
showAllLikes: function(evt) {
|
||||
evt.preventDefault();
|
||||
this._showAll("likes", this.model.interactions.likes);
|
||||
},
|
||||
|
||||
showAllReshares: function(evt) {
|
||||
evt.preventDefault();
|
||||
this._showAll("reshares", this.model.interactions.reshares);
|
||||
}
|
||||
});
|
||||
// @license-end
|
||||
|
|
|
|||
|
|
@ -152,5 +152,16 @@
|
|||
|
||||
.interaction-avatars {
|
||||
overflow: hidden;
|
||||
|
||||
.author-name:focus,
|
||||
.author-name:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.loader {
|
||||
height: $line-height-computed;
|
||||
vertical-align: text-bottom;
|
||||
width: $line-height-computed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,12 @@
|
|||
{{{personImage this "small" "micro"}}}
|
||||
{{/linkToAuthor}}
|
||||
{{/each}}
|
||||
{{#if showMoreReshares}}
|
||||
<div class="loader hidden">
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
<div id="show-all-reshares" class="btn btn-sm btn-link">{{t "show_all"}}</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
|
@ -25,6 +31,12 @@
|
|||
{{{personImage this "small" "micro"}}}
|
||||
{{/linkToAuthor}}
|
||||
{{/each}}
|
||||
{{#if showMoreLikes}}
|
||||
<div class="loader hidden">
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
<div id="show-all-likes" class="btn btn-sm btn-link">{{t "show_all"}}</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
|
|
|||
|
|
@ -22,11 +22,11 @@ class PostsController < ApplicationController
|
|||
presenter = PostPresenter.new(post, current_user)
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
gon.post = presenter
|
||||
gon.post = presenter.with_initial_interactions
|
||||
render locals: {post: presenter}
|
||||
end
|
||||
format.mobile { render locals: {post: post} }
|
||||
format.json { render json: presenter }
|
||||
format.json { render json: presenter.with_interactions }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -39,16 +39,6 @@ class PostsController < ApplicationController
|
|||
head :not_found
|
||||
end
|
||||
|
||||
def interactions
|
||||
respond_to do |format|
|
||||
format.json {
|
||||
post = post_service.find!(params[:id])
|
||||
render json: PostInteractionPresenter.new(post, current_user)
|
||||
}
|
||||
format.any { head :not_acceptable }
|
||||
end
|
||||
end
|
||||
|
||||
def mentionable
|
||||
respond_to do |format|
|
||||
format.json {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ class ResharesController < ApplicationController
|
|||
rescue ActiveRecord::RecordNotFound, ActiveRecord::RecordInvalid
|
||||
render plain: I18n.t("reshares.create.error"), status: 422
|
||||
else
|
||||
render json: ExtremePostPresenter.new(reshare, current_user), status: 201
|
||||
render json: PostPresenter.new(reshare, current_user).with_interactions, status: 201
|
||||
end
|
||||
|
||||
def index
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
#this file should go away, hence the name that is so full of lulz
|
||||
#post interactions should probably be a decorator, and used in very few places... maybe?
|
||||
class ExtremePostPresenter
|
||||
def initialize(post, current_user)
|
||||
@post = post
|
||||
@current_user = current_user
|
||||
end
|
||||
|
||||
def as_json(options={})
|
||||
post = PostPresenter.new(@post, @current_user)
|
||||
interactions = PostInteractionPresenter.new(@post, @current_user)
|
||||
post.as_json.merge!(:interactions => interactions.as_json)
|
||||
end
|
||||
end
|
||||
|
|
@ -14,6 +14,20 @@ class PostPresenter < BasePresenter
|
|||
.merge(non_directly_retrieved_attributes)
|
||||
end
|
||||
|
||||
def with_interactions
|
||||
interactions = PostInteractionPresenter.new(@post, current_user)
|
||||
as_json.merge!(interactions: interactions.as_json)
|
||||
end
|
||||
|
||||
def with_initial_interactions
|
||||
as_json.tap do |post|
|
||||
post[:interactions].merge!(
|
||||
likes: LikeService.new(current_user).find_for_post(@post.id).limit(30).as_api_response(:backbone),
|
||||
reshares: ReshareService.new(current_user).find_for_post(@post.id).limit(30).as_api_response(:backbone)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def metas_attributes
|
||||
{
|
||||
keywords: {name: "keywords", content: comma_separated_tags},
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ en:
|
|||
comma: ","
|
||||
edit: "Edit"
|
||||
no_results: "No results found"
|
||||
show_all: "Show all"
|
||||
|
||||
admins:
|
||||
dashboard:
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ Rails.application.routes.draw do
|
|||
|
||||
resources :posts, only: %i(show destroy) do
|
||||
member do
|
||||
get :interactions
|
||||
get :mentionable
|
||||
end
|
||||
|
||||
|
|
|
|||
11
spec/controllers/jasmine_fixtures/posts_spec.rb
Normal file
11
spec/controllers/jasmine_fixtures/posts_spec.rb
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
require "spec_helper"
|
||||
|
||||
describe PostsController, type: :controller do
|
||||
describe "#show" do
|
||||
it "generates the post_json fixture", fixture: true do
|
||||
post = alice.post(:status_message, text: "hello world", public: true)
|
||||
get :show, params: {id: post.id}, format: :json
|
||||
save_fixture(response.body, "post_json")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -129,38 +129,6 @@ describe PostsController, type: :controller do
|
|||
end
|
||||
end
|
||||
|
||||
describe "#interactions" do
|
||||
context "user not signed in" do
|
||||
it "returns a 401 for private posts and format json" do
|
||||
get :interactions, params: {id: post.id}, format: :json
|
||||
expect(response.status).to eq(401)
|
||||
expect(JSON.parse(response.body)["error"]).to eq(I18n.t("devise.failure.unauthenticated"))
|
||||
end
|
||||
|
||||
it "returns a 406 for private posts and format html" do
|
||||
get :interactions, params: {id: post.id}
|
||||
expect(response.status).to eq(406)
|
||||
end
|
||||
end
|
||||
|
||||
context "user signed in" do
|
||||
before do
|
||||
sign_in alice
|
||||
end
|
||||
|
||||
it "shows interactions of a post as json" do
|
||||
get :interactions, params: {id: post.id}, format: :json
|
||||
expect(response.body).to eq(PostInteractionPresenter.new(post, alice).to_json)
|
||||
end
|
||||
|
||||
it "returns a 406 for format html" do
|
||||
sign_in alice
|
||||
get :interactions, params: {id: post.id}
|
||||
expect(response.status).to eq(406)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#mentionable" do
|
||||
context "with a user signed in" do
|
||||
before do
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
describe("app.pages.SinglePostViewer", function(){
|
||||
beforeEach(function() {
|
||||
window.gon={};gon.post = {id: 42};
|
||||
window.gon = {};
|
||||
gon.post = $.parseJSON(spec.readFixture("post_json"));
|
||||
this.view = new app.pages.SinglePostViewer();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -9,14 +9,6 @@ describe("app.views.SinglePostCommentStream", function() {
|
|||
expect(this.view.CommentView).toBe(app.views.ExpandedComment);
|
||||
});
|
||||
|
||||
it("calls render when the comments collection has been resetted", function() {
|
||||
spyOn(app.views.SinglePostCommentStream.prototype, "render");
|
||||
this.view.initialize();
|
||||
expect(app.views.SinglePostCommentStream.prototype.render).not.toHaveBeenCalled();
|
||||
this.post.comments.reset();
|
||||
expect(app.views.SinglePostCommentStream.prototype.render).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calls setupBindings", function() {
|
||||
spyOn(app.views.SinglePostCommentStream.prototype, "setupBindings");
|
||||
this.view.initialize();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
describe("app.views.SinglePostInteractionCounts", function() {
|
||||
beforeEach(function() {
|
||||
this.post = factory.post();
|
||||
this.post = factory.postWithInteractions();
|
||||
this.view = new app.views.SinglePostInteractionCounts({model: this.post});
|
||||
});
|
||||
|
||||
|
|
@ -12,5 +12,151 @@ describe("app.views.SinglePostInteractionCounts", function() {
|
|||
this.post.interactions.trigger("change");
|
||||
expect(app.views.SinglePostInteractionCounts.prototype.render).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calls render when the likes change", function() {
|
||||
spyOn(app.views.SinglePostInteractionCounts.prototype, "render");
|
||||
this.view.initialize();
|
||||
expect(app.views.SinglePostInteractionCounts.prototype.render).not.toHaveBeenCalled();
|
||||
this.post.interactions.likes.trigger("change");
|
||||
expect(app.views.SinglePostInteractionCounts.prototype.render).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calls render when the reshares change", function() {
|
||||
spyOn(app.views.SinglePostInteractionCounts.prototype, "render");
|
||||
this.view.initialize();
|
||||
expect(app.views.SinglePostInteractionCounts.prototype.render).not.toHaveBeenCalled();
|
||||
this.post.interactions.reshares.trigger("change");
|
||||
expect(app.views.SinglePostInteractionCounts.prototype.render).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("render", function() {
|
||||
it("doesn't show a #show-all-likes link if there are no additional likes", function() {
|
||||
this.view.render();
|
||||
expect(this.view.$("#show-all-likes").length).toBe(0);
|
||||
});
|
||||
|
||||
it("shows a #show-all-likes link if there are additional likes", function() {
|
||||
this.view.model.interactions.set("likes_count", this.view.model.interactions.likes.length + 1);
|
||||
this.view.render();
|
||||
expect(this.view.$("#show-all-likes").length).toBe(1);
|
||||
});
|
||||
|
||||
it("doesn't show a #show-all-reshares link if there are no additional reshares", function() {
|
||||
this.view.render();
|
||||
expect(this.view.$("#show-all-reshares").length).toBe(0);
|
||||
});
|
||||
|
||||
it("shows a #show-all-reshares link if there are additional reshares", function() {
|
||||
this.view.model.interactions.set("reshares_count", this.view.model.interactions.reshares.length + 1);
|
||||
this.view.render();
|
||||
expect(this.view.$("#show-all-reshares").length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("showAllLikes", function() {
|
||||
it("is called when clicking #show-all-likes", function() {
|
||||
spyOn(this.view, "showAllLikes");
|
||||
this.view.delegateEvents();
|
||||
this.view.model.interactions.set("likes_count", this.view.model.interactions.likes.length + 1);
|
||||
this.view.render();
|
||||
expect(this.view.showAllLikes).not.toHaveBeenCalled();
|
||||
this.view.$("#show-all-likes").click();
|
||||
expect(this.view.showAllLikes).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calls _showAll", function() {
|
||||
spyOn(this.view, "_showAll");
|
||||
this.view.showAllLikes($.Event());
|
||||
expect(this.view._showAll).toHaveBeenCalledWith("likes", this.view.model.interactions.likes);
|
||||
});
|
||||
});
|
||||
|
||||
describe("showAllReshares", function() {
|
||||
it("is called when clicking #show-all-reshares", function() {
|
||||
spyOn(this.view, "showAllReshares");
|
||||
this.view.delegateEvents();
|
||||
this.view.model.interactions.set("reshares_count", this.view.model.interactions.reshares.length + 1);
|
||||
this.view.render();
|
||||
expect(this.view.showAllReshares).not.toHaveBeenCalled();
|
||||
this.view.$("#show-all-reshares").click();
|
||||
expect(this.view.showAllReshares).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calls _showAll", function() {
|
||||
spyOn(this.view, "_showAll");
|
||||
this.view.showAllReshares($.Event());
|
||||
expect(this.view._showAll).toHaveBeenCalledWith("reshares", this.view.model.interactions.reshares);
|
||||
});
|
||||
});
|
||||
|
||||
describe("_showAll", function() {
|
||||
beforeEach(function() {
|
||||
this.view.model.interactions.set("likes_count", this.view.model.interactions.likes.length + 1);
|
||||
this.view.model.interactions.set("reshares_count", this.view.model.interactions.reshares.length + 1);
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
context("with likes", function() {
|
||||
it("hides the #show-all-likes link", function() {
|
||||
expect(this.view.$("#show-all-likes")).not.toHaveClass("hidden");
|
||||
expect(this.view.$("#show-all-reshares")).not.toHaveClass("hidden");
|
||||
this.view._showAll("likes", this.view.model.interactions.likes);
|
||||
expect(this.view.$("#show-all-likes")).toHaveClass("hidden");
|
||||
expect(this.view.$("#show-all-reshares")).not.toHaveClass("hidden");
|
||||
});
|
||||
|
||||
it("shows the likes loader", function() {
|
||||
expect(this.view.$("#likes .loader")).toHaveClass("hidden");
|
||||
expect(this.view.$("#reshares .loader")).toHaveClass("hidden");
|
||||
this.view._showAll("likes", this.view.model.interactions.likes);
|
||||
expect(this.view.$("#likes .loader")).not.toHaveClass("hidden");
|
||||
expect(this.view.$("#reshares .loader")).toHaveClass("hidden");
|
||||
});
|
||||
|
||||
it("calls #fetch on the model", function() {
|
||||
spyOn(this.view.model.interactions.likes, "fetch");
|
||||
this.view._showAll("likes", this.view.model.interactions.likes);
|
||||
expect(this.view.model.interactions.likes.fetch).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("triggers 'change' after a successfull fetch", function() {
|
||||
spyOn(this.view.model.interactions.likes, "trigger");
|
||||
this.view._showAll("likes", this.view.model.interactions.likes);
|
||||
jasmine.Ajax.requests.mostRecent().respondWith({status: 200, responseText: "{\"id\": 1}"});
|
||||
expect(this.view.model.interactions.likes.trigger).toHaveBeenCalledWith("change");
|
||||
});
|
||||
});
|
||||
|
||||
context("with reshares", function() {
|
||||
it("hides the #show-all-reshares link", function() {
|
||||
expect(this.view.$("#show-all-likes")).not.toHaveClass("hidden");
|
||||
expect(this.view.$("#show-all-reshares")).not.toHaveClass("hidden");
|
||||
this.view._showAll("reshares", this.view.model.interactions.reshares);
|
||||
expect(this.view.$("#show-all-likes")).not.toHaveClass("hidden");
|
||||
expect(this.view.$("#show-all-reshares")).toHaveClass("hidden");
|
||||
});
|
||||
|
||||
it("shows the reshares loader", function() {
|
||||
expect(this.view.$("#likes .loader")).toHaveClass("hidden");
|
||||
expect(this.view.$("#reshares .loader")).toHaveClass("hidden");
|
||||
this.view._showAll("reshares", this.view.model.interactions.reshares);
|
||||
expect(this.view.$("#likes .loader")).toHaveClass("hidden");
|
||||
expect(this.view.$("#reshares .loader")).not.toHaveClass("hidden");
|
||||
});
|
||||
|
||||
it("calls #fetch on the model", function() {
|
||||
spyOn(this.view.model.interactions.reshares, "fetch");
|
||||
this.view._showAll("reshares", this.view.model.interactions.reshares);
|
||||
expect(this.view.model.interactions.reshares.fetch).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("triggers 'change' after a successfull fetch", function() {
|
||||
spyOn(this.view.model.interactions.reshares, "trigger");
|
||||
this.view._showAll("reshares", this.view.model.interactions.reshares);
|
||||
jasmine.Ajax.requests.mostRecent().respondWith({status: 200, responseText: "{\"id\": 1}"});
|
||||
expect(this.view.model.interactions.reshares.trigger).toHaveBeenCalledWith("change");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -21,6 +21,16 @@ var factory = {
|
|||
return _.extend(defaultAttrs, overrides);
|
||||
},
|
||||
|
||||
reshare: function(overrides) {
|
||||
var defaultAttrs = {
|
||||
"created_at": "2012-01-04T00:55:30Z",
|
||||
"author": this.author(),
|
||||
"guid": this.guid(),
|
||||
"id": this.id.next()
|
||||
};
|
||||
return _.extend(defaultAttrs, overrides);
|
||||
},
|
||||
|
||||
aspectMembershipAttrs: function(overrides) {
|
||||
var id = this.id.next();
|
||||
var defaultAttrs = {
|
||||
|
|
@ -207,6 +217,24 @@ var factory = {
|
|||
return new app.models.Post(_.extend(defaultAttrs, overrides));
|
||||
},
|
||||
|
||||
postWithInteractions: function(overrides) {
|
||||
var likes = _.range(10).map(function() { return factory.like(); });
|
||||
var reshares = _.range(15).map(function() { return factory.reshare(); });
|
||||
var comments = _.range(20).map(function() { return factory.comment(); });
|
||||
var defaultAttrs = _.extend(factory.postAttrs(), {
|
||||
"author": this.author(),
|
||||
"interactions": {
|
||||
"reshares_count": 15,
|
||||
"likes_count": 10,
|
||||
"comments_count": 20,
|
||||
"comments": comments,
|
||||
"likes": likes,
|
||||
"reshares": reshares
|
||||
}
|
||||
});
|
||||
return new app.models.Post(_.extend(defaultAttrs, overrides));
|
||||
},
|
||||
|
||||
statusMessage : function(overrides){
|
||||
//intentionally doesn't have an author to mirror creation process, maybe we should change the creation process
|
||||
return new app.models.StatusMessage(_.extend(factory.postAttrs(), overrides));
|
||||
|
|
|
|||
|
|
@ -1,44 +1,93 @@
|
|||
describe PostPresenter do
|
||||
before do
|
||||
@sm = FactoryGirl.create(:status_message, public: true)
|
||||
@sm_with_poll = FactoryGirl.create(:status_message_with_poll, public: true)
|
||||
@presenter = PostPresenter.new(@sm, bob)
|
||||
@unauthenticated_presenter = PostPresenter.new(@sm)
|
||||
end
|
||||
let(:status_message) { FactoryGirl.create(:status_message, public: true) }
|
||||
let(:status_message_with_poll) { FactoryGirl.create(:status_message_with_poll, public: true) }
|
||||
let(:presenter) { PostPresenter.new(status_message, bob) }
|
||||
let(:unauthenticated_presenter) { PostPresenter.new(status_message) }
|
||||
|
||||
it "takes a post and an optional user" do
|
||||
expect(@presenter).not_to be_nil
|
||||
expect(presenter).not_to be_nil
|
||||
end
|
||||
|
||||
describe "#as_json" do
|
||||
it "works with a user" do
|
||||
expect(@presenter.as_json).to be_a Hash
|
||||
expect(presenter.as_json).to be_a Hash
|
||||
end
|
||||
|
||||
it "works without a user" do
|
||||
expect(@unauthenticated_presenter.as_json).to be_a Hash
|
||||
expect(unauthenticated_presenter.as_json).to be_a Hash
|
||||
end
|
||||
end
|
||||
|
||||
context "post with interactions" do
|
||||
before do
|
||||
bob.like!(status_message)
|
||||
bob.reshare!(status_message)
|
||||
end
|
||||
|
||||
describe "#with_interactions" do
|
||||
it "works with a user" do
|
||||
post_hash = presenter.with_interactions
|
||||
expect(post_hash).to be_a Hash
|
||||
expect(post_hash[:interactions]).to eq PostInteractionPresenter.new(status_message, bob).as_json
|
||||
end
|
||||
|
||||
it "works without a user" do
|
||||
post_hash = unauthenticated_presenter.with_interactions
|
||||
expect(post_hash).to be_a Hash
|
||||
expect(post_hash[:interactions]).to eq PostInteractionPresenter.new(status_message, nil).as_json
|
||||
end
|
||||
end
|
||||
|
||||
describe "#with_initial_interactions" do
|
||||
it "works with a user" do
|
||||
post_hash = presenter.with_initial_interactions
|
||||
expect(post_hash).to be_a Hash
|
||||
expect(post_hash[:interactions][:likes]).to eq(
|
||||
LikeService.new(bob).find_for_post(status_message.id).as_api_response(:backbone)
|
||||
)
|
||||
expect(post_hash[:interactions][:reshares]).to eq(
|
||||
ReshareService.new(bob).find_for_post(status_message.id).as_api_response(:backbone)
|
||||
)
|
||||
end
|
||||
|
||||
it "works without a user" do
|
||||
post_hash = unauthenticated_presenter.with_initial_interactions
|
||||
expect(post_hash).to be_a Hash
|
||||
expect(post_hash[:interactions][:likes]).to eq(
|
||||
LikeService.new.find_for_post(status_message.id).as_api_response(:backbone)
|
||||
)
|
||||
expect(post_hash[:interactions][:reshares]).to eq(
|
||||
ReshareService.new.find_for_post(status_message.id).as_api_response(:backbone)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#user_like" do
|
||||
before do
|
||||
bob.like!(status_message)
|
||||
end
|
||||
|
||||
it "includes the users like" do
|
||||
bob.like!(@sm)
|
||||
expect(@presenter.send(:user_like)).to be_present
|
||||
expect(presenter.send(:user_like)).to be_present
|
||||
end
|
||||
|
||||
it "is nil if the user is not authenticated" do
|
||||
expect(@unauthenticated_presenter.send(:user_like)).to be_nil
|
||||
expect(unauthenticated_presenter.send(:user_like)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "#user_reshare" do
|
||||
before do
|
||||
bob.reshare!(status_message)
|
||||
end
|
||||
|
||||
it "includes the users reshare" do
|
||||
bob.reshare!(@sm)
|
||||
expect(@presenter.send(:user_reshare)).to be_present
|
||||
expect(presenter.send(:user_reshare)).to be_present
|
||||
end
|
||||
|
||||
it "is nil if the user is not authenticated" do
|
||||
expect(@unauthenticated_presenter.send(:user_reshare)).to be_nil
|
||||
expect(unauthenticated_presenter.send(:user_reshare)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -67,23 +116,23 @@ describe PostPresenter do
|
|||
it "delegates to message.title" do
|
||||
message = double(present?: true)
|
||||
expect(message).to receive(:title)
|
||||
@presenter.post = double(message: message)
|
||||
@presenter.send(:title)
|
||||
presenter.post = double(message: message)
|
||||
presenter.send(:title)
|
||||
end
|
||||
end
|
||||
|
||||
context "with posts without text" do
|
||||
it "displays a messaage with the post class" do
|
||||
@sm = double(message: double(present?: false), author: bob.person, author_name: bob.person.name)
|
||||
@presenter.post = @sm
|
||||
expect(@presenter.send(:title)).to eq("A post from #{@sm.author.name}")
|
||||
sm = double(message: double(present?: false), author: bob.person, author_name: bob.person.name)
|
||||
presenter.post = sm
|
||||
expect(presenter.send(:title)).to eq("A post from #{sm.author.name}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#poll" do
|
||||
it "works without a user" do
|
||||
presenter = PostPresenter.new(@sm_with_poll)
|
||||
presenter = PostPresenter.new(status_message_with_poll)
|
||||
expect(presenter.as_json).to be_a(Hash)
|
||||
end
|
||||
end
|
||||
|
|
@ -134,15 +183,15 @@ describe PostPresenter do
|
|||
|
||||
describe "#build_open_graph_cache" do
|
||||
it "returns a dummy og cache if the og cache is missing" do
|
||||
expect(@presenter.build_open_graph_cache.image).to be_nil
|
||||
expect(presenter.build_open_graph_cache.image).to be_nil
|
||||
end
|
||||
|
||||
context "with an open graph cache" do
|
||||
it "delegates to as_api_response" do
|
||||
og_cache = double("open_graph_cache")
|
||||
expect(og_cache).to receive(:as_api_response).with(:backbone)
|
||||
@presenter.post = double(open_graph_cache: og_cache)
|
||||
@presenter.send(:build_open_graph_cache)
|
||||
presenter.post = double(open_graph_cache: og_cache)
|
||||
presenter.send(:build_open_graph_cache)
|
||||
end
|
||||
|
||||
it "returns the open graph cache data" do
|
||||
|
|
|
|||
Loading…
Reference in a new issue