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,30 @@
1
+ describe("Zest.Telephony.Views.StatusView", function() {
2
+ describe("toggle status", function() {
3
+ var view;
4
+
5
+ beforeEach(function() {
6
+ setFixtures('<div id="status"></div>');
7
+ view = new Zest.Telephony.Views.StatusView({csrId: 200, el: $("#status")});
8
+ view.render();
9
+ })
10
+
11
+ it("handles status change via the button click", function() {
12
+ spyOn(view.agent, 'toggleAvailable');
13
+ view.$("button").click();
14
+
15
+ expect(view.agent.toggleAvailable).toHaveBeenCalled();
16
+ });
17
+
18
+ it("handles status change from Pusher event", function() {
19
+ $(document).trigger("telephony:csrDidChangeStatus", [{status: "some_status"}]);
20
+ expect(view.$("button")).toHaveText("Some status");
21
+ });
22
+
23
+ describe("on a call", function() {
24
+ it("disbales the button", function() {
25
+ $(document).trigger("telephony:csrDidChangeStatus", [{status: "on_a_call"}]);
26
+ expect(view.$('button')).toBeDisabled();
27
+ });
28
+ });
29
+ });
30
+ });
@@ -0,0 +1,187 @@
1
+ describe("Zest.Telephony.Views.TransferView", function() {
2
+ var view;
3
+ var request;
4
+
5
+ beforeEach(function() {
6
+ setFixtures('<div id="transfer"></div>');
7
+ view = new Zest.Telephony.Views.TransferView({
8
+ el: $("#transfer")
9
+ });
10
+
11
+ jasmine.Ajax.useMock();
12
+
13
+ view.render();
14
+
15
+ request = mostRecentAjaxRequest();
16
+ var data = [
17
+ {
18
+ id: 123,
19
+ name: 'abc',
20
+ csr_id: 1000,
21
+ csr_type: 'A'
22
+ },
23
+ {
24
+ id: 456,
25
+ name: 'xyz',
26
+ csr_id: 1001,
27
+ csr_type: 'B'
28
+ }
29
+ ];
30
+ request.response({status: 200, responseText: JSON.stringify(data)});
31
+ });
32
+
33
+ describe("selecting agent by pressing the enter key", function() {
34
+ it("selects the best matched agent", function() {
35
+ $('form').submit();
36
+
37
+ expect($('input[name=selected_agent]').val()).toMatch(/abc/);
38
+ });
39
+ });
40
+
41
+ describe("agents list", function() {
42
+ describe("when agent is not selected", function() {
43
+ it("does not show the initial transfer button", function() {
44
+ expect($('#telephony-transfer')).toContain("span.controls.hidden");
45
+ });
46
+
47
+ it("shows the list of agents", function() {
48
+ var $agents = $("li", view.el);
49
+ expect($agents[0].innerHTML).toMatch(/abc/);
50
+ expect($agents[1].innerHTML).toMatch(/xyz/);
51
+ });
52
+ });
53
+
54
+ describe("when agent is selected", function() {
55
+ var agent;
56
+
57
+ beforeEach(function() {
58
+ agent = new Zest.Telephony.Models.Agent({
59
+ name: "Bruce",
60
+ status: "available",
61
+ phone_ext: "14"
62
+ });
63
+ });
64
+
65
+ it("displays the selected agent's info", function() {
66
+ $('#transfer').trigger("agentDidSelect", agent);
67
+
68
+ expect(view.$('input[name=selected_agent]')).toHaveValue(agent.displayText());
69
+ });
70
+
71
+ it("disables the filter", function() {
72
+ $('#transfer').trigger("agentDidSelect", agent);
73
+
74
+ expect(view.$('input[name=selected_agent]')).toBeDisabled();
75
+ });
76
+
77
+ it("display agent status", function() {
78
+ $('#transfer').trigger("agentDidSelect", agent);
79
+ expect(view.$('form .agent-input span')).toHaveClass('icon-user available');
80
+ });
81
+
82
+ describe("agent is available", function() {
83
+ it("defaults to two-step", function() {
84
+ $('#transfer').trigger("agentDidSelect", agent);
85
+
86
+ expect(view.$('#transfer_type_two_step')).toBeChecked();
87
+ expect(view.$('#transfer_type_two_step')).not.toBeDisabled();
88
+ expect(view.$('#transfer_type_one_step')).not.toBeChecked();
89
+ });
90
+ });
91
+
92
+ describe("agent is not available", function() {
93
+ it("disables the 2-step radio", function() {
94
+ agent.set({status: "not_available"});
95
+
96
+ $('#transfer').trigger("agentDidSelect", agent);
97
+
98
+ expect(view.$('#transfer_type_one_step')).toBeChecked();
99
+ expect(view.$('#transfer_type_two_step')).toBeDisabled();
100
+ expect(view.$('#transfer_type_two_step')).not.toBeChecked();
101
+ });
102
+ });
103
+ });
104
+
105
+ describe("backspace button", function() {
106
+ var agent;
107
+
108
+ beforeEach(function() {
109
+ agent = new Zest.Telephony.Models.Agent({
110
+ name: "Bruce",
111
+ status: "available",
112
+ phone_ext: "14"
113
+ });
114
+ });
115
+
116
+ it("is not shown when agent is not selected", function() {
117
+ expect(view.$('form span.backspace')).toHaveClass('hidden');
118
+ });
119
+
120
+ it("is visible when agent is selected", function() {
121
+ $('#transfer').trigger("agentDidSelect", agent);
122
+ expect(view.$('form span.backspace')).not.toHaveClass('hidden');
123
+ });
124
+
125
+ it("handles click event", function() {
126
+ $('#transfer').trigger("agentDidSelect", agent);
127
+
128
+ spyOn(view, 'render');
129
+ $('span.backspace').click();
130
+ expect(view.transfer.get("selectedAgent")).toBeNull();
131
+ expect(view.render).toHaveBeenCalled();
132
+ });
133
+ });
134
+ });
135
+
136
+ describe("when initiating a transfer", function() {
137
+ describe('to an transferrable agent', function() {
138
+ beforeEach(function() {
139
+ var agent = new Zest.Telephony.Models.Agent({
140
+ name: "Bruce",
141
+ status: "available",
142
+ phone_ext: "14"
143
+ });
144
+ $('#transfer').trigger("agentDidSelect", agent);
145
+
146
+ view.$('form #transfer_type_one_step').click();
147
+ view.$('button.initiate-transfer').click();
148
+ });
149
+
150
+ it("saves the transfer", function() {
151
+ expect(view.transfer.get('transferType')).toEqual('one_step');
152
+ });
153
+ });
154
+
155
+ describe('to an untransferrable agent', function() {
156
+ var transferFailedSpy;
157
+
158
+ beforeEach(function() {
159
+ var agent = new Zest.Telephony.Models.Agent({
160
+ name: "Bruce",
161
+ status: "available",
162
+ phone_ext: "14"
163
+ });
164
+ $('#transfer').trigger("agentDidSelect", agent);
165
+
166
+ transferFailedSpy = jasmine.createSpy('transferFailed');
167
+ $(document).bind('transferFailed', transferFailedSpy);
168
+
169
+ view.$('form #transfer_type_one_step').click();
170
+ view.$('button.initiate-transfer').click();
171
+ });
172
+
173
+ it("triggers a transfer failed event", function() {
174
+ var transferRequest = mostRecentAjaxRequest();
175
+ var responseBody = {
176
+ errors: ['Agent is unavailable']
177
+ }
178
+ transferRequest.response({ status: 422,
179
+ responseText: JSON.stringify(responseBody)});
180
+ expect(transferFailedSpy).toHaveBeenCalled();
181
+ var call = transferFailedSpy.mostRecentCall;
182
+ expect(call.args[1]).toMatch(/agent is unavailable/i);
183
+ });
184
+ });
185
+ });
186
+ });
187
+
@@ -0,0 +1,77 @@
1
+ describe("Zest.Telephony.Views.TwilioClientView", function() {
2
+ describe("#initialize", function() {
3
+ var view;
4
+
5
+ beforeEach(function() {
6
+ jasmine.Ajax.useMock();
7
+ setFixtures("<div class='wrapper'> </div>");
8
+ view = new Zest.Telephony.Views.TwilioClientView({
9
+ csrId: 123
10
+ });
11
+ });
12
+
13
+ it("retrieves a twilio token for an agent", function() {
14
+ var request = mostRecentAjaxRequest();
15
+ expect(request.url).toBe('/zestphone/twilio_client/token?csr_id=123');
16
+ });
17
+ });
18
+
19
+ describe("#deviceAnswer", function() {
20
+ var view;
21
+ var conn = { accept: function(){} };
22
+
23
+ beforeEach(function() {
24
+ jasmine.Ajax.useMock();
25
+ setFixtures("<div class='wrapper'> </div>");
26
+ //agent = { onACall: function() {} };
27
+ view = new Zest.Telephony.Views.TwilioClientView({
28
+ csrId: 123
29
+ });
30
+ view.connection = conn;
31
+ });
32
+
33
+ it("registers a function to confirm a page reload", function() {
34
+ spyOn(view, 'disallowBrowserReload');
35
+ view.deviceAnswer();
36
+
37
+ expect(view.disallowBrowserReload).toHaveBeenCalled();
38
+ });
39
+
40
+ it("disables the answer button", function() {
41
+ view.deviceAnswer();
42
+
43
+ expect(view.$('button.answer')).toBeDisabled();
44
+ expect(view.$('button.answer')).not.toHaveClass('hidden');
45
+ });
46
+ });
47
+
48
+ describe("#deviceHangup", function() {
49
+ var view;
50
+
51
+ beforeEach(function() {
52
+ window.Twilio = { Device: { disconnectAll: function() {} } };
53
+ jasmine.Ajax.useMock();
54
+ setFixtures("<div class='wrapper'> </div>");
55
+ view = new Zest.Telephony.Views.TwilioClientView({
56
+ csrId: 123
57
+ });
58
+ });
59
+
60
+ afterEach(function() {
61
+ window.Twilio = null;
62
+ });
63
+
64
+ it("unregisters a function to confirm a page reload", function() {
65
+ spyOn(view, 'allowBrowserReload');
66
+ view.deviceHangup();
67
+
68
+ expect(view.allowBrowserReload).toHaveBeenCalled();
69
+ });
70
+
71
+ it("hides the hangup button", function() {
72
+ view.deviceHangup();
73
+
74
+ expect(view.$('button.hangup')).toHaveClass('hidden');
75
+ });
76
+ });
77
+ });
@@ -0,0 +1,20 @@
1
+ describe('Zest.Telephony.Views.WidgetView', function () {
2
+ describe('#render', function () {
3
+ var widgetView;
4
+
5
+ beforeEach(function () {
6
+ widgetView = new Zest.Telephony.Views.WidgetView();
7
+ widgetView.render();
8
+ });
9
+
10
+ it('adds the widget CSS styles', function() {
11
+ var widgetCss = _.last($('head link'));
12
+ expect($(widgetCss)).toHaveAttr('href', '/assets/telephony/widget.css');
13
+ });
14
+
15
+ it('adds the conversation view', function() {
16
+ expect($(widgetView.el)).toContain('.conversation-wrapper');
17
+ });
18
+ });
19
+ });
20
+
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ module Telephony::Concerns::Controllers
4
+ describe TwilioRequestVerifier, :type => :controller do
5
+ let(:token) { '123456789' }
6
+ let(:signature) { '3ugjK9yQlIvxbBwmdF/7BfDG5Uk=' }
7
+
8
+ controller do
9
+ include TwilioRequestVerifier
10
+
11
+ def index
12
+ head :ok
13
+ end
14
+ end
15
+
16
+ it 'verifies twilio request' do
17
+ twilio_params = {
18
+ 'ToState' => 'California',
19
+ 'CalledState' => 'California',
20
+ 'Direction' => 'inbound',
21
+ 'FromState' => 'CA',
22
+ 'AccountSid' => 'ACFAKEACCOUNTSID',
23
+ 'Caller' => '+15555551235',
24
+ 'CallerZip' => '94108',
25
+ 'CallerCountry' => 'US',
26
+ 'From' => '+15555551235',
27
+ 'FromCity' => 'SAN FRANCISCO',
28
+ 'CallerCity' => 'SAN FRANCISCO',
29
+ 'To' => '+15555551235',
30
+ 'FromZip' => '94108',
31
+ 'FromCountry' => 'US',
32
+ 'ToCity' => '',
33
+ 'CallStatus' => 'ringing',
34
+ 'CalledCity' => '',
35
+ 'CallerState' => 'CA',
36
+ 'CalledZip' => '',
37
+ 'ToZip' => '',
38
+ 'ToCountry' => 'US',
39
+ 'CallSid' => 'CAFAKECALLSID',
40
+ 'CalledCountry' => 'US',
41
+ 'Called' => '+15555551234',
42
+ 'ApiVersion' => '2010-04-01',
43
+ 'ApplicationSid' => 'APFAKEAPPLICATIONSID'
44
+ }
45
+
46
+ controller.stub(:twilio_config).and_return({
47
+ :auth_token => token,
48
+ :callback_domain => "example.com",
49
+ :use_twilio_digest_auth => true
50
+ })
51
+
52
+ request.stub(:fullpath).and_return("/validate/voice")
53
+ request.stub_chain(:headers, :[]).with('HTTP_X_TWILIO_SIGNATURE')
54
+ .and_return(signature)
55
+ post :index, twilio_params
56
+
57
+ response.code.should == "200"
58
+ end
59
+
60
+ context "with invalid params" do
61
+ before do
62
+ request.stub_chain(:headers, :[]).with('HTTP_X_TWILIO_SIGNATURE')
63
+ .and_return(signature)
64
+ controller.stub(:twilio_config).and_return({
65
+ :auth_token => token,
66
+ :use_twilio_digest_auth => true
67
+ })
68
+ end
69
+
70
+ it "returns a 401" do
71
+ get :index, { :foo => 'bar' }
72
+ response.code.should == "401"
73
+ response.body.should == "Twilio request signature verification failed."
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,342 @@
1
+ require 'spec_helper'
2
+
3
+ module Telephony
4
+ describe ConversationData do
5
+ describe '.filter' do
6
+ before do
7
+ create :conversation, created_at: 5.days.ago
8
+ create_list :terminated_conversation, 2, created_at: 4.days.ago
9
+ create :terminated_conversation
10
+ create_list :in_progress_conversation, 3
11
+ create :connecting_conversation
12
+ end
13
+
14
+ context 'given no parameters' do
15
+ it 'returns all conversations' do
16
+ ConversationData.filter.should have(8).items
17
+ end
18
+ end
19
+
20
+ context 'given a state parameter' do
21
+ it 'returns conversations with that state' do
22
+ ConversationData.filter(state: 'terminated').should have(3).items
23
+ end
24
+ end
25
+
26
+ context 'given a since parameter' do
27
+ it 'returns all conversations that were created after that date' do
28
+ ConversationData.filter(since: 2.days.ago.to_param).should have(5).items
29
+ end
30
+ end
31
+
32
+ context 'given a combination of parameters' do
33
+ it 'returns all conversations that match all parameters' do
34
+ ConversationData.filter(since: 2.days.ago.to_param, state: 'terminated').should have(1).item
35
+ end
36
+ end
37
+
38
+ context 'any other parameters' do
39
+ it 'ignores them' do
40
+ ConversationData.filter(foo: 'bar').should have(8).items
41
+ end
42
+ end
43
+ end
44
+
45
+ describe '.search' do
46
+ context 'by default' do
47
+ before do
48
+ 4.times do |n|
49
+ create :conversation, created_at: n.days.from_now
50
+ end
51
+ create :call_with_recording, conversation: Conversation.last
52
+ create :call_with_recording, conversation: Conversation.last
53
+ create :conversation_connect_event, conversation: Conversation.last
54
+
55
+ @conversations = ConversationData.search
56
+ @calls = @conversations.first.calls
57
+ @events = @conversations.first.events
58
+ @recordings = @calls.first.recordings
59
+ end
60
+
61
+ it 'returns conversations, events, calls and recordings' do
62
+ @conversations.count.should == 4
63
+ @calls.count.should == 2
64
+ @events.count.should == 1
65
+ @recordings.count.should == 1
66
+ end
67
+
68
+ it 'returns conversations paginated' do
69
+ @conversations.should respond_to(:total_pages)
70
+ end
71
+
72
+ it 'returns call most recently created first' do
73
+ @conversations.should have_at_least(2).calls
74
+ @conversations.each_with_index do |call, index|
75
+ if @conversations[index.succ].present?
76
+ call.created_at.should be > @conversations[index.succ].created_at
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ context 'given a csr id' do
83
+ before do
84
+ agent = create :agent, csr_id: 321
85
+ conversation = create :conversation
86
+ create :conversation
87
+ create :conversation_connect_event, conversation: conversation
88
+
89
+ create :call_with_recording,
90
+ conversation: conversation,
91
+ agent: agent
92
+
93
+ @conversations = ConversationData.search csr_id: 321
94
+ end
95
+
96
+ it 'returns only their conversations' do
97
+ @conversations.count.should == 1
98
+ end
99
+ end
100
+
101
+ context 'given query with a loan id value' do
102
+ before do
103
+ create :outbound_conversation, loan_id: 123
104
+ create :outbound_conversation, loan_id: 123
105
+ create :outbound_conversation
106
+
107
+ @conversations = ConversationData.search q: 123
108
+ end
109
+
110
+ it 'returns only conversations with that exact loan id' do
111
+ @conversations.count.should == 2
112
+ end
113
+ end
114
+
115
+ context 'given query with a phone number value' do
116
+ before do
117
+ conversation = create :conversation
118
+ create :call,
119
+ conversation: conversation,
120
+ number: '555-555-1111'
121
+ create :call,
122
+ conversation: conversation,
123
+ number: '333-333-1111'
124
+ create :conversation
125
+
126
+ @conversations = ConversationData.search q: '555-555-1111'
127
+ end
128
+
129
+ it 'returns only conversations with that phone number' do
130
+ @conversations.count.should == 1
131
+ @conversations.first.calls.size.should == 2
132
+ end
133
+ end
134
+
135
+ context 'given query with a conversation id value' do
136
+ before do
137
+ create :outbound_conversation
138
+ create :outbound_conversation
139
+ @find_me = create :outbound_conversation
140
+
141
+ @conversations = ConversationData.search q: @find_me.id
142
+ end
143
+
144
+ it 'returns only conversations with that id' do
145
+ @conversations.should == [@find_me]
146
+ end
147
+ end
148
+
149
+ context 'given query with a phone number and an agent csr id' do
150
+ before do
151
+ agent = create :agent, csr_id: 123
152
+ conversation = create :conversation
153
+ create :call,
154
+ conversation: conversation,
155
+ number: '555-555-1111',
156
+ agent: agent
157
+ create :call,
158
+ conversation: conversation,
159
+ number: '333-333-1111'
160
+ other_conversation = create :conversation
161
+ create :call,
162
+ conversation: other_conversation,
163
+ number: '555-555-1111'
164
+
165
+
166
+ @conversations = ConversationData.search q: '555-555-1111', csr_id: 123
167
+ end
168
+
169
+ it 'returns only conversations with that phone number and agent csr id' do
170
+ @conversations.count.should == 1
171
+ @conversations.first.calls.size.should == 2
172
+ end
173
+ end
174
+
175
+ context 'given a datetime range' do
176
+ before do
177
+ start_date = Time.utc(2012, 12, 2, 19, 30, 0).to_s
178
+ end_date = Time.utc(2012, 12, 11, 19, 30, 0).to_s
179
+
180
+ create :conversation,
181
+ created_at: Time.utc(2012, 12, 1, 19, 30, 0)
182
+ create :conversation,
183
+ created_at: Time.utc(2012, 12, 3, 5, 31, 0)
184
+ create :conversation,
185
+ created_at: Time.utc(2012, 12, 10, 20, 31, 0)
186
+ create :conversation,
187
+ created_at: Time.utc(2012, 12, 11, 0, 32, 0)
188
+
189
+ @conversations = ConversationData.search start_date: start_date,
190
+ end_date: end_date
191
+ end
192
+
193
+ it 'returns conversations in a datetime range' do
194
+ @conversations.count.should == 3
195
+ end
196
+ end
197
+
198
+ context 'given a start date' do
199
+ before do
200
+ start_date = Time.utc(2012, 12, 11, 0, 32, 0).to_s
201
+
202
+ create :conversation,
203
+ created_at: Time.utc(2012, 12, 1, 19, 30, 0)
204
+ create :conversation,
205
+ created_at: Time.utc(2012, 12, 3, 5, 31, 0)
206
+ create :conversation,
207
+ created_at: Time.utc(2012, 12, 10, 20, 31, 0)
208
+ create :conversation,
209
+ created_at: Time.utc(2012, 12, 11, 0, 32, 0)
210
+
211
+ @conversations = ConversationData.search start_date: start_date
212
+ end
213
+
214
+ it 'returns conversations from that date until now' do
215
+ @conversations.count.should == 1
216
+ end
217
+ end
218
+
219
+ context 'given a page' do
220
+ before do
221
+ create_list :call, 3
222
+
223
+ @page = 2
224
+ @conversations = ConversationData.search page: @page
225
+ end
226
+
227
+ it 'returns that page of conversations' do
228
+ @conversations.current_page.should == @page
229
+ end
230
+ end
231
+ end
232
+
233
+ describe '.counts' do
234
+ before do
235
+ Timecop.travel(Time.local(2012, 12, 10, 19, 30, 0))
236
+ end
237
+
238
+ context 'by default' do
239
+ before do
240
+ FactoryGirl.create_list :conversation, 2, :initiator_id => 1
241
+ FactoryGirl.create_list :conversation, 3, :initiator_id => 2
242
+ conversation_without_an_initiator = FactoryGirl.create :conversation,
243
+ :initiator_id => nil
244
+
245
+ FactoryGirl.create_list :conversation, 1, :initiator_id => 3
246
+ FactoryGirl.create :conversation,
247
+ :initiator_id => 3,
248
+ :created_at => 31.days.ago
249
+
250
+ @counts = ConversationData.counts
251
+ end
252
+
253
+ it 'returns the total number of conversations in the last 30 days per agent' do
254
+ @counts.keys.sort.should == [1, 2, 3]
255
+ end
256
+ end
257
+
258
+ context 'given a datetime range' do
259
+ before do
260
+ start_date = 1.week.ago.strftime('%m/%d/%Y 00:00:00 PST')
261
+ end_date = Date.today.strftime('%m/%d/%Y 23:59:59 PST')
262
+ FactoryGirl.create :conversation,
263
+ :created_at => Time.utc(2012, 12, 1, 19, 30, 0),
264
+ :initiator_id => 5
265
+ FactoryGirl.create :conversation,
266
+ :created_at => Time.utc(2012, 12, 3, 5, 31, 0),
267
+ :initiator_id => 10
268
+ FactoryGirl.create :conversation,
269
+ :created_at => Time.utc(2012, 12, 10, 20, 31, 0),
270
+ :initiator_id => 20
271
+ FactoryGirl.create :conversation,
272
+ :created_at => Time.utc(2012, 12, 11, 0, 32, 0),
273
+ :initiator_id => 30
274
+
275
+ @counts = ConversationData.counts :start_date => start_date,
276
+ :end_date => end_date
277
+ end
278
+
279
+ it 'returns the total number of conversations in the date range per agent' do
280
+ @counts.should == { 20 => 1, 30 =>1 }
281
+ end
282
+ end
283
+
284
+ context 'given a datetime but without time zone' do
285
+ before do
286
+ start_date = 1.week.ago.strftime('%m/%d/%Y 00:00:00')
287
+ end_date = Date.today.strftime('%m/%d/%Y 00:00:00')
288
+ FactoryGirl.create :conversation,
289
+ :created_at => Time.utc(2012, 12, 1, 19, 30, 0),
290
+ :initiator_id => 5
291
+ FactoryGirl.create :conversation,
292
+ :created_at => Time.utc(2012, 12, 3, 5, 31, 0),
293
+ :initiator_id => 10
294
+ FactoryGirl.create :conversation,
295
+ :created_at => Time.utc(2012, 12, 10, 20, 31, 0),
296
+ :initiator_id => 20
297
+ FactoryGirl.create :conversation,
298
+ :created_at => Time.utc(2012, 12, 11, 0, 32, 0),
299
+ :initiator_id => 30
300
+
301
+ @counts = ConversationData.counts :start_date => start_date,
302
+ :end_date => end_date
303
+ end
304
+
305
+ it 'uses treat the default timezone as UTC and returns the total number of conversations in the date range per agent' do
306
+ @counts.should == { 10 =>1 }
307
+ end
308
+ end
309
+
310
+
311
+ context 'given a date only range' do
312
+ before do
313
+ start_date = 1.week.ago.strftime('%m/%d/%Y')
314
+ end_date = Date.today.strftime('%m/%d/%Y')
315
+ FactoryGirl.create :conversation,
316
+ :created_at => Time.utc(2012, 12, 1, 19, 30, 0),
317
+ :initiator_id => 5
318
+ FactoryGirl.create :conversation,
319
+ :created_at => Time.utc(2012, 12, 3, 5, 31, 0),
320
+ :initiator_id => 10
321
+ FactoryGirl.create :conversation,
322
+ :created_at => Time.utc(2012, 12, 9, 23, 50, 40),
323
+ :initiator_id => 20
324
+ FactoryGirl.create :conversation,
325
+ :created_at => Time.utc(2012, 12, 11, 0, 32, 0),
326
+ :initiator_id => 30
327
+
328
+ @counts = ConversationData.counts :start_date => start_date,
329
+ :end_date => end_date
330
+ end
331
+
332
+ it 'uses treat the default timezone as UTC and returns the total number of conversations in the date range per agent' do
333
+ @counts.should == { 10 =>1, 20 => 1}
334
+ end
335
+ end
336
+
337
+ after do
338
+ Timecop.return
339
+ end
340
+ end
341
+ end
342
+ end