Use typeahead on conversations
This commit is contained in:
parent
5269a0d3c0
commit
f2fdaf1daf
27 changed files with 749 additions and 262 deletions
|
|
@ -79,6 +79,9 @@ app.pages.Contacts = Backbone.View.extend({
|
|||
},
|
||||
|
||||
showMessageModal: function(){
|
||||
$("#conversationModal").on("modal:loaded", function() {
|
||||
new app.views.ConversationsForm({prefill: gon.conversationPrefill});
|
||||
});
|
||||
app.helpers.showModal("#conversationModal");
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -5,40 +5,83 @@ app.views.ConversationsForm = Backbone.View.extend({
|
|||
|
||||
events: {
|
||||
"keydown .conversation-message-text": "keyDown",
|
||||
"click .conversation-recipient-tag .remove": "removeRecipient"
|
||||
},
|
||||
|
||||
initialize: function(opts) {
|
||||
this.contacts = _.has(opts, "contacts") ? opts.contacts : null;
|
||||
this.prefill = [];
|
||||
if (_.has(opts, "prefillName") && _.has(opts, "prefillValue")) {
|
||||
this.prefill = [{name: opts.prefillName, value: opts.prefillValue}];
|
||||
opts = opts || {};
|
||||
this.conversationRecipients = [];
|
||||
|
||||
this.typeaheadElement = this.$el.find("#contacts-search-input");
|
||||
this.contactsIdsListInput = this.$el.find("#contact-ids");
|
||||
this.tagListElement = this.$("#recipients-tag-list");
|
||||
|
||||
this.search = new app.views.SearchBase({
|
||||
el: this.$el.find("#new-conversation"),
|
||||
typeaheadInput: this.typeaheadElement,
|
||||
customSearch: true,
|
||||
autoselect: true,
|
||||
remoteRoute: {url: "/contacts", extraParameters: "mutual=true"}
|
||||
});
|
||||
|
||||
this.bindTypeaheadEvents();
|
||||
|
||||
this.tagListElement.empty();
|
||||
if (opts.prefill) {
|
||||
this.prefill(opts.prefill);
|
||||
}
|
||||
this.prepareAutocomplete(this.contacts);
|
||||
|
||||
this.$("form#new-conversation").on("ajax:success", this.conversationCreateSuccess);
|
||||
this.$("form#new-conversation").on("ajax:error", this.conversationCreateError);
|
||||
},
|
||||
|
||||
prepareAutocomplete: function(data){
|
||||
this.$("#contact-autocomplete").autoSuggest(data, {
|
||||
selectedItemProp: "name",
|
||||
searchObjProps: "name",
|
||||
asHtmlID: "contact_ids",
|
||||
retrieveLimit: 10,
|
||||
minChars: 1,
|
||||
keyDelay: 0,
|
||||
startText: '',
|
||||
emptyText: Diaspora.I18n.t("no_results"),
|
||||
preFill: this.prefill
|
||||
});
|
||||
$("#contact_ids").attr("aria-labelledby", "toLabel").focus();
|
||||
addRecipient: function(person) {
|
||||
this.conversationRecipients.push(person);
|
||||
this.updateContactIdsListInput();
|
||||
/* eslint-disable camelcase */
|
||||
this.tagListElement.append(HandlebarsTemplates.conversation_recipient_tag_tpl(person));
|
||||
/* eslint-enable camelcase */
|
||||
},
|
||||
|
||||
keyDown : function(evt) {
|
||||
if(evt.which === Keycodes.ENTER && evt.ctrlKey) {
|
||||
prefill: function(handles) {
|
||||
handles.forEach(this.addRecipient.bind(this));
|
||||
},
|
||||
|
||||
updateContactIdsListInput: function() {
|
||||
this.contactsIdsListInput.val(_(this.conversationRecipients).pluck("id").join(","));
|
||||
this.search.ignoreDiasporaIds.length = 0;
|
||||
this.conversationRecipients.forEach(this.search.ignorePersonForSuggestions.bind(this.search));
|
||||
},
|
||||
|
||||
bindTypeaheadEvents: function() {
|
||||
this.typeaheadElement.on("typeahead:select", function(evt, person) {
|
||||
this.onSuggestionSelection(person);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
onSuggestionSelection: function(person) {
|
||||
this.addRecipient(person);
|
||||
this.typeaheadElement.typeahead("val", "");
|
||||
},
|
||||
|
||||
keyDown: function(evt) {
|
||||
if (evt.which === Keycodes.ENTER && evt.ctrlKey) {
|
||||
$(evt.target).parents("form").submit();
|
||||
}
|
||||
},
|
||||
|
||||
removeRecipient: function(evt) {
|
||||
var $recipientTagEl = $(evt.target).parents(".conversation-recipient-tag");
|
||||
var diasporaHandle = $recipientTagEl.data("diaspora-handle");
|
||||
|
||||
this.conversationRecipients = this.conversationRecipients.filter(function(person) {
|
||||
return diasporaHandle.localeCompare(person.handle) !== 0;
|
||||
});
|
||||
|
||||
this.updateContactIdsListInput();
|
||||
$recipientTagEl.remove();
|
||||
},
|
||||
|
||||
conversationCreateSuccess: function(evt, data) {
|
||||
app._changeLocation(Routes.conversation(data.id));
|
||||
},
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ app.views.ConversationsInbox = Backbone.View.extend({
|
|||
},
|
||||
|
||||
initialize: function() {
|
||||
new app.views.ConversationsForm({contacts: gon.contacts});
|
||||
new app.views.ConversationsForm();
|
||||
this.setupConversation();
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -79,8 +79,11 @@ app.views.ProfileHeader = app.views.Base.extend({
|
|||
},
|
||||
|
||||
showMessageModal: function(){
|
||||
$("#conversationModal").on("modal:loaded", function() {
|
||||
new app.views.ConversationsForm({prefill: gon.conversationPrefill});
|
||||
});
|
||||
app.helpers.showModal("#conversationModal");
|
||||
},
|
||||
}
|
||||
});
|
||||
// @license-end
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ app.views.PublisherMention = app.views.SearchBase.extend({
|
|||
typeaheadInput: this.typeaheadInput,
|
||||
customSearch: true,
|
||||
autoselect: true,
|
||||
remoteRoute: "/contacts"
|
||||
remoteRoute: {url: "/contacts"}
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -28,9 +28,13 @@ app.views.SearchBase = app.views.Base.extend({
|
|||
};
|
||||
|
||||
// Allow bloodhound to look for remote results if there is a route given in the options
|
||||
if(options.remoteRoute) {
|
||||
if (options.remoteRoute && options.remoteRoute.url) {
|
||||
var extraParameters = "";
|
||||
if (options.remoteRoute.extraParameters) {
|
||||
extraParameters += "&" + options.remoteRoute.extraParameters;
|
||||
}
|
||||
bloodhoundOptions.remote = {
|
||||
url: options.remoteRoute + ".json?q=%QUERY",
|
||||
url: options.remoteRoute.url + ".json?q=%QUERY" + extraParameters,
|
||||
wildcard: "%QUERY",
|
||||
transform: this.transformBloodhoundResponse.bind(this)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ app.views.Search = app.views.SearchBase.extend({
|
|||
this.searchInput = this.$("#q");
|
||||
app.views.SearchBase.prototype.initialize.call(this, {
|
||||
typeaheadInput: this.searchInput,
|
||||
remoteRoute: this.$el.attr("action"),
|
||||
remoteRoute: {url: this.$el.attr("action")},
|
||||
suggestionLink: true
|
||||
});
|
||||
this.searchInput.on("typeahead:select", this.suggestionSelected);
|
||||
|
|
|
|||
|
|
@ -183,10 +183,60 @@
|
|||
}
|
||||
// scss-lint:enable SelectorDepth
|
||||
|
||||
#new_conversation_pane {
|
||||
.new-conversation {
|
||||
ul.as-selections { width: 100% !important; }
|
||||
|
||||
input#contact_ids { box-shadow: none; }
|
||||
|
||||
label { font-weight: bold; }
|
||||
|
||||
.twitter-typeahead,
|
||||
.tt-menu {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.recipients-tag-list {
|
||||
.conversation-recipient-tag {
|
||||
background-color: $brand-primary;
|
||||
border-radius: $btn-border-radius-base;
|
||||
display: inline-flex;
|
||||
margin: 0 2px $form-group-margin-bottom;
|
||||
padding: 8px;
|
||||
|
||||
&:first-child { margin-left: 0; }
|
||||
|
||||
&:last-child { margin-right: 0; }
|
||||
|
||||
div {
|
||||
align-self: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
height: 40px;
|
||||
margin-right: 8px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.name-and-handle {
|
||||
color: $white;
|
||||
margin-right: 8px;
|
||||
text-align: left;
|
||||
|
||||
.diaspora-id { font-size: $font-size-small; }
|
||||
}
|
||||
|
||||
.entypo-circled-cross {
|
||||
color: $white;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
|
||||
&:hover { color: $light-grey; }
|
||||
}
|
||||
}
|
||||
|
||||
.new-conversation.form-horizontal .form-group:last-of-type { margin-bottom: 0; }
|
||||
|
|
|
|||
|
|
@ -61,3 +61,5 @@
|
|||
.subject { padding: 0 10px; }
|
||||
|
||||
.message-count, .unread-message-count { margin: 10px 2px; }
|
||||
|
||||
.new-conversation .as-selections { background-color: transparent; }
|
||||
|
|
|
|||
12
app/assets/templates/conversation_recipient_tag_tpl.jst.hbs
Normal file
12
app/assets/templates/conversation_recipient_tag_tpl.jst.hbs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<div class="conversation-recipient-tag clearfix" data-diaspora-handle="{{ handle }}">
|
||||
<div href="{{ url }}">
|
||||
<img src="{{ avatar }}" class="avatar img-responsive center-block">
|
||||
</div>
|
||||
<div class="pull-left clearfix name-and-handle" href="{{ url }}">
|
||||
<div class="name">{{ name }}</div>
|
||||
<div class="diaspora-id">{{ handle }}</div>
|
||||
</div>
|
||||
<div class="remove pull-right clearfix">
|
||||
<i class="entypo-circled-cross"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -17,7 +17,8 @@ class ContactsController < ApplicationController
|
|||
# Used for mentions in the publisher and pagination on the contacts page
|
||||
format.json {
|
||||
@people = if params[:q].present?
|
||||
Person.search(params[:q], current_user, only_contacts: true).limit(15)
|
||||
mutual = params[:mutual].present? && params[:mutual]
|
||||
Person.search(params[:q], current_user, only_contacts: true, mutual: mutual).limit(15)
|
||||
else
|
||||
set_up_contacts_json
|
||||
end
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ class ConversationsController < ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
contact_ids = params[:contact_ids]
|
||||
|
||||
# Can't split nil
|
||||
if contact_ids
|
||||
contact_ids = contact_ids.split(',') if contact_ids.is_a? String
|
||||
person_ids = current_user.contacts.where(id: contact_ids).pluck(:person_id)
|
||||
# Contacts autocomplete does not work the same way on mobile and desktop
|
||||
# Mobile returns contact ids array while desktop returns person id
|
||||
# This will have to be removed when mobile autocomplete is ported to Typeahead
|
||||
recipients_param, column = [%i(contact_ids id), %i(person_ids person_id)].find {|param, _| params[param].present? }
|
||||
if recipients_param
|
||||
person_ids = current_user.contacts.where(column => params[recipients_param].split(",")).pluck(:person_id)
|
||||
end
|
||||
|
||||
opts = params.require(:conversation).permit(:subject)
|
||||
|
|
@ -91,17 +91,23 @@ class ConversationsController < ApplicationController
|
|||
return
|
||||
end
|
||||
|
||||
@contacts_json = contacts_data.to_json
|
||||
@contact_ids = ""
|
||||
|
||||
if params[:contact_id]
|
||||
@contact_ids = current_user.contacts.find(params[:contact_id]).id
|
||||
elsif params[:aspect_id]
|
||||
@contact_ids = current_user.aspects.find(params[:aspect_id]).contacts.map{|c| c.id}.join(',')
|
||||
end
|
||||
if session[:mobile_view] == true && request.format.html?
|
||||
@contacts_json = contacts_data.to_json
|
||||
|
||||
@contact_ids = if params[:contact_id]
|
||||
current_user.contacts.find(params[:contact_id]).id
|
||||
elsif params[:aspect_id]
|
||||
current_user.aspects.find(params[:aspect_id]).contacts.pluck(:id).join(",")
|
||||
end
|
||||
|
||||
render :layout => true
|
||||
else
|
||||
if params[:contact_id]
|
||||
gon.push conversation_prefill: [current_user.contacts.find(params[:contact_id]).person.as_json]
|
||||
elsif params[:aspect_id]
|
||||
gon.push conversation_prefill: current_user.aspects
|
||||
.find(params[:aspect_id]).contacts.map {|c| c.person.as_json }
|
||||
end
|
||||
render :layout => false
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ class Person < ActiveRecord::Base
|
|||
[where_clause, q_tokens]
|
||||
end
|
||||
|
||||
def self.search(search_str, user, only_contacts: false)
|
||||
def self.search(search_str, user, only_contacts: false, mutual: false)
|
||||
search_str.strip!
|
||||
return none if search_str.blank? || search_str.size < 2
|
||||
|
||||
|
|
@ -159,6 +159,8 @@ class Person < ActiveRecord::Base
|
|||
).searchable(user)
|
||||
end
|
||||
|
||||
query = query.where(contacts: {sharing: true, receiving: true}) if mutual
|
||||
|
||||
query.where(closed_account: false)
|
||||
.where(sql, *tokens)
|
||||
.includes(:profile)
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
.spinner
|
||||
|
||||
-if @aspect
|
||||
#new_conversation_pane
|
||||
.conversations-form-container#new_conversation_pane
|
||||
= render 'shared/modal',
|
||||
:path => new_conversation_path(:aspect_id => @aspect.id, :name => @aspect.name, :modal => true),
|
||||
:title => t('conversations.index.new_conversation'),
|
||||
|
|
|
|||
|
|
@ -2,9 +2,13 @@
|
|||
= form_for Conversation.new, html: {id: "new-conversation",
|
||||
class: "new-conversation form-horizontal"}, remote: true do |conversation|
|
||||
.form-group
|
||||
%label#toLabel{for: "contact_ids"}
|
||||
= t(".to")
|
||||
= text_field_tag "contact_autocomplete", nil, id: "contact-autocomplete", class: "form-control"
|
||||
%label#to-label{for: "contacts-search-input"}= t(".to")
|
||||
.recipients-tag-list.clearfix#recipients-tag-list
|
||||
= text_field_tag "contact_autocomplete", nil, id: "contacts-search-input", class: "form-control"
|
||||
- unless defined?(mobile) && mobile
|
||||
= text_field_tag "person_ids", nil, id: "contact-ids", type: "hidden",
|
||||
aria: {labelledby: "to-label"}
|
||||
|
||||
.form-group
|
||||
%label#subject-label{for: "conversation-subject"}
|
||||
= t(".subject")
|
||||
|
|
@ -14,12 +18,14 @@
|
|||
aria: {labelledby: "subject-label"},
|
||||
value: "",
|
||||
placeholder: t("conversations.new.subject_default")
|
||||
|
||||
.form-group
|
||||
%label.sr-only#message-label{for: "new-message-text"} = t(".message")
|
||||
%label.sr-only#message-label{for: "new-message-text"}= t(".message")
|
||||
= text_area_tag "conversation[text]", "",
|
||||
rows: 5,
|
||||
id: "new-message-text",
|
||||
class: "conversation-message-text input-block-level form-control",
|
||||
aria: {labelledby: "message-label"}
|
||||
|
||||
.form-group
|
||||
= conversation.submit t(".send"), "data-disable-with" => t(".sending"), :class => "btn btn-primary pull-right"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,2 @@
|
|||
:javascript
|
||||
$(document).ready(function () {
|
||||
var data = $.parseJSON( "#{escape_javascript(@contacts_json)}" );
|
||||
new app.views.ConversationsForm({
|
||||
el: $("form#new-conversation").parent(),
|
||||
contacts: data,
|
||||
prefillName: "#{h params[:name]}",
|
||||
prefillValue: "#{@contact_ids}"
|
||||
});
|
||||
});
|
||||
|
||||
= include_gon camel_case: true
|
||||
= render 'conversations/new'
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
:plain
|
||||
$(document).ready(function () {
|
||||
var data = $.parseJSON( "#{escape_javascript(@contacts_json).html_safe}" ),
|
||||
autocompleteInput = $("#contact-autocomplete");
|
||||
autocompleteInput = $("#contacts-search-input");
|
||||
|
||||
autocompleteInput.autoSuggest(data, {
|
||||
selectedItemProp: "name",
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
retrieveLimit: 10,
|
||||
minChars: 1,
|
||||
keyDelay: 0,
|
||||
startText: '',
|
||||
startText: "",
|
||||
emptyText: "#{t("no_results")}",
|
||||
preFill: [{name : "#{h params[:name]}",
|
||||
value : "#{@contact_ids}"}]
|
||||
|
|
@ -27,6 +27,6 @@
|
|||
#flash-messages
|
||||
.container-fluid.row
|
||||
%h3
|
||||
= t('conversations.index.new_conversation')
|
||||
= t("conversations.index.new_conversation")
|
||||
|
||||
= render 'conversations/new'
|
||||
= render "conversations/new", mobile: true
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
id: 'mentionModal'
|
||||
|
||||
-if @contact
|
||||
#new_conversation_pane
|
||||
.conversations-form-container#new_conversation_pane
|
||||
= render 'shared/modal',
|
||||
path: new_conversation_path(:contact_id => @contact.id, name: @contact.person.name, modal: true),
|
||||
title: t('conversations.index.new_conversation'),
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ end
|
|||
Then /^I send a message with subject "([^"]*)" and text "([^"]*)" to "([^"]*)"$/ do |subject, text, person|
|
||||
step %(I am on the conversations page)
|
||||
within("#new-conversation", match: :first) do
|
||||
step %(I fill in "contact_autocomplete" with "#{person}")
|
||||
step %(I press the first ".as-result-item" within ".as-results")
|
||||
find("#contacts-search-input").native.send_key(person.to_s)
|
||||
step %(I press the first ".tt-suggestion" within ".twitter-typeahead")
|
||||
step %(I fill in "conversation-subject" with "#{subject}")
|
||||
step %(I fill in "new-message-text" with "#{text}")
|
||||
step %(I press "Send")
|
||||
|
|
@ -26,8 +26,8 @@ end
|
|||
Then /^I send a message with subject "([^"]*)" and text "([^"]*)" to "([^"]*)" using keyboard shortcuts$/ do |subject, text, person|
|
||||
step %(I am on the conversations page)
|
||||
within("#new-conversation", match: :first) do
|
||||
step %(I fill in "contact_autocomplete" with "#{person}")
|
||||
step %(I press the first ".as-result-item" within ".as-results")
|
||||
find("#contacts-search-input").native.send_key(person.to_s)
|
||||
step %(I press the first ".tt-suggestion" within ".twitter-typeahead")
|
||||
step %(I fill in "conversation-subject" with "#{subject}")
|
||||
step %(I fill in "new-message-text" with "#{text}")
|
||||
find("#new-message-text").native.send_key %i(Ctrl Return)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ describe ContactsController, :type => :controller do
|
|||
@person1 = FactoryGirl.create(:person)
|
||||
bob.share_with(@person1, bob.aspects.first)
|
||||
@person2 = FactoryGirl.create(:person)
|
||||
@person3 = FactoryGirl.create(:person)
|
||||
bob.contacts.create(person: @person3, aspects: [bob.aspects.first], receiving: true, sharing: true)
|
||||
end
|
||||
|
||||
it "succeeds" do
|
||||
|
|
@ -53,6 +55,15 @@ describe ContactsController, :type => :controller do
|
|||
get :index, q: @person2.first_name, format: "json"
|
||||
expect(response.body).to eq([].to_json)
|
||||
end
|
||||
|
||||
it "only returns mutual contacts when mutual parameter is true" do
|
||||
get :index, q: @person1.first_name, mutual: true, format: "json"
|
||||
expect(response.body).to eq([].to_json)
|
||||
get :index, q: @person2.first_name, mutual: true, format: "json"
|
||||
expect(response.body).to eq([].to_json)
|
||||
get :index, q: @person3.first_name, mutual: true, format: "json"
|
||||
expect(response.body).to eq([@person3].to_json)
|
||||
end
|
||||
end
|
||||
|
||||
context "for pagination on the contacts page" do
|
||||
|
|
|
|||
|
|
@ -16,48 +16,57 @@ describe ConversationsController, :type => :controller do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#new modal' do
|
||||
it 'succeeds' do
|
||||
get :new, :modal => true
|
||||
expect(response).to be_success
|
||||
end
|
||||
describe "#new modal" do
|
||||
context "desktop and mobile" do
|
||||
it "succeeds" do
|
||||
get :new, modal: true
|
||||
expect(response).to be_success
|
||||
end
|
||||
|
||||
it "assigns a json list of contacts that are sharing with the person" do
|
||||
sharing_user = FactoryGirl.create(:user_with_aspect)
|
||||
sharing_user.share_with(alice.person, sharing_user.aspects.first)
|
||||
get :new, :modal => true
|
||||
expect(assigns(:contacts_json)).to include(alice.contacts.where(sharing: true, receiving: true).first.person.name)
|
||||
alice.contacts << Contact.new(:person_id => eve.person.id, :user_id => alice.id, :sharing => false, :receiving => true)
|
||||
expect(assigns(:contacts_json)).not_to include(alice.contacts.where(sharing: false).first.person.name)
|
||||
expect(assigns(:contacts_json)).not_to include(alice.contacts.where(receiving: false).first.person.name)
|
||||
end
|
||||
it "assigns a contact if passed a contact id" do
|
||||
get :new, contact_id: alice.contacts.first.id, modal: true
|
||||
expect(controller.gon.conversation_prefill).to eq([alice.contacts.first.person.as_json])
|
||||
end
|
||||
|
||||
it "assigns a contact if passed a contact id" do
|
||||
get :new, :contact_id => alice.contacts.first.id, :modal => true
|
||||
expect(assigns(:contact_ids)).to eq(alice.contacts.first.id)
|
||||
end
|
||||
it "assigns a set of contacts if passed an aspect id" do
|
||||
get :new, aspect_id: alice.aspects.first.id, modal: true
|
||||
expect(controller.gon.conversation_prefill).to eq(alice.aspects.first.contacts.map {|c| c.person.as_json })
|
||||
end
|
||||
|
||||
it "assigns a set of contacts if passed an aspect id" do
|
||||
get :new, :aspect_id => alice.aspects.first.id, :modal => true
|
||||
expect(assigns(:contact_ids)).to eq(alice.aspects.first.contacts.map(&:id).join(','))
|
||||
end
|
||||
|
||||
it "does not allow XSS via the name parameter" do
|
||||
["</script><script>alert(1);</script>",
|
||||
'"}]});alert(1);(function f() {var foo = [{b:"'].each do |xss|
|
||||
get :new, :modal => true, name: xss
|
||||
expect(response.body).not_to include xss
|
||||
it "does not allow XSS via the name parameter" do
|
||||
["</script><script>alert(1);</script>",
|
||||
'"}]});alert(1);(function f() {var foo = [{b:"'].each do |xss|
|
||||
get :new, modal: true, name: xss
|
||||
expect(response.body).not_to include xss
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "does not allow XSS via the profile name" do
|
||||
xss = "<script>alert(0);</script>"
|
||||
contact = alice.contacts.first
|
||||
contact.person.profile.update_attribute(:first_name, xss)
|
||||
get :new, :modal => true
|
||||
json = JSON.parse(assigns(:contacts_json)).first
|
||||
expect(json['value'].to_s).to eq(contact.id.to_s)
|
||||
expect(json['name']).to_not include(xss)
|
||||
context "mobile" do
|
||||
before do
|
||||
controller.session[:mobile_view] = true
|
||||
end
|
||||
|
||||
it "assigns a json list of contacts that are sharing with the person" do
|
||||
sharing_user = FactoryGirl.create(:user_with_aspect)
|
||||
sharing_user.share_with(alice.person, sharing_user.aspects.first)
|
||||
get :new, modal: true
|
||||
expect(assigns(:contacts_json))
|
||||
.to include(alice.contacts.where(sharing: true, receiving: true).first.person.name)
|
||||
alice.contacts << Contact.new(person_id: eve.person.id, user_id: alice.id, sharing: false, receiving: true)
|
||||
expect(assigns(:contacts_json)).not_to include(alice.contacts.where(sharing: false).first.person.name)
|
||||
expect(assigns(:contacts_json)).not_to include(alice.contacts.where(receiving: false).first.person.name)
|
||||
end
|
||||
|
||||
it "does not allow XSS via the profile name" do
|
||||
xss = "<script>alert(0);</script>"
|
||||
contact = alice.contacts.first
|
||||
contact.person.profile.update_attribute(:first_name, xss)
|
||||
get :new, modal: true
|
||||
json = JSON.parse(assigns(:contacts_json)).first
|
||||
expect(json["value"].to_s).to eq(contact.id.to_s)
|
||||
expect(json["name"]).to_not include(xss)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -115,203 +124,323 @@ describe ConversationsController, :type => :controller do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#create' do
|
||||
context 'with a valid conversation' do
|
||||
before do
|
||||
@hash = {
|
||||
:format => :js,
|
||||
:conversation => {
|
||||
:subject => "secret stuff",
|
||||
:text => 'text debug'
|
||||
},
|
||||
:contact_ids => [alice.contacts.first.id]
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates a conversation' do
|
||||
expect {
|
||||
post :create, @hash
|
||||
}.to change(Conversation, :count).by(1)
|
||||
end
|
||||
|
||||
it 'creates a message' do
|
||||
expect {
|
||||
post :create, @hash
|
||||
}.to change(Message, :count).by(1)
|
||||
end
|
||||
|
||||
it "responds with the conversation id as JSON" do
|
||||
post :create, @hash
|
||||
expect(response).to be_success
|
||||
expect(JSON.parse(response.body)["id"]).to eq(Conversation.first.id)
|
||||
end
|
||||
|
||||
it 'sets the author to the current_user' do
|
||||
@hash[:author] = FactoryGirl.create(:user)
|
||||
post :create, @hash
|
||||
expect(Message.first.author).to eq(alice.person)
|
||||
expect(Conversation.first.author).to eq(alice.person)
|
||||
end
|
||||
|
||||
it 'dispatches the conversation' do
|
||||
cnv = Conversation.create(
|
||||
{
|
||||
:author => alice.person,
|
||||
:participant_ids => [alice.contacts.first.person.id, alice.person.id],
|
||||
:subject => 'not spam',
|
||||
:messages_attributes => [ {:author => alice.person, :text => 'cool stuff'} ]
|
||||
describe "#create" do
|
||||
context "desktop" do
|
||||
context "with a valid conversation" do
|
||||
before do
|
||||
@hash = {
|
||||
format: :js,
|
||||
conversation: {subject: "secret stuff", text: "text debug"},
|
||||
person_ids: [alice.contacts.first.person.id]
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
expect(Diaspora::Federation::Dispatcher).to receive(:defer_dispatch)
|
||||
post :create, @hash
|
||||
end
|
||||
end
|
||||
it "creates a conversation" do
|
||||
expect { post :create, @hash }.to change(Conversation, :count).by(1)
|
||||
end
|
||||
|
||||
context 'with empty subject' do
|
||||
before do
|
||||
@hash = {
|
||||
:format => :js,
|
||||
:conversation => {
|
||||
:subject => ' ',
|
||||
:text => 'text debug'
|
||||
},
|
||||
:contact_ids => [alice.contacts.first.id]
|
||||
}
|
||||
end
|
||||
it "creates a message" do
|
||||
expect { post :create, @hash }.to change(Message, :count).by(1)
|
||||
end
|
||||
|
||||
it 'creates a conversation' do
|
||||
expect {
|
||||
it "responds with the conversation id as JSON" do
|
||||
post :create, @hash
|
||||
}.to change(Conversation, :count).by(1)
|
||||
end
|
||||
expect(response).to be_success
|
||||
expect(JSON.parse(response.body)["id"]).to eq(Conversation.first.id)
|
||||
end
|
||||
|
||||
it 'creates a message' do
|
||||
expect {
|
||||
it "sets the author to the current_user" do
|
||||
@hash[:author] = FactoryGirl.create(:user)
|
||||
post :create, @hash
|
||||
}.to change(Message, :count).by(1)
|
||||
expect(Message.first.author).to eq(alice.person)
|
||||
expect(Conversation.first.author).to eq(alice.person)
|
||||
end
|
||||
|
||||
it "dispatches the conversation" do
|
||||
Conversation.create(author: alice.person, participant_ids: [alice.contacts.first.person.id, alice.person.id],
|
||||
subject: "not spam", messages_attributes: [{author: alice.person, text: "cool stuff"}])
|
||||
|
||||
expect(Diaspora::Federation::Dispatcher).to receive(:defer_dispatch)
|
||||
post :create, @hash
|
||||
end
|
||||
end
|
||||
|
||||
it "responds with the conversation id as JSON" do
|
||||
post :create, @hash
|
||||
expect(response).to be_success
|
||||
expect(JSON.parse(response.body)["id"]).to eq(Conversation.first.id)
|
||||
context "with empty subject" do
|
||||
before do
|
||||
@hash = {
|
||||
format: :js,
|
||||
conversation: {subject: " ", text: "text debug"},
|
||||
person_ids: [alice.contacts.first.person.id]
|
||||
}
|
||||
end
|
||||
|
||||
it "creates a conversation" do
|
||||
expect { post :create, @hash }.to change(Conversation, :count).by(1)
|
||||
end
|
||||
|
||||
it "creates a message" do
|
||||
expect { post :create, @hash }.to change(Message, :count).by(1)
|
||||
end
|
||||
|
||||
it "responds with the conversation id as JSON" do
|
||||
post :create, @hash
|
||||
expect(response).to be_success
|
||||
expect(JSON.parse(response.body)["id"]).to eq(Conversation.first.id)
|
||||
end
|
||||
end
|
||||
|
||||
context "with empty text" do
|
||||
before do
|
||||
@hash = {
|
||||
format: :js,
|
||||
conversation: {subject: "secret stuff", text: " "},
|
||||
person_ids: [alice.contacts.first.person.id]
|
||||
}
|
||||
end
|
||||
|
||||
it "does not create a conversation" do
|
||||
count = Conversation.count
|
||||
post :create, @hash
|
||||
expect(Conversation.count).to eq(count)
|
||||
end
|
||||
|
||||
it "does not create a message" do
|
||||
count = Message.count
|
||||
post :create, @hash
|
||||
expect(Message.count).to eq(count)
|
||||
end
|
||||
|
||||
it "responds with an error message" do
|
||||
post :create, @hash
|
||||
expect(response).not_to be_success
|
||||
expect(response.body).to eq(I18n.t("conversations.create.fail"))
|
||||
end
|
||||
end
|
||||
|
||||
context "with empty contact" do
|
||||
before do
|
||||
@hash = {
|
||||
format: :js,
|
||||
conversation: {subject: "secret stuff", text: "text debug"},
|
||||
person_ids: " "
|
||||
}
|
||||
end
|
||||
|
||||
it "does not create a conversation" do
|
||||
count = Conversation.count
|
||||
post :create, @hash
|
||||
expect(Conversation.count).to eq(count)
|
||||
end
|
||||
|
||||
it "does not create a message" do
|
||||
count = Message.count
|
||||
post :create, @hash
|
||||
expect(Message.count).to eq(count)
|
||||
end
|
||||
|
||||
it "responds with an error message" do
|
||||
post :create, @hash
|
||||
expect(response).not_to be_success
|
||||
expect(response.body).to eq(I18n.t("javascripts.conversation.create.no_recipient"))
|
||||
end
|
||||
end
|
||||
|
||||
context "with nil contact" do
|
||||
before do
|
||||
@hash = {
|
||||
format: :js,
|
||||
conversation: {subject: "secret stuff", text: "text debug"},
|
||||
person_ids: nil
|
||||
}
|
||||
end
|
||||
|
||||
it "does not create a conversation" do
|
||||
count = Conversation.count
|
||||
post :create, @hash
|
||||
expect(Conversation.count).to eq(count)
|
||||
end
|
||||
|
||||
it "does not create a message" do
|
||||
count = Message.count
|
||||
post :create, @hash
|
||||
expect(Message.count).to eq(count)
|
||||
end
|
||||
|
||||
it "responds with an error message" do
|
||||
post :create, @hash
|
||||
expect(response).not_to be_success
|
||||
expect(response.body).to eq(I18n.t("javascripts.conversation.create.no_recipient"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with empty text' do
|
||||
context "mobile" do
|
||||
before do
|
||||
@hash = {
|
||||
:format => :js,
|
||||
:conversation => {
|
||||
:subject => 'secret stuff',
|
||||
:text => ' '
|
||||
},
|
||||
:contact_ids => [alice.contacts.first.id]
|
||||
}
|
||||
controller.session[:mobile_view] = true
|
||||
end
|
||||
|
||||
it 'does not create a conversation' do
|
||||
count = Conversation.count
|
||||
post :create, @hash
|
||||
expect(Conversation.count).to eq(count)
|
||||
context "with a valid conversation" do
|
||||
before do
|
||||
@hash = {
|
||||
format: :js,
|
||||
conversation: {subject: "secret stuff", text: "text debug"},
|
||||
contact_ids: [alice.contacts.first.id]
|
||||
}
|
||||
end
|
||||
|
||||
it "creates a conversation" do
|
||||
expect { post :create, @hash }.to change(Conversation, :count).by(1)
|
||||
end
|
||||
|
||||
it "creates a message" do
|
||||
expect { post :create, @hash }.to change(Message, :count).by(1)
|
||||
end
|
||||
|
||||
it "responds with the conversation id as JSON" do
|
||||
post :create, @hash
|
||||
expect(response).to be_success
|
||||
expect(JSON.parse(response.body)["id"]).to eq(Conversation.first.id)
|
||||
end
|
||||
|
||||
it "sets the author to the current_user" do
|
||||
@hash[:author] = FactoryGirl.create(:user)
|
||||
post :create, @hash
|
||||
expect(Message.first.author).to eq(alice.person)
|
||||
expect(Conversation.first.author).to eq(alice.person)
|
||||
end
|
||||
|
||||
it "dispatches the conversation" do
|
||||
Conversation.create(author: alice.person, participant_ids: [alice.contacts.first.person.id, alice.person.id],
|
||||
subject: "not spam", messages_attributes: [{author: alice.person, text: "cool stuff"}])
|
||||
|
||||
expect(Diaspora::Federation::Dispatcher).to receive(:defer_dispatch)
|
||||
post :create, @hash
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not create a message' do
|
||||
count = Message.count
|
||||
post :create, @hash
|
||||
expect(Message.count).to eq(count)
|
||||
context "with empty subject" do
|
||||
before do
|
||||
@hash = {
|
||||
format: :js,
|
||||
conversation: {subject: " ", text: "text debug"},
|
||||
contact_ids: [alice.contacts.first.id]
|
||||
}
|
||||
end
|
||||
|
||||
it "creates a conversation" do
|
||||
expect { post :create, @hash }.to change(Conversation, :count).by(1)
|
||||
end
|
||||
|
||||
it "creates a message" do
|
||||
expect { post :create, @hash }.to change(Message, :count).by(1)
|
||||
end
|
||||
|
||||
it "responds with the conversation id as JSON" do
|
||||
post :create, @hash
|
||||
expect(response).to be_success
|
||||
expect(JSON.parse(response.body)["id"]).to eq(Conversation.first.id)
|
||||
end
|
||||
end
|
||||
|
||||
it "responds with an error message" do
|
||||
post :create, @hash
|
||||
expect(response).not_to be_success
|
||||
expect(response.body).to eq(I18n.t("conversations.create.fail"))
|
||||
end
|
||||
end
|
||||
context "with empty text" do
|
||||
before do
|
||||
@hash = {
|
||||
format: :js,
|
||||
conversation: {subject: "secret stuff", text: " "},
|
||||
contact_ids: [alice.contacts.first.id]
|
||||
}
|
||||
end
|
||||
|
||||
context 'with empty contact' do
|
||||
before do
|
||||
@hash = {
|
||||
:format => :js,
|
||||
:conversation => {
|
||||
:subject => 'secret stuff',
|
||||
:text => 'text debug'
|
||||
},
|
||||
:contact_ids => ' '
|
||||
}
|
||||
it "does not create a conversation" do
|
||||
count = Conversation.count
|
||||
post :create, @hash
|
||||
expect(Conversation.count).to eq(count)
|
||||
end
|
||||
|
||||
it "does not create a message" do
|
||||
count = Message.count
|
||||
post :create, @hash
|
||||
expect(Message.count).to eq(count)
|
||||
end
|
||||
|
||||
it "responds with an error message" do
|
||||
post :create, @hash
|
||||
expect(response).not_to be_success
|
||||
expect(response.body).to eq(I18n.t("conversations.create.fail"))
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not create a conversation' do
|
||||
count = Conversation.count
|
||||
post :create, @hash
|
||||
expect(Conversation.count).to eq(count)
|
||||
context "with empty contact" do
|
||||
before do
|
||||
@hash = {
|
||||
format: :js,
|
||||
conversation: {subject: "secret stuff", text: "text debug"},
|
||||
contact_ids: " "
|
||||
}
|
||||
end
|
||||
|
||||
it "does not create a conversation" do
|
||||
count = Conversation.count
|
||||
post :create, @hash
|
||||
expect(Conversation.count).to eq(count)
|
||||
end
|
||||
|
||||
it "does not create a message" do
|
||||
count = Message.count
|
||||
post :create, @hash
|
||||
expect(Message.count).to eq(count)
|
||||
end
|
||||
|
||||
it "responds with an error message" do
|
||||
post :create, @hash
|
||||
expect(response).not_to be_success
|
||||
expect(response.body).to eq(I18n.t("javascripts.conversation.create.no_recipient"))
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not create a message' do
|
||||
count = Message.count
|
||||
post :create, @hash
|
||||
expect(Message.count).to eq(count)
|
||||
end
|
||||
context "with nil contact" do
|
||||
before do
|
||||
@hash = {
|
||||
format: :js,
|
||||
conversation: {subject: "secret stuff", text: "text debug"},
|
||||
contact_ids: nil
|
||||
}
|
||||
end
|
||||
|
||||
it "responds with an error message" do
|
||||
post :create, @hash
|
||||
expect(response).not_to be_success
|
||||
expect(response.body).to eq(I18n.t("javascripts.conversation.create.no_recipient"))
|
||||
end
|
||||
end
|
||||
it "does not create a conversation" do
|
||||
count = Conversation.count
|
||||
post :create, @hash
|
||||
expect(Conversation.count).to eq(count)
|
||||
end
|
||||
|
||||
context 'with nil contact' do
|
||||
before do
|
||||
@hash = {
|
||||
:format => :js,
|
||||
:conversation => {
|
||||
:subject => 'secret stuff',
|
||||
:text => 'text debug'
|
||||
},
|
||||
:contact_ids => nil
|
||||
}
|
||||
end
|
||||
|
||||
it 'does not create a conversation' do
|
||||
count = Conversation.count
|
||||
post :create, @hash
|
||||
expect(Conversation.count).to eq(count)
|
||||
end
|
||||
|
||||
it 'does not create a message' do
|
||||
count = Message.count
|
||||
post :create, @hash
|
||||
expect(Message.count).to eq(count)
|
||||
end
|
||||
|
||||
it "responds with an error message" do
|
||||
post :create, @hash
|
||||
expect(response).not_to be_success
|
||||
expect(response.body).to eq(I18n.t("javascripts.conversation.create.no_recipient"))
|
||||
it "does not create a message" do
|
||||
count = Message.count
|
||||
post :create, @hash
|
||||
expect(Message.count).to eq(count)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#show' do
|
||||
describe "#show" do
|
||||
before do
|
||||
hash = {
|
||||
:author => alice.person,
|
||||
:participant_ids => [alice.contacts.first.person.id, alice.person.id],
|
||||
:subject => 'not spam',
|
||||
:messages_attributes => [ {:author => alice.person, :text => 'cool stuff'} ]
|
||||
author: alice.person,
|
||||
participant_ids: [alice.contacts.first.person.id, alice.person.id],
|
||||
subject: "not spam",
|
||||
messages_attributes: [{author: alice.person, text: "cool stuff"}]
|
||||
}
|
||||
@conversation = Conversation.create(hash)
|
||||
end
|
||||
|
||||
it 'succeeds with json' do
|
||||
it "succeeds with json" do
|
||||
get :show, :id => @conversation.id, :format => :json
|
||||
expect(response).to be_success
|
||||
expect(assigns[:conversation]).to eq(@conversation)
|
||||
expect(response.body).to include @conversation.guid
|
||||
end
|
||||
|
||||
it 'redirects to index' do
|
||||
it "redirects to index" do
|
||||
get :show, :id => @conversation.id
|
||||
expect(response).to redirect_to(conversations_path(:conversation_id => @conversation.id))
|
||||
end
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ describe ConversationsController, :type => :controller do
|
|||
|
||||
get :index, :conversation_id => @conv1.id
|
||||
save_fixture(html_for("body"), "conversations_read")
|
||||
|
||||
get :new, modal: true
|
||||
save_fixture(response.body, "conversations_modal")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -277,4 +277,31 @@ describe("app.pages.Contacts", function(){
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("showMessageModal", function() {
|
||||
beforeEach(function() {
|
||||
$("body").append("<div id='conversationModal'/>").append(spec.readFixture("conversations_modal"));
|
||||
});
|
||||
|
||||
it("calls app.helpers.showModal", function() {
|
||||
spyOn(app.helpers, "showModal");
|
||||
this.view.showMessageModal();
|
||||
expect(app.helpers.showModal);
|
||||
});
|
||||
|
||||
it("app.views.ConversationsForm with correct parameters when modal is loaded", function() {
|
||||
gon.conversationPrefill = [
|
||||
{id: 1, name: "diaspora user", handle: "diaspora-user@pod.tld"},
|
||||
{id: 2, name: "other diaspora user", handle: "other-diaspora-user@pod.tld"},
|
||||
{id: 3, name: "user@pod.tld", handle: "user@pod.tld"}
|
||||
];
|
||||
|
||||
spyOn(app.views.ConversationsForm.prototype, "initialize");
|
||||
this.view.showMessageModal();
|
||||
$("#conversationModal").trigger("modal:loaded");
|
||||
expect($("#conversationModal").length).toBe(1);
|
||||
expect(app.views.ConversationsForm.prototype.initialize)
|
||||
.toHaveBeenCalledWith({prefill: gon.conversationPrefill});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,12 +1,133 @@
|
|||
describe("app.views.ConversationsForm", function() {
|
||||
beforeEach(function() {
|
||||
spec.loadFixture("conversations_read");
|
||||
this.target = new app.views.ConversationsForm();
|
||||
});
|
||||
|
||||
describe("initialize", function() {
|
||||
it("initializes the conversation participants list", function() {
|
||||
expect(this.target.conversationRecipients).toEqual([]);
|
||||
});
|
||||
|
||||
it("initializes the search view", function() {
|
||||
spyOn(app.views.SearchBase.prototype, "initialize");
|
||||
this.target.initialize();
|
||||
expect(app.views.SearchBase.prototype.initialize).toHaveBeenCalled();
|
||||
expect(app.views.SearchBase.prototype.initialize.calls.argsFor(0)[0].customSearch).toBe(true);
|
||||
expect(app.views.SearchBase.prototype.initialize.calls.argsFor(0)[0].autoselect).toBe(true);
|
||||
expect(app.views.SearchBase.prototype.initialize.calls.argsFor(0)[0].remoteRoute).toEqual({
|
||||
url: "/contacts",
|
||||
extraParameters: "mutual=true"
|
||||
});
|
||||
expect(this.target.search).toBeDefined();
|
||||
});
|
||||
|
||||
it("calls bindTypeaheadEvents", function() {
|
||||
spyOn(app.views.ConversationsForm.prototype, "bindTypeaheadEvents");
|
||||
this.target.initialize();
|
||||
expect(app.views.ConversationsForm.prototype.bindTypeaheadEvents).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calls prefill correctly", function() {
|
||||
spyOn(app.views.ConversationsForm.prototype, "prefill");
|
||||
this.target.initialize();
|
||||
expect(app.views.ConversationsForm.prototype.prefill).not.toHaveBeenCalled();
|
||||
this.target.initialize({prefill: {}});
|
||||
expect(app.views.ConversationsForm.prototype.prefill).toHaveBeenCalledWith({});
|
||||
});
|
||||
});
|
||||
|
||||
describe("addRecipient", function() {
|
||||
beforeEach(function() {
|
||||
$("#conversation-new").removeClass("hidden");
|
||||
$("#conversation-show").addClass("hidden");
|
||||
});
|
||||
|
||||
it("add the participant", function() {
|
||||
expect(this.target.conversationRecipients).toEqual([]);
|
||||
this.target.addRecipient({name: "diaspora user", handle: "diaspora-user@pod.tld"});
|
||||
expect(this.target.conversationRecipients).toEqual([{name: "diaspora user", handle: "diaspora-user@pod.tld"}]);
|
||||
});
|
||||
|
||||
it("call updateContactIdsListInput", function() {
|
||||
spyOn(app.views.ConversationsForm.prototype, "updateContactIdsListInput");
|
||||
this.target.addRecipient({name: "diaspora user", handle: "diaspora-user@pod.tld"});
|
||||
expect(app.views.ConversationsForm.prototype.updateContactIdsListInput).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("adds a recipient tag", function() {
|
||||
expect($(".conversation-recipient-tag").length).toBe(0);
|
||||
this.target.addRecipient({name: "diaspora user", handle: "diaspora-user@pod.tld"});
|
||||
expect($(".conversation-recipient-tag").length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("prefill", function() {
|
||||
beforeEach(function() {
|
||||
this.prefills = [{name: "diaspora user"}, {name: "other diaspora user"}, {name: "user"}];
|
||||
});
|
||||
|
||||
it("call addRecipient for each prefilled participant", function() {
|
||||
spyOn(app.views.ConversationsForm.prototype, "addRecipient");
|
||||
this.target.prefill(this.prefills);
|
||||
expect(app.views.ConversationsForm.prototype.addRecipient).toHaveBeenCalledTimes(this.prefills.length);
|
||||
var allArgsFlattened = app.views.ConversationsForm.prototype.addRecipient.calls.allArgs().map(function(arg) {
|
||||
return arg[0];
|
||||
});
|
||||
expect(allArgsFlattened).toEqual(this.prefills);
|
||||
});
|
||||
});
|
||||
|
||||
describe("updateContactIdsListInput", function() {
|
||||
beforeEach(function() {
|
||||
this.target.conversationRecipients.push({id: 1, name: "diaspora user", handle: "diaspora-user@pod.tld"});
|
||||
this.target.conversationRecipients
|
||||
.push({id: 2, name: "other diaspora user", handle: "other-diaspora-user@pod.tld"});
|
||||
this.target.conversationRecipients.push({id: 3, name: "user@pod.tld", handle: "user@pod.tld"});
|
||||
});
|
||||
|
||||
it("updates hidden input value", function() {
|
||||
this.target.updateContactIdsListInput();
|
||||
expect(this.target.contactsIdsListInput.val()).toBe("1,2,3");
|
||||
});
|
||||
|
||||
it("calls app.views.SearchBase.ignorePersonForSuggestions() for each participant", function() {
|
||||
spyOn(app.views.SearchBase.prototype, "ignorePersonForSuggestions");
|
||||
this.target.updateContactIdsListInput();
|
||||
expect(app.views.SearchBase.prototype.ignorePersonForSuggestions).toHaveBeenCalledTimes(3);
|
||||
expect(app.views.SearchBase.prototype.ignorePersonForSuggestions.calls.argsFor(0)[0])
|
||||
.toEqual({id: 1, name: "diaspora user", handle: "diaspora-user@pod.tld"});
|
||||
expect(app.views.SearchBase.prototype.ignorePersonForSuggestions.calls.argsFor(1)[0])
|
||||
.toEqual({id: 2, name: "other diaspora user", handle: "other-diaspora-user@pod.tld"});
|
||||
expect(app.views.SearchBase.prototype.ignorePersonForSuggestions.calls.argsFor(2)[0])
|
||||
.toEqual({id: 3, name: "user@pod.tld", handle: "user@pod.tld"});
|
||||
});
|
||||
});
|
||||
|
||||
describe("bindTypeaheadEvents", function() {
|
||||
it("calls onSuggestionSelection() when clicking on a result", function() {
|
||||
spyOn(app.views.ConversationsForm.prototype, "onSuggestionSelection");
|
||||
var event = $.Event("typeahead:select");
|
||||
var person = {name: "diaspora user"};
|
||||
this.target.typeaheadElement.trigger(event, [person]);
|
||||
expect(app.views.ConversationsForm.prototype.onSuggestionSelection).toHaveBeenCalledWith(person);
|
||||
});
|
||||
});
|
||||
|
||||
describe("onSuggestionSelection", function() {
|
||||
it("calls addRecipient, updateContactIdsListInput and $.fn.typeahead", function() {
|
||||
spyOn(app.views.ConversationsForm.prototype, "addRecipient");
|
||||
spyOn($.fn, "typeahead");
|
||||
var person = {name: "diaspora user"};
|
||||
this.target.onSuggestionSelection(person);
|
||||
expect(app.views.ConversationsForm.prototype.addRecipient).toHaveBeenCalledWith(person);
|
||||
expect($.fn.typeahead).toHaveBeenCalledWith("val", "");
|
||||
});
|
||||
});
|
||||
|
||||
describe("keyDown", function() {
|
||||
beforeEach(function() {
|
||||
this.submitCallback = jasmine.createSpy().and.returnValue(false);
|
||||
new app.views.ConversationsForm();
|
||||
});
|
||||
|
||||
context("on new message form", function() {
|
||||
|
|
@ -52,6 +173,54 @@ describe("app.views.ConversationsForm", function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe("removeRecipient", function() {
|
||||
beforeEach(function() {
|
||||
this.target.addRecipient({id: 1, name: "diaspora user", handle: "diaspora-user@pod.tld"});
|
||||
this.target.addRecipient({id: 2, name: "other diaspora user", handle: "other-diaspora-user@pod.tld"});
|
||||
this.target.addRecipient({id: 3, name: "user@pod.tld", handle: "user@pod.tld"});
|
||||
});
|
||||
|
||||
it("removes the user from conversation recipients when clicking the tag's remove button", function() {
|
||||
expect(this.target.conversationRecipients).toEqual([
|
||||
{id: 1, name: "diaspora user", handle: "diaspora-user@pod.tld"},
|
||||
{id: 2, name: "other diaspora user", handle: "other-diaspora-user@pod.tld"},
|
||||
{id: 3, name: "user@pod.tld", handle: "user@pod.tld"}
|
||||
]);
|
||||
|
||||
$("[data-diaspora-handle='diaspora-user@pod.tld'] .remove").click();
|
||||
|
||||
expect(this.target.conversationRecipients).toEqual([
|
||||
{id: 2, name: "other diaspora user", handle: "other-diaspora-user@pod.tld"},
|
||||
{id: 3, name: "user@pod.tld", handle: "user@pod.tld"}
|
||||
]);
|
||||
|
||||
$("[data-diaspora-handle='other-diaspora-user@pod.tld'] .remove").click();
|
||||
|
||||
expect(this.target.conversationRecipients).toEqual([
|
||||
{id: 3, name: "user@pod.tld", handle: "user@pod.tld"}
|
||||
]);
|
||||
|
||||
$("[data-diaspora-handle='user@pod.tld'] .remove").click();
|
||||
|
||||
expect(this.target.conversationRecipients).toEqual([]);
|
||||
});
|
||||
|
||||
it("removes the tag element when clicking the tag's remove button", function() {
|
||||
expect($("[data-diaspora-handle='diaspora-user@pod.tld']").length).toBe(1);
|
||||
$("[data-diaspora-handle='diaspora-user@pod.tld'] .remove").click();
|
||||
expect($("[data-diaspora-handle='other-diaspora-user@pod.tld']").length).toBe(1);
|
||||
$("[data-diaspora-handle='other-diaspora-user@pod.tld'] .remove").click();
|
||||
expect($("[data-diaspora-handle='user@pod.tld']").length).toBe(1);
|
||||
$("[data-diaspora-handle='user@pod.tld'] .remove").click();
|
||||
});
|
||||
|
||||
it("calls updateContactIdsListInput", function() {
|
||||
spyOn(app.views.ConversationsForm.prototype, "updateContactIdsListInput");
|
||||
$("[data-diaspora-handle='diaspora-user@pod.tld'] .remove").click();
|
||||
expect(app.views.ConversationsForm.prototype.updateContactIdsListInput).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("conversationCreateSuccess", function() {
|
||||
it("is called when there was a successful ajax request for the conversation form", function() {
|
||||
spyOn(app.views.ConversationsForm.prototype, "conversationCreateSuccess");
|
||||
|
|
|
|||
|
|
@ -30,4 +30,30 @@ describe("app.views.ProfileHeader", function() {
|
|||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("showMessageModal", function() {
|
||||
beforeEach(function() {
|
||||
$("body").append("<div id='conversationModal'/>").append(spec.readFixture("conversations_modal"));
|
||||
});
|
||||
|
||||
it("calls app.helpers.showModal", function() {
|
||||
spyOn(app.helpers, "showModal");
|
||||
this.view.showMessageModal();
|
||||
expect(app.helpers.showModal);
|
||||
});
|
||||
|
||||
it("app.views.ConversationsForm with correct parameterswhen modal is loaded", function() {
|
||||
gon.conversationPrefill = [
|
||||
{id: 1, name: "diaspora user", handle: "diaspora-user@pod.tld"},
|
||||
{id: 2, name: "other diaspora user", handle: "other-diaspora-user@pod.tld"},
|
||||
{id: 3, name: "user@pod.tld", handle: "user@pod.tld"}
|
||||
];
|
||||
|
||||
spyOn(app.views.ConversationsForm.prototype, "initialize");
|
||||
this.view.showMessageModal();
|
||||
$("#conversationModal").trigger("modal:loaded");
|
||||
expect(app.views.ConversationsForm.prototype.initialize)
|
||||
.toHaveBeenCalledWith({prefill: gon.conversationPrefill});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ describe("app.views.PublisherMention", function() {
|
|||
expect(call.args[0].typeaheadInput.selector).toBe("#publisher .typeahead-mention-box");
|
||||
expect(call.args[0].customSearch).toBeTruthy();
|
||||
expect(call.args[0].autoselect).toBeTruthy();
|
||||
expect(call.args[0].remoteRoute).toBe("/contacts");
|
||||
expect(call.args[0].remoteRoute).toEqual({url: "/contacts"});
|
||||
});
|
||||
|
||||
it("calls bindTypeaheadEvents", function() {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ describe("app.views.Search", function() {
|
|||
this.view = new app.views.Search({el: "#search_people_form"});
|
||||
var call = app.views.SearchBase.prototype.initialize.calls.mostRecent();
|
||||
expect(call.args[0].typeaheadInput.selector).toBe("#search_people_form #q");
|
||||
expect(call.args[0].remoteRoute).toBe("/search");
|
||||
expect(call.args[0].remoteRoute).toEqual({url: "/search"});
|
||||
});
|
||||
|
||||
it("binds typeahead:select", function() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue