diff --git a/app/assets/javascripts/app/pages/contacts.js b/app/assets/javascripts/app/pages/contacts.js
index 7c041026c..eca20fa33 100644
--- a/app/assets/javascripts/app/pages/contacts.js
+++ b/app/assets/javascripts/app/pages/contacts.js
@@ -8,7 +8,6 @@ app.pages.Contacts = Backbone.View.extend({
"click #contacts_visibility_toggle" : "toggleContactVisibility",
"click #chat_privilege_toggle" : "toggleChatPrivilege",
"click #change_aspect_name" : "showAspectNameForm",
- "keyup #contact_list_search" : "searchContactList",
"click .conversation_button": "showMessageModal",
"click #invitations-button": "showInvitationsModal"
},
@@ -79,10 +78,6 @@ app.pages.Contacts = Backbone.View.extend({
$(".header > h3").show();
},
- searchContactList: function(e) {
- this.stream.search($(e.target).val());
- },
-
showMessageModal: function(){
app.helpers.showModal("#conversationModal");
},
diff --git a/app/assets/javascripts/app/router.js b/app/assets/javascripts/app/router.js
index 6c27e8b5c..5f8a3cc96 100644
--- a/app/assets/javascripts/app/router.js
+++ b/app/assets/javascripts/app/router.js
@@ -81,13 +81,14 @@ app.Router = Backbone.Router.extend({
).render();
},
- contacts: function() {
+ contacts: function(params) {
app.aspect = new app.models.Aspect(gon.preloads.aspect);
this._loadContacts();
var stream = new app.views.ContactStream({
collection: app.contacts,
- el: $(".stream.contacts #contact_stream")
+ el: $(".stream.contacts #contact_stream"),
+ urlParams: params
});
app.page = new app.pages.Contacts({stream: stream});
diff --git a/app/assets/javascripts/app/views/contact_stream_view.js b/app/assets/javascripts/app/views/contact_stream_view.js
index ad93301bd..cbc254c37 100644
--- a/app/assets/javascripts/app/views/contact_stream_view.js
+++ b/app/assets/javascripts/app/views/contact_stream_view.js
@@ -1,77 +1,79 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.ContactStream = Backbone.View.extend({
- initialize: function() {
- this.itemCount = 0;
- this.perPage = 25;
- this.query = '';
- this.resultList = this.collection.toArray();
+ initialize: function(opts) {
+ this.page = 1;
var throttledScroll = _.throttle(_.bind(this.infScroll, this), 200);
$(window).scroll(throttledScroll);
- this.on('renderContacts', this.renderContacts, this);
+ this.on("fetchContacts", this.fetchContacts, this);
+ this.urlParams = opts.urlParams;
},
render: function() {
- if( _.isEmpty(this.resultList) ) {
- var content = document.createDocumentFragment();
- content = '
' +
- '
' +
- Diaspora.I18n.t('contacts.search_no_results') +
- '
' +
- '';
- this.$el.html(content);
- } else {
- this.$el.html('');
- this.renderContacts();
- }
+ this.fetchContacts();
},
- renderContacts: function() {
+ fetchContacts: function() {
this.$el.addClass("loading");
- var content = document.createDocumentFragment();
- _.rest(_.first(this.resultList , this.itemCount + this.perPage), this.itemCount).forEach( function(item) {
- var view = new app.views.Contact({model: item});
- content.appendChild(view.render().el);
+ $("#paginate .loader").removeClass("hidden");
+ $.ajax(this._fetchUrl(), {
+ context: this
+ }).success(function(response) {
+ if (response.length === 0) {
+ this.onEmptyResponse();
+ } else {
+ this.appendContactViews(response);
+ this.page++;
+ }
});
+ },
- var size = _.size(this.resultList);
- if( this.itemCount + this.perPage >= size ){
- this.itemCount = size;
- this.off('renderContacts');
- } else {
- this.itemCount += this.perPage;
+ _fetchUrl: function() {
+ var url = Routes.contacts({format: "json", page: this.page});
+ if (this.urlParams) {
+ url += "&" + this.urlParams;
}
+ return url;
+ },
+
+ onEmptyResponse: function() {
+ if (this.collection.length === 0) {
+ var content = document.createDocumentFragment();
+ content = "" +
+ "
" +
+ Diaspora.I18n.t("contacts.search_no_results") +
+ "
" +
+ "";
+ this.$el.html(content);
+ }
+ this.off("fetchContacts");
+ this.$el.removeClass("loading");
+ $("#paginate .loader").addClass("hidden");
+ },
+
+ appendContactViews: function(contacts) {
+ var content = document.createDocumentFragment();
+ contacts.forEach(function(contactData) {
+ var contact = new app.models.Contact(contactData);
+ this.collection.add(contact);
+ var view = new app.views.Contact({model: contact});
+ content.appendChild(view.render().el);
+ }.bind(this));
this.$el.append(content);
this.$el.removeClass("loading");
- },
-
- search: function(query) {
- query = query.trim();
- if( query || this.query ) {
- this.off('renderContacts');
- this.on('renderContacts', this.renderContacts, this);
- this.itemCount = 0;
- if( query ) {
- this.query = query;
- var regex = new RegExp(query,'i');
- this.resultList = this.collection.filter(function(contact) {
- return regex.test(contact.get('person').name) ||
- regex.test(contact.get('person').diaspora_id);
- });
- } else {
- this.resultList = this.collection.toArray();
- this.query = '';
- }
- this.render();
- }
+ $("#paginate .loader").addClass("hidden");
},
infScroll: function() {
- if( this.$el.hasClass('loading') ) return;
+ if (this.$el.hasClass("loading")) {
+ return;
+ }
var distanceTop = $(window).height() + $(window).scrollTop(),
distanceBottom = $(document).height() - distanceTop;
- if(distanceBottom < 300) this.trigger('renderContacts');
+ if (distanceBottom < 300) {
+ this.trigger("fetchContacts");
+ }
}
});
// @license-end
diff --git a/app/assets/stylesheets/contacts.scss b/app/assets/stylesheets/contacts.scss
index d50252757..aaef928f7 100644
--- a/app/assets/stylesheets/contacts.scss
+++ b/app/assets/stylesheets/contacts.scss
@@ -10,8 +10,13 @@
margin-bottom: 11px;
margin-top: 11px;
}
+ }
- .aspect-controls { margin: 7px -10px 7px 0; }
+ .stream.contacts .aspect-controls {
+ margin-bottom: 7px;
+ margin-left: 30px;
+ margin-right: -10px;
+ margin-top: 7px;
}
}
@@ -23,7 +28,7 @@
display: none;
}
#contact_list_search {
- margin: 11px 30px 0 0;
+ margin: 11px 0 0;
width: 150px;
&:focus { width: 250px; }
}
diff --git a/app/controllers/contacts_controller.rb b/app/controllers/contacts_controller.rb
index d3734da9b..a0be1dcec 100644
--- a/app/controllers/contacts_controller.rb
+++ b/app/controllers/contacts_controller.rb
@@ -14,9 +14,13 @@ class ContactsController < ApplicationController
# Used by the mobile site
format.mobile { set_up_contacts_mobile }
- # Used for mentions in the publisher
+ # Used for mentions in the publisher and pagination on the contacts page
format.json {
- @people = Person.search(params[:q], current_user, only_contacts: true).limit(15)
+ @people = if params[:q].present?
+ Person.search(params[:q], current_user, only_contacts: true).limit(15)
+ else
+ set_up_contacts_json
+ end
render json: @people
}
end
@@ -30,30 +34,54 @@ class ContactsController < ApplicationController
private
def set_up_contacts
- type = params[:set].presence
- type ||= "by_aspect" if params[:a_id].present?
- type ||= "receiving"
+ if params[:a_id].present?
+ @aspect = current_user.aspects.find(params[:a_id])
+ gon.preloads[:aspect] = AspectPresenter.new(@aspect).as_json
+ end
+ @contacts_size = current_user.contacts.size
+ end
- @contacts = contacts_by_type(type)
- @contacts_size = @contacts.length
- gon.preloads[:contacts] = @contacts.map {|c| ContactPresenter.new(c, current_user).full_hash_with_person }
+ def set_up_contacts_json
+ type = params[:set].presence
+ if params[:a_id].present?
+ type ||= "by_aspect"
+ @aspect = current_user.aspects.find(params[:a_id])
+ end
+ type ||= "receiving"
+ contacts_by_type(type).paginate(page: params[:page], per_page: 25)
+ .map {|c| ContactPresenter.new(c, current_user).full_hash_with_person }
end
def contacts_by_type(type)
- case type
+ order = ["profiles.first_name ASC", "profiles.last_name ASC", "profiles.diaspora_handle ASC"]
+ contacts = case type
when "all"
+ order.unshift "receiving DESC"
current_user.contacts
when "only_sharing"
current_user.contacts.only_sharing
when "receiving"
current_user.contacts.receiving
when "by_aspect"
- @aspect = current_user.aspects.find(params[:a_id])
- gon.preloads[:aspect] = AspectPresenter.new(@aspect).as_json
- current_user.contacts
+ order.unshift "contact_id IS NOT NULL DESC"
+ contacts_by_aspect(@aspect.id)
else
raise ArgumentError, "unknown type #{type}"
end
+ contacts.includes(person: :profile)
+ .order(order)
+ end
+
+ def contacts_by_aspect(aspect_id)
+ contacts = current_user.contacts.arel_table
+ aspect_memberships = AspectMembership.arel_table
+ current_user.contacts.joins(
+ contacts.outer_join(aspect_memberships).on(
+ aspect_memberships[:aspect_id].eq(aspect_id).and(
+ aspect_memberships[:contact_id].eq(contacts[:id])
+ )
+ ).join_sources
+ )
end
def set_up_contacts_mobile
diff --git a/app/views/contacts/_header.html.haml b/app/views/contacts/_header.html.haml
index e6059e005..d8d323fb1 100644
--- a/app/views/contacts/_header.html.haml
+++ b/app/views/contacts/_header.html.haml
@@ -20,7 +20,11 @@
= link_to @aspect, method: "delete", data: { confirm: t("aspects.edit.confirm_remove_aspect") }, class: "delete contacts_button", id: "delete_aspect" do
%i.entypo-trash.contacts-header-icon{title: t("delete")}
.pull-right.contact-list-search
- = search_field_tag :contact_search, "", id: "contact_list_search", class: "search-query form-control", placeholder: t("contacts.index.user_search")
+ %form#contact-search-form{role: "search", method: "get", action: "/search"}
+ = search_field_tag :q, "",
+ id: "contact_list_search",
+ class: "search-query form-control",
+ placeholder: t("contacts.index.user_search")
%h3
%span#aspect_name
= @aspect.name
@@ -32,6 +36,13 @@
= aspect.submit t('aspects.edit.update'), 'data-disable-with' => t('aspects.edit.updating'), class: "btn btn-default"
- else
+ .pull-right.contact-list-search
+ %form#contact-search-form{role: "search", method: "get", action: "/search"}
+ = search_field_tag :q, "",
+ id: "contact_list_search",
+ class: "search-query form-control",
+ placeholder: t("contacts.index.user_search")
+
%h3
- case params["set"]
- when "only_sharing"
diff --git a/app/views/contacts/index.html.haml b/app/views/contacts/index.html.haml
index ebc6d2e43..2a8de4f32 100644
--- a/app/views/contacts/index.html.haml
+++ b/app/views/contacts/index.html.haml
@@ -29,6 +29,10 @@
.btn.btn-link{ 'data-toggle' => 'modal' }
= t('invitations.new.invite_someone_to_join')
+ #paginate
+ %span.loader.hidden
+ .spinner
+
-if @aspect
#new_conversation_pane
= render 'shared/modal',
diff --git a/spec/controllers/contacts_controller_spec.rb b/spec/controllers/contacts_controller_spec.rb
index 27f9b9def..7a8ab7a13 100644
--- a/spec/controllers/contacts_controller_spec.rb
+++ b/spec/controllers/contacts_controller_spec.rb
@@ -24,51 +24,97 @@ describe ContactsController, :type => :controller do
expect(response).to be_success
end
- it "assigns contacts" do
+ it "doesn't assign contacts" do
get :index
contacts = assigns(:contacts)
- expect(contacts.to_set).to eq(bob.contacts.to_set)
- end
-
- it "shows only contacts a user is sharing with" do
- contact = bob.contacts.first
- contact.update_attributes(:sharing => false)
-
- get :index
- contacts = assigns(:contacts)
- expect(contacts.to_set).to eq(bob.contacts.receiving.to_set)
- end
-
- it "shows all contacts (sharing and receiving)" do
- contact = bob.contacts.first
- contact.update_attributes(:sharing => false)
-
- get :index, :set => "all"
- contacts = assigns(:contacts)
- expect(contacts.to_set).to eq(bob.contacts.to_set)
+ expect(contacts).to be_nil
end
end
context "format json" do
- before do
- @person1 = FactoryGirl.create(:person)
- bob.share_with(@person1, bob.aspects.first)
- @person2 = FactoryGirl.create(:person)
+ context "for the contacts search" do
+ before do
+ @person1 = FactoryGirl.create(:person)
+ bob.share_with(@person1, bob.aspects.first)
+ @person2 = FactoryGirl.create(:person)
+ end
+
+ it "succeeds" do
+ get :index, q: @person1.first_name, format: "json"
+ expect(response).to be_success
+ end
+
+ it "responds with json" do
+ get :index, q: @person1.first_name, format: "json"
+ expect(response.body).to eq([@person1].to_json)
+ end
+
+ it "only returns contacts" do
+ get :index, q: @person2.first_name, format: "json"
+ expect(response.body).to eq([].to_json)
+ end
end
- it "succeeds" do
- get :index, q: @person1.first_name, format: "json"
- expect(response).to be_success
- end
+ context "for pagination on the contacts page" do
+ context "without parameters" do
+ it "returns contacts" do
+ get :index, format: "json", page: "1"
+ contact_ids = JSON.parse(response.body).map {|c| c["id"] }
+ expect(contact_ids.to_set).to eq(bob.contacts.map(&:id).to_set)
+ end
- it "responds with json" do
- get :index, q: @person1.first_name, format: "json"
- expect(response.body).to eq([@person1].to_json)
- end
+ it "returns only contacts which are receiving (the user is sharing with them)" do
+ contact = bob.contacts.first
+ contact.update_attributes(receiving: false)
- it "only returns contacts" do
- get :index, q: @person2.first_name, format: "json"
- expect(response.body).to eq([].to_json)
+ get :index, format: "json", page: "1"
+ contact_ids = JSON.parse(response.body).map {|c| c["id"] }
+ expect(contact_ids.to_set).to eq(bob.contacts.receiving.map(&:id).to_set)
+ expect(contact_ids).not_to include(contact.id)
+ end
+ end
+
+ context "set: all" do
+ before do
+ contact = bob.contacts.first
+ contact.update_attributes(receiving: false)
+ end
+
+ it "returns all contacts (sharing and receiving)" do
+ get :index, format: "json", page: "1", set: "all"
+ contact_ids = JSON.parse(response.body).map {|c| c["id"] }
+ expect(contact_ids.to_set).to eq(bob.contacts.map(&:id).to_set)
+ end
+
+ it "sorts contacts by receiving status" do
+ get :index, format: "json", page: "1", set: "all"
+ contact_ids = JSON.parse(response.body).map {|c| c["id"] }
+ expect(contact_ids).to eq(bob.contacts.order("receiving DESC").map(&:id))
+ expect(contact_ids.last).to eq(bob.contacts.first.id)
+ end
+ end
+
+ context "with an aspect id" do
+ before do
+ @aspect = bob.aspects.create(name: "awesome contacts")
+ @person = FactoryGirl.create(:person)
+ bob.share_with(@person, @aspect)
+ end
+
+ it "returns all contacts" do
+ get :index, format: "json", a_id: @aspect.id, page: "1"
+ contact_ids = JSON.parse(response.body).map {|c| c["id"] }
+ expect(contact_ids.to_set).to eq(bob.contacts.map(&:id).to_set)
+ end
+
+ it "sorts contacts by aspect memberships" do
+ get :index, format: "json", a_id: @aspect.id, page: "1"
+ expect(JSON.parse(response.body).first["person"]["id"]).to eq(@person.id)
+
+ get :index, format: "json", a_id: bob.aspects.first.id, page: "1"
+ expect(JSON.parse(response.body).first["person"]["id"]).not_to eq(@person.id)
+ end
+ end
end
end
end
diff --git a/spec/controllers/jasmine_fixtures/contacts_spec.rb b/spec/controllers/jasmine_fixtures/contacts_spec.rb
index 261d3a88d..3e6cb3c85 100644
--- a/spec/controllers/jasmine_fixtures/contacts_spec.rb
+++ b/spec/controllers/jasmine_fixtures/contacts_spec.rb
@@ -17,7 +17,11 @@ describe ContactsController, :type => :controller do
it "generates the aspects_manage fixture", :fixture => true do
get :index, :a_id => @aspect.id
save_fixture(html_for("body"), "aspects_manage")
- save_fixture(controller.gon.preloads[:contacts].to_json, "aspects_manage_contacts_json")
+ end
+
+ it "generates the aspects_manage_contacts_json fixture", fixture: true do
+ get :index, format: :json, a_id: @aspect.id, page: "1"
+ save_fixture(response.body, "aspects_manage_contacts_json")
end
it "generates the contacts_json fixture", :fixture => true do
diff --git a/spec/javascripts/app/pages/contacts_spec.js b/spec/javascripts/app/pages/contacts_spec.js
index 961378a5c..a40b899d2 100644
--- a/spec/javascripts/app/pages/contacts_spec.js
+++ b/spec/javascripts/app/pages/contacts_spec.js
@@ -88,19 +88,6 @@ describe("app.pages.Contacts", function(){
});
});
- context('search contact list', function() {
- beforeEach(function() {
- this.searchinput = $('#contact_list_search');
- });
-
- it('calls stream.search', function() {
- this.view.stream.search = jasmine.createSpy();
- this.searchinput.val("Username");
- this.searchinput.trigger('keyup');
- expect(this.view.stream.search).toHaveBeenCalledWith("Username");
- });
- });
-
describe("updateBadgeCount", function() {
it("increases the badge count of an aspect", function() {
var aspect = $("#aspect_nav .aspect").eq(0);
diff --git a/spec/javascripts/app/views/contact_stream_view_spec.js b/spec/javascripts/app/views/contact_stream_view_spec.js
index 955dd2e7b..7ae93bb53 100644
--- a/spec/javascripts/app/views/contact_stream_view_spec.js
+++ b/spec/javascripts/app/views/contact_stream_view_spec.js
@@ -2,76 +2,199 @@ describe("app.views.ContactStream", function() {
beforeEach(function() {
loginAs({name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}});
spec.loadFixture("aspects_manage");
- this.contacts = new app.collections.Contacts($.parseJSON(spec.readFixture("contacts_json")));
- app.aspect = new app.models.Aspect(this.contacts.first().get('aspect_memberships')[0].aspect);
+ this.contacts = new app.collections.Contacts();
+ this.contactsData = $.parseJSON(spec.readFixture("contacts_json"));
+ app.aspect = new app.models.Aspect(this.contactsData[0].aspect_memberships[0].aspect);
this.view = new app.views.ContactStream({
collection : this.contacts,
- el: $('.stream.contacts #contact_stream')
+ el: $(".stream.contacts #contact_stream"),
+ urlParams: "set=all"
});
-
- this.view.perPage=1;
-
- //clean the page
- this.view.$el.html('');
});
describe("initialize", function() {
it("binds an infinite scroll listener", function() {
spyOn($.fn, "scroll");
- new app.views.ContactStream({collection : this.contacts});
+ new app.views.ContactStream({collection: this.contacts});
expect($.fn.scroll).toHaveBeenCalled();
});
+
+ it("binds 'fetchContacts'", function() {
+ spyOn(app.views.ContactStream.prototype, "fetchContacts");
+ this.view = new app.views.ContactStream({collection: this.contacts});
+ this.view.trigger("fetchContacts");
+ expect(app.views.ContactStream.prototype.fetchContacts).toHaveBeenCalled();
+ });
+
+ it("sets the current page for pagination to 1", function() {
+ expect(this.view.page).toBe(1);
+ });
+
+ it("sets urlParams to the given value", function() {
+ expect(this.view.urlParams).toBe("set=all");
+ });
});
- describe("search", function() {
- it("filters the contacts", function() {
+ describe("render", function() {
+ it("calls fetchContacts", function() {
+ spyOn(this.view, "fetchContacts");
this.view.render();
- expect(this.view.$el.html()).toContain("alice");
- this.view.search("eve");
- expect(this.view.$el.html()).not.toContain("alice");
- expect(this.view.$el.html()).toContain("eve");
+ expect(this.view.fetchContacts).toHaveBeenCalled();
+ });
+ });
+
+ describe("fetchContacts", function() {
+ it("adds the loading class", function() {
+ expect(this.view.$el).not.toHaveClass("loading");
+ this.view.fetchContacts();
+ expect(this.view.$el).toHaveClass("loading");
+ });
+
+ it("displays the loading spinner", function() {
+ expect($("#paginate .loader")).toHaveClass("hidden");
+ this.view.fetchContacts();
+ expect($("#paginate .loader")).not.toHaveClass("hidden");
+ });
+
+ it("calls $.ajax with the URL given by _fetchUrl", function() {
+ spyOn(this.view, "_fetchUrl").and.returnValue("/myAwesomeFetchUrl?foo=bar");
+ this.view.fetchContacts();
+ expect(jasmine.Ajax.requests.mostRecent().url).toBe("/myAwesomeFetchUrl?foo=bar");
+ });
+
+ it("calls onEmptyResponse on an empty response", function() {
+ spyOn(this.view, "onEmptyResponse");
+ this.view.fetchContacts();
+ jasmine.Ajax.requests.mostRecent().respondWith({status: 200, responseText: JSON.stringify([])});
+ expect(this.view.onEmptyResponse).toHaveBeenCalled();
+ });
+
+ it("calls appendContactViews on a non-empty response", function() {
+ spyOn(this.view, "appendContactViews");
+ this.view.fetchContacts();
+ jasmine.Ajax.requests.mostRecent().respondWith({status: 200, responseText: JSON.stringify(this.contactsData)});
+ expect(this.view.appendContactViews).toHaveBeenCalledWith(this.contactsData);
+ });
+
+ it("increases the current page on a non-empty response", function() {
+ this.view.page = 42;
+ this.view.fetchContacts();
+ jasmine.Ajax.requests.mostRecent().respondWith({status: 200, responseText: JSON.stringify(this.contactsData)});
+ expect(this.view.page).toBe(43);
+ });
+ });
+
+ describe("_fetchUrl", function() {
+ it("returns the correct URL to fetch contacts", function() {
+ this.view.page = 15;
+ this.view.urlParams = undefined;
+ expect(this.view._fetchUrl()).toBe("/contacts.json?page=15");
+ });
+
+ it("appends urlParams if those are set", function() {
+ this.view.page = 23;
+ expect(this.view._fetchUrl()).toBe("/contacts.json?page=23&set=all");
+ });
+ });
+
+ describe("onEmptyResponse", function() {
+ context("with an empty collection", function() {
+ it("adds a 'no contacts' div", function() {
+ this.view.onEmptyResponse();
+ expect(this.view.$("#no_contacts").text().trim()).toBe(Diaspora.I18n.t("contacts.search_no_results"));
+ });
+
+ it("hides the loading spinner", function() {
+ this.view.$el.addClass("loading");
+ $("#paginate .loader").removeClass("hidden");
+ this.view.onEmptyResponse();
+ expect(this.view.$el).not.toHaveClass("loading");
+ expect($("#paginate .loader")).toHaveClass("hidden");
+ });
+
+ it("unbinds 'fetchContacts'", function() {
+ spyOn(this.view, "off");
+ this.view.onEmptyResponse();
+ expect(this.view.off).toHaveBeenCalledWith("fetchContacts");
+ });
+ });
+
+ context("with a non-empty collection", function() {
+ beforeEach(function() {
+ this.view.collection.add(factory.contact());
+ });
+
+ it("adds no 'no contacts' div", function() {
+ this.view.onEmptyResponse();
+ expect(this.view.$("#no_contacts").length).toBe(0);
+ });
+
+ it("hides the loading spinner", function() {
+ this.view.$el.addClass("loading");
+ $("#paginate .loader").removeClass("hidden");
+ this.view.onEmptyResponse();
+ expect(this.view.$el).not.toHaveClass("loading");
+ expect($("#paginate .loader")).toHaveClass("hidden");
+ });
+
+ it("unbinds 'fetchContacts'", function() {
+ spyOn(this.view, "off");
+ this.view.onEmptyResponse();
+ expect(this.view.off).toHaveBeenCalledWith("fetchContacts");
+ });
+ });
+ });
+
+ describe("appendContactViews", function() {
+ it("hides the loading spinner", function() {
+ this.view.$el.addClass("loading");
+ $("#paginate .loader").removeClass("hidden");
+ this.view.appendContactViews(this.contactsData);
+ expect(this.view.$el).not.toHaveClass("loading");
+ expect($("#paginate .loader")).toHaveClass("hidden");
+ });
+
+ it("adds all contacts to an empty collection", function() {
+ expect(this.view.collection.length).toBe(0);
+ this.view.appendContactViews(this.contactsData);
+ expect(this.view.collection.length).toBe(this.contactsData.length);
+ expect(this.view.collection.pluck("id")).toEqual(_.pluck(this.contactsData, "id"));
+ });
+
+ it("appends contacts to an existing collection", function() {
+ this.view.collection.add(this.contactsData[0]);
+ expect(this.view.collection.length).toBe(1);
+ this.view.appendContactViews(_.rest(this.contactsData));
+ expect(this.view.collection.length).toBe(this.contactsData.length);
+ expect(this.view.collection.pluck("id")).toEqual(_.pluck(this.contactsData, "id"));
+ });
+
+ it("renders all added contacts", function() {
+ expect(this.view.$(".stream_element.contact").length).toBe(0);
+ this.view.appendContactViews(this.contactsData);
+ expect(this.view.$(".stream_element.contact").length).toBe(this.contactsData.length);
+ });
+
+ it("appends contacts to an existing contact list", function() {
+ this.view.appendContactViews([this.contactsData[0]]);
+ expect(this.view.$(".stream_element.contact").length).toBe(1);
+ this.view.appendContactViews(_.rest(this.contactsData));
+ expect(this.view.$(".stream_element.contact").length).toBe(this.contactsData.length);
});
});
describe("infScroll", function() {
beforeEach(function() {
- this.view.off("renderContacts");
+ this.view.off("fetchContacts");
this.fn = jasmine.createSpy();
- this.view.on("renderContacts", this.fn);
+ this.view.on("fetchContacts", this.fn);
spyOn($.fn, "height").and.returnValue(0);
spyOn($.fn, "scrollTop").and.returnValue(100);
});
- it("triggers renderContacts when the user is at the bottom of the page", function() {
+ it("triggers fetchContacts when the user is at the bottom of the page", function() {
this.view.infScroll();
expect(this.fn).toHaveBeenCalled();
});
});
-
- describe("render", function() {
- beforeEach(function() {
- spyOn(this.view, "renderContacts");
- });
-
- it("calls renderContacts", function() {
- this.view.render();
- expect(this.view.renderContacts).toHaveBeenCalled();
- });
- });
-
- describe("renderContacts", function() {
- beforeEach(function() {
- this.view.off("renderContacts");
- this.view.renderContacts();
- });
-
- it("renders perPage contacts", function() {
- expect(this.view.$el.find('.stream_element.contact').length).toBe(1);
- });
-
- it("renders more contacts when called a second time", function() {
- this.view.renderContacts();
- expect(this.view.$el.find('.stream_element.contact').length).toBe(2);
- });
- });
});