message_train 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.rspec +1 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +7 -0
  7. data/Gemfile +51 -0
  8. data/LICENSE.txt +20 -0
  9. data/README.rdoc +83 -0
  10. data/Rakefile +71 -0
  11. data/VERSION +1 -0
  12. data/app/assets/javascripts/message_train.js +138 -0
  13. data/app/assets/stylesheets/message_train.scss +107 -0
  14. data/app/controllers/message_train/application_controller.rb +64 -0
  15. data/app/controllers/message_train/boxes_controller.rb +37 -0
  16. data/app/controllers/message_train/conversations_controller.rb +38 -0
  17. data/app/controllers/message_train/messages_controller.rb +74 -0
  18. data/app/controllers/message_train/participants_controller.rb +36 -0
  19. data/app/helpers/message_train/application_helper.rb +4 -0
  20. data/app/helpers/message_train/boxes_helper.rb +30 -0
  21. data/app/helpers/message_train/conversations_helper.rb +73 -0
  22. data/app/helpers/message_train/messages_helper.rb +58 -0
  23. data/app/models/message_train/attachment.rb +17 -0
  24. data/app/models/message_train/box.rb +268 -0
  25. data/app/models/message_train/conversation.rb +101 -0
  26. data/app/models/message_train/ignore.rb +18 -0
  27. data/app/models/message_train/message.rb +146 -0
  28. data/app/models/message_train/receipt.rb +70 -0
  29. data/app/views/application/404.html.haml +3 -0
  30. data/app/views/message_train/application/results.json.jbuilder +8 -0
  31. data/app/views/message_train/boxes/_dropdown_list.html.haml +5 -0
  32. data/app/views/message_train/boxes/_list_item.html.haml +5 -0
  33. data/app/views/message_train/boxes/_widget.html.haml +6 -0
  34. data/app/views/message_train/boxes/show.html.haml +47 -0
  35. data/app/views/message_train/conversations/_conversation.html.haml +9 -0
  36. data/app/views/message_train/conversations/_deleted_toggle.html.haml +1 -0
  37. data/app/views/message_train/conversations/_ignored_toggle.html.haml +4 -0
  38. data/app/views/message_train/conversations/_read_toggle.html.haml +4 -0
  39. data/app/views/message_train/conversations/_toggle.html.haml +1 -0
  40. data/app/views/message_train/conversations/_trashed_toggle.html.haml +4 -0
  41. data/app/views/message_train/conversations/show.html.haml +11 -0
  42. data/app/views/message_train/conversations/show.json.jbuilder +2 -0
  43. data/app/views/message_train/messages/_deleted_toggle.html.haml +1 -0
  44. data/app/views/message_train/messages/_form.html.haml +8 -0
  45. data/app/views/message_train/messages/_message.html.haml +35 -0
  46. data/app/views/message_train/messages/_read_toggle.html.haml +4 -0
  47. data/app/views/message_train/messages/_toggle.html.haml +1 -0
  48. data/app/views/message_train/messages/_trashed_toggle.html.haml +4 -0
  49. data/app/views/message_train/messages/edit.html.haml +2 -0
  50. data/app/views/message_train/messages/new.html.haml +2 -0
  51. data/app/views/message_train/messages/show.json.jbuilder +2 -0
  52. data/app/views/message_train/participants/_field.html.haml +4 -0
  53. data/app/views/message_train/participants/_participant.json.jbuilder +5 -0
  54. data/app/views/message_train/participants/index.json.jbuilder +3 -0
  55. data/app/views/message_train/participants/show.json.jbuilder +1 -0
  56. data/bin/rails +12 -0
  57. data/config/initializers/bootstrap_pager_config.rb +10 -0
  58. data/config/initializers/date_time.rb +5 -0
  59. data/config/locales/en.yml +50 -0
  60. data/config/routes.rb +11 -0
  61. data/db/migrate/20150721145319_create_message_train_conversations.rb +9 -0
  62. data/db/migrate/20150721160322_create_message_train_messages.rb +14 -0
  63. data/db/migrate/20150721161144_create_message_train_attachments.rb +10 -0
  64. data/db/migrate/20150721161940_create_message_train_receipts.rb +16 -0
  65. data/db/migrate/20150721163838_create_message_train_ignores.rb +11 -0
  66. data/lib/generators/message_train/install/install_generator.rb +30 -0
  67. data/lib/generators/message_train/install/templates/initializer.rb +10 -0
  68. data/lib/generators/message_train/utils.rb +16 -0
  69. data/lib/message_train/configuration.rb +35 -0
  70. data/lib/message_train/engine.rb +19 -0
  71. data/lib/message_train/localization.rb +31 -0
  72. data/lib/message_train/mixin.rb +83 -0
  73. data/lib/message_train/version.rb +7 -0
  74. data/lib/message_train.rb +20 -0
  75. data/lib/tasks/message_train_tasks.rake +0 -0
  76. data/message_train.gemspec +325 -0
  77. data/night_train.gemspec +274 -0
  78. data/spec/controllers/message_train/boxes_controller_spec.rb +82 -0
  79. data/spec/controllers/message_train/conversations_controller_spec.rb +86 -0
  80. data/spec/controllers/message_train/messages_controller_spec.rb +210 -0
  81. data/spec/controllers/message_train/participants_controller_spec.rb +35 -0
  82. data/spec/dummy/.rspec +2 -0
  83. data/spec/dummy/Rakefile +6 -0
  84. data/spec/dummy/app/assets/files/message_train/attachments/1917-Boys_Race_Above-Wiki.jpg +0 -0
  85. data/spec/dummy/app/assets/files/message_train/attachments/ACD_Silverbarn's_Mayumi.jpg +0 -0
  86. data/spec/dummy/app/assets/files/message_train/attachments/ACD_obedience.jpg +0 -0
  87. data/spec/dummy/app/assets/files/message_train/attachments/A_Man_and_His_Dog.jpg +0 -0
  88. data/spec/dummy/app/assets/files/message_train/attachments/Alaskan_Malamute_agility_a-frame.jpg +0 -0
  89. data/spec/dummy/app/assets/files/message_train/attachments/American_water_spaniel_02.jpg +0 -0
  90. data/spec/dummy/app/assets/files/message_train/attachments/Aport_konkurs_rybnik.jpg +0 -0
  91. data/spec/dummy/app/assets/files/message_train/attachments/Askerhytta.jpg +0 -0
  92. data/spec/dummy/app/assets/files/message_train/attachments/Bambisj.jpg +0 -0
  93. data/spec/dummy/app/assets/files/message_train/attachments/Cattle_dog_skating.jpg +0 -0
  94. data/spec/dummy/app/assets/files/message_train/attachments/Dog's_Olympics_training_ground_-_geograph.org.uk_-_660324.jpg +0 -0
  95. data/spec/dummy/app/assets/files/message_train/attachments/DogShowHierarchy.png +0 -0
  96. data/spec/dummy/app/assets/files/message_train/attachments/Dog_weight_pull.jpg +0 -0
  97. data/spec/dummy/app/assets/files/message_train/attachments/Draghuntpostcard.jpg +0 -0
  98. data/spec/dummy/app/assets/files/message_train/attachments/Esham_51.JPG +0 -0
  99. data/spec/dummy/app/assets/files/message_train/attachments/FastCourseExample.jpg +0 -0
  100. data/spec/dummy/app/assets/files/message_train/attachments/Fotos_joel_141.JPG +0 -0
  101. data/spec/dummy/app/assets/files/message_train/attachments/Galgo_Spanish_male_brindle.jpg +0 -0
  102. data/spec/dummy/app/assets/files/message_train/attachments/Golden-retriever-carlos-bei-der-dummyarbeit.jpg +0 -0
  103. data/spec/dummy/app/assets/files/message_train/attachments/Habavk.jpg +0 -0
  104. data/spec/dummy/app/assets/files/message_train/attachments/Haie_rci.svg +1714 -0
  105. data/spec/dummy/app/assets/files/message_train/attachments/Hanging_18.jpg +0 -0
  106. data/spec/dummy/app/assets/files/message_train/attachments/Hundewa.jpg +0 -0
  107. data/spec/dummy/app/assets/files/message_train/attachments/K9ProSports_AttackOnHandler.jpg +0 -0
  108. data/spec/dummy/app/assets/files/message_train/attachments/K9ProSports_heel.jpg +0 -0
  109. data/spec/dummy/app/assets/files/message_train/attachments/K9Pro_Civil_agitation.JPG +0 -0
  110. data/spec/dummy/app/assets/files/message_train/attachments/TrainingSchutzhundRetrieveOverWall.jpg +0 -0
  111. data/spec/dummy/app/assets/files/message_train/attachments/example.pdf +0 -0
  112. data/spec/dummy/app/assets/files/message_train/attachments/letterlegal5.doc +0 -0
  113. data/spec/dummy/app/assets/files/message_train/attachments/pdf-sample.pdf +0 -0
  114. data/spec/dummy/app/assets/files/message_train/attachments/sample.pdf +0 -0
  115. data/spec/dummy/app/assets/files/message_train/attachments/tips.doc +0 -0
  116. data/spec/dummy/app/assets/files/message_train/attachments/wd-spectools-word-sample-04.doc +0 -0
  117. data/spec/dummy/app/assets/images/logo.svg +140 -0
  118. data/spec/dummy/app/assets/javascripts/application.js +17 -0
  119. data/spec/dummy/app/assets/stylesheets/application.css.scss +35 -0
  120. data/spec/dummy/app/controllers/application_controller.rb +14 -0
  121. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  122. data/spec/dummy/app/mailers/.keep +0 -0
  123. data/spec/dummy/app/models/.keep +0 -0
  124. data/spec/dummy/app/models/group.rb +19 -0
  125. data/spec/dummy/app/models/role.rb +10 -0
  126. data/spec/dummy/app/models/user.rb +23 -0
  127. data/spec/dummy/app/views/layouts/_top_navigation.html.haml +5 -0
  128. data/spec/dummy/app/views/layouts/application.html.haml +29 -0
  129. data/spec/dummy/bin/bundle +3 -0
  130. data/spec/dummy/bin/rails +4 -0
  131. data/spec/dummy/bin/rake +4 -0
  132. data/spec/dummy/bin/setup +29 -0
  133. data/spec/dummy/config/application.rb +40 -0
  134. data/spec/dummy/config/boot.rb +5 -0
  135. data/spec/dummy/config/database.yml +25 -0
  136. data/spec/dummy/config/environment.rb +5 -0
  137. data/spec/dummy/config/environments/development.rb +41 -0
  138. data/spec/dummy/config/environments/production.rb +79 -0
  139. data/spec/dummy/config/environments/test.rb +42 -0
  140. data/spec/dummy/config/initializers/assets.rb +11 -0
  141. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  142. data/spec/dummy/config/initializers/bootstrap_leather.rb +7 -0
  143. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  144. data/spec/dummy/config/initializers/devise.rb +265 -0
  145. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  146. data/spec/dummy/config/initializers/inflections.rb +16 -0
  147. data/spec/dummy/config/initializers/message_train.rb +10 -0
  148. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  149. data/spec/dummy/config/initializers/paperclip.rb +2 -0
  150. data/spec/dummy/config/initializers/rolify.rb +7 -0
  151. data/spec/dummy/config/initializers/session_store.rb +3 -0
  152. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  153. data/spec/dummy/config/locales/devise.en.yml +60 -0
  154. data/spec/dummy/config/locales/en.yml +23 -0
  155. data/spec/dummy/config/routes.rb +5 -0
  156. data/spec/dummy/config/secrets.yml +22 -0
  157. data/spec/dummy/config/unused/temporary_i18n_debugging.rb +12 -0
  158. data/spec/dummy/config.ru +4 -0
  159. data/spec/dummy/db/migrate/20150721140013_devise_create_users.rb +42 -0
  160. data/spec/dummy/db/migrate/20150721141009_rolify_create_roles.rb +19 -0
  161. data/spec/dummy/db/migrate/20150721141128_create_groups.rb +11 -0
  162. data/spec/dummy/db/migrate/20150721150307_add_display_name_column_to_users.rb +6 -0
  163. data/spec/dummy/db/migrate/20150724142846_create_message_train_conversations.night_train.rb +10 -0
  164. data/spec/dummy/db/migrate/20150724142847_create_message_train_messages.night_train.rb +15 -0
  165. data/spec/dummy/db/migrate/20150724142848_create_message_train_attachments.night_train.rb +11 -0
  166. data/spec/dummy/db/migrate/20150724142849_create_message_train_receipts.night_train.rb +17 -0
  167. data/spec/dummy/db/migrate/20150724142850_create_message_train_ignores.night_train.rb +12 -0
  168. data/spec/dummy/db/schema.rb +121 -0
  169. data/spec/dummy/db/seeds/conversations.seeds.rb +86 -0
  170. data/spec/dummy/db/seeds/development/conversations.seeds.rb +9 -0
  171. data/spec/dummy/db/seeds/groups.seeds.rb +9 -0
  172. data/spec/dummy/db/seeds/test/conversations.seeds.rb +9 -0
  173. data/spec/dummy/db/seeds/users.seeds.rb +15 -0
  174. data/spec/dummy/db/seeds.rb +0 -0
  175. data/spec/dummy/db/test.sqlite3 +0 -0
  176. data/spec/dummy/lib/assets/.keep +0 -0
  177. data/spec/dummy/log/.keep +0 -0
  178. data/spec/dummy/public/404.html +67 -0
  179. data/spec/dummy/public/422.html +67 -0
  180. data/spec/dummy/public/500.html +66 -0
  181. data/spec/dummy/public/capybara.html +193 -0
  182. data/spec/dummy/public/favicon.ico +0 -0
  183. data/spec/factories/attachment.rb +6 -0
  184. data/spec/factories/group.rb +20 -0
  185. data/spec/factories/message.rb +68 -0
  186. data/spec/factories/user.rb +8 -0
  187. data/spec/features/boxes_spec.rb +50 -0
  188. data/spec/features/conversations_spec.rb +36 -0
  189. data/spec/features/messages_spec.rb +53 -0
  190. data/spec/helpers/message_train/boxes_helper_spec.rb +44 -0
  191. data/spec/message_train_spec.rb +7 -0
  192. data/spec/models/group_spec.rb +23 -0
  193. data/spec/models/message_train/attachment_spec.rb +12 -0
  194. data/spec/models/message_train/box_spec.rb +195 -0
  195. data/spec/models/message_train/conversation_spec.rb +98 -0
  196. data/spec/models/message_train/ignore_spec.rb +27 -0
  197. data/spec/models/message_train/message_spec.rb +83 -0
  198. data/spec/models/message_train/receipt_spec.rb +73 -0
  199. data/spec/models/user_spec.rb +68 -0
  200. data/spec/rails_helper.rb +83 -0
  201. data/spec/spec_helper.rb +92 -0
  202. data/spec/support/controller_behaviors.rb +88 -0
  203. data/spec/support/controller_macros.rb +13 -0
  204. data/spec/support/conversations.rb +10 -0
  205. data/spec/support/factory_girl.rb +3 -0
  206. data/spec/support/feature_behaviors.rb +62 -0
  207. data/spec/support/groups.rb +3 -0
  208. data/spec/support/loaded_site.rb +5 -0
  209. data/spec/support/shared_connection.rb +13 -0
  210. data/spec/support/users.rb +6 -0
  211. data/spec/support/utilities.rb +8 -0
  212. data/vendor/assets/javascripts/bootstrap-tags.js +623 -0
  213. data/vendor/assets/stylesheets/bootstrap-tags.css +78 -0
  214. metadata +608 -0
@@ -0,0 +1,20 @@
1
+ FactoryGirl.define do
2
+ factory :group do
3
+ title { Faker::Lorem.sentence }
4
+ description { Faker::Lorem.paragraphs([*1..5].sample).join("\n\n") }
5
+ transient do
6
+ owner { User.order("RANDOM()").first }
7
+ member_count 5
8
+ end
9
+ # the after(:create) yields two values; the user instance itself and the
10
+ # evaluator, which stores all values from the factory, including transient
11
+ # attributes; `create_list`'s second argument is the number of records
12
+ # to create and we make sure the user is associated properly to the post
13
+ after(:create) do |group, evaluator|
14
+ evaluator.owner.add_role(:owner, group)
15
+ User.where('id != ?', evaluator.owner.id).order("RANDOM()").limit(evaluator.member_count).find_each do |user|
16
+ user.add_role(:member, group)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,68 @@
1
+ FactoryGirl.define do
2
+ factory :message, class: 'MessageTrain::Message' do
3
+ sender {
4
+ User
5
+ .order('RANDOM()')
6
+ .first
7
+ }
8
+ recipients_to_save { { 'users' =>
9
+ User
10
+ .where('id != ?', sender.id)
11
+ .order('RANDOM()')
12
+ .limit([*1..5].sample)
13
+ .collect { |x| x.slug }
14
+ .join(', ')
15
+ } }
16
+ subject { Faker::Lorem.sentence }
17
+ body { Faker::Lorem.paragraphs([*1..5].sample).join("\n\n") }
18
+ transient do
19
+ generate_attachment? { [*1..100].sample >= 80 }
20
+ generate_response? { [*1..100].sample >= 50 }
21
+ generate_ignore? { [*1..100].sample >= 80 }
22
+ end
23
+
24
+ # the after(:create) yields two values; the user instance itself and the
25
+ # evaluator, which stores all values from the factory, including transient
26
+ # attributes; `create_list`'s second argument is the number of records
27
+ # to create and we make sure the user is associated properly to the post
28
+ after(:create) do |message, evaluator|
29
+ if evaluator.generate_attachment?
30
+ FactoryGirl.create(:attachment, message: message)
31
+ end
32
+ if message.recipients_to_save['users'].present?
33
+ participants = (message.recipients_to_save['users'].split(',') + [message.sender.slug]).collect{ |x| x.strip }
34
+ if evaluator.generate_ignore?
35
+ ignorer = User.friendly.find(participants.sample)
36
+ participants = participants - [ignorer.slug]
37
+ message.conversation.set_ignored(ignorer)
38
+ end
39
+ if evaluator.generate_response? && participants.count > 1
40
+ response_sender = User.friendly.find(participants.sample)
41
+ response_recipients = participants - [response_sender.slug]
42
+ FactoryGirl.create(
43
+ :message,
44
+ sender: response_sender,
45
+ recipients_to_save: { 'users' => response_recipients.join(', ') },
46
+ conversation: message.conversation
47
+ )
48
+ end
49
+ end
50
+ end
51
+
52
+ factory :message_from_random_sender do
53
+ sender {
54
+ recipient_user_ids = recipients_to_save['users'].split(',').collect{ |x| User.friendly.find(x.strip).id }
55
+ User
56
+ .where('NOT (id IN (?))', recipient_user_ids )
57
+ .order('RANDOM()')
58
+ .first
59
+ }
60
+ end
61
+
62
+ factory :simple_message do
63
+ generate_ignore? false
64
+ generate_response? false
65
+ generate_attachment? false
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,8 @@
1
+ FactoryGirl.define do
2
+ factory :user do
3
+ display_name { Faker::Name.name }
4
+ email { "#{display_name.gsub(' ', '.').downcase}.#{rand(1000).to_s}@example.com" }
5
+ password "password"
6
+ password_confirmation "password"
7
+ end
8
+ end
@@ -0,0 +1,50 @@
1
+ require 'rails_helper'
2
+ RSpec.feature 'Boxes' do
3
+ include_context 'loaded site'
4
+
5
+ before do
6
+ login_as first_user
7
+ end
8
+ describe 'Showing' do
9
+ describe 'at /box/in' do
10
+ before do
11
+ visit '/box/in'
12
+ end
13
+ it_behaves_like 'a bootstrap page listing a collection of items', MessageTrain::Conversation, plural_title: 'Inbox', minimum: 2
14
+ end
15
+ end
16
+ describe 'Marking', js: true do
17
+ describe 'at /box/in' do
18
+ describe 'without checking anything' do
19
+ before do
20
+ visit '/box/in'
21
+ click_button 'Mark'
22
+ click_link 'mark-read'
23
+ end
24
+ it_behaves_like 'a bootstrap page with an alert', 'warning', 'Nothing to do'
25
+ end
26
+ describe 'after checking a box' do
27
+ describe 'Marking Read' do
28
+ before do
29
+ visit '/box/in'
30
+ click_link 'Last'
31
+ check "objects_conversations_#{unread_conversation.id.to_s}"
32
+ click_button 'Mark'
33
+ click_link 'mark-read'
34
+ end
35
+ it_behaves_like 'a bootstrap page with an alert', 'info', 'Update successful'
36
+ end
37
+ describe 'Marking Ignored' do
38
+ before do
39
+ visit '/box/in'
40
+ click_link 'Last'
41
+ check "objects_conversations_#{unread_conversation.id.to_s}"
42
+ click_button 'Mark'
43
+ click_link 'mark-ignored'
44
+ end
45
+ it_behaves_like 'a bootstrap page with an alert', 'info', 'Update successful'
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,36 @@
1
+ require 'rails_helper'
2
+ RSpec.feature 'Conversations' do
3
+ include_context 'loaded site'
4
+
5
+ before do
6
+ login_as first_user
7
+ end
8
+ describe 'Showing' do
9
+ describe 'at /box/in/conversations/:id' do
10
+ before do
11
+ visit '/box/in/conversations/'+unread_conversation.id.to_s
12
+ end
13
+ it_behaves_like 'a bootstrap page showing an item', MessageTrain::Conversation, 'Unread Conversation'
14
+ end
15
+ end
16
+ describe 'Marking', js: true do
17
+ describe 'at /box/in/conversations/:id' do
18
+ describe 'Marking Read' do
19
+ before do
20
+ visit '/box/in/conversations/'+unread_conversation.id.to_s
21
+ click_link "mark_read_#{unread_conversation.messages.first.id.to_s}"
22
+ end
23
+ it_behaves_like 'a bootstrap page with an alert', 'info', 'Update successful'
24
+ end
25
+ describe 'Marking Ignored' do
26
+ before do
27
+ visit '/box/in/conversations/'+unread_conversation.id.to_s
28
+ accept_confirm do
29
+ click_link 'Mark as Ignored'
30
+ end
31
+ end
32
+ it_behaves_like 'a bootstrap page with an alert', 'info', 'Update successful'
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,53 @@
1
+ require 'rails_helper'
2
+ RSpec.feature 'Messages' do
3
+ include_context 'loaded site'
4
+
5
+ before do
6
+ login_as first_user
7
+ end
8
+ describe 'Drafting', js: true do
9
+ describe 'at /box/in/messages' do
10
+ before do
11
+ visit '/box/in/messages/new/'
12
+ recipient_input = find(:css, "#message_recipients_to_save_users .tags-input")
13
+ recipient_input.set("")
14
+ fill_in 'Subject', with: 'This is a draft.'
15
+ fill_in 'Body', with: 'This is the body.'
16
+ click_button 'Send'
17
+ end
18
+ it_behaves_like 'a bootstrap page with an alert', 'warning', 'Message saved as draft.'
19
+ end
20
+ end
21
+ describe 'Composing', js: true do
22
+ describe 'at /box/in/messages' do
23
+ before do
24
+ visit '/box/in/messages/new/'
25
+ recipient_input = find(:css, "#message_recipients_to_save_users .tags-input")
26
+ recipient_input.set("sec")
27
+ recipient_input.native.send_keys :arrow_down
28
+ recipient_input.native.send_keys :return
29
+ fill_in 'Subject', with: 'This is the subject.'
30
+ fill_in 'Body', with: 'This is the body.'
31
+ click_button 'Send'
32
+ end
33
+ it_behaves_like 'a bootstrap page with an alert', 'info', 'Message sent.'
34
+ end
35
+ end
36
+ describe 'Editing a Draft', js: true do
37
+ describe 'at /box/in/messages' do
38
+ before do
39
+ visit "/box/in/conversations/#{draft_conversation.id}"
40
+ recipient_input = find(:css, "#message_recipients_to_save_users .tags-input")
41
+ recipient_input.set("sec")
42
+ recipient_input.native.send_keys :arrow_down
43
+ recipient_input.native.send_keys :return
44
+ fill_in 'Subject', with: 'This is the subject.'
45
+ fill_in 'Body', with: 'This is the body.'
46
+ find(:css, "#message_draft").set(false)
47
+ click_button 'Send'
48
+ end
49
+ it_behaves_like 'a bootstrap page with an alert', 'info', 'Message sent.'
50
+ it_behaves_like 'a bootstrap page without an alert', 'warning'
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,44 @@
1
+ require 'rails_helper'
2
+
3
+ describe MessageTrain::BoxesHelper do
4
+ include_context 'loaded site'
5
+ include ControllerMacros
6
+
7
+ before do
8
+ login_user first_user
9
+ end
10
+ context '#box_nav_item' do
11
+ subject { helper.box_nav_item(first_user.box(:in)) }
12
+ it { should have_tag 'li', text: /Inbox/ }
13
+ it { should have_tag 'span', text: /[0-9]+/, with: { class: 'badge' } }
14
+ end
15
+
16
+ context '#box_list_item' do
17
+ subject { helper.box_list_item(first_user.box(:in), class: 'foo') }
18
+ it { should have_tag 'li', text: /Inbox/, with: { class: 'foo' } }
19
+ it { should have_tag 'span', text: /[0-9]+/, with: { class: 'badge' } }
20
+ end
21
+
22
+ context '#boxes_widget' do
23
+ subject { helper.boxes_widget }
24
+ it { should have_tag 'h3', text: /Messages/ }
25
+ it { should have_tag 'ul', with: { class: 'list-group' } }
26
+ it { should have_tag 'li', with: { class: 'list-group-item' }, count: 6 }
27
+ end
28
+
29
+ context '#boxes_dropdown_list' do
30
+ subject { helper.boxes_dropdown_list }
31
+ it { should have_tag 'ul', with: { class: 'dropdown-menu' } }
32
+ it { should have_tag 'li', minimum: 5 }
33
+ end
34
+
35
+ context '#box_participant_slug' do
36
+ subject { helper.box_participant_slug(first_user) }
37
+ it { should eq 'first-user' }
38
+ end
39
+
40
+ context '#box_participant_name' do
41
+ subject { helper.box_participant_name(first_user) }
42
+ it { should eq 'First User' }
43
+ end
44
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "MessageTrain" do
4
+ it 'should return correct version string' do
5
+ MessageTrain.version_string.should == "MessageTrain version #{MessageTrain::VERSION}"
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe Group do
4
+ describe 'Model' do
5
+ # MessageTrain Gem, and Rolify Gem by extension
6
+ it { should have_many(:roles) }
7
+
8
+ # MessageTrain Gem
9
+ it { should have_many(:receipts) }
10
+ end
11
+
12
+ describe 'Scopes and Methods from Message Train' do
13
+ include_context 'loaded site'
14
+
15
+ describe '#conversations' do
16
+ subject { first_group.conversations(:in) }
17
+ its(:first) { should be_a MessageTrain::Conversation }
18
+ its(:count) { should eq 1 }
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,12 @@
1
+ require 'rails_helper'
2
+
3
+ module MessageTrain
4
+ RSpec.describe Attachment do
5
+ describe 'Model' do
6
+ it { should belong_to :message }
7
+ end
8
+ describe 'Scopes and Methods' do
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,195 @@
1
+ require 'rails_helper'
2
+
3
+ module MessageTrain
4
+ RSpec.describe Box do
5
+ describe 'Model' do
6
+
7
+ end
8
+ describe 'Scopes and Methods' do
9
+ include_context 'loaded site'
10
+
11
+ describe '#unread_count' do
12
+ subject { first_user.box.unread_count }
13
+ it { should be >= 2 }
14
+ end
15
+ describe '#conversations' do
16
+ context 'with division as :in' do
17
+ subject { first_user.box.conversations }
18
+ it { should_not include sent_conversation }
19
+ it { should include unread_conversation }
20
+ it { should include read_conversation }
21
+ it { should_not include ignored_conversation }
22
+ it { should_not include trashed_conversation }
23
+ it { should_not include deleted_conversation }
24
+ it { should_not include draft_conversation }
25
+ end
26
+ context 'with division as :sent' do
27
+ subject { first_user.box(:sent).conversations }
28
+ it { should include sent_conversation }
29
+ it { should_not include unread_conversation }
30
+ it { should_not include read_conversation }
31
+ it { should_not include ignored_conversation }
32
+ it { should_not include trashed_conversation }
33
+ it { should_not include deleted_conversation }
34
+ it { should_not include draft_conversation }
35
+ end
36
+ context 'with division as :trash' do
37
+ subject { first_user.box(:trash).conversations }
38
+ it { should_not include sent_conversation }
39
+ it { should_not include unread_conversation }
40
+ it { should_not include read_conversation }
41
+ it { should_not include ignored_conversation }
42
+ it { should include trashed_conversation }
43
+ it { should_not include deleted_conversation }
44
+ it { should_not include draft_conversation }
45
+ end
46
+ context 'with division as :drafts' do
47
+ subject { first_user.box(:drafts).conversations }
48
+ it { should_not include sent_conversation }
49
+ it { should_not include trashed_conversation }
50
+ it { should_not include read_conversation }
51
+ it { should_not include unread_conversation }
52
+ it { should_not include deleted_conversation }
53
+ it { should_not include ignored_conversation }
54
+ it { should include draft_conversation }
55
+ end
56
+ context 'with unread set to true' do
57
+ subject { first_user.box.conversations(unread: true) }
58
+ it { should_not include sent_conversation }
59
+ it { should include unread_conversation }
60
+ it { should_not include read_conversation }
61
+ it { should_not include ignored_conversation }
62
+ it { should_not include trashed_conversation }
63
+ it { should_not include deleted_conversation }
64
+ it { should_not include draft_conversation }
65
+ end
66
+ end
67
+ describe '#ignore' do
68
+ context 'when authorized' do
69
+ before do
70
+ first_user.box.ignore(read_conversation)
71
+ end
72
+ subject { read_conversation.is_ignored?(first_user) }
73
+ it { should be true}
74
+ end
75
+ context 'when not authorized' do
76
+ before do
77
+ last_user.box.ignore(read_conversation)
78
+ end
79
+ subject { last_user.box.errors.all.first[:message] }
80
+ it { should match /Access to Conversation ([0-9]+) denied/ }
81
+ end
82
+ end
83
+ describe '#unignore' do
84
+ context 'when authorized' do
85
+ before do
86
+ first_user.box.unignore(ignored_conversation)
87
+ end
88
+ subject { ignored_conversation.is_ignored?(first_user) }
89
+ it { should be false }
90
+ end
91
+ context 'when not authorized' do
92
+ before do
93
+ last_user.box.unignore(read_conversation)
94
+ end
95
+ subject { last_user.box.errors.all.first[:message] }
96
+ it { should match /Access to Conversation ([0-9]+) denied/ }
97
+ end
98
+ end
99
+ describe '#title' do
100
+ context 'with division set to :in' do
101
+ subject { first_user.box.title }
102
+ it { should eq 'Inbox' }
103
+ end
104
+ context 'with division set to :sent' do
105
+ subject { first_user.box(:sent).title }
106
+ it { should eq 'Sent Messages' }
107
+ end
108
+ context 'with division set to :all' do
109
+ subject { first_user.box(:all).title }
110
+ it { should eq 'All Messages' }
111
+ end
112
+ context 'with division set to :drafts' do
113
+ subject { first_user.box(:drafts).title }
114
+ it { should eq 'Drafts' }
115
+ end
116
+ context 'with division set to :trash' do
117
+ subject { first_user.box(:trash).title }
118
+ it { should eq 'Trash' }
119
+ end
120
+ end
121
+ describe '#mark' do
122
+ before do
123
+ first_user.box.mark('unread', conversations: [read_conversation.id.to_s])
124
+ end
125
+ subject { read_conversation.includes_read_for?(first_user) }
126
+ it { should be false }
127
+ end
128
+ describe '.send_message' do
129
+ context 'when message is valid' do
130
+ let(:valid_attributes) { { recipients_to_save: {'users' => 'second-user, third-user'}, subject: 'Test Message', body: 'This is the content.' } }
131
+ subject { first_user.box.send_message(valid_attributes) }
132
+ it { should be_a MessageTrain::Message }
133
+ its(:new_record?) { should be false }
134
+ its('errors.full_messages.to_sentence') { should eq '' }
135
+ its(:recipients) { should include second_user }
136
+ its(:recipients) { should include third_user }
137
+ its(:draft) { should be false }
138
+ end
139
+ context 'when message is invalid' do
140
+ let(:invalid_attributes) { { recipients_to_save: {'users' => 'second-user, third-user'}, subject: nil, body: 'This is the content.' } }
141
+ subject { first_user.box.send_message(invalid_attributes) }
142
+ it { should be_a_new MessageTrain::Message }
143
+ its('errors.full_messages.to_sentence') { should eq "Subject can't be blank" }
144
+ its(:recipients) { should be_empty }
145
+ end
146
+ end
147
+ describe '.update_message' do
148
+ let(:draft_message) { draft_conversation.messages.first }
149
+ context 'when message is valid' do
150
+ let(:valid_attributes) { {
151
+ recipients_to_save: {'users' => 'second-user, third-user'},
152
+ subject: 'Test Message',
153
+ body: 'This is the content.',
154
+ draft: false
155
+ } }
156
+ subject { first_user.box.update_message(draft_message, valid_attributes) }
157
+ it { should be_a MessageTrain::Message }
158
+ its('errors.full_messages.to_sentence') { should eq '' }
159
+ its(:recipients) { should include second_user }
160
+ its(:recipients) { should include third_user }
161
+ its(:subject) { should eq 'Test Message' }
162
+ its(:draft) { should be false }
163
+ end
164
+ context 'when message is invalid' do
165
+ let(:invalid_attributes) { {
166
+ recipients_to_save: {'users' => 'second-user, third-user'},
167
+ subject: '',
168
+ body: 'This is the content.',
169
+ draft: false
170
+ } }
171
+ subject { first_user.box.update_message(draft_message, invalid_attributes) }
172
+ it { should be_a MessageTrain::Message }
173
+ its('errors.full_messages.to_sentence') { should eq "Subject can't be blank" }
174
+ its(:recipients) { should be_empty }
175
+ end
176
+ end
177
+
178
+ describe '.new_message' do
179
+ context 'when conversation is set' do
180
+ let(:expected_recipients) { { 'users' => 'second-user' } }
181
+ subject { first_user.box.new_message(conversation_id: unread_conversation.id) }
182
+ it { should be_a_new MessageTrain::Message }
183
+ its(:subject) { should eq 'Re: Unread Conversation' }
184
+ its(:recipients_to_save) { should eq expected_recipients }
185
+ end
186
+ context 'when conversation is not set' do
187
+ subject { first_user.box.new_message }
188
+ it { should be_a_new MessageTrain::Message }
189
+ its(:subject) { should eq nil }
190
+ its(:recipients_to_save) { should be_empty }
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,98 @@
1
+ require 'rails_helper'
2
+
3
+ module MessageTrain
4
+ RSpec.describe Conversation do
5
+ describe 'Model' do
6
+ it { should have_many :messages }
7
+ it { should have_many :ignores }
8
+ it { should have_many(:receipts).through(:messages) }
9
+ end
10
+ describe 'Scopes and Methods' do
11
+ include_context 'loaded site'
12
+ describe '#default_recipients_for' do
13
+ subject { sent_conversation.default_recipients_for(first_user) }
14
+ it { should include second_user }
15
+ it { should_not include first_user }
16
+ it { should_not include third_user }
17
+ end
18
+ describe '#set_ignored and #is_ignored?' do
19
+ subject { ignored_conversation.is_ignored?(first_user) }
20
+ it { should be true }
21
+ end
22
+ describe '#set_unignored' do
23
+ before do
24
+ ignored_conversation.set_unignored(first_user)
25
+ end
26
+ subject { ignored_conversation.is_ignored?(first_user) }
27
+ it { should be false }
28
+ end
29
+ describe '#mark and #includes_read_for?' do
30
+ before do
31
+ unread_conversation.mark(:read, first_user)
32
+ end
33
+ subject { unread_conversation.includes_read_for?(first_user) }
34
+ it { should be true }
35
+ end
36
+ describe '#includes_drafts_by?' do
37
+ subject { draft_conversation.includes_drafts_by?(first_user) }
38
+ it { should be true }
39
+ end
40
+ describe '.with_drafts_by' do
41
+ subject { MessageTrain::Conversation.with_drafts_by(first_user) }
42
+ its(:first) { should be_a MessageTrain::Conversation }
43
+ its(:count) { should eq 1 }
44
+ end
45
+ describe 'Dynamic Methods' do
46
+ describe '.trashed_for' do
47
+ subject { MessageTrain::Conversation.with_trashed_for(first_user) }
48
+ it { should include trashed_conversation }
49
+ end
50
+ describe '.untrashed_for' do
51
+ subject { MessageTrain::Conversation.with_untrashed_for(first_user) }
52
+ it { should_not include trashed_conversation }
53
+ end
54
+ describe '.deleted_for' do
55
+ subject { MessageTrain::Conversation.with_deleted_for(first_user) }
56
+ it { should include deleted_conversation }
57
+ end
58
+ describe '.undeleted_for' do
59
+ subject { MessageTrain::Conversation.with_undeleted_for(first_user) }
60
+ it { should_not include deleted_conversation }
61
+ end
62
+ describe '.read_for' do
63
+ subject { MessageTrain::Conversation.with_read_for(first_user) }
64
+ it { should include read_conversation }
65
+ end
66
+ describe '.unread_for' do
67
+ subject { MessageTrain::Conversation.with_unread_for(first_user) }
68
+ it { should include sent_conversation }
69
+ it { should include unread_conversation }
70
+ end
71
+ describe '#includes_trashed_to?' do
72
+ subject { trashed_conversation.includes_trashed_to?(first_user) }
73
+ it { should be true }
74
+ end
75
+ describe '#includes_read_to?' do
76
+ subject { read_conversation.includes_read_to?(first_user) }
77
+ it { should be true }
78
+ end
79
+ describe '#includes_deleted_to?' do
80
+ subject { deleted_conversation.includes_deleted_to?(first_user) }
81
+ it { should be true }
82
+ end
83
+ describe '#includes_receipts_to?' do
84
+ subject { unread_conversation.includes_receipts_to?(first_user) }
85
+ it { should be true }
86
+ end
87
+ describe '#includes_receipts_by?' do
88
+ subject { sent_conversation.includes_receipts_by?(first_user) }
89
+ it { should be true }
90
+ end
91
+ describe '#includes_receipts_for?' do
92
+ subject { sent_conversation.includes_receipts_for?(first_user) }
93
+ it { should be true }
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,27 @@
1
+ require 'rails_helper'
2
+
3
+ module MessageTrain
4
+ RSpec.describe Ignore do
5
+ describe 'Model' do
6
+ it { should belong_to :conversation }
7
+ it { should belong_to :participant }
8
+ it { should validate_presence_of :conversation }
9
+ it { should validate_presence_of :participant }
10
+ end
11
+ describe 'Scopes and Methods' do
12
+ include_context 'loaded site'
13
+ describe '.find_all_by_participant' do
14
+ subject { ignored_conversation.ignores.find_all_by_participant(first_user) }
15
+ its(:first) { should be_a MessageTrain::Ignore }
16
+ end
17
+ describe '.conversation_ids' do
18
+ subject { MessageTrain::Ignore.conversation_ids }
19
+ it { should include ignored_conversation.id }
20
+ end
21
+ describe '.conversations' do
22
+ subject { MessageTrain::Ignore.conversations }
23
+ it { should include ignored_conversation }
24
+ end
25
+ end
26
+ end
27
+ end