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,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Telephony::NumberHelper do
4
+ before do
5
+ @call = mock("some call model")
6
+ @call.extend(Telephony::NumberHelper)
7
+ end
8
+
9
+ describe "#normalize_number" do
10
+ it "removes non digit charactors" do
11
+ @call.normalize_number("(213)323-3233").should == "2133233233"
12
+ end
13
+
14
+ it "removes the leading 1" do
15
+ @call.normalize_number("1300-300-4000").should == "3003004000"
16
+ end
17
+ end
18
+
19
+ describe "#extract_area_code" do
20
+ it "returns the area code" do
21
+ @call.extract_area_code("213-563-5633").should == "213"
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Telephony::Jobs::AgentOffline do
4
+ describe '#perform' do
5
+ let(:old_timestamp) { 50 }
6
+ let(:new_timestamp) { 100 }
7
+
8
+ before do
9
+ @agent = create :available_agent,
10
+ timestamp_of_last_presence_event: old_timestamp
11
+ @job = Telephony::Jobs::AgentOffline.new @agent.id, new_timestamp
12
+ @job.perform
13
+ end
14
+
15
+ context "when an agent didn't receive any new status change events" do
16
+ it "sets an agent to offline" do
17
+ @agent.reload.should be_offline
18
+ end
19
+
20
+ it "updates a timestamp" do
21
+ @agent.reload.timestamp_of_last_presence_event.should == new_timestamp
22
+ end
23
+ end
24
+
25
+ context "when an agent received a newer status change event" do
26
+ let(:old_timestamp) { 150 }
27
+
28
+ it "doesn't change the status" do
29
+ @agent.reload.should be_available
30
+ end
31
+
32
+ it "doesn't update a timestamp" do
33
+ @agent.reload.timestamp_of_last_presence_event.should == old_timestamp
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ module Telephony
4
+ module Jobs
5
+ describe PusherEvent do
6
+ describe '#perform' do
7
+ before do
8
+ event = {
9
+ channel: 'channel',
10
+ name: 'name',
11
+ data: 'data'
12
+ }
13
+
14
+ Telephony::PusherEventPublisher.should_receive(:push)
15
+ .with(event)
16
+
17
+ @job = PusherEvent.new(event)
18
+ end
19
+
20
+ it 'publishes an agent status change' do
21
+ @job.perform
22
+ end
23
+ end
24
+
25
+ describe '#failure' do
26
+ before do
27
+ @job = PusherEvent.new({})
28
+
29
+ ActiveSupport::Notifications.should_receive(:instrument)
30
+ Rails.logger.should_receive(:error)
31
+ end
32
+
33
+ it "notifies an error" do
34
+ @job.failure
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,439 @@
1
+ require 'spec_helper'
2
+
3
+ module Telephony
4
+ module Providers
5
+ describe TwilioProvider do
6
+ before do
7
+ @config = YAML.load_file(Rails.root.join('config', 'twilio.yml'))['test']
8
+ @config.to_options!
9
+ @twilio_provider = TwilioProvider.new @config
10
+ end
11
+
12
+ describe '.new' do
13
+ context 'given a Twilio configuration' do
14
+ before do
15
+ @config = {
16
+ account_sid: 'account_sid',
17
+ auth_token: 'auth_token',
18
+ outbound_caller_id: 'outbound_caller_id',
19
+ callback_root: 'callback_root'
20
+ }
21
+ @config.to_options!
22
+
23
+ @twilio_provider = TwilioProvider.new @config
24
+ end
25
+
26
+ it 'sets its attributes to match that configuration' do
27
+ @twilio_provider.account_sid.should == @config[:account_sid]
28
+ @twilio_provider.auth_token.should == @config[:auth_token]
29
+ @twilio_provider.outbound_caller_id.should == @config[:outbound_caller_id]
30
+ @twilio_provider.callback_root.should == @config[:callback_root]
31
+ end
32
+ end
33
+ end
34
+
35
+ describe '#call', :vcr do
36
+ before do
37
+ @call_id = '1'
38
+ @phone_number = '555-555-1234'
39
+ @caller_id = '500-555-0006'
40
+
41
+ @call = @twilio_provider.call @call_id, @phone_number, @caller_id
42
+ end
43
+
44
+ it 'places a call' do
45
+ @call.should be
46
+ end
47
+
48
+ it 'includes a status change callback url' do
49
+ request = VCR
50
+ .current_cassette
51
+ .serializable_hash['http_interactions'][0]['request']
52
+ body = Rack::Utils.parse_nested_query request['body']['string']
53
+ body['StatusCallback'].should =~ %r{calls/#{@call_id}/done$}
54
+ end
55
+
56
+ it 'allows the phone to ring for 60 seconds' do
57
+ request = VCR
58
+ .current_cassette
59
+ .serializable_hash['http_interactions'][0]['request']
60
+ body = Rack::Utils.parse_nested_query request['body']['string']
61
+ body['Timeout'].should == '60'
62
+ end
63
+ end
64
+
65
+ describe 'when an exception occurs' do
66
+ before do
67
+ @call_id = '1'
68
+ @phone_number = '555-555-1234'
69
+ @caller_id = '500-555-0006'
70
+ @twilio_provider = TwilioProvider.new @config
71
+ @client = @twilio_provider.client
72
+ end
73
+
74
+ it 'raises a Telephony::Error::Connection exception and logs the error' do
75
+ @client
76
+ .should_receive(:account)
77
+ .and_raise(::Twilio::REST::RequestError.new(''))
78
+
79
+ Rails.logger.should_receive(:error)
80
+
81
+ expect do
82
+ @twilio_provider.call @call_id, @phone_number, @caller_id
83
+ end.to raise_error(Telephony::Error::Connection)
84
+ end
85
+ end
86
+
87
+ describe '#redirect_to_conference' do
88
+ context 'by default' do
89
+ before do
90
+ call_id = '1'
91
+ call = double 'call'
92
+ call
93
+ .should_receive(:redirect_to)
94
+ .with("#{@config[:callback_root]}/providers/twilio/calls/#{call_id}/join_conference")
95
+ ::Twilio::REST::Client
96
+ .any_instance
97
+ .stub_chain(:account, :calls, :find)
98
+ .and_return(call)
99
+ @ok = @twilio_provider.redirect_to_conference call_id, 'sid'
100
+ end
101
+
102
+ it 'should not raise an error' do
103
+ @ok.should be_true
104
+ end
105
+ end
106
+
107
+ context 'when trying to redirect a non existing call' do
108
+ before do
109
+ call = double 'call'
110
+ call
111
+ .stub(:redirect_to)
112
+ .and_raise(::Twilio::REST::RequestError.new('something'))
113
+ ::Twilio::REST::Client
114
+ .any_instance
115
+ .stub_chain(:account, :calls, :find)
116
+ .and_return(call)
117
+
118
+ end
119
+
120
+ it 'raises a Telephony Error' do
121
+ expect {
122
+ @twilio_provider.redirect_to_conference 'call_id', 'sid'
123
+ }.to raise_error(Telephony::Error::Connection)
124
+ end
125
+ end
126
+
127
+ context 'when the REST call to twilio times out' do
128
+ before do
129
+ call = double 'call'
130
+ call
131
+ .stub(:redirect_to)
132
+ .and_raise(::Timeout::Error.new('something'))
133
+ ::Twilio::REST::Client
134
+ .any_instance
135
+ .stub_chain(:account, :calls, :find)
136
+ .and_return(call)
137
+
138
+ end
139
+
140
+ it 'raises a Telephony Error' do
141
+ expect {
142
+ @twilio_provider.redirect_to_conference 'call_id', 'sid'
143
+ }.to raise_error(Telephony::Error::Connection)
144
+ end
145
+ end
146
+ end
147
+
148
+ describe '#redirect_to_hold' do
149
+ context 'by default' do
150
+ before do
151
+ call_id = '1'
152
+ call = double 'call'
153
+ call
154
+ .should_receive(:redirect_to)
155
+ .with(/complete_hold/)
156
+ ::Twilio::REST::Client
157
+ .any_instance
158
+ .stub_chain(:account, :calls, :find)
159
+ .and_return(call)
160
+ @ok = @twilio_provider.redirect_to_hold call_id, 'sid'
161
+ end
162
+
163
+ it 'returns true' do
164
+ @ok.should be_true
165
+ end
166
+ end
167
+
168
+ context 'when trying to redirect a non existing call' do
169
+ before do
170
+ call = double 'call'
171
+ call
172
+ .stub(:redirect_to)
173
+ .and_raise(::Twilio::REST::RequestError.new('something'))
174
+
175
+ ::Twilio::REST::Client
176
+ .any_instance
177
+ .stub_chain(:account, :calls, :find)
178
+ .and_return(call)
179
+ end
180
+
181
+ it 'raises a Telephony Error' do
182
+ expect {
183
+ @twilio_provider.redirect_to_hold 'call_id', 'sid'
184
+ }.to raise_error(Telephony::Error::Connection)
185
+ end
186
+ end
187
+ end
188
+
189
+ describe '#dial_into_conference', :vcr do
190
+ before do
191
+ @call_id = '1'
192
+ @phone_number = '555-555-1238'
193
+ @caller_id = '500-555-0006'
194
+
195
+ @twilio_provider.dial_into_conference @call_id, @phone_number, @caller_id
196
+ end
197
+
198
+ it 'allows the phone to ring for 15 seconds' do
199
+ request = VCR
200
+ .current_cassette
201
+ .serializable_hash['http_interactions'][0]['request']
202
+ body = Rack::Utils.parse_nested_query request['body']['string']
203
+ body['Timeout'].should == '15'
204
+ end
205
+
206
+ it 'places a call to redirect the participant to a conference' do
207
+ request = VCR
208
+ .current_cassette
209
+ .serializable_hash['http_interactions'][0]['request']
210
+ body = Rack::Utils.parse_nested_query request['body']['string']
211
+ body['Url'].should =~ %r{calls/#{@call_id}/join_conference$}
212
+ end
213
+ end
214
+
215
+ describe "#buy_number_for_area_code" do
216
+ # NOTE do NOT change the cassette name or it will buy a new number from Twilio
217
+ use_vcr_cassette "buying number for an area code"
218
+
219
+ before do
220
+ @area_code = "500"
221
+ @expected_number = "5555559876"
222
+
223
+ @number = @twilio_provider.buy_number_for_area_code(@area_code)
224
+ end
225
+
226
+ it 'returns nil in case of error' do
227
+ @twilio_provider.should_receive(:buy_number).and_raise(Exception.new)
228
+ number = @twilio_provider.buy_number_for_area_code(@area_code)
229
+ number.should == nil
230
+ end
231
+ end
232
+
233
+ describe '#uncallable_number' do
234
+ before do
235
+ @uncallable_number = @twilio_provider.uncallable_number
236
+ end
237
+
238
+ it "returns a number that Twilio can't call" do
239
+ @uncallable_number.should == 'this-number-is-not-whitelisted'
240
+ end
241
+ end
242
+
243
+ describe '#hangup', :vcr do
244
+ context 'by default' do
245
+ before do
246
+ conversation = create :conversation, caller_id: "5005550006"
247
+ @call = create :call, conversation: conversation
248
+ @call.make!
249
+
250
+ ::Twilio::REST::Call.any_instance.should_receive(:hangup)
251
+ end
252
+
253
+ it 'hangs up the call' do
254
+ @twilio_provider.hangup @call.sid
255
+ end
256
+ end
257
+
258
+ context 'when an exception occurs' do
259
+ before do
260
+ Rails.logger.should_receive(:error)
261
+ ::Twilio::REST::Call.any_instance.should_receive(:hangup)
262
+ .and_raise(StandardError.new('Some error'))
263
+ end
264
+
265
+ it "logs the error and returns false" do
266
+ value = @twilio_provider.hangup 'sid'
267
+ value.should be_false
268
+ end
269
+ end
270
+ end
271
+
272
+ describe '#dial' do
273
+ context 'by default' do
274
+ before do
275
+ call_id = 1
276
+ call = mock 'call'
277
+ call
278
+ .should_receive(:redirect_to)
279
+ .with("#{@config[:callback_root]}/providers/twilio/calls/#{call_id}/dial")
280
+ ::Twilio::REST::Client
281
+ .any_instance
282
+ .stub_chain(:account, :calls, :find)
283
+ .and_return(call)
284
+
285
+ @ret = @twilio_provider.redirect_to_dial call_id, 'sid'
286
+ end
287
+
288
+ it 'should not raise an error' do
289
+ @ret.should be_true
290
+ end
291
+ end
292
+
293
+ context 'when trying to dial from a non existing call' do
294
+ before do
295
+ call = double 'call'
296
+ call
297
+ .stub(:redirect_to)
298
+ .and_raise(::Twilio::REST::RequestError.new('something'))
299
+ ::Twilio::REST::Client
300
+ .any_instance
301
+ .stub_chain(:account, :calls, :find)
302
+ .and_return(call)
303
+ end
304
+
305
+ it 'returns a failure status and a failure message' do
306
+ expect {
307
+ @twilio_provider.redirect_to_dial 'call_id', 'sid'
308
+ }.to raise_error(Telephony::Error::Connection)
309
+ end
310
+ end
311
+ end
312
+
313
+ describe '#call_ended?' do
314
+ context 'given the sid of an in progress call' do
315
+ before do
316
+ sid = 'sid'
317
+ stub_request(:get, %r{/Accounts/\w+/Calls/#{sid}.json})
318
+ .to_return(body: { status: 'in-progress' }.to_json)
319
+
320
+ VCR.turned_off do
321
+ @call_ended = Telephony.provider.call_ended? sid
322
+ end
323
+ end
324
+
325
+ it 'returns false' do
326
+ @call_ended.should be_false
327
+ end
328
+ end
329
+
330
+ context 'given the sid of a terminated call' do
331
+ before do
332
+ sid = 'sid'
333
+ stub_request(:get, %r{/Accounts/\w+/Calls/#{sid}.json})
334
+ .to_return(body: { status: 'completed' }.to_json)
335
+
336
+ VCR.turned_off do
337
+ @call_ended = Telephony.provider.call_ended? sid
338
+ end
339
+ end
340
+
341
+ it 'returns true' do
342
+ @call_ended.should be_true
343
+ end
344
+ end
345
+
346
+ context 'when a Twilio::REST::RequestError occurs' do
347
+ before do
348
+ sid = 'sid'
349
+ stub_request(:get, %r{/Accounts/\w+/Calls/#{sid}.json})
350
+ .to_raise(::Twilio::REST::RequestError.new(''))
351
+
352
+ Rails.logger.should_not_receive(:error)
353
+ VCR.turned_off do
354
+ @call_ended = Telephony.provider.call_ended? sid
355
+ end
356
+ end
357
+
358
+ it 'does not log the error and returns false' do
359
+ @call_ended.should be_false
360
+ end
361
+ end
362
+
363
+ context 'when another exception occurs' do
364
+ before do
365
+ sid = 'sid'
366
+ stub_request(:get, %r{/Accounts/\w+/Calls/#{sid}.json})
367
+ .to_raise(StandardError.new('Some error'))
368
+
369
+ Rails.logger.should_receive(:error)
370
+ VCR.turned_off do
371
+ @call_ended = Telephony.provider.call_ended? sid
372
+ end
373
+ end
374
+
375
+ it 'logs the error and returns false' do
376
+ @call_ended.should be_false
377
+ end
378
+ end
379
+
380
+ context 'given the sid of a call without a status' do
381
+ before do
382
+ sid = 'sid'
383
+ stub_request(:get, %r{/Accounts/\w+/Calls/#{sid}.json})
384
+ .to_return(body: { sid: 'sid' }.to_json)
385
+
386
+ VCR.turned_off do
387
+ @call_ended = Telephony.provider.call_ended? sid
388
+ end
389
+ end
390
+
391
+ it 'returns false' do
392
+ @call_ended.should be_false
393
+ end
394
+ end
395
+ end
396
+
397
+
398
+ describe "#caller_id_for" do
399
+ use_vcr_cassette "display a local caller id"
400
+
401
+ before do
402
+ @area_code = '213'
403
+ @number = '2131234567'
404
+ end
405
+
406
+ context "cached area code" do
407
+ before do
408
+ @twilio_provider.should_receive(:incoming_phone_number_from_cache).with(@area_code).and_return(@number)
409
+ end
410
+
411
+ it 'uses the number' do
412
+ @twilio_provider.caller_id_for(@area_code).should == @number
413
+ end
414
+ end
415
+
416
+ context "existing area code" do
417
+ before do
418
+ @twilio_provider.should_receive(:fetch_existing_incoming_phone_numbers).with(@area_code).and_return(@number)
419
+ end
420
+
421
+ it 'reuse the the number' do
422
+ @twilio_provider.caller_id_for(@area_code).should == @number
423
+ end
424
+ end
425
+
426
+ context "missing area code" do
427
+ before do
428
+ @twilio_provider.should_receive(:fetch_existing_incoming_phone_numbers).with(@area_code).and_return(nil)
429
+ @twilio_provider.should_receive(:buy_number_for_area_code).with(@area_code).and_return(@number)
430
+ end
431
+
432
+ it 'buy the number' do
433
+ @twilio_provider.caller_id_for(@area_code).should == @number
434
+ end
435
+ end
436
+ end
437
+ end
438
+ end
439
+ end