telephony 1.0.3

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 (365) hide show
  1. checksums.yaml +15 -0
  2. data/README.md +105 -0
  3. data/Rakefile +39 -0
  4. data/app/assets/fonts/telephony/zest-telephony.eot +0 -0
  5. data/app/assets/fonts/telephony/zest-telephony.svg +73 -0
  6. data/app/assets/fonts/telephony/zest-telephony.ttf +0 -0
  7. data/app/assets/fonts/telephony/zest-telephony.woff +0 -0
  8. data/app/assets/images/telephony/backspace.png +0 -0
  9. data/app/assets/images/telephony/icon-spinner.gif +0 -0
  10. data/app/assets/javascripts/telephony/application.js +12 -0
  11. data/app/assets/javascripts/telephony/collections/agents.js +4 -0
  12. data/app/assets/javascripts/telephony/config.js.erb +11 -0
  13. data/app/assets/javascripts/telephony/models/agent.js +103 -0
  14. data/app/assets/javascripts/telephony/models/conversation.js +226 -0
  15. data/app/assets/javascripts/telephony/models/device.js +30 -0
  16. data/app/assets/javascripts/telephony/models/transfer.js +65 -0
  17. data/app/assets/javascripts/telephony/namespace.js +6 -0
  18. data/app/assets/javascripts/telephony/push.js +80 -0
  19. data/app/assets/javascripts/telephony/vendor/backbone.js +1159 -0
  20. data/app/assets/javascripts/telephony/vendor/chosen.jquery.js +1018 -0
  21. data/app/assets/javascripts/telephony/vendor/jquery.form.js +1117 -0
  22. data/app/assets/javascripts/telephony/vendor/pusher.js +1304 -0
  23. data/app/assets/javascripts/telephony/vendor/test_dependencies.js +18 -0
  24. data/app/assets/javascripts/telephony/vendor/underscore.js +839 -0
  25. data/app/assets/javascripts/telephony/views/agents_view.js +69 -0
  26. data/app/assets/javascripts/telephony/views/application_view.js +78 -0
  27. data/app/assets/javascripts/telephony/views/call_queue_view.js +21 -0
  28. data/app/assets/javascripts/telephony/views/conversation_buttons_view.js +15 -0
  29. data/app/assets/javascripts/telephony/views/conversation_view.js +322 -0
  30. data/app/assets/javascripts/telephony/views/status_view.js +36 -0
  31. data/app/assets/javascripts/telephony/views/transfer_view.js +87 -0
  32. data/app/assets/javascripts/telephony/views/twilio_client_view.js +117 -0
  33. data/app/assets/javascripts/telephony/views/widget_view.js +55 -0
  34. data/app/assets/javascripts/telephony/widget.js +5 -0
  35. data/app/assets/javascripts/templates/telephony/agents_view.jst.ejs +8 -0
  36. data/app/assets/javascripts/templates/telephony/call_queue_view.jst.ejs +2 -0
  37. data/app/assets/javascripts/templates/telephony/conversation_buttons_view.jst.ejs +23 -0
  38. data/app/assets/javascripts/templates/telephony/conversation_view.jst.ejs +10 -0
  39. data/app/assets/javascripts/templates/telephony/status_view.jst.ejs +4 -0
  40. data/app/assets/javascripts/templates/telephony/transfer_view.jst.ejs +38 -0
  41. data/app/assets/javascripts/templates/telephony/twilio_client_view.jst.ejs +10 -0
  42. data/app/assets/stylesheets/telephony/font.css +43 -0
  43. data/app/assets/stylesheets/telephony/reset.css.scss +10 -0
  44. data/app/assets/stylesheets/telephony/widget.css.scss +512 -0
  45. data/app/controllers/telephony/agents_controller.rb +46 -0
  46. data/app/controllers/telephony/application_controller.rb +4 -0
  47. data/app/controllers/telephony/call_centers_controller.rb +9 -0
  48. data/app/controllers/telephony/conversations_controller.rb +74 -0
  49. data/app/controllers/telephony/inbound/conversation_queues_controller.rb +21 -0
  50. data/app/controllers/telephony/playable_listeners_controller.rb +43 -0
  51. data/app/controllers/telephony/providers/twilio/application_controller.rb +26 -0
  52. data/app/controllers/telephony/providers/twilio/calls_controller.rb +130 -0
  53. data/app/controllers/telephony/providers/twilio/inbound_calls_controller.rb +54 -0
  54. data/app/controllers/telephony/providers/twilio/musics_controller.rb +12 -0
  55. data/app/controllers/telephony/providers/twilio/voicemails_controller.rb +29 -0
  56. data/app/controllers/telephony/signals/agents/presences_controller.rb +64 -0
  57. data/app/controllers/telephony/transfers_controller.rb +18 -0
  58. data/app/controllers/telephony/twilio_client_controller.rb +37 -0
  59. data/app/controllers/telephony/voicemails_controller.rb +13 -0
  60. data/app/controllers/telephony/widget_controller.rb +10 -0
  61. data/app/helpers/telephony/application_helper.rb +4 -0
  62. data/app/helpers/telephony/calls_helper.rb +13 -0
  63. data/app/models/telephony/agent.rb +186 -0
  64. data/app/models/telephony/agent_state_machine.rb +54 -0
  65. data/app/models/telephony/base.rb +7 -0
  66. data/app/models/telephony/blacklisted_number.rb +5 -0
  67. data/app/models/telephony/call.rb +94 -0
  68. data/app/models/telephony/call_center.rb +26 -0
  69. data/app/models/telephony/call_state_machine.rb +98 -0
  70. data/app/models/telephony/conversation.rb +273 -0
  71. data/app/models/telephony/conversation_state_machine.rb +109 -0
  72. data/app/models/telephony/conversations_presenter.rb +83 -0
  73. data/app/models/telephony/events.rb +6 -0
  74. data/app/models/telephony/events/answer.rb +6 -0
  75. data/app/models/telephony/events/base.rb +118 -0
  76. data/app/models/telephony/events/busy.rb +6 -0
  77. data/app/models/telephony/events/call_answered.rb +22 -0
  78. data/app/models/telephony/events/call_fail.rb +6 -0
  79. data/app/models/telephony/events/complete_hold.rb +28 -0
  80. data/app/models/telephony/events/complete_one_step_transfer.rb +17 -0
  81. data/app/models/telephony/events/complete_resume.rb +28 -0
  82. data/app/models/telephony/events/complete_two_step_transfer.rb +6 -0
  83. data/app/models/telephony/events/conference.rb +6 -0
  84. data/app/models/telephony/events/connect.rb +22 -0
  85. data/app/models/telephony/events/customer_left_two_step_transfer.rb +6 -0
  86. data/app/models/telephony/events/dial_agent.rb +6 -0
  87. data/app/models/telephony/events/ended.rb +19 -0
  88. data/app/models/telephony/events/enqueue.rb +6 -0
  89. data/app/models/telephony/events/fail_one_step_transfer.rb +9 -0
  90. data/app/models/telephony/events/fail_two_step_transfer.rb +6 -0
  91. data/app/models/telephony/events/initialize_widget.rb +15 -0
  92. data/app/models/telephony/events/initiate_hold.rb +6 -0
  93. data/app/models/telephony/events/initiate_one_step_transfer.rb +6 -0
  94. data/app/models/telephony/events/initiate_resume.rb +6 -0
  95. data/app/models/telephony/events/initiate_two_step_transfer.rb +6 -0
  96. data/app/models/telephony/events/leave_two_step_transfer.rb +17 -0
  97. data/app/models/telephony/events/leave_voicemail.rb +22 -0
  98. data/app/models/telephony/events/no_answer.rb +6 -0
  99. data/app/models/telephony/events/play_closed_greeting.rb +6 -0
  100. data/app/models/telephony/events/play_message.rb +6 -0
  101. data/app/models/telephony/events/reject.rb +6 -0
  102. data/app/models/telephony/events/rona.rb +6 -0
  103. data/app/models/telephony/events/start.rb +17 -0
  104. data/app/models/telephony/events/straight_to_voicemail.rb +6 -0
  105. data/app/models/telephony/events/terminate.rb +6 -0
  106. data/app/models/telephony/events/transfer.rb +35 -0
  107. data/app/models/telephony/inbound_conversation_queue.rb +101 -0
  108. data/app/models/telephony/playable.rb +7 -0
  109. data/app/models/telephony/playable_listener.rb +42 -0
  110. data/app/models/telephony/pusher_event_publisher.rb +26 -0
  111. data/app/models/telephony/recording.rb +4 -0
  112. data/app/models/telephony/voicemail.rb +36 -0
  113. data/app/observers/telephony/agent_observer.rb +8 -0
  114. data/app/observers/telephony/call_observer.rb +15 -0
  115. data/app/observers/telephony/conversation_observer.rb +9 -0
  116. data/app/observers/telephony/event_observer.rb +9 -0
  117. data/app/views/layouts/telephony/application.html.erb +14 -0
  118. data/app/views/telephony/providers/twilio/calls/child_detached.builder +5 -0
  119. data/app/views/telephony/providers/twilio/calls/complete_hold.builder +6 -0
  120. data/app/views/telephony/providers/twilio/calls/connect.builder +13 -0
  121. data/app/views/telephony/providers/twilio/calls/dial.builder +14 -0
  122. data/app/views/telephony/providers/twilio/calls/done.builder +5 -0
  123. data/app/views/telephony/providers/twilio/calls/join_conference.builder +7 -0
  124. data/app/views/telephony/providers/twilio/calls/whisper_tone.builder +3 -0
  125. data/app/views/telephony/providers/twilio/inbound_calls/closed_hours.builder +7 -0
  126. data/app/views/telephony/providers/twilio/inbound_calls/create.builder +8 -0
  127. data/app/views/telephony/providers/twilio/inbound_calls/enqueue.builder +8 -0
  128. data/app/views/telephony/providers/twilio/inbound_calls/reject.builder +5 -0
  129. data/app/views/telephony/providers/twilio/inbound_calls/wait_music.builder +5 -0
  130. data/app/views/telephony/providers/twilio/musics/hold.builder +5 -0
  131. data/app/views/telephony/providers/twilio/voicemails/new.builder +6 -0
  132. data/app/views/telephony/twilio_client/index.html.erb +74 -0
  133. data/app/views/telephony/widget/index.erb +3 -0
  134. data/config/cucumber.yml +8 -0
  135. data/config/database.yml +57 -0
  136. data/config/environment.rb +67 -0
  137. data/config/initializers/pusher.rb +21 -0
  138. data/config/initializers/telephony.rb +44 -0
  139. data/config/routes.rb +94 -0
  140. data/config/wopr.yml.example +38 -0
  141. data/db/migrate/20130806213053_bootstrap_db.rb +105 -0
  142. data/db/migrate/20131009204026_create_telephony_blacklisted_numbers.rb +11 -0
  143. data/lib/agent_generator.rb +24 -0
  144. data/lib/tasks/cucumber.rake +65 -0
  145. data/lib/tasks/jasmine.rake +8 -0
  146. data/lib/tasks/telephony_tasks.rake +4 -0
  147. data/lib/telephony.rb +34 -0
  148. data/lib/telephony/concerns/controllers/twilio_request_verifier.rb +39 -0
  149. data/lib/telephony/conversation_data.rb +67 -0
  150. data/lib/telephony/engine.rb +19 -0
  151. data/lib/telephony/error.rb +8 -0
  152. data/lib/telephony/error/agent_on_a_call.rb +2 -0
  153. data/lib/telephony/error/base.rb +3 -0
  154. data/lib/telephony/error/connection.rb +2 -0
  155. data/lib/telephony/error/not_in_progress.rb +2 -0
  156. data/lib/telephony/error/queue_empty.rb +2 -0
  157. data/lib/telephony/helper.rb +11 -0
  158. data/lib/telephony/jobs/agent_offline.rb +28 -0
  159. data/lib/telephony/jobs/pusher_event.rb +21 -0
  160. data/lib/telephony/providers/twilio_provider.rb +162 -0
  161. data/lib/telephony/version.rb +3 -0
  162. data/spec/controllers/telephony/agents_controller_spec.rb +117 -0
  163. data/spec/controllers/telephony/call_centers_controller_spec.rb +25 -0
  164. data/spec/controllers/telephony/conversations_controller_spec.rb +229 -0
  165. data/spec/controllers/telephony/playable_listeners_controller_spec.rb +138 -0
  166. data/spec/controllers/telephony/providers/twilio/calls_controller_spec.rb +33 -0
  167. data/spec/controllers/telephony/providers/twilio/musics_controller_spec.rb +20 -0
  168. data/spec/controllers/telephony/signals/agents/presences_controller_spec.rb +154 -0
  169. data/spec/controllers/telephony/twilio_client_controller_spec.rb +58 -0
  170. data/spec/controllers/telephony/widget_controller_spec.rb +25 -0
  171. data/spec/dummy/Rakefile +7 -0
  172. data/spec/dummy/app/assets/javascripts/application.js +6 -0
  173. data/spec/dummy/app/assets/javascripts/lib/event_logger.js +82 -0
  174. data/spec/dummy/app/assets/javascripts/vendor/backbone.js +1159 -0
  175. data/spec/dummy/app/assets/javascripts/vendor/chosen.jquery.js +1018 -0
  176. data/spec/dummy/app/assets/javascripts/vendor/jquery.form.js +1117 -0
  177. data/spec/dummy/app/assets/javascripts/vendor/underscore.js +839 -0
  178. data/spec/dummy/app/assets/stylesheets/application.css +23 -0
  179. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  180. data/spec/dummy/app/controllers/widget_host_controller.rb +2 -0
  181. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  182. data/spec/dummy/app/views/layouts/application.html.erb +12 -0
  183. data/spec/dummy/app/views/widget_host/index.html.erb +53 -0
  184. data/spec/dummy/config.ru +4 -0
  185. data/spec/dummy/config/application.rb +61 -0
  186. data/spec/dummy/config/boot.rb +10 -0
  187. data/spec/dummy/config/call_centers.yml +35 -0
  188. data/spec/dummy/config/call_centers.yml.example +35 -0
  189. data/spec/dummy/config/database.yml +56 -0
  190. data/spec/dummy/config/environment.rb +5 -0
  191. data/spec/dummy/config/environments/cucumber.rb +39 -0
  192. data/spec/dummy/config/environments/development.rb +39 -0
  193. data/spec/dummy/config/environments/production.rb +67 -0
  194. data/spec/dummy/config/environments/test.rb +39 -0
  195. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  196. data/spec/dummy/config/initializers/inflections.rb +15 -0
  197. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  198. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  199. data/spec/dummy/config/initializers/session_store.rb +8 -0
  200. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  201. data/spec/dummy/config/locales/en.yml +5 -0
  202. data/spec/dummy/config/pusher.yml +14 -0
  203. data/spec/dummy/config/pusher.yml.example +14 -0
  204. data/spec/dummy/config/routes.rb +5 -0
  205. data/spec/dummy/config/sms_whitelist.json.example +4 -0
  206. data/spec/dummy/config/twilio.yml +57 -0
  207. data/spec/dummy/config/twilio.yml.example +57 -0
  208. data/spec/dummy/db/schema.rb +122 -0
  209. data/spec/dummy/db/seeds.rb +6 -0
  210. data/spec/dummy/log/production.log +156 -0
  211. data/spec/dummy/log/test.log +13812 -0
  212. data/spec/dummy/public/404.html +26 -0
  213. data/spec/dummy/public/422.html +26 -0
  214. data/spec/dummy/public/500.html +25 -0
  215. data/spec/dummy/public/assets/application-628309db7ce8c0abdd91f8f796881599.js +32 -0
  216. data/spec/dummy/public/assets/application-628309db7ce8c0abdd91f8f796881599.js.gz +0 -0
  217. data/spec/dummy/public/assets/application-6c551430f3bdd32ba57c9c41e5f91846.css +24 -0
  218. data/spec/dummy/public/assets/application-6c551430f3bdd32ba57c9c41e5f91846.css.gz +0 -0
  219. data/spec/dummy/public/assets/telephony/backspace-9f5b195e049ed90eb20ecdda6e264b0a.png +0 -0
  220. data/spec/dummy/public/assets/telephony/icon-spinner-ddbae473a70c5425810aeee31d4a8ad7.gif +0 -0
  221. data/spec/dummy/public/assets/telephony/zest-telephony-0e6b13673634da80de9ae09bcca253e3.eot +0 -0
  222. data/spec/dummy/public/assets/telephony/zest-telephony-37f394757ccb11b978f16d9fd32cb3b5.ttf +0 -0
  223. data/spec/dummy/public/assets/telephony/zest-telephony-e7efdbc60c0a1c8404951bc79c7fb3a8.woff +0 -0
  224. data/spec/dummy/public/assets/telephony/zest-telephony-f34287df626936908408f53a33db9e83.svg +73 -0
  225. data/spec/dummy/public/favicon.ico +0 -0
  226. data/spec/dummy/script/rails +6 -0
  227. data/spec/dummy/tmp/cache/assets/C98/020/sprockets%2Fa83f8254d688334ff179984e423f502f +0 -0
  228. data/spec/dummy/tmp/cache/assets/CA4/550/sprockets%2F407f9191fe573f2435c55c017cb0d022 +0 -0
  229. data/spec/dummy/tmp/cache/assets/CAA/6A0/sprockets%2F3265550e2d2c7f5a864ca85d255504c2 +0 -0
  230. data/spec/dummy/tmp/cache/assets/CB0/F60/sprockets%2F462f9efed31e6710732376ae06c27208 +0 -0
  231. data/spec/dummy/tmp/cache/assets/CB6/E20/sprockets%2Fa7092cd9f16917099d754f1380af13e1 +0 -0
  232. data/spec/dummy/tmp/cache/assets/CC7/9F0/sprockets%2F0517100815cd45c43fec1d8510fcb630 +0 -0
  233. data/spec/dummy/tmp/cache/assets/CCA/BF0/sprockets%2F16ea15e0b5d367c34b812612a4207e3e +0 -0
  234. data/spec/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
  235. data/spec/dummy/tmp/cache/assets/CF1/960/sprockets%2F36ef26985821ec702c74dae4af789903 +0 -0
  236. data/spec/dummy/tmp/cache/assets/D07/F30/sprockets%2Fd12862f20df0c9ef7aa2b4216d247079 +0 -0
  237. data/spec/dummy/tmp/cache/assets/D09/BF0/sprockets%2F5c8595efaa8cc5235a09801c60e25a16 +0 -0
  238. data/spec/dummy/tmp/cache/assets/D0D/250/sprockets%2F5e759d66e374910dae5a51b221e6e3a4 +0 -0
  239. data/spec/dummy/tmp/cache/assets/D0D/E30/sprockets%2Fa282879e0788ba133f5bdc211cf134e8 +0 -0
  240. data/spec/dummy/tmp/cache/assets/D11/8E0/sprockets%2F2a193a8af29963f1113bef05788e02ff +0 -0
  241. data/spec/dummy/tmp/cache/assets/D14/270/sprockets%2F7f95a752910ed33d728532f8fabd340d +0 -0
  242. data/spec/dummy/tmp/cache/assets/D17/BE0/sprockets%2F58749d7aea4ca766c2520801f0f96b9b +0 -0
  243. data/spec/dummy/tmp/cache/assets/D18/270/sprockets%2Fbb2d88813b1795a97e9a7c14100eef77 +0 -0
  244. data/spec/dummy/tmp/cache/assets/D22/310/sprockets%2Fbae9148ce1229214dfbb0c425b0054d0 +0 -0
  245. data/spec/dummy/tmp/cache/assets/D30/EB0/sprockets%2F2c668b88beb4c9805d91ed4587f549e7 +0 -0
  246. data/spec/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  247. data/spec/dummy/tmp/cache/assets/D3C/690/sprockets%2F681ba175fdd2da8d590b7e624b6135a6 +0 -0
  248. data/spec/dummy/tmp/cache/assets/D44/BC0/sprockets%2F7214f66dd91fdd51f00c3882293f5eea +0 -0
  249. data/spec/dummy/tmp/cache/assets/D46/FA0/sprockets%2F4f9543cd9e68ba3141fd488afb13681b +0 -0
  250. data/spec/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
  251. data/spec/dummy/tmp/cache/assets/D51/9A0/sprockets%2Fb6e4cbcf30f6f611671695f79ea846d5 +0 -0
  252. data/spec/dummy/tmp/cache/assets/D55/920/sprockets%2F0a02242aa4c4febcc796419aba103c69 +0 -0
  253. data/spec/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
  254. data/spec/dummy/tmp/cache/assets/D5F/6F0/sprockets%2F155e7bc44f326371cce0ca8bc5b2414e +0 -0
  255. data/spec/dummy/tmp/cache/assets/D6B/250/sprockets%2Fe4eecce7a3371b11038c92f07cb28e67 +0 -0
  256. data/spec/dummy/tmp/cache/assets/D6B/E70/sprockets%2F103a456ae3692e3cca20b7696fdee8f0 +0 -0
  257. data/spec/dummy/tmp/cache/assets/D7F/AD0/sprockets%2Fb1c02d12303b1fe2f981771cbb0c4cbd +0 -0
  258. data/spec/dummy/tmp/cache/assets/D81/D00/sprockets%2F0c68b6d9dce98681a2a710d9fc3f7e39 +0 -0
  259. data/spec/dummy/tmp/cache/assets/D94/4D0/sprockets%2F4608a5bdf27ddf11bcaed1375911e9a2 +0 -0
  260. data/spec/dummy/tmp/cache/assets/D9A/580/sprockets%2F433005b19aaf7ed5cba9c7f8cd30b775 +0 -0
  261. data/spec/dummy/tmp/cache/assets/DA4/270/sprockets%2F860ab3bd30204e02c0a5d82bd4bbabe1 +0 -0
  262. data/spec/dummy/tmp/cache/assets/DA4/480/sprockets%2F53e39285bb57a2ddb6ff856b2eb491da +0 -0
  263. data/spec/dummy/tmp/cache/assets/DA6/3C0/sprockets%2F587c7e35f47442eadaa530acc5e9f5e6 +0 -0
  264. data/spec/dummy/tmp/cache/assets/DAB/DA0/sprockets%2Fb8fb5e8518ed3d38a7f6a8f18a9208aa +0 -0
  265. data/spec/dummy/tmp/cache/assets/DB2/700/sprockets%2Ffa85cb79b94fce1b8344d983ea863d2e +0 -0
  266. data/spec/dummy/tmp/cache/assets/DCC/4F0/sprockets%2Fbc0f3c61e16afb5a285ba8b39846ed8b +0 -0
  267. data/spec/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
  268. data/spec/dummy/tmp/cache/assets/DDC/BE0/sprockets%2F2c0a4d4675a61b014aedbbabb3f071fc +0 -0
  269. data/spec/dummy/tmp/cache/assets/E04/640/sprockets%2F3c5ddf2bbefd9d70f1b506b519cf856e +0 -0
  270. data/spec/dummy/tmp/cache/assets/E04/7F0/sprockets%2F35f1b2ff729a5ec21f6dc5f7d6faa67b +0 -0
  271. data/spec/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  272. data/spec/dummy/tmp/cache/assets/E07/230/sprockets%2Febd4f2dd708879d593aff008ccb75acc +0 -0
  273. data/spec/dummy/tmp/cache/assets/E17/9D0/sprockets%2F8db2b79f6c4eabe7a9ebe7a7898ec066 +0 -0
  274. data/spec/dummy/tmp/cache/assets/E31/F10/sprockets%2Fcebacf56667da1cc2961b59fd922eafb +0 -0
  275. data/spec/factories/agent.rb +26 -0
  276. data/spec/factories/calls.rb +106 -0
  277. data/spec/factories/conversations.rb +171 -0
  278. data/spec/factories/events.rb +250 -0
  279. data/spec/factories/playable_listeners.rb +6 -0
  280. data/spec/factories/recordings.rb +6 -0
  281. data/spec/factories/voicemails.rb +8 -0
  282. data/spec/fixtures/vcr_cassettes/Authenticating_an_online_user.yml +90 -0
  283. data/spec/fixtures/vcr_cassettes/Creating_a_conversation/creates_a_new_call.yml +42 -0
  284. data/spec/fixtures/vcr_cassettes/Creating_a_conversation/creates_a_new_conversation.yml +42 -0
  285. data/spec/fixtures/vcr_cassettes/Creating_a_conversation/returns_the_conversation_as_JSON.yml +42 -0
  286. data/spec/fixtures/vcr_cassettes/Pusher_channel_as_JSON.yml +90 -0
  287. data/spec/fixtures/vcr_cassettes/Pusher_to_publish_the_event.yml +32 -0
  288. data/spec/fixtures/vcr_cassettes/Reloading_the_widget_during_a_call/_create/during_a_call/after_a_failed_transfer/pushes_the_event_for_the_active_call.yml +1110 -0
  289. data/spec/fixtures/vcr_cassettes/Telephony_ConversationsController/_create/by_default/creates_a_new_call.yml +45 -0
  290. data/spec/fixtures/vcr_cassettes/Telephony_ConversationsController/_create/by_default/creates_a_new_conversation.yml +43 -0
  291. data/spec/fixtures/vcr_cassettes/Telephony_ConversationsController/_create/by_default/returns_the_conversation_as_JSON.yml +44 -0
  292. data/spec/fixtures/vcr_cassettes/Telephony_Providers_TwilioProvider/_call/allows_the_phone_to_ring_for_60_seconds.yml +42 -0
  293. data/spec/fixtures/vcr_cassettes/Telephony_Providers_TwilioProvider/_call/includes_a_status_change_callback_url.yml +42 -0
  294. data/spec/fixtures/vcr_cassettes/Telephony_Providers_TwilioProvider/_call/places_a_call.yml +42 -0
  295. data/spec/fixtures/vcr_cassettes/Telephony_Providers_TwilioProvider/_dial_into_conference/allows_the_phone_to_ring_for_15_seconds.yml +42 -0
  296. data/spec/fixtures/vcr_cassettes/Telephony_Providers_TwilioProvider/_dial_into_conference/places_a_call_to_redirect_the_participant_to_a_conference.yml +42 -0
  297. data/spec/fixtures/vcr_cassettes/Telephony_Providers_TwilioProvider/_hangup/by_default/hangs_up_the_call.yml +42 -0
  298. data/spec/fixtures/vcr_cassettes/buying_number_for_an_area_code.yml +42 -0
  299. data/spec/fixtures/vcr_cassettes/returns_success.yml +90 -0
  300. data/spec/javascripts/helpers/jasmine-jquery.js +546 -0
  301. data/spec/javascripts/helpers/mock-ajax.js +207 -0
  302. data/spec/javascripts/support/jasmine.yml +80 -0
  303. data/spec/javascripts/telephony/models/agent_spec.js +40 -0
  304. data/spec/javascripts/telephony/models/conversation_spec.js +515 -0
  305. data/spec/javascripts/telephony/models/device_spec.js +80 -0
  306. data/spec/javascripts/telephony/models/transfer_spec.js +22 -0
  307. data/spec/javascripts/telephony/push_spec.js +427 -0
  308. data/spec/javascripts/telephony/views/agents_view_spec.js +101 -0
  309. data/spec/javascripts/telephony/views/application_view_spec.js +74 -0
  310. data/spec/javascripts/telephony/views/conversation_view_spec.js +626 -0
  311. data/spec/javascripts/telephony/views/status_view_spec.js +30 -0
  312. data/spec/javascripts/telephony/views/transfer_view_spec.js +187 -0
  313. data/spec/javascripts/telephony/views/twilio_client_view_spec.js +77 -0
  314. data/spec/javascripts/telephony/views/widget_view_spec.js +20 -0
  315. data/spec/lib/telephony/concerns/controllers/twilio_request_verifier_spec.rb +77 -0
  316. data/spec/lib/telephony/conversation_data_spec.rb +342 -0
  317. data/spec/lib/telephony/helper_spec.rb +24 -0
  318. data/spec/lib/telephony/jobs/agent_offline_spec.rb +37 -0
  319. data/spec/lib/telephony/jobs/pusher_event_spec.rb +39 -0
  320. data/spec/lib/telephony/providers/twilio_provider_spec.rb +439 -0
  321. data/spec/lib/telephony_spec.rb +104 -0
  322. data/spec/models/telephony/agent_spec.rb +479 -0
  323. data/spec/models/telephony/agent_state_machine_spec.rb +131 -0
  324. data/spec/models/telephony/call_center_spec.rb +32 -0
  325. data/spec/models/telephony/call_spec.rb +597 -0
  326. data/spec/models/telephony/call_state_machine_spec.rb +472 -0
  327. data/spec/models/telephony/conversation_spec.rb +751 -0
  328. data/spec/models/telephony/conversation_state_machine_spec.rb +387 -0
  329. data/spec/models/telephony/conversations_presenter_spec.rb +40 -0
  330. data/spec/models/telephony/event_spec.rb +716 -0
  331. data/spec/models/telephony/inbound_conversation_queue_spec.rb +243 -0
  332. data/spec/models/telephony/playable_listener_spec.rb +43 -0
  333. data/spec/models/telephony/pusher_event_publisher_spec.rb +36 -0
  334. data/spec/models/telephony/recording_spec.rb +6 -0
  335. data/spec/models/telephony/voicemail_spec.rb +96 -0
  336. data/spec/observers/telephony/agent_observer_spec.rb +21 -0
  337. data/spec/observers/telephony/event_observer_spec.rb +19 -0
  338. data/spec/requests/create_conversation_spec.rb +39 -0
  339. data/spec/requests/dequeue_call_spec.rb +42 -0
  340. data/spec/requests/list_conversations_spec.rb +33 -0
  341. data/spec/requests/presences_spec.rb +51 -0
  342. data/spec/requests/providers/twilio/calls/child_answered_spec.rb +24 -0
  343. data/spec/requests/providers/twilio/calls/child_detached_spec.rb +249 -0
  344. data/spec/requests/providers/twilio/calls/dial_spec.rb +132 -0
  345. data/spec/requests/providers/twilio/calls/done_spec.rb +69 -0
  346. data/spec/requests/providers/twilio/calls/join_conference_spec.rb +56 -0
  347. data/spec/requests/providers/twilio/calls/leave_queue_spec.rb +44 -0
  348. data/spec/requests/providers/twilio/calls/parent_answered_spec.rb +59 -0
  349. data/spec/requests/providers/twilio/inbound/connect_dequeued_call_spec.rb +124 -0
  350. data/spec/requests/providers/twilio/inbound/enqueue_inbound_call_spec.rb +108 -0
  351. data/spec/requests/providers/twilio/inbound/enqueued_call_wait_music_spec.rb +13 -0
  352. data/spec/requests/providers/twilio/voicemails/leave_voicemail_spec.rb +78 -0
  353. data/spec/requests/search_conversations_spec.rb +101 -0
  354. data/spec/requests/transfer_spec.rb +65 -0
  355. data/spec/requests/voicemail_api_spec.rb +58 -0
  356. data/spec/spec_helper.rb +27 -0
  357. data/spec/support/factory_girl.rb +5 -0
  358. data/spec/support/matchers/be_complete_hold.rb +20 -0
  359. data/spec/support/matchers/be_whisper_tone.rb +35 -0
  360. data/spec/support/matchers/join_conference.rb +23 -0
  361. data/spec/support/nokogiri.rb +1 -0
  362. data/spec/support/pusher_helper.rb +24 -0
  363. data/spec/support/vcr.rb +12 -0
  364. data/spec/support/webmock.rb +1 -0
  365. metadata +692 -0
@@ -0,0 +1,751 @@
1
+ require 'spec_helper'
2
+
3
+ module Telephony
4
+ describe Conversation do
5
+ describe '.begin!' do
6
+ before do
7
+ @from = '222-222-2222'
8
+ @agent = create :agent, phone_number: @from
9
+ @args = {
10
+ from_type: 'csr',
11
+ from_id: @agent.csr_id,
12
+ from: @from,
13
+ to_type: 'borrower',
14
+ to_id: 2,
15
+ to: '111-111-1111',
16
+ loan_id: 1
17
+ }
18
+ @count = Conversation.count
19
+ Telephony
20
+ .provider
21
+ .stub(:caller_id_for)
22
+ .and_return('111')
23
+ @sid = 'sid'
24
+ Telephony
25
+ .provider
26
+ .stub(:call)
27
+ .and_return(OpenStruct.new(sid: @sid))
28
+
29
+ @conversation = Conversation.begin! @args
30
+ end
31
+
32
+ it 'creates a conversation' do
33
+ Conversation.count.should == @count + 1
34
+ end
35
+
36
+ it 'creates an agent leg for the conversation' do
37
+ call = @conversation.calls.first
38
+ call.should be_agent
39
+ call.number.should == @args[:from]
40
+ call.participant_id.should == @args[:from_id]
41
+ call.participant_type.should == @args[:from_type]
42
+ call.agent.should == @agent
43
+ end
44
+
45
+ it 'creates a customer leg for the conversation' do
46
+ call = @conversation.calls.last
47
+ call.should_not be_agent
48
+ call.number.should == @args[:to]
49
+ call.participant_id.should == @args[:to_id]
50
+ call.participant_type.should == @args[:to_type]
51
+ end
52
+
53
+ it 'connects the conversation' do
54
+ @conversation.should be_connecting
55
+ end
56
+
57
+ it 'connects the initiating call' do
58
+ call = @conversation.calls.first
59
+ call.should be_connecting
60
+ call.sid.should == @sid
61
+ end
62
+ end
63
+
64
+ describe "caller_id" do
65
+ let(:args) do
66
+ {
67
+ from_type: 'csr',
68
+ from_id: @agent.csr_id,
69
+ from: '222-222-2222',
70
+ to_type: 'borrower',
71
+ to_id: 2,
72
+ to: '111-111-1111',
73
+ loan_id: 1
74
+ }
75
+ end
76
+ let(:sid) { 'sid' }
77
+ let(:outbound_caller_id) { "18888888888" }
78
+
79
+ before do
80
+ Telephony.provider.stub(:caller_id_for).and_return('111-111-1111')
81
+ Telephony.provider.stub(:call).and_return(OpenStruct.new(sid: sid))
82
+ end
83
+
84
+ context "when generate caller id is true" do
85
+ before do
86
+ @agent = create :agent, generate_caller_id: true
87
+ @conversation = Conversation.begin! args
88
+ end
89
+
90
+ it "will use the same area code as the customer's area code" do
91
+ @conversation.caller_id.should == '111-111-1111'
92
+ end
93
+ end
94
+
95
+ context "when generate caller id is false" do
96
+ before do
97
+ @agent = create :agent, generate_caller_id: false
98
+ Telephony.provider.stub(:outbound_caller_id).and_return(outbound_caller_id)
99
+ @conversation = Conversation.begin! args
100
+ end
101
+
102
+ it "will use the same area code as the customer's area code" do
103
+ @conversation.caller_id.should == outbound_caller_id
104
+ end
105
+ end
106
+ end
107
+
108
+ describe '#terminate_conferenced_calls' do
109
+ before do
110
+ @calls = [
111
+ build(:in_conference_call),
112
+ build(:in_conference_call),
113
+ build(:in_conference_call)
114
+ ]
115
+ @non_hungup_call = @calls.first
116
+ @conversation = @non_hungup_call.conversation
117
+ @conversation.calls = @calls
118
+
119
+ provider_stub = 'stub'
120
+ Telephony.stub(:provider).and_return(provider_stub)
121
+ provider_stub.should_receive(:hangup).exactly(2).times
122
+ provider_stub.should_not_receive(:hangup).with(@non_hungup_call.sid)
123
+
124
+ end
125
+
126
+ it 'calls hangup on all calls that are in_conference' do
127
+ @conversation.terminate_conferenced_calls @non_hungup_call.id
128
+ end
129
+
130
+ it 'terminates all calls that are in_conference' do
131
+ @conversation.terminate_conferenced_calls @non_hungup_call.id
132
+
133
+ terminated_calls = @calls - [@non_hungup_call]
134
+
135
+ @non_hungup_call.should be_in_conference
136
+ terminated_calls.each { |call| call.should be_terminated }
137
+ end
138
+ end
139
+
140
+ describe '#transfer!' do
141
+ context 'by default' do
142
+ before do
143
+ @number = '111-111-1111'
144
+ @transfer_id = '999'
145
+ @conversation = create :conversation, state: 'in_progress'
146
+ @call = create :active_agent_leg, conversation: @conversation
147
+ @participant = create :in_progress_call, conversation: @conversation
148
+ @agent = create :available_agent, csr_id: @transfer_id
149
+ Telephony
150
+ .provider
151
+ .should_receive(:redirect_to_dial)
152
+
153
+ @conversation.transfer! @transfer_id, true
154
+ @conversation = Conversation.find @conversation.id
155
+ end
156
+
157
+ it 'transfers a call using its telephony provider' do
158
+ @conversation.should be_one_step_transferring
159
+ end
160
+
161
+ it 'creates calls' do
162
+ @conversation.should have(3).calls
163
+ end
164
+ end
165
+
166
+ context 'given a conversation that is not in progress or in progress hold' do
167
+ before do
168
+ @conversation = create :conversation, state: 'in_progress_two_step_transfer'
169
+ end
170
+
171
+ it 'should return an error' do
172
+ @conversation.transfer!(1, true).should be_false
173
+ @conversation.errors[:base].should == ["Conversation already #{@conversation.state}"]
174
+ end
175
+ end
176
+
177
+ context 'given a one-step transfer' do
178
+ before do
179
+ conversation = create :conversation, state: 'in_progress'
180
+ call = create :active_agent_leg, conversation: conversation
181
+ participant = create :in_progress_call, conversation: conversation
182
+ number = '111-111-1111'
183
+ agent = create :agent, phone_number: number
184
+ Telephony
185
+ .provider
186
+ .should_receive(:redirect_to_dial)
187
+ .with(participant.id, participant.sid)
188
+ .and_return(true)
189
+
190
+ @ok = conversation.transfer! agent.csr_id, true
191
+ end
192
+
193
+ it 'returns true' do
194
+ @ok.should be_true
195
+ end
196
+ end
197
+
198
+ context 'given a one-step transfer for a call that has terminated' do
199
+ before do
200
+ conversation = create :conversation, state: 'in_progress'
201
+ @call = create :call, conversation: conversation
202
+ create :participant, conversation: conversation
203
+ agent = create :agent
204
+
205
+ Telephony
206
+ .provider
207
+ .stub(:redirect_to_dial)
208
+ .and_raise(Telephony::Error::Connection.new("Error message"))
209
+
210
+ @ok = conversation.transfer! agent.csr_id, true
211
+ end
212
+
213
+ it 'returns false' do
214
+ @ok.should be_false
215
+ end
216
+
217
+ it 'has a validation error' do
218
+ @call.conversation.errors[:base].should have(1).error
219
+ end
220
+ end
221
+
222
+ context 'given a one-step transfer to an offline agent' do
223
+ before do
224
+ @conversation = create :in_progress_conversation
225
+ create :active_agent_leg, conversation: @conversation
226
+ create :in_progress_call, conversation: @conversation
227
+ offline_agent = create :offline_agent
228
+
229
+ Telephony
230
+ .provider
231
+ .stub(:redirect_to_dial)
232
+ .and_return(true)
233
+
234
+ @conversation.transfer! offline_agent.csr_id, true
235
+ end
236
+
237
+ it 'transitions the conversation into leaving a voicemail' do
238
+ @conversation.reload
239
+ @conversation.should be_leaving_voicemail
240
+ end
241
+
242
+ it 'creates a straight to voicemail event' do
243
+ @conversation.events.last(2).first.should be_kind_of(Telephony::Events::StraightToVoicemail)
244
+ end
245
+ end
246
+
247
+ context 'given a successful two-step transfer' do
248
+ before do
249
+ call = double 'call', sid: 1
250
+ provider = double('Provider')
251
+ provider.stub(:dial_into_conference).and_return(call)
252
+ Telephony.stub(:provider).and_return(provider)
253
+
254
+ @conversation = create :conversation, state: 'in_progress'
255
+ @call = create :active_agent_leg, conversation: @conversation
256
+ @participant = create :participant, conversation: @conversation
257
+ agent = create :available_agent
258
+
259
+ Telephony
260
+ .provider
261
+ .should_receive(:redirect_to_conference)
262
+ .with(@conversation.customer.id, @participant.sid)
263
+ .and_return(true)
264
+
265
+ @participants_count = @conversation.calls.count
266
+ @ok = @conversation.transfer! agent.csr_id, false
267
+
268
+ @conversation.reload
269
+ end
270
+
271
+ after do
272
+ Telephony.unstub :provider
273
+ end
274
+
275
+ it "adds another participant to itself" do
276
+ @conversation.calls.count.should == @participants_count + 1
277
+ end
278
+
279
+ it 'returns true' do
280
+ @ok.should be_true
281
+ end
282
+ end
283
+
284
+ context 'given a two step transfer for a call that has terminated' do
285
+ before do
286
+ @conversation = create :conversation, state: 'in_progress'
287
+ @call = create :call, conversation: @conversation
288
+ create :participant, conversation: @conversation
289
+ agent = create :agent
290
+
291
+ Telephony
292
+ .provider
293
+ .stub(:redirect_to_conference)
294
+ .and_raise(Telephony::Error::Connection.new)
295
+
296
+ @ok = @conversation.transfer! agent.csr_id, false
297
+ end
298
+
299
+ it 'returns false' do
300
+ @ok.should be_false
301
+ end
302
+
303
+ it 'has a validation error' do
304
+ @conversation.errors[:base].should have(1).error
305
+ end
306
+ end
307
+
308
+ context 'given a two step transfer to an unavailable agent' do
309
+ before do
310
+ @conversation = create :conversation, state: 'in_progress'
311
+ create :active_agent_leg, conversation: @conversation
312
+ create :customer_leg, conversation: @conversation
313
+ agent = create :not_available_agent
314
+
315
+ @ok = @conversation.transfer! agent.csr_id, false
316
+ end
317
+
318
+ it 'returns false' do
319
+ @ok.should be_false
320
+ end
321
+
322
+ it 'has a validation error' do
323
+ @conversation.errors[:base].should have(1).error
324
+ @conversation.errors[:base].first.should =~ /agent is unavailable/i
325
+ end
326
+ end
327
+
328
+ context 'given a transfer for a conversation that has terminated' do
329
+ before do
330
+ @conversation = create :conversation, state: :terminated
331
+ @call = create :call, conversation: @conversation
332
+ create :participant, conversation: @conversation
333
+ agent = create :agent
334
+
335
+ @ok = @conversation.transfer! agent.csr_id, true
336
+ end
337
+
338
+ it 'returns false' do
339
+ @ok.should be_false
340
+ end
341
+
342
+ it 'has a validation error' do
343
+ @conversation.errors[:base].should have(1).error
344
+ end
345
+ end
346
+ end
347
+
348
+ describe '#as_json' do
349
+ before do
350
+ conversation = create :conversation
351
+ conversation.calls.build
352
+ conversation.calls.build
353
+ conversation.calls.first.recordings.build
354
+
355
+ @json = conversation.as_json
356
+ end
357
+
358
+ it 'includes the conversations calls and their recordings' do
359
+ @json.should have_key(:calls)
360
+ @json[:calls].should have(2).items
361
+ @json[:calls].first.should have_key(:recordings)
362
+ @json[:calls].first[:recordings].should have(1).item
363
+ end
364
+ end
365
+
366
+ describe '.new' do
367
+ before do
368
+ @conversation = Conversation.new
369
+ end
370
+
371
+ it 'defaults its conversation type to "outbound"' do
372
+ @conversation.should be_outbound
373
+ end
374
+ end
375
+
376
+ describe '.create_inbound!' do
377
+ before do
378
+ @args = {
379
+ number: 'number'
380
+ }
381
+ @count = Conversation.count
382
+
383
+ @conversation = Conversation.create_inbound! @args
384
+ end
385
+
386
+ it 'creates a new inbound conversation' do
387
+ Conversation.count.should == @count + 1
388
+ @conversation.should be_inbound
389
+ end
390
+
391
+ it "sets the conversation's attributes" do
392
+ @conversation.number.should == @args[:number]
393
+ end
394
+ end
395
+
396
+ describe '.find_with_lock' do
397
+ before do
398
+ agent = create :agent
399
+ @conversation = create :conversation
400
+ create(:call, conversation: @conversation, agent: agent)
401
+ end
402
+
403
+ it 'yields the conversation' do
404
+ Conversation.find_with_lock @conversation.id do |conversation|
405
+ @yielded_conversation = conversation
406
+ end
407
+
408
+ @yielded_conversation.should == @conversation
409
+ end
410
+ end
411
+
412
+ describe '.find_inbound_with_lock' do
413
+ before do
414
+ @conversation = create :conversation
415
+ end
416
+
417
+ it 'yields the conversation' do
418
+ Conversation.find_inbound_with_lock @conversation.id do |conversation|
419
+ @yielded_conversation = conversation
420
+ end
421
+
422
+ @yielded_conversation.should == @conversation
423
+ end
424
+ end
425
+
426
+ describe '#check_for_successful_hold' do
427
+ context 'given a conversation with the customer already on hold' do
428
+ before do
429
+ @conversation = create :initiating_hold_conversation
430
+ @agent_leg = create(:active_agent_leg,
431
+ conversation: @conversation)
432
+ create(:customer_leg,
433
+ state: 'in_progress_hold',
434
+ conversation: @conversation)
435
+ end
436
+
437
+ context 'when the agent has moved to a conference' do
438
+ before do
439
+ @agent_leg.conference!
440
+ end
441
+
442
+ it 'transitions to in_progress_hold' do
443
+ @conversation.reload.state.should == 'in_progress_hold'
444
+ end
445
+ end
446
+
447
+ context 'when the agent has not moved to a conference' do
448
+ it 'does not transition' do
449
+ @conversation.check_for_successful_hold
450
+
451
+ @conversation.reload.state.should == 'initiating_hold'
452
+ end
453
+ end
454
+ end
455
+
456
+ context 'given a conversation with the agents already in a conference' do
457
+ before do
458
+ @conversation = create :initiating_hold_conversation
459
+ create(:active_agent_leg,
460
+ state: 'in_conference',
461
+ conversation: @conversation)
462
+ @customer_leg = create(:customer_leg,
463
+ conversation: @conversation)
464
+ create(:active_agent_leg,
465
+ state: 'in_conference',
466
+ conversation: @conversation)
467
+ end
468
+
469
+ context 'when the customer has transitioned to in_progress_hold' do
470
+ before do
471
+ @customer_leg.complete_hold!
472
+ end
473
+
474
+ it 'transitions to in_progress_hold' do
475
+ @conversation.reload.state.should == 'in_progress_hold'
476
+ end
477
+ end
478
+
479
+ context 'when the customer has not transitioned to in_progress_hold' do
480
+ it 'does not transition' do
481
+ @conversation.check_for_successful_hold
482
+
483
+ @conversation.reload.state.should == 'initiating_hold'
484
+ end
485
+ end
486
+ end
487
+ end
488
+
489
+ describe '#check_for_successful_resume' do
490
+ before do
491
+ @conversation = create :initiating_resume_conversation
492
+ create(:active_agent_leg,
493
+ state: 'in_conference',
494
+ conversation: @conversation)
495
+ @customer_leg = create(:customer_leg,
496
+ state: 'in_progress_hold',
497
+ conversation: @conversation)
498
+ end
499
+
500
+ context 'when the customer moves to a conference' do
501
+ before do
502
+ @customer_leg.conference!
503
+ end
504
+
505
+ it 'transitions to in_progress_hold' do
506
+ @conversation.reload.state.should == 'in_progress'
507
+ end
508
+ end
509
+
510
+ context 'when the customer has not moved to a conference' do
511
+ it 'does not transition' do
512
+ @conversation.check_for_successful_resume
513
+
514
+ @conversation.reload.state.should == 'initiating_resume'
515
+ end
516
+ end
517
+ end
518
+
519
+ describe '#hold!' do
520
+ context 'given a conversation in a non-transferable state' do
521
+ before do
522
+ @conversation = create :two_step_transferring_conversation
523
+ end
524
+
525
+ it 'raises an exception' do
526
+ expect {
527
+ @conversation.hold!
528
+ }.to raise_error(StateMachine::InvalidTransition)
529
+ end
530
+ end
531
+
532
+ context 'given a conversation with conference calls' do
533
+ context 'with one agent' do
534
+ before do
535
+ @conversation = create :in_progress_conversation
536
+ create(:active_agent_leg,
537
+ state: 'in_conference',
538
+ conversation: @conversation)
539
+ create(:customer_leg,
540
+ state: 'in_conference',
541
+ conversation: @conversation)
542
+ end
543
+
544
+ it 'redirects the customer to the hold queue and the agent to a conference (so the agent hears hold music)' do
545
+ @conversation.active_agent_leg.should_receive(:redirect_to_conference)
546
+ @conversation.customer.should_receive(:redirect_to_hold)
547
+
548
+ @conversation.hold!
549
+ end
550
+ end
551
+
552
+ context 'with two agents' do
553
+ before do
554
+ @conversation = create :in_progress_two_step_transfer_conversation
555
+ create(:active_agent_leg,
556
+ state: 'in_conference',
557
+ conversation: @conversation)
558
+ create(:customer_leg,
559
+ state: 'in_conference',
560
+ conversation: @conversation)
561
+ create(:active_agent_leg,
562
+ state: 'in_conference',
563
+ conversation: @conversation)
564
+ end
565
+
566
+ it 'redirects the customer to the hold queue and leaves the agents in the conference' do
567
+ @conversation.active_agent_legs.each {|leg| leg.should_not_receive(:redirect_to_conference) }
568
+ @conversation.customer.should_receive(:redirect_to_hold)
569
+
570
+ @conversation.hold!
571
+ end
572
+ end
573
+ end
574
+
575
+ context 'given a customer as the parent call' do
576
+ before do
577
+ @conversation = create :in_progress_conversation
578
+ create(:customer_leg,
579
+ conversation: @conversation)
580
+ create(:active_agent_leg,
581
+ conversation: @conversation)
582
+ end
583
+
584
+ it "changes the conversation to initiating hold" do
585
+ @conversation.active_agent_leg.stub(:redirect_to_conference)
586
+ @conversation.hold!
587
+ @conversation.reload.should be_initiating_hold
588
+ end
589
+
590
+ it 'redirects the agent to a conference' do
591
+ @conversation.active_agent_leg.should_receive(:redirect_to_conference)
592
+ @conversation.hold!
593
+ end
594
+ end
595
+
596
+ context 'given an agent as the parent call' do
597
+ before do
598
+ @conversation = create :in_progress_conversation
599
+ create(:active_agent_leg,
600
+ conversation: @conversation)
601
+ create(:customer_leg,
602
+ conversation: @conversation)
603
+ end
604
+
605
+ it 'redirects the customer to the hold queue' do
606
+ @conversation.customer.should_receive(:redirect_to_hold)
607
+ @conversation.hold!
608
+ end
609
+ end
610
+ end
611
+
612
+ describe '#resume!' do
613
+ context 'given a conversation in a non-resumable state' do
614
+ before do
615
+ @conversation = create :two_step_transferring_conversation
616
+ end
617
+
618
+ it 'raises an exception' do
619
+ expect {
620
+ @conversation.resume!
621
+ }.to raise_error(StateMachine::InvalidTransition)
622
+ end
623
+ end
624
+
625
+ context 'given a conversation with conference calls' do
626
+ before do
627
+ @conversation = create :in_progress_hold_conversation
628
+ create(:active_agent_leg,
629
+ state: 'in_conference',
630
+ conversation: @conversation)
631
+ create(:customer_leg,
632
+ state: 'in_progress_hold',
633
+ conversation: @conversation)
634
+ end
635
+
636
+ it 'redirects the customer to a conference' do
637
+ @conversation.customer.should_receive(:redirect_to_conference)
638
+
639
+ @conversation.resume!
640
+ end
641
+ end
642
+ end
643
+
644
+ describe '#active_agent_leg' do
645
+ context 'given a conversation with active and inactive agent legs' do
646
+ before do
647
+ @conversation = create :conversation
648
+ create(:inactive_agent_leg,
649
+ conversation: @conversation)
650
+ create(:customer_leg,
651
+ conversation: @conversation)
652
+ create(:active_agent_leg,
653
+ conversation: @conversation)
654
+
655
+ @active_agent_leg = @conversation.active_agent_leg
656
+ end
657
+
658
+ it 'returns the active agent leg' do
659
+ @active_agent_leg.should == @conversation.calls[2]
660
+ end
661
+ end
662
+ end
663
+
664
+ describe "#first_active_agent" do
665
+ context "outbound call" do
666
+ let(:agent1) { build :agent }
667
+ let(:conversation) { build :outbound_conversation }
668
+
669
+ before do
670
+ conversation.calls.first.agent = agent1
671
+ end
672
+
673
+ it "returns the first call's agent" do
674
+ conversation.first_active_agent.should == agent1
675
+ end
676
+ end
677
+ end
678
+
679
+ describe "#customer" do
680
+ context "inbound call" do
681
+ let(:conversation) { build :inbound_conversation }
682
+
683
+ it "returns the first call leg" do
684
+ conversation.customer.should == conversation.calls.first
685
+ end
686
+ end
687
+
688
+ context "outbound call" do
689
+ let(:conversation) { build :outbound_conversation }
690
+
691
+ it "returns the second call leg" do
692
+ conversation.customer.should == conversation.calls.second
693
+ end
694
+ end
695
+ end
696
+
697
+ describe "#child_call" do
698
+ let(:conversation) { build :outbound_conversation }
699
+
700
+ context "given a sid" do
701
+ it "returns the call with that sid" do
702
+ call = conversation.calls.first
703
+ conversation.child_call(call.sid).should == call
704
+ end
705
+ end
706
+
707
+ context "given nil" do
708
+ it "returns the second active call" do
709
+ call = conversation.calls.last
710
+ conversation.child_call.should == call
711
+ end
712
+ end
713
+ end
714
+
715
+ describe '#first_inactive_agent' do
716
+ context 'given a conversation with an inactive agent' do
717
+ before do
718
+ @agent = create :agent
719
+ conversation = create :conversation
720
+ call = create :call,
721
+ agent: @agent,
722
+ conversation: conversation
723
+ call.terminate!
724
+
725
+ @first_inactive_agent = conversation.first_inactive_agent
726
+ end
727
+
728
+ it 'returns the inactive agent' do
729
+ @first_inactive_agent.should == @agent
730
+ end
731
+ end
732
+
733
+ context 'given a conversation without an inactive agent' do
734
+ before do
735
+ agent = create :agent
736
+ conversation = create :conversation
737
+ call = create :call,
738
+ agent: agent,
739
+ conversation: conversation
740
+ call.connect!
741
+
742
+ @first_inactive_agent = conversation.first_inactive_agent
743
+ end
744
+
745
+ it 'returns nil' do
746
+ @first_inactive_agent.should be_nil
747
+ end
748
+ end
749
+ end
750
+ end
751
+ end