diff --git a/app/assets/templates/contact_tpl.jst.hbs b/app/assets/templates/contact_tpl.jst.hbs
new file mode 100644
index 000000000..a89542f4e
--- /dev/null
+++ b/app/assets/templates/contact_tpl.jst.hbs
@@ -0,0 +1,23 @@
+
diff --git a/app/assets/templates/single-post-viewer/single-post-content_tpl.jst.hbs b/app/assets/templates/single-post-viewer/single-post-content_tpl.jst.hbs
index a144e0390..e804b3c95 100644
--- a/app/assets/templates/single-post-viewer/single-post-content_tpl.jst.hbs
+++ b/app/assets/templates/single-post-viewer/single-post-content_tpl.jst.hbs
@@ -3,26 +3,26 @@
{{#if root}}
- {{#linkToPerson root.author}}
+ {{#linkToAuthor root.author}}
{{{personImage this 'medium'}}}
- {{/linkToPerson}}
+ {{/linkToAuthor}}
{{else}}
- {{#linkToPerson author}}
+ {{#linkToAuthor author}}
{{{personImage this 'medium'}}}
- {{/linkToPerson}}
+ {{/linkToAuthor}}
{{/if}}
{{#if root}}
- {{#linkToPerson root.author}}
+ {{#linkToAuthor root.author}}
{{name}}
- {{/linkToPerson}}
+ {{/linkToAuthor}}
{{else}}
- {{#linkToPerson author}}
+ {{#linkToAuthor author}}
{{name}}
- {{/linkToPerson}}
+ {{/linkToAuthor}}
{{/if}}
@@ -69,14 +69,14 @@
@@ -21,9 +21,9 @@
{{#each likes}}
- {{#linkToPerson author}}
+ {{#linkToAuthor author}}
{{{personImage this 'small' 'micro'}}}
- {{/linkToPerson}}
+ {{/linkToAuthor}}
{{/each}}
diff --git a/app/assets/templates/stream-faces_tpl.jst.hbs b/app/assets/templates/stream-faces_tpl.jst.hbs
index 6db001f19..2e9898905 100644
--- a/app/assets/templates/stream-faces_tpl.jst.hbs
+++ b/app/assets/templates/stream-faces_tpl.jst.hbs
@@ -1,5 +1,5 @@
{{#people}}
- {{#linkToPerson this}}
+ {{#linkToAuthor this}}
{{{personImage this "small"}}}
- {{/linkToPerson}}
+ {{/linkToAuthor}}
{{/people}}
diff --git a/app/assets/templates/stream-frame_tpl.jst.hbs b/app/assets/templates/stream-frame_tpl.jst.hbs
index 5574a479d..a9f55163c 100644
--- a/app/assets/templates/stream-frame_tpl.jst.hbs
+++ b/app/assets/templates/stream-frame_tpl.jst.hbs
@@ -14,16 +14,16 @@
diff --git a/app/controllers/aspect_memberships_controller.rb b/app/controllers/aspect_memberships_controller.rb
index 5009902b9..eddfa3935 100644
--- a/app/controllers/aspect_memberships_controller.rb
+++ b/app/controllers/aspect_memberships_controller.rb
@@ -34,10 +34,7 @@ class AspectMembershipsController < ApplicationController
respond_to do |format|
format.json do
if success
- render :json => {
- :person_id => contact.person_id,
- :aspect_ids => contact.aspects.map{|a| a.id}
- }
+ render :json => AspectMembershipPresenter.new(membership).base_hash
else
render :text => membership.errors.full_messages, :status => 403
end
@@ -57,7 +54,9 @@ class AspectMembershipsController < ApplicationController
flash.now[:notice] = I18n.t('aspects.add_to_aspect.success')
respond_with do |format|
format.json do
- render :json => AspectMembership.where(:contact_id => @contact.id, :aspect_id => @aspect.id).first.to_json
+ render :json => AspectMembershipPresenter.new(
+ AspectMembership.where(:contact_id => @contact.id, :aspect_id => @aspect.id).first)
+ .base_hash
end
format.all { redirect_to :back }
diff --git a/app/controllers/contacts_controller.rb b/app/controllers/contacts_controller.rb
index 9a57ba3e4..0e6168c9c 100644
--- a/app/controllers/contacts_controller.rb
+++ b/app/controllers/contacts_controller.rb
@@ -40,32 +40,24 @@ class ContactsController < ApplicationController
@contacts = contacts_by_type(type)
@contacts_size = @contacts.length
+ gon.preloads[:contacts] = @contacts.map{ |c| ContactPresenter.new(c, current_user).full_hash_with_person }
end
def contacts_by_type(type)
- contacts = case type
+ case type
when "all"
- [current_user.contacts]
+ current_user.contacts
when "only_sharing"
- [current_user.contacts.only_sharing]
+ current_user.contacts.only_sharing
when "receiving"
- [current_user.contacts.receiving]
+ current_user.contacts.receiving
when "by_aspect"
@aspect = current_user.aspects.find(params[:a_id])
- @contacts_in_aspect = @aspect.contacts
- @contacts_not_in_aspect = current_user.contacts.where.not(contacts: {id: @contacts_in_aspect.pluck(:id) })
- [@contacts_in_aspect, @contacts_not_in_aspect].map {|relation|
- relation.includes(:aspect_memberships)
- }
+ gon.preloads[:aspect] = AspectPresenter.new(@aspect).as_json
+ current_user.contacts
else
raise ArgumentError, "unknown type #{type}"
end
-
- contacts.map {|relation|
- relation.includes(:person => :profile).to_a.tap {|contacts|
- contacts.sort_by! {|contact| contact.person.name }
- }
- }.inject(:+).paginate(:page => params[:page], :per_page => 25)
end
def set_up_contacts_mobile
diff --git a/app/helpers/contacts_helper.rb b/app/helpers/contacts_helper.rb
index 5ea96b5a2..3fdee52f3 100644
--- a/app/helpers/contacts_helper.rb
+++ b/app/helpers/contacts_helper.rb
@@ -1,25 +1,9 @@
module ContactsHelper
def contact_aspect_dropdown(contact)
- membership = contact.aspect_memberships.where(:aspect_id => @aspect.id).first unless @aspect.nil?
-
- if membership
- content_tag(:i, nil, :class => 'entypo circled-cross contact_remove-from-aspect',
- :title => t('contacts.index.remove_contact'),
- 'data-aspect_id' => @aspect.id,
- 'data-person_id' => contact.person_id,
- 'data-membership_id' => membership.id )
-
- elsif @aspect.nil?
- render :partial => 'people/relationship_action',
- :locals => { :person => contact.person,
- :contact => contact,
- :current_user => current_user }
- else
- content_tag(:i, nil, :class => 'entypo circled-plus contact_add-to-aspect',
- :title => t('contacts.index.add_contact'),
- 'data-aspect_id' => @aspect.id,
- 'data-person_id' => contact.person_id )
- end
+ render :partial => 'people/relationship_action',
+ :locals => { :person => contact.person,
+ :contact => contact,
+ :current_user => current_user }
end
def start_a_conversation_link(aspect, contacts_size)
diff --git a/app/presenters/aspect_membership_presenter.rb b/app/presenters/aspect_membership_presenter.rb
new file mode 100644
index 000000000..28a96e38f
--- /dev/null
+++ b/app/presenters/aspect_membership_presenter.rb
@@ -0,0 +1,11 @@
+class AspectMembershipPresenter < BasePresenter
+ def initialize(membership)
+ @membership = membership
+ end
+
+ def base_hash
+ { id: @membership.id,
+ aspect: AspectPresenter.new(@membership.aspect).as_json,
+ }
+ end
+end
diff --git a/app/presenters/contact_presenter.rb b/app/presenters/contact_presenter.rb
new file mode 100644
index 000000000..9ab8b2d26
--- /dev/null
+++ b/app/presenters/contact_presenter.rb
@@ -0,0 +1,17 @@
+class ContactPresenter < BasePresenter
+ def base_hash
+ { id: id,
+ person_id: person_id
+ }
+ end
+
+ def full_hash
+ base_hash.merge({
+ aspect_memberships: aspect_memberships.map{ |membership| AspectMembershipPresenter.new(membership).base_hash }
+ })
+ end
+
+ def full_hash_with_person
+ full_hash.merge({person: PersonPresenter.new(person).full_hash_with_profile})
+ end
+end
diff --git a/app/views/contacts/_contact.html.haml b/app/views/contacts/_contact.html.haml
deleted file mode 100644
index 3e6aeb8a4..000000000
--- a/app/views/contacts/_contact.html.haml
+++ /dev/null
@@ -1,12 +0,0 @@
-- membership = contact.aspect_memberships.where(:aspect_id => @aspect.id).first unless @aspect.nil?
-.media.stream_element{:id => contact.person_id, :class => ("in_aspect" if membership)}
- .pull-right
- = contact_aspect_dropdown(contact)
- .media-object.pull-left
- = person_image_link(contact.person, :size => :thumb_small)
- .media-body
- = person_link(contact.person, :class => 'name')
- .info.diaspora_handle
- = contact.person_diaspora_handle
- .info.tags
- = Diaspora::Taggable.format_tags(contact.person.profile.tag_string)
diff --git a/app/views/contacts/index.html.haml b/app/views/contacts/index.html.haml
index 4d8f223b4..62161cbfa 100644
--- a/app/views/contacts/index.html.haml
+++ b/app/views/contacts/index.html.haml
@@ -11,8 +11,9 @@
= render 'contacts/header'
- if @contacts_size > 0
- = render @contacts
- = will_paginate @contacts
+ #contact_stream
+ -# JS
+
- else
.no_contacts
%h3
diff --git a/config/locales/javascript/javascript.en.yml b/config/locales/javascript/javascript.en.yml
index 78fdc4533..77fbeb3d1 100644
--- a/config/locales/javascript/javascript.en.yml
+++ b/config/locales/javascript/javascript.en.yml
@@ -48,6 +48,7 @@ en:
remove_contact: "Remove contact"
error_add: "Couldn't add <%= name %> to the aspect :("
error_remove: "Couldn't remove <%= name %> from the aspect :("
+ search_no_results: "No contacts found"
my_activity: "My Activity"
my_stream: "Stream"
diff --git a/spec/controllers/aspect_memberships_controller_spec.rb b/spec/controllers/aspect_memberships_controller_spec.rb
index 0a5e125e9..abdae8de8 100644
--- a/spec/controllers/aspect_memberships_controller_spec.rb
+++ b/spec/controllers/aspect_memberships_controller_spec.rb
@@ -72,14 +72,14 @@ describe AspectMembershipsController, :type => :controller do
end
context 'json' do
- it 'returns a list of aspect ids for the person' do
+ it 'returns the aspect membership' do
post :create,
:format => :json,
:person_id => @person.id,
:aspect_id => @aspect0.id
contact = @controller.current_user.contact_for(@person)
- expect(response.body).to eq(contact.aspect_memberships.first.to_json)
+ expect(response.body).to eq(AspectMembershipPresenter.new(contact.aspect_memberships.first).base_hash.to_json)
end
end
end
diff --git a/spec/controllers/jasmine_fixtures/contacts_spec.rb b/spec/controllers/jasmine_fixtures/contacts_spec.rb
index f21500def..6311c3b34 100644
--- a/spec/controllers/jasmine_fixtures/contacts_spec.rb
+++ b/spec/controllers/jasmine_fixtures/contacts_spec.rb
@@ -10,12 +10,20 @@ describe ContactsController, :type => :controller do
AppConfig.chat.enabled = true
@aspect = bob.aspects.create(:name => "another aspect")
bob.share_with alice.person, @aspect
+ bob.share_with eve.person, @aspect
sign_in :user, bob
end
- it "generates a jasmine fixture", :fixture => true do
+ it "generates the aspects_manage fixture", :fixture => true do
get :index, :a_id => @aspect.id
save_fixture(html_for("body"), "aspects_manage")
end
+
+ it "generates the contacts_json fixture", :fixture => true do
+ json = bob.contacts.map { |c|
+ ContactPresenter.new(c, bob).full_hash_with_person
+ }.to_json
+ save_fixture(json, "contacts_json")
+ end
end
end
diff --git a/spec/javascripts/app/collections/contacts_collection_spec.js b/spec/javascripts/app/collections/contacts_collection_spec.js
new file mode 100644
index 000000000..3b64d4bd9
--- /dev/null
+++ b/spec/javascripts/app/collections/contacts_collection_spec.js
@@ -0,0 +1,37 @@
+describe("app.collections.Contacts", function(){
+ beforeEach(function(){
+ this.collection = new app.collections.Contacts();
+ });
+
+ describe("comparator", function() {
+ beforeEach(function(){
+ this.aspect = new app.models.Aspect({id: 42, name: "cats"});
+ this.con1 = new app.models.Contact({
+ person: { name: "aaa" },
+ aspect_memberships: []
+ });
+ this.con2 = new app.models.Contact({
+ person: { name: "aaa" },
+ aspect_memberships: [{id: 23, aspect: this.aspect}]
+ });
+ this.con3 = new app.models.Contact({
+ person: { name: "zzz" },
+ aspect_memberships: [{id: 23, aspect: this.aspect}]
+ });
+ });
+
+ it("should compare the username if app.aspect is not present", function() {
+ expect(this.collection.comparator(this.con1, this.con3)).toBeLessThan(0);
+ });
+
+ it("should compare the aspect memberships if app.aspect is present", function() {
+ app.aspect = this.aspect;
+ expect(this.collection.comparator(this.con1, this.con3)).toBeGreaterThan(0);
+ });
+
+ it("should compare the username if the contacts have equal aspect memberships", function() {
+ app.aspect = this.aspect;
+ expect(this.collection.comparator(this.con2, this.con3)).toBeLessThan(0);
+ });
+ });
+});
diff --git a/spec/javascripts/app/models/contact_spec.js b/spec/javascripts/app/models/contact_spec.js
new file mode 100644
index 000000000..265039792
--- /dev/null
+++ b/spec/javascripts/app/models/contact_spec.js
@@ -0,0 +1,20 @@
+describe("app.models.Contact", function() {
+
+ beforeEach(function(){
+ this.aspect = factory.aspect();
+ this.contact = new app.models.Contact({
+ person: { name: "aaa" },
+ aspect_memberships: [{id: 42, aspect: this.aspect}]
+ });
+ });
+
+ describe("inAspect", function(){
+ it("returns true if the contact has been added to the aspect", function(){
+ expect(this.contact.inAspect(this.aspect.id)).toBeTruethy;
+ });
+
+ it("returns false if the contact hasn't been added to the aspect", function(){
+ expect(this.contact.inAspect(this.aspect.id+1)).toBeFalsy;
+ });
+ });
+});
diff --git a/spec/javascripts/app/pages/contacts_spec.js b/spec/javascripts/app/pages/contacts_spec.js
new file mode 100644
index 000000000..6aa1975bf
--- /dev/null
+++ b/spec/javascripts/app/pages/contacts_spec.js
@@ -0,0 +1,101 @@
+describe("app.pages.Contacts", function(){
+ beforeEach(function() {
+ spec.loadFixture("aspects_manage");
+ this.view = new app.pages.Contacts({
+ stream: {
+ render: function(){}
+ }
+ });
+ Diaspora.I18n.load({
+ contacts: {
+ aspect_list_is_visible: "Contacts in this aspect are able to see each other.",
+ aspect_list_is_not_visible: "Contacts in this aspect are not able to see each other.",
+ aspect_chat_is_enabled: "Contacts in this aspect are able to chat with you.",
+ aspect_chat_is_not_enabled: "Contacts in this aspect are not able to chat with you.",
+ }
+ });
+ });
+
+ context('toggle chat privilege', function() {
+ beforeEach(function() {
+ this.chat_toggle = $("#chat_privilege_toggle");
+ this.chat_icon = $("#chat_privilege_toggle .entypo");
+ });
+
+ it('updates the title for the tooltip', function() {
+ expect(this.chat_icon.attr('data-original-title')).toBe(
+ Diaspora.I18n.t("contacts.aspect_chat_is_not_enabled")
+ );
+ this.chat_toggle.trigger('click');
+ expect(this.chat_icon.attr('data-original-title')).toBe(
+ Diaspora.I18n.t("contacts.aspect_chat_is_enabled")
+ );
+ });
+
+ it('toggles the chat icon', function() {
+ expect(this.chat_icon.hasClass('enabled')).toBeFalsy;
+ this.chat_toggle.trigger('click');
+ expect(this.chat_icon.hasClass('enabled')).toBeTruethy;
+ });
+ });
+
+ context('toggle contacts visibility', function() {
+ beforeEach(function() {
+ this.visibility_toggle = $("#contacts_visibility_toggle");
+ this.lock_icon = $("#contacts_visibility_toggle .entypo");
+ });
+
+ it('updates the title for the tooltip', function() {
+ expect(this.lock_icon.attr('data-original-title')).toBe(
+ Diaspora.I18n.t("contacts.aspect_list_is_visible")
+ );
+
+ this.visibility_toggle.trigger('click');
+
+ expect(this.lock_icon.attr('data-original-title')).toBe(
+ Diaspora.I18n.t("contacts.aspect_list_is_not_visible")
+ );
+ });
+
+ it('toggles the lock icon', function() {
+ expect(this.lock_icon.hasClass('lock-open')).toBeTruethy;
+ expect(this.lock_icon.hasClass('lock')).toBeFalsy;
+
+ this.visibility_toggle.trigger('click');
+
+ expect(this.lock_icon.hasClass('lock')).toBeTruethy;
+ expect(this.lock_icon.hasClass('lock-open')).toBeFalsy;
+ });
+ });
+
+ context('show aspect name form', function() {
+ beforeEach(function() {
+ this.button = $('#change_aspect_name');
+ });
+
+ it('shows the form', function() {
+ expect($('#aspect_name_form').css('display')).toBe('none');
+ this.button.trigger('click');
+ expect($('#aspect_name_form').css('display')).not.toBe('none');
+ });
+
+ it('hides the aspect name', function() {
+ expect($('.header > h3').css('display')).not.toBe('none');
+ this.button.trigger('click');
+ expect($('.header > h3').css('display')).toBe('none');
+ });
+ });
+
+ 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");
+ });
+ });
+});
diff --git a/spec/javascripts/app/views/contact_stream_view_spec.js b/spec/javascripts/app/views/contact_stream_view_spec.js
new file mode 100644
index 000000000..955dd2e7b
--- /dev/null
+++ b/spec/javascripts/app/views/contact_stream_view_spec.js
@@ -0,0 +1,77 @@
+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.view = new app.views.ContactStream({
+ collection : this.contacts,
+ el: $('.stream.contacts #contact_stream')
+ });
+
+ 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});
+ expect($.fn.scroll).toHaveBeenCalled();
+ });
+ });
+
+ describe("search", function() {
+ it("filters the contacts", function() {
+ 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");
+ });
+ });
+
+ describe("infScroll", function() {
+ beforeEach(function() {
+ this.view.off("renderContacts");
+ this.fn = jasmine.createSpy();
+ this.view.on("renderContacts", 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() {
+ 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);
+ });
+ });
+});
diff --git a/spec/javascripts/app/views/contact_view_spec.js b/spec/javascripts/app/views/contact_view_spec.js
new file mode 100644
index 000000000..0dee502e9
--- /dev/null
+++ b/spec/javascripts/app/views/contact_view_spec.js
@@ -0,0 +1,136 @@
+describe("app.views.Contact", function(){
+ beforeEach(function() {
+ this.aspect1 = factory.aspect({id: 1});
+ this.aspect2 = factory.aspect({id: 2});
+
+ this.model = new app.models.Contact({
+ person_id: 42,
+ person: { id: 42, name: 'alice' },
+ aspect_memberships: [{id: 23, aspect: this.aspect1}]
+ });
+ this.view = new app.views.Contact({ model: this.model });
+ Diaspora.I18n.load({
+ contacts: {
+ add_contact: "Add contact",
+ remove_contact: "Remove contact",
+ error_add: "Couldn't add <%= name %> to the aspect :(",
+ error_remove: "Couldn't remove <%= name %> from the aspect :("
+ }
+ });
+ });
+
+ context("#presenter", function() {
+ it("contains necessary elements", function() {
+ app.aspect = this.aspect1;
+ expect(this.view.presenter()).toEqual(jasmine.objectContaining({
+ person_id: 42,
+ person: jasmine.objectContaining({id: 42, name: 'alice'}),
+ in_aspect: 'in_aspect'
+ }));
+ });
+ });
+
+ context('add contact to aspect', function() {
+ beforeEach(function() {
+ app.aspect = this.aspect2;
+ this.view.render();
+ this.button = this.view.$el.find('.contact_add-to-aspect');
+ this.contact = this.view.$el.find('.stream_element.contact');
+ this.aspect_membership = {id: 42, aspect: app.aspect.toJSON()};
+ this.response = JSON.stringify(this.aspect_membership);
+ });
+
+ it('sends a correct ajax request', function() {
+ this.button.trigger('click');
+ var obj = $.parseJSON(jasmine.Ajax.requests.mostRecent().params);
+ expect(obj.person_id).toBe(this.model.get('person_id'));
+ expect(obj.aspect_id).toBe(app.aspect.get('id'));
+ });
+
+ it('adds a aspect_membership to the contact', function() {
+ expect(this.model.aspect_memberships.length).toBe(1);
+ $('.contact_add-to-aspect',this.contact).trigger('click');
+ jasmine.Ajax.requests.mostRecent().response({
+ status: 200, // success
+ responseText: this.response
+ });
+ expect(this.model.aspect_memberships.length).toBe(2);
+ });
+
+ it('calls render', function() {
+ spyOn(this.view, 'render');
+ $('.contact_add-to-aspect',this.contact).trigger('click');
+ jasmine.Ajax.requests.mostRecent().response({
+ status: 200, // success
+ responseText: this.response
+ });
+ expect(this.view.render).toHaveBeenCalled();
+ });
+
+
+ it('displays a flash message on errors', function(){
+ $('.contact_add-to-aspect',this.contact).trigger('click');
+ jasmine.Ajax.requests.mostRecent().response({
+ status: 400, // fail
+ });
+ expect($('[id^="flash"]')).toBeErrorFlashMessage(
+ Diaspora.I18n.t(
+ 'contacts.error_add',
+ {name: this.model.get('person').name}
+ )
+ );
+ });
+ });
+
+ context('remove contact from aspect', function() {
+ beforeEach(function() {
+ app.aspect = this.aspect1;
+ this.view.render();
+ this.button = this.view.$el.find('.contact_remove-from-aspect');
+ this.contact = this.view.$el.find('.stream_element.contact');
+ this.aspect_membership = this.model.aspect_memberships.first().toJSON();
+ this.response = JSON.stringify(this.aspect_membership);
+ });
+
+ it('sends a correct ajax request', function() {
+ $('.contact_remove-from-aspect',this.contact).trigger('click');
+ expect(jasmine.Ajax.requests.mostRecent().url).toBe(
+ "/aspect_memberships/"+this.aspect_membership.id
+ );
+ });
+
+ it('removes the aspect_membership from the contact', function() {
+ expect(this.model.aspect_memberships.length).toBe(1);
+ $('.contact_remove-from-aspect',this.contact).trigger('click');
+ jasmine.Ajax.requests.mostRecent().response({
+ status: 200, // success
+ responseText: this.response
+ });
+ expect(this.model.aspect_memberships.length).toBe(0);
+ });
+
+ it('calls render', function() {
+ spyOn(this.view, 'render');
+ $('.contact_remove-from-aspect',this.contact).trigger('click');
+ jasmine.Ajax.requests.mostRecent().response({
+ status: 200, // success
+ responseText: this.response,
+ });
+ expect(this.view.render).toHaveBeenCalled();
+ });
+
+ it('displays a flash message on errors', function(){
+ $('.contact_remove-from-aspect',this.contact).trigger('click');
+ jasmine.Ajax.requests.mostRecent().response({
+ status: 400, // fail
+ });
+ expect($('[id^="flash"]')).toBeErrorFlashMessage(
+ Diaspora.I18n.t(
+ 'contacts.error_remove',
+ {name: this.model.get('person').name}
+ )
+ );
+ });
+ });
+
+});
diff --git a/spec/javascripts/app/views/contacts_view_spec.js b/spec/javascripts/app/views/contacts_view_spec.js
deleted file mode 100644
index b3a9f736c..000000000
--- a/spec/javascripts/app/views/contacts_view_spec.js
+++ /dev/null
@@ -1,240 +0,0 @@
-describe("app.views.Contacts", function(){
- beforeEach(function() {
- spec.loadFixture("aspects_manage");
- this.view = new app.views.Contacts();
- Diaspora.I18n.load({
- contacts: {
- add_contact: "Add contact",
- aspect_list_is_visible: "Contacts in this aspect are able to see each other.",
- aspect_list_is_not_visible: "Contacts in this aspect are not able to see each other.",
- aspect_chat_is_enabled: "Contacts in this aspect are able to chat with you.",
- aspect_chat_is_not_enabled: "Contacts in this aspect are not able to chat with you.",
- remove_contact: "Remove contact",
- error_add: "Couldn't add <%= name %> to the aspect :(",
- error_remove: "Couldn't remove <%= name %> from the aspect :("
- }
- });
- });
-
- context('toggle chat privilege', function() {
- beforeEach(function() {
- this.chat_toggle = $("#chat_privilege_toggle");
- this.chat_icon = $("#chat_privilege_toggle .entypo");
- });
-
- it('updates the title for the tooltip', function() {
- expect(this.chat_icon.attr('data-original-title')).toBe(
- Diaspora.I18n.t("contacts.aspect_chat_is_not_enabled")
- );
- this.chat_toggle.trigger('click');
- expect(this.chat_icon.attr('data-original-title')).toBe(
- Diaspora.I18n.t("contacts.aspect_chat_is_enabled")
- );
- });
-
- it('toggles the chat icon', function() {
- expect(this.chat_icon.hasClass('enabled')).toBeFalsy;
- this.chat_toggle.trigger('click');
- expect(this.chat_icon.hasClass('enabled')).toBeTruethy;
- });
- });
-
- context('toggle contacts visibility', function() {
- beforeEach(function() {
- this.visibility_toggle = $("#contacts_visibility_toggle");
- this.lock_icon = $("#contacts_visibility_toggle .entypo");
- });
-
- it('updates the title for the tooltip', function() {
- expect(this.lock_icon.attr('data-original-title')).toBe(
- Diaspora.I18n.t("contacts.aspect_list_is_visible")
- );
-
- this.visibility_toggle.trigger('click');
-
- expect(this.lock_icon.attr('data-original-title')).toBe(
- Diaspora.I18n.t("contacts.aspect_list_is_not_visible")
- );
- });
-
- it('toggles the lock icon', function() {
- expect(this.lock_icon.hasClass('lock-open')).toBeTruethy;
- expect(this.lock_icon.hasClass('lock')).toBeFalsy;
-
- this.visibility_toggle.trigger('click');
-
- expect(this.lock_icon.hasClass('lock')).toBeTruethy;
- expect(this.lock_icon.hasClass('lock-open')).toBeFalsy;
- });
- });
-
- context('show aspect name form', function() {
- beforeEach(function() {
- this.button = $('#change_aspect_name');
- });
-
- it('shows the form', function() {
- expect($('#aspect_name_form').css('display')).toBe('none');
- this.button.trigger('click');
- expect($('#aspect_name_form').css('display')).not.toBe('none');
- });
-
- it('hides the aspect name', function() {
- expect($('.header > h3').css('display')).not.toBe('none');
- this.button.trigger('click');
- expect($('.header > h3').css('display')).toBe('none');
- });
- });
-
- context('add contact to aspect', function() {
- beforeEach(function() {
- this.contact = $('#people_stream .stream_element').last();
- this.button = this.contact.find('.contact_add-to-aspect');
- this.person_id = this.button.attr('data-person_id');
- this.aspect_id = this.button.attr('data-aspect_id');
- });
-
- it('sends a correct ajax request', function() {
- jasmine.Ajax.install();
- $('.contact_add-to-aspect',this.contact).trigger('click');
- var obj = $.parseJSON(jasmine.Ajax.requests.mostRecent().params);
- expect(obj.person_id).toBe(this.person_id);
- expect(obj.aspect_id).toBe(this.aspect_id);
- });
-
- it('adds a membership id to the contact', function() {
- jasmine.Ajax.install();
- $('.contact_add-to-aspect',this.contact).trigger('click');
- jasmine.Ajax.requests.mostRecent().response({
- status: 200, // success
- responseText: '{ "id": 42 }'
- });
- expect(this.button.attr('data-membership_id')).toBe('42');
- });
-
- it('displays a flash message on errors', function(){
- jasmine.Ajax.install();
- $('.contact_add-to-aspect',this.contact).trigger('click');
- jasmine.Ajax.requests.mostRecent().response({
- status: 400, // fail
- });
- expect($('[id^="flash"]')).toBeErrorFlashMessage(
- Diaspora.I18n.t(
- 'contacts.error_add',
- {name: this.contact.find('.name').text()}
- )
- );
- });
-
- it('changes the appearance of the contact', function() {
- expect(this.button.hasClass('contact_add-to-aspect')).toBeTruethy;
- expect(this.button.hasClass('circled-cross')).toBeTruethy;
- expect(this.contact.hasClass('in_aspect')).toBeTruethy;
- expect(this.button.hasClass('contact_remove-from-aspect')).toBeFalsy;
- expect(this.button.hasClass('circled-plus')).toBeFalsy;
- expect(this.button.attr('data-original-title')).toBe(
- Diaspora.I18n.t('contacts.add_contact')
- );
- jasmine.Ajax.install();
- $('.contact_add-to-aspect',this.contact).trigger('click');
- jasmine.Ajax.requests.mostRecent().response({
- status: 200, // success
- responseText: '{ "id": 42 }'
- });
- expect(this.button.hasClass('contact_add-to-aspect')).toBeFalsy;
- expect(this.button.hasClass('circled-cross')).toBeFalsy;
- expect(this.contact.hasClass('in_aspect')).toBeFalsy;
- expect(this.button.hasClass('contact_remove-from-aspect')).toBeTruethy;
- expect(this.button.hasClass('circled-plus')).toBeTruethy;
- expect(this.button.attr('data-original-title')).toBe(
- Diaspora.I18n.t('contacts.remove_contact')
- );
- });
- });
-
- context('remove contact from aspect', function() {
- beforeEach(function() {
- this.contact = $('#people_stream .stream_element').first();
- this.button = this.contact.find('.contact_remove-from-aspect');
- this.person_id = this.button.attr('data-person_id');
- this.aspect_id = this.button.attr('data-aspect_id');
- this.membership_id = this.button.attr('data-membership_id');
-
- });
-
- it('sends a correct ajax request', function() {
- jasmine.Ajax.install();
- $('.contact_remove-from-aspect',this.contact).trigger('click');
- expect(jasmine.Ajax.requests.mostRecent().url).toBe(
- "/aspect_memberships/"+this.membership_id
- );
- });
-
- it('removes the membership id from the contact', function() {
- jasmine.Ajax.install();
- $('.contact_remove-from-aspect',this.contact).trigger('click');
- jasmine.Ajax.requests.mostRecent().response({
- status: 200, // success
- responseText: '{}'
- });
- expect(this.button.attr('data-membership_id')).toBe(undefined);
- });
-
- it('displays a flash message on errors', function(){
- jasmine.Ajax.install();
- $('.contact_remove-from-aspect',this.contact).trigger('click');
- jasmine.Ajax.requests.mostRecent().response({
- status: 400, // fail
- });
- expect($('[id^="flash"]')).toBeErrorFlashMessage(
- Diaspora.I18n.t(
- 'contacts.error_remove',
- {name: this.contact.find('.name').text()}
- )
- );
- });
-
- it('changes the appearance of the contact', function() {
- expect(this.button.hasClass('contact_add-to-aspect')).toBeFalsy;
- expect(this.button.hasClass('circled-cross')).toBeFalsy;
- expect(this.contact.hasClass('in_aspect')).toBeFalsy;
- expect(this.button.hasClass('contact_remove-from-aspect')).toBeTruethy;
- expect(this.button.hasClass('circled-plus')).toBeTruethy;
- expect(this.button.attr('data-original-title')).toBe(
- Diaspora.I18n.t('contacts.remove_contact')
- );
-
- jasmine.Ajax.install();
- $('.contact_remove-from-aspect',this.contact).trigger('click');
- jasmine.Ajax.requests.mostRecent().response({
- status: 200, // success
- responseText: '{}'
- });
-
- expect(this.button.hasClass('contact_add-to-aspect')).toBeTruethy;
- expect(this.button.hasClass('circled-cross')).toBeTruethy;
- expect(this.contact.hasClass('in_aspect')).toBeTruethy;
- expect(this.button.hasClass('contact_remove-from-aspect')).toBeFalsy;
- expect(this.button.hasClass('circled-plus')).toBeFalsy;
- expect(this.button.attr('data-original-title')).toBe(
- Diaspora.I18n.t('contacts.add_contact')
- );
- });
- });
-
- context('search contact list', function() {
- beforeEach(function() {
- this.searchinput = $('#contact_list_search');
- this.username = $('.stream_element .name').first().text();
- });
-
- it('filters the contact list by name', function() {
- expect($('.stream_element').length).toBeGreaterThan(1);
- this.searchinput.val(this.username);
- this.searchinput.trigger('keyup');
- expect($('.stream_element:visible').length).toBe(1);
- expect($('.stream_element:visible .name').first().text()).toBe(this.username);
- });
- });
-
-});
diff --git a/spec/presenters/aspect_membership_presenter_spec.rb b/spec/presenters/aspect_membership_presenter_spec.rb
new file mode 100644
index 000000000..86d25de66
--- /dev/null
+++ b/spec/presenters/aspect_membership_presenter_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe AspectMembershipPresenter do
+ before do
+ @am = alice.aspects.where(:name => "generic").first.aspect_memberships.first
+ @presenter = AspectMembershipPresenter.new(@am)
+ end
+
+ describe '#base_hash' do
+ it 'works' do
+ expect(@presenter.base_hash).to be_present
+ end
+ end
+
+end
diff --git a/spec/presenters/contact_presenter_spec.rb b/spec/presenters/contact_presenter_spec.rb
new file mode 100644
index 000000000..2a1e0cefa
--- /dev/null
+++ b/spec/presenters/contact_presenter_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+
+describe ContactPresenter do
+ before do
+ @presenter = ContactPresenter.new(alice.contact_for(bob.person))
+ end
+
+ describe '#base_hash' do
+ it 'works' do
+ expect(@presenter.base_hash).to be_present
+ end
+ end
+
+ describe '#full_hash' do
+ it 'works' do
+ expect(@presenter.full_hash).to be_present
+ end
+ end
+
+ describe '#full_hash_with_person' do
+ it 'works' do
+ expect(@presenter.full_hash_with_person).to be_present
+ end
+ end
+
+end