open_conference_ware 1.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (471) hide show
  1. data.tar.gz.sig +0 -0
  2. data/.autotest +14 -0
  3. data/.coveralls.yml +1 -0
  4. data/.gitignore +21 -0
  5. data/.rspec +1 -0
  6. data/.travis.yml +18 -0
  7. data/CHANGES.md +280 -0
  8. data/CONTRIBUTORS.markdown +17 -0
  9. data/Gemfile +21 -0
  10. data/Gemfile.lock +296 -0
  11. data/Guardfile +24 -0
  12. data/LICENSE.txt +23 -0
  13. data/README.markdown +173 -0
  14. data/Rakefile +27 -0
  15. data/TODO.txt +22 -0
  16. data/app/assets/images/open_conference_ware/accept.png +0 -0
  17. data/app/assets/images/open_conference_ware/arrow-left.png +0 -0
  18. data/app/assets/images/open_conference_ware/arrow-out.png +0 -0
  19. data/app/assets/images/open_conference_ware/arrow-right.png +0 -0
  20. data/app/assets/images/open_conference_ware/arrow-up.png +0 -0
  21. data/app/assets/images/open_conference_ware/arrow_refresh.png +0 -0
  22. data/app/assets/images/open_conference_ware/comment.png +0 -0
  23. data/app/assets/images/open_conference_ware/comment_edit.png +0 -0
  24. data/app/assets/images/open_conference_ware/delete.png +0 -0
  25. data/app/assets/images/open_conference_ware/disk.png +0 -0
  26. data/app/assets/images/open_conference_ware/document-blank.png +0 -0
  27. data/app/assets/images/open_conference_ware/document.png +0 -0
  28. data/app/assets/images/open_conference_ware/edit.png +0 -0
  29. data/app/assets/images/open_conference_ware/extras/001_01.png +0 -0
  30. data/app/assets/images/open_conference_ware/extras/001_05.png +0 -0
  31. data/app/assets/images/open_conference_ware/extras/001_05x16.png +0 -0
  32. data/app/assets/images/open_conference_ware/extras/001_21.png +0 -0
  33. data/app/assets/images/open_conference_ware/extras/001_23.png +0 -0
  34. data/app/assets/images/open_conference_ware/extras/001_24.png +0 -0
  35. data/app/assets/images/open_conference_ware/extras/001_29.png +0 -0
  36. data/app/assets/images/open_conference_ware/extras/01.png +0 -0
  37. data/app/assets/images/open_conference_ware/extras/05.png +0 -0
  38. data/app/assets/images/open_conference_ware/extras/06.png +0 -0
  39. data/app/assets/images/open_conference_ware/extras/1.png +0 -0
  40. data/app/assets/images/open_conference_ware/extras/14.png +0 -0
  41. data/app/assets/images/open_conference_ware/extras/16.png +0 -0
  42. data/app/assets/images/open_conference_ware/extras/2.png +0 -0
  43. data/app/assets/images/open_conference_ware/extras/21.png +0 -0
  44. data/app/assets/images/open_conference_ware/extras/28.png +0 -0
  45. data/app/assets/images/open_conference_ware/extras/3.png +0 -0
  46. data/app/assets/images/open_conference_ware/extras/31.png +0 -0
  47. data/app/assets/images/open_conference_ware/extras/32.png +0 -0
  48. data/app/assets/images/open_conference_ware/extras/35.png +0 -0
  49. data/app/assets/images/open_conference_ware/extras/36.png +0 -0
  50. data/app/assets/images/open_conference_ware/extras/4.png +0 -0
  51. data/app/assets/images/open_conference_ware/extras/44.png +0 -0
  52. data/app/assets/images/open_conference_ware/extras/45.png +0 -0
  53. data/app/assets/images/open_conference_ware/extras/46.png +0 -0
  54. data/app/assets/images/open_conference_ware/extras/cancel.png +0 -0
  55. data/app/assets/images/open_conference_ware/extras/cancelx16.png +0 -0
  56. data/app/assets/images/open_conference_ware/extras/icon.trashcan.gif +0 -0
  57. data/app/assets/images/open_conference_ware/favorite.png +0 -0
  58. data/app/assets/images/open_conference_ware/feed.png +0 -0
  59. data/app/assets/images/open_conference_ware/heart.png +0 -0
  60. data/app/assets/images/open_conference_ware/house.png +0 -0
  61. data/app/assets/images/open_conference_ware/ignite_portland_header3_clear.png +0 -0
  62. data/app/assets/images/open_conference_ware/ignite_portland_header3_clear_cropped.png +0 -0
  63. data/app/assets/images/open_conference_ware/jquery.wysiwyg.gif +0 -0
  64. data/app/assets/images/open_conference_ware/lock.png +0 -0
  65. data/app/assets/images/open_conference_ware/new_window.gif +0 -0
  66. data/app/assets/images/open_conference_ware/openid-icon.gif +0 -0
  67. data/app/assets/images/open_conference_ware/pencil.png +0 -0
  68. data/app/assets/images/open_conference_ware/plus.png +0 -0
  69. data/app/assets/images/open_conference_ware/quote.png +0 -0
  70. data/app/assets/images/open_conference_ware/rails.png +0 -0
  71. data/app/assets/images/open_conference_ware/redx.png +0 -0
  72. data/app/assets/images/open_conference_ware/reset-fff.png +0 -0
  73. data/app/assets/images/open_conference_ware/spinner-big.gif +0 -0
  74. data/app/assets/images/open_conference_ware/spinner.gif +0 -0
  75. data/app/assets/images/open_conference_ware/star.png +0 -0
  76. data/app/assets/images/open_conference_ware/tag_blue.png +0 -0
  77. data/app/assets/images/open_conference_ware/time.png +0 -0
  78. data/app/assets/javascripts/open_conference_ware/application.js +18 -0
  79. data/app/assets/javascripts/open_conference_ware/base.js +15 -0
  80. data/app/assets/javascripts/open_conference_ware/favorites.js +76 -0
  81. data/app/assets/javascripts/open_conference_ware/ie.js +2 -0
  82. data/app/assets/javascripts/open_conference_ware/menu.js +47 -0
  83. data/app/assets/javascripts/open_conference_ware/persona.js +10 -0
  84. data/app/assets/javascripts/open_conference_ware/proposals.js +208 -0
  85. data/app/assets/javascripts/open_conference_ware/schedule.js +20 -0
  86. data/app/assets/javascripts/open_conference_ware/spinner.js +11 -0
  87. data/app/assets/javascripts/open_conference_ware/util.js +23 -0
  88. data/app/assets/stylesheets/open_conference_ware/application.css +8 -0
  89. data/app/assets/stylesheets/open_conference_ware/common.css.scss +491 -0
  90. data/app/assets/stylesheets/open_conference_ware/custom.css.scss +1033 -0
  91. data/app/assets/stylesheets/open_conference_ware/scaffold.css +54 -0
  92. data/app/controllers/open_conference_ware/application_controller.rb +488 -0
  93. data/app/controllers/open_conference_ware/authentications_controller.rb +42 -0
  94. data/app/controllers/open_conference_ware/comments_controller.rb +86 -0
  95. data/app/controllers/open_conference_ware/events_controller.rb +36 -0
  96. data/app/controllers/open_conference_ware/manage/events_controller.rb +157 -0
  97. data/app/controllers/open_conference_ware/manage/snippets_controller.rb +110 -0
  98. data/app/controllers/open_conference_ware/proposals_controller.rb +521 -0
  99. data/app/controllers/open_conference_ware/rooms_controller.rb +130 -0
  100. data/app/controllers/open_conference_ware/schedule_items_controller.rb +128 -0
  101. data/app/controllers/open_conference_ware/selector_votes_controller.rb +71 -0
  102. data/app/controllers/open_conference_ware/session_types_controller.rb +123 -0
  103. data/app/controllers/open_conference_ware/tracks_controller.rb +118 -0
  104. data/app/controllers/open_conference_ware/user_favorites_controller.rb +68 -0
  105. data/app/controllers/open_conference_ware/users_controller.rb +120 -0
  106. data/app/helpers/open_conference_ware/application_helper.rb +167 -0
  107. data/app/helpers/open_conference_ware/authentications_helper.rb +25 -0
  108. data/app/helpers/open_conference_ware/cache_if_helper.rb +13 -0
  109. data/app/helpers/open_conference_ware/display_link_to_helper.rb +25 -0
  110. data/app/helpers/open_conference_ware/display_textile_for_helper.rb +11 -0
  111. data/app/helpers/open_conference_ware/field_annotation_helper.rb +11 -0
  112. data/app/helpers/open_conference_ware/focus_helper.rb +16 -0
  113. data/app/helpers/open_conference_ware/localcss_helper.rb +10 -0
  114. data/app/helpers/open_conference_ware/page_title_helper.rb +17 -0
  115. data/app/helpers/open_conference_ware/proposals_helper.rb +54 -0
  116. data/app/helpers/open_conference_ware/rooms_helper.rb +5 -0
  117. data/app/helpers/open_conference_ware/scroll_to_helper.rb +15 -0
  118. data/app/helpers/open_conference_ware/session_types_helper.rb +5 -0
  119. data/app/helpers/open_conference_ware/snippets_helper.rb +36 -0
  120. data/app/helpers/open_conference_ware/time_range_helper.rb +198 -0
  121. data/app/helpers/open_conference_ware/tracks_helper.rb +9 -0
  122. data/app/helpers/open_conference_ware/user_favorites_helper.rb +15 -0
  123. data/app/mailers/open_conference_ware/speaker_mailer.rb +51 -0
  124. data/app/mixins/open_conference_ware/breadcrumbs_mixin.rb +76 -0
  125. data/app/mixins/open_conference_ware/cache_lookups_mixin.rb +128 -0
  126. data/app/mixins/open_conference_ware/faux_routes_mixin.rb +83 -0
  127. data/app/mixins/open_conference_ware/normalize_url_mixin.rb +40 -0
  128. data/app/mixins/open_conference_ware/public_attributes_mixin.rb +62 -0
  129. data/app/mixins/open_conference_ware/schedule_overlaps_mixin.rb +12 -0
  130. data/app/mixins/open_conference_ware/settings_checkers_mixin.rb +50 -0
  131. data/app/mixins/open_conference_ware/simple_slug_mixin.rb +26 -0
  132. data/app/models/open_conference_ware/authentication.rb +47 -0
  133. data/app/models/open_conference_ware/base.rb +6 -0
  134. data/app/models/open_conference_ware/comment.rb +26 -0
  135. data/app/models/open_conference_ware/event.rb +227 -0
  136. data/app/models/open_conference_ware/proposal.rb +625 -0
  137. data/app/models/open_conference_ware/room.rb +42 -0
  138. data/app/models/open_conference_ware/schedule.rb +216 -0
  139. data/app/models/open_conference_ware/schedule_item.rb +55 -0
  140. data/app/models/open_conference_ware/selector_vote.rb +22 -0
  141. data/app/models/open_conference_ware/session_type.rb +33 -0
  142. data/app/models/open_conference_ware/snippet.rb +58 -0
  143. data/app/models/open_conference_ware/track.rb +51 -0
  144. data/app/models/open_conference_ware/user.rb +262 -0
  145. data/app/models/open_conference_ware/user_favorite.rb +42 -0
  146. data/app/observers/open_conference_ware/cache_watcher.rb +42 -0
  147. data/app/views/layouts/open_conference_ware/_header.html.erb +106 -0
  148. data/app/views/layouts/open_conference_ware/application.atom.erb +1 -0
  149. data/app/views/layouts/open_conference_ware/application.html.erb +126 -0
  150. data/app/views/layouts/open_conference_ware/scaffold.html.erb +21 -0
  151. data/app/views/open_conference_ware/404.html.erb +8 -0
  152. data/app/views/open_conference_ware/422.html.erb +11 -0
  153. data/app/views/open_conference_ware/500.html.erb +11 -0
  154. data/app/views/open_conference_ware/_email_link.html.erb +24 -0
  155. data/app/views/open_conference_ware/_google_analytics.html.erb +9 -0
  156. data/app/views/open_conference_ware/authentications/_developer.html.erb +7 -0
  157. data/app/views/open_conference_ware/authentications/_openid.html.erb +11 -0
  158. data/app/views/open_conference_ware/authentications/_persona.html.erb +17 -0
  159. data/app/views/open_conference_ware/authentications/sign_in.html.erb +12 -0
  160. data/app/views/open_conference_ware/comments/_list.html.erb +31 -0
  161. data/app/views/open_conference_ware/comments/index.atom.builder +25 -0
  162. data/app/views/open_conference_ware/comments/index.html.erb +3 -0
  163. data/app/views/open_conference_ware/events/index.html.erb +9 -0
  164. data/app/views/open_conference_ware/events/show.html.erb +0 -0
  165. data/app/views/open_conference_ware/events/speakers.html.erb +7 -0
  166. data/app/views/open_conference_ware/manage/events/_form.html.erb +107 -0
  167. data/app/views/open_conference_ware/manage/events/edit.html.erb +3 -0
  168. data/app/views/open_conference_ware/manage/events/index.html.erb +41 -0
  169. data/app/views/open_conference_ware/manage/events/new.html.erb +3 -0
  170. data/app/views/open_conference_ware/manage/events/proposals.html.erb +133 -0
  171. data/app/views/open_conference_ware/manage/events/show.html.erb +181 -0
  172. data/app/views/open_conference_ware/manage/snippets/_form.html.erb +30 -0
  173. data/app/views/open_conference_ware/manage/snippets/edit.html.erb +3 -0
  174. data/app/views/open_conference_ware/manage/snippets/index.html.erb +34 -0
  175. data/app/views/open_conference_ware/manage/snippets/new.html.erb +3 -0
  176. data/app/views/open_conference_ware/manage/snippets/show.html.erb +32 -0
  177. data/app/views/open_conference_ware/proposals/_admin_controls.html.erb +33 -0
  178. data/app/views/open_conference_ware/proposals/_form.html.erb +381 -0
  179. data/app/views/open_conference_ware/proposals/_list.html.erb +164 -0
  180. data/app/views/open_conference_ware/proposals/_list_concise.html.erb +93 -0
  181. data/app/views/open_conference_ware/proposals/_manage_speakers.html.erb +30 -0
  182. data/app/views/open_conference_ware/proposals/_room_control.html.erb +21 -0
  183. data/app/views/open_conference_ware/proposals/_schedule_block.html.erb +8 -0
  184. data/app/views/open_conference_ware/proposals/_schedule_control.html.erb +13 -0
  185. data/app/views/open_conference_ware/proposals/_search_speakers.html.erb +20 -0
  186. data/app/views/open_conference_ware/proposals/_sub_list.html.erb +65 -0
  187. data/app/views/open_conference_ware/proposals/_sub_list_for_kind.html.erb +31 -0
  188. data/app/views/open_conference_ware/proposals/_transition_control.html.erb +16 -0
  189. data/app/views/open_conference_ware/proposals/create.html.erb +29 -0
  190. data/app/views/open_conference_ware/proposals/edit.html.erb +7 -0
  191. data/app/views/open_conference_ware/proposals/index.atom.builder +63 -0
  192. data/app/views/open_conference_ware/proposals/index.html.erb +46 -0
  193. data/app/views/open_conference_ware/proposals/new.html.erb +3 -0
  194. data/app/views/open_conference_ware/proposals/schedule.html.erb +108 -0
  195. data/app/views/open_conference_ware/proposals/sessions_index_terse.html.erb +22 -0
  196. data/app/views/open_conference_ware/proposals/show.html.erb +316 -0
  197. data/app/views/open_conference_ware/proposals/stats.html.erb +72 -0
  198. data/app/views/open_conference_ware/rooms/_form.html.erb +36 -0
  199. data/app/views/open_conference_ware/rooms/edit.html.erb +3 -0
  200. data/app/views/open_conference_ware/rooms/index.html.erb +13 -0
  201. data/app/views/open_conference_ware/rooms/new.html.erb +3 -0
  202. data/app/views/open_conference_ware/rooms/show.html.erb +27 -0
  203. data/app/views/open_conference_ware/schedule_items/_form.html.erb +35 -0
  204. data/app/views/open_conference_ware/schedule_items/edit.html.erb +3 -0
  205. data/app/views/open_conference_ware/schedule_items/index.html.erb +31 -0
  206. data/app/views/open_conference_ware/schedule_items/new.html.erb +3 -0
  207. data/app/views/open_conference_ware/schedule_items/show.html.erb +30 -0
  208. data/app/views/open_conference_ware/selector_votes/index.html.erb +81 -0
  209. data/app/views/open_conference_ware/session_types/_form.html.erb +23 -0
  210. data/app/views/open_conference_ware/session_types/edit.html.erb +3 -0
  211. data/app/views/open_conference_ware/session_types/index.html.erb +18 -0
  212. data/app/views/open_conference_ware/session_types/new.html.erb +3 -0
  213. data/app/views/open_conference_ware/session_types/show.html.erb +14 -0
  214. data/app/views/open_conference_ware/speaker_mailer/speaker_email.text.erb +14 -0
  215. data/app/views/open_conference_ware/tracks/_colors.css.erb +7 -0
  216. data/app/views/open_conference_ware/tracks/_form.html.erb +36 -0
  217. data/app/views/open_conference_ware/tracks/edit.html.erb +3 -0
  218. data/app/views/open_conference_ware/tracks/index.html.erb +25 -0
  219. data/app/views/open_conference_ware/tracks/new.html.erb +3 -0
  220. data/app/views/open_conference_ware/tracks/show.html.erb +19 -0
  221. data/app/views/open_conference_ware/user_favorites/index.html.erb +12 -0
  222. data/app/views/open_conference_ware/users/_account_box.html.erb +10 -0
  223. data/app/views/open_conference_ware/users/_block.html.erb +80 -0
  224. data/app/views/open_conference_ware/users/_list.html.erb +17 -0
  225. data/app/views/open_conference_ware/users/edit.html.erb +73 -0
  226. data/app/views/open_conference_ware/users/index.html.erb +28 -0
  227. data/app/views/open_conference_ware/users/new.html.erb +7 -0
  228. data/app/views/open_conference_ware/users/proposals.html.erb +24 -0
  229. data/app/views/open_conference_ware/users/show.html.erb +5 -0
  230. data/app/views/open_conference_ware/welcome/test.html.haml +8 -0
  231. data/bin/rails +8 -0
  232. data/ci/Gemfile.ci +13 -0
  233. data/ci/copy_database_config.rb +6 -0
  234. data/ci/database.mysql.yml +5 -0
  235. data/ci/database.postgresql.yml +4 -0
  236. data/ci/database.sqlite.yml +5 -0
  237. data/config/initializers/acts_as_taggable_on.rb +2 -0
  238. data/config/initializers/date_time_formats.rb +9 -0
  239. data/config/routes.rb +74 -0
  240. data/db/migrate/20131203235128_create_open_conference_ware_authentications.rb +14 -0
  241. data/db/migrate/20131203235216_create_open_conference_ware_comments.rb +14 -0
  242. data/db/migrate/20131203235418_create_open_conference_ware_events.rb +27 -0
  243. data/db/migrate/20131203235524_create_open_conference_ware_proposals.rb +36 -0
  244. data/db/migrate/20131203235723_create_open_conference_ware_rooms.rb +20 -0
  245. data/db/migrate/20131203235831_create_open_conference_ware_schedule_items.rb +18 -0
  246. data/db/migrate/20131203235910_create_open_conference_ware_selector_votes.rb +10 -0
  247. data/db/migrate/20131203235934_create_open_conference_ware_session_types.rb +14 -0
  248. data/db/migrate/20131204000008_create_open_conference_ware_snippets.rb +15 -0
  249. data/db/migrate/20131204000048_create_open_conference_ware_taggings.rb +16 -0
  250. data/db/migrate/20131204000129_create_open_conference_ware_tags.rb +7 -0
  251. data/db/migrate/20131204000147_create_open_conference_ware_tracks.rb +15 -0
  252. data/db/migrate/20131204000230_create_open_conference_ware_user_favorites.rb +10 -0
  253. data/db/migrate/20131204000251_create_open_conference_ware_users.rb +24 -0
  254. data/db/migrate/20131204000355_create_proposals_users_join_table.rb +8 -0
  255. data/db/seeds.rb +31 -0
  256. data/features/comment_create.feature +9 -0
  257. data/features/comment_destroy.feature +22 -0
  258. data/features/comment_new.feature +22 -0
  259. data/features/step_definitions/authentication_steps.rb +22 -0
  260. data/features/step_definitions/comment_create_steps.rb +5 -0
  261. data/features/step_definitions/comment_destroy_steps.rb +13 -0
  262. data/features/step_definitions/comment_new_steps.rb +41 -0
  263. data/features/step_definitions/notification_steps.rb +3 -0
  264. data/features/step_definitions/web_steps.rb +263 -0
  265. data/features/support/env.rb +58 -0
  266. data/features/support/helpers.rb +17 -0
  267. data/features/support/paths.rb +35 -0
  268. data/gem-public_cert.pem +22 -0
  269. data/lib/defer_proxy.rb +127 -0
  270. data/lib/ext/color_rgb_serializer_fix.rb +18 -0
  271. data/lib/ext/string_possessiveize.rb +7 -0
  272. data/lib/ext/vpim_icalendar_extra_properties.rb +25 -0
  273. data/lib/generators/open_conference_ware/install/USAGE +12 -0
  274. data/lib/generators/open_conference_ware/install/install_generator.rb +35 -0
  275. data/lib/generators/open_conference_ware/install/templates/config_initializer.rb +110 -0
  276. data/lib/generators/open_conference_ware/install/templates/omniauth_initializer.rb +33 -0
  277. data/lib/open_conference_ware.rb +156 -0
  278. data/lib/open_conference_ware/dependencies.rb +38 -0
  279. data/lib/open_conference_ware/engine.rb +38 -0
  280. data/lib/open_conference_ware/omni_auth_builder.rb +8 -0
  281. data/lib/open_conference_ware/version.rb +3 -0
  282. data/lib/rwikibot_page_drone.rb +70 -0
  283. data/lib/tasks/.gitkeep +0 -0
  284. data/lib/tasks/audio.rake +53 -0
  285. data/lib/tasks/cucumber.rake +65 -0
  286. data/lib/tasks/export.rake +170 -0
  287. data/lib/tasks/mediawiki.rake +96 -0
  288. data/lib/tasks/open_conference_ware_tasks.rake +16 -0
  289. data/lib/tasks/schedule.rake +103 -0
  290. data/lib/tasks/snippets.rake +23 -0
  291. data/open_conference_ware.gemspec +67 -0
  292. data/original_graphics/favorite.psd +0 -0
  293. data/spec/controllers/open_conference_ware/application_controller_spec.rb +309 -0
  294. data/spec/controllers/open_conference_ware/authentications_controller_spec.rb +87 -0
  295. data/spec/controllers/open_conference_ware/comments_controller_spec.rb +146 -0
  296. data/spec/controllers/open_conference_ware/events_controller_spec.rb +102 -0
  297. data/spec/controllers/open_conference_ware/manage_events_controller_spec.rb +103 -0
  298. data/spec/controllers/open_conference_ware/proposals_controller_spec.rb +1273 -0
  299. data/spec/controllers/open_conference_ware/rooms_controller_spec.rb +190 -0
  300. data/spec/controllers/open_conference_ware/selector_votes_controller_spec.rb +263 -0
  301. data/spec/controllers/open_conference_ware/session_types_controller_spec.rb +190 -0
  302. data/spec/controllers/open_conference_ware/sessions_routing_spec.rb +13 -0
  303. data/spec/controllers/open_conference_ware/tracks_controller_spec.rb +190 -0
  304. data/spec/controllers/open_conference_ware/user_favorites_controller_spec.rb +127 -0
  305. data/spec/controllers/open_conference_ware/users_controller_spec.rb +166 -0
  306. data/spec/dummy/README.rdoc +28 -0
  307. data/spec/dummy/Rakefile +6 -0
  308. data/spec/dummy/app/assets/images/.keep +0 -0
  309. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  310. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  311. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  312. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  313. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  314. data/spec/dummy/app/mailers/.keep +0 -0
  315. data/spec/dummy/app/models/.keep +0 -0
  316. data/spec/dummy/app/models/concerns/.keep +0 -0
  317. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  318. data/spec/dummy/bin/bundle +3 -0
  319. data/spec/dummy/bin/rails +4 -0
  320. data/spec/dummy/bin/rake +4 -0
  321. data/spec/dummy/config.ru +4 -0
  322. data/spec/dummy/config/application.rb +30 -0
  323. data/spec/dummy/config/boot.rb +5 -0
  324. data/spec/dummy/config/database.yml +25 -0
  325. data/spec/dummy/config/environment.rb +5 -0
  326. data/spec/dummy/config/environments/development.rb +29 -0
  327. data/spec/dummy/config/environments/production.rb +80 -0
  328. data/spec/dummy/config/environments/test.rb +36 -0
  329. data/spec/dummy/config/initializers/01_open_conference_ware.rb +110 -0
  330. data/spec/dummy/config/initializers/02_omniauth.rb +17 -0
  331. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  332. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  333. data/spec/dummy/config/initializers/inflections.rb +16 -0
  334. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  335. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  336. data/spec/dummy/config/initializers/session_store.rb +3 -0
  337. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  338. data/spec/dummy/config/locales/en.yml +23 -0
  339. data/spec/dummy/config/routes.rb +3 -0
  340. data/spec/dummy/db/schema.rb +219 -0
  341. data/spec/dummy/db/seeds.rb +10 -0
  342. data/spec/dummy/lib/assets/.keep +0 -0
  343. data/spec/dummy/log/.keep +0 -0
  344. data/spec/dummy/public/404.html +58 -0
  345. data/spec/dummy/public/422.html +58 -0
  346. data/spec/dummy/public/500.html +57 -0
  347. data/spec/dummy/public/favicon.ico +0 -0
  348. data/spec/factories/authentication_factory.rb +16 -0
  349. data/spec/factories/common_factory.rb +4 -0
  350. data/spec/factories/event_factory.rb +26 -0
  351. data/spec/factories/proposal_factory.rb +57 -0
  352. data/spec/factories/proposal_user_factory.rb +7 -0
  353. data/spec/factories/room_factory.rb +11 -0
  354. data/spec/factories/schedule_item_factory.rb +13 -0
  355. data/spec/factories/session_types_factory.rb +10 -0
  356. data/spec/factories/snippet_factory.rb +7 -0
  357. data/spec/factories/track_factory.rb +12 -0
  358. data/spec/factories/user_factory.rb +22 -0
  359. data/spec/factories/user_favorite_factory.rb +7 -0
  360. data/spec/features/sign_in_spec.rb +11 -0
  361. data/spec/features/snippets_spec.rb +38 -0
  362. data/spec/fixtures/open_conference_ware_comments.yml +27 -0
  363. data/spec/fixtures/open_conference_ware_events.yml +56 -0
  364. data/spec/fixtures/open_conference_ware_proposals.yml +256 -0
  365. data/spec/fixtures/open_conference_ware_rooms.yml +13 -0
  366. data/spec/fixtures/open_conference_ware_schedule_items.yml +23 -0
  367. data/spec/fixtures/open_conference_ware_session_types.yml +19 -0
  368. data/spec/fixtures/open_conference_ware_snippets.yml +62 -0
  369. data/spec/fixtures/open_conference_ware_tracks.yml +65 -0
  370. data/spec/fixtures/open_conference_ware_user_favorites.yml +7 -0
  371. data/spec/fixtures/open_conference_ware_users.yml +87 -0
  372. data/spec/helpers/open_conference_ware/application_helper_spec.rb +58 -0
  373. data/spec/helpers/open_conference_ware/authentications_helper_spec.rb +15 -0
  374. data/spec/helpers/open_conference_ware/display_link_to_helper_spec.rb +52 -0
  375. data/spec/helpers/open_conference_ware/proposals_helper_spec.rb +32 -0
  376. data/spec/helpers/open_conference_ware/rooms_helper_spec.rb +11 -0
  377. data/spec/helpers/open_conference_ware/session_types_helper_spec.rb +15 -0
  378. data/spec/helpers/open_conference_ware/time_range_helper_spec.rb +56 -0
  379. data/spec/helpers/open_conference_ware/tracks_helper_spec.rb +11 -0
  380. data/spec/helpers/open_conference_ware/user_favorites_helper_spec.rb +11 -0
  381. data/spec/integration/open_conference_ware/cache_lookups_mixin_spec.rb +122 -0
  382. data/spec/mixins/open_conference_ware/normalize_url_mixin_spec.rb +65 -0
  383. data/spec/models/open_conference_ware/authentication_spec.rb +67 -0
  384. data/spec/models/open_conference_ware/event_spec.rb +211 -0
  385. data/spec/models/open_conference_ware/proposal_spec.rb +606 -0
  386. data/spec/models/open_conference_ware/room_spec.rb +14 -0
  387. data/spec/models/open_conference_ware/schedule_item_spec.rb +12 -0
  388. data/spec/models/open_conference_ware/schedule_spec.rb +358 -0
  389. data/spec/models/open_conference_ware/selector_vote_spec.rb +12 -0
  390. data/spec/models/open_conference_ware/session_type_spec.rb +15 -0
  391. data/spec/models/open_conference_ware/snippet_spec.rb +13 -0
  392. data/spec/models/open_conference_ware/speaker_mailer_spec.rb +91 -0
  393. data/spec/models/open_conference_ware/track_spec.rb +36 -0
  394. data/spec/models/open_conference_ware/user_favorite_spec.rb +41 -0
  395. data/spec/models/open_conference_ware/user_spec.rb +77 -0
  396. data/spec/ocw_config.rb +76 -0
  397. data/spec/rcov.opts +2 -0
  398. data/spec/spec.opts +3 -0
  399. data/spec/spec_helper.rb +55 -0
  400. data/spec/spec_helper_customizations.rb +125 -0
  401. data/spec/support/add_all_helpers_to_view.rb +24 -0
  402. data/spec/support/authenticated_test_helper.rb +27 -0
  403. data/spec/support/fixture_shortcuts.rb +18 -0
  404. data/spec/support/omniauth.rb +34 -0
  405. data/spec/support/valid_params_extraction.rb +15 -0
  406. data/spec/views/open_conference_ware/proposals/_room_control.html.erb_spec.rb +23 -0
  407. data/spec/views/open_conference_ware/proposals/_transition_control.html.erb_spec.rb +14 -0
  408. data/spec/views/open_conference_ware/proposals/show.html.erb_spec.rb +128 -0
  409. data/spec/views/open_conference_ware/rooms/index.html.erb_spec.rb +19 -0
  410. data/spec/views/open_conference_ware/rooms/show.html.erb_spec.rb +15 -0
  411. data/spec/views/open_conference_ware/selector_votes/index.html.erb_spec.rb +41 -0
  412. data/spec/views/open_conference_ware/session_types/edit.html.erb_spec.rb +25 -0
  413. data/spec/views/open_conference_ware/session_types/index.html.erb_spec.rb +19 -0
  414. data/spec/views/open_conference_ware/session_types/new.html.erb_spec.rb +24 -0
  415. data/spec/views/open_conference_ware/session_types/show.html.erb_spec.rb +18 -0
  416. data/spec/views/open_conference_ware/tracks/edit.html.erb_spec.rb +25 -0
  417. data/spec/views/open_conference_ware/tracks/index.html.erb_spec.rb +56 -0
  418. data/spec/views/open_conference_ware/tracks/new.html.erb_spec.rb +26 -0
  419. data/spec/views/open_conference_ware/tracks/show.html.erb_spec.rb +23 -0
  420. data/spec/views/open_conference_ware/user_favorites/index.html.erb_spec.rb +29 -0
  421. data/spec/views/open_conference_ware/users/index_spec.rb +24 -0
  422. data/util/add_id3_tags_to_mp3.rb +57 -0
  423. data/util/schedule_demo.rb +36 -0
  424. data/util/seed_schedule.rb +61 -0
  425. data/util/sessions_to_lanyrd.rb +166 -0
  426. data/util/transfer_schedule_items.rb +25 -0
  427. data/util/update_proposal_status_from_voting_spreadsheet.rb +26 -0
  428. data/util/user_favorites_contention_report.rb +42 -0
  429. data/util/user_favorites_contention_report.sh +9 -0
  430. data/vendor/assets/fonts/glyphicons-halflings-regular.eot +0 -0
  431. data/vendor/assets/fonts/glyphicons-halflings-regular.svg +228 -0
  432. data/vendor/assets/fonts/glyphicons-halflings-regular.ttf +0 -0
  433. data/vendor/assets/fonts/glyphicons-halflings-regular.woff +0 -0
  434. data/vendor/assets/images/farbtastic/marker.png +0 -0
  435. data/vendor/assets/images/farbtastic/mask.png +0 -0
  436. data/vendor/assets/images/farbtastic/wheel.png +0 -0
  437. data/vendor/assets/images/idselector/aol.ico +0 -0
  438. data/vendor/assets/images/idselector/arrow.gif +0 -0
  439. data/vendor/assets/images/idselector/arrow_white_back.png +0 -0
  440. data/vendor/assets/images/idselector/arrow_white_forward.png +0 -0
  441. data/vendor/assets/images/idselector/blogger.ico +0 -0
  442. data/vendor/assets/images/idselector/claimid.ico +0 -0
  443. data/vendor/assets/images/idselector/flickr.ico +0 -0
  444. data/vendor/assets/images/idselector/google.ico +0 -0
  445. data/vendor/assets/images/idselector/lj.ico +0 -0
  446. data/vendor/assets/images/idselector/myopenid.ico +0 -0
  447. data/vendor/assets/images/idselector/openid.ico +0 -0
  448. data/vendor/assets/images/idselector/technorati.ico +0 -0
  449. data/vendor/assets/images/idselector/verisign.ico +0 -0
  450. data/vendor/assets/images/idselector/vidoop2.ico +0 -0
  451. data/vendor/assets/images/idselector/vox.ico +0 -0
  452. data/vendor/assets/images/idselector/yahoo.ico +0 -0
  453. data/vendor/assets/javascripts/audiojs/audio.min.js +23 -0
  454. data/vendor/assets/javascripts/audiojs/audiojs.swf +0 -0
  455. data/vendor/assets/javascripts/audiojs/player-graphics.gif +0 -0
  456. data/vendor/assets/javascripts/bootstrap.js +1999 -0
  457. data/vendor/assets/javascripts/farbtastic.js +329 -0
  458. data/vendor/assets/javascripts/html5shiv.js +8 -0
  459. data/vendor/assets/javascripts/idselector.js +538 -0
  460. data/vendor/assets/javascripts/idselector~original.js +532 -0
  461. data/vendor/assets/javascripts/jquery-migrate-1.2.1.js +521 -0
  462. data/vendor/assets/javascripts/jquery.localScroll.js +106 -0
  463. data/vendor/assets/javascripts/jquery.scrollTo.js +174 -0
  464. data/vendor/assets/javascripts/respond.min.js +6 -0
  465. data/vendor/assets/stylesheets/bootstrap-theme.css +384 -0
  466. data/vendor/assets/stylesheets/bootstrap.css +6805 -0
  467. data/vendor/assets/stylesheets/farbtastic.css.scss +38 -0
  468. data/vendor/assets/stylesheets/persona-buttons.css +229 -0
  469. data/vendor/assets/stylesheets/reset-fonts-grids.css +7 -0
  470. metadata +1174 -0
  471. metadata.gz.sig +0 -0
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+
3
+ describe OpenConferenceWare::AuthenticationsController do
4
+ routes { OpenConferenceWare::Engine.routes }
5
+
6
+ describe "GET sign_in" do
7
+ before { get :sign_in }
8
+
9
+ it "renders the sign_in template" do
10
+ expect(response).to render_template("sign_in")
11
+ end
12
+ end
13
+
14
+ describe "GET create" do
15
+ context "with no auth hash" do
16
+ before { get :create }
17
+
18
+ it "redirects to the sign in page" do
19
+ expect(response).to redirect_to(sign_in_path)
20
+ end
21
+ end
22
+
23
+ context "with an auth hash" do
24
+ context "matching an existing User's Authentication" do
25
+ let(:existing_user) { create(:user) }
26
+ let(:authentication) { create(:authentication,
27
+ provider: 'existing',
28
+ user: existing_user) }
29
+
30
+ before do
31
+ OmniAuth.config.add_mock(:existing, {uid: authentication.uid})
32
+ request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:existing]
33
+ end
34
+
35
+ shared_examples_for "signs the user in" do
36
+ it "signs the user in" do
37
+ expect(controller.current_user).to eq existing_user
38
+ end
39
+ end
40
+
41
+ context "when not already signed in" do
42
+ before { get :create }
43
+ include_examples "signs the user in"
44
+ end
45
+
46
+ context "when already signed in" do
47
+ before { login_as(create(:user)) }
48
+ before { get :create }
49
+ include_examples "signs the user in"
50
+ end
51
+ end
52
+
53
+ describe "containing an unknown UID" do
54
+ describe "when already signed in" do
55
+ let(:logged_in_user) { create(:user) }
56
+ let(:uid) { "logged-in:#{Time.now.to_i}" }
57
+
58
+ before do
59
+ login_as(logged_in_user)
60
+ OmniAuth.config.add_mock(:logged_in, {uid: uid})
61
+ request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:logged_in]
62
+
63
+ get :create
64
+ end
65
+
66
+ it "associates the new Authentication with the signed-in user" do
67
+ expect(logged_in_user.authentications.map(&:uid)).to include(uid)
68
+ end
69
+ end
70
+
71
+ describe "when not signed in" do
72
+ let(:uid) { "new-user:#{Time.now.to_i}" }
73
+ before do
74
+ OmniAuth.config.add_mock(:new_user, {uid: uid})
75
+ request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:new_user]
76
+
77
+ get :create
78
+ end
79
+
80
+ it "signs in as a new user" do
81
+ expect(controller.current_user.authentications.first.uid).to eq uid
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,146 @@
1
+ require 'spec_helper'
2
+
3
+ describe OpenConferenceWare::CommentsController do
4
+ render_views
5
+ fixtures :all
6
+ routes { OpenConferenceWare::Engine.routes }
7
+
8
+ before do
9
+ @event = events(:open)
10
+ @proposal = proposals(:quentin_widgets)
11
+ end
12
+
13
+ describe "index" do
14
+ shared_examples_for "shared forbidden index behaviors" do
15
+ describe "HTML" do
16
+ before do
17
+ get :index
18
+ end
19
+
20
+ it "should get redirect" do
21
+ response.should be_redirect
22
+ end
23
+ end
24
+ end
25
+
26
+ shared_examples_for "shared allowed index behaviors" do
27
+ describe "Atom" do
28
+ it "should get error if not key was specified" do
29
+ get :index, format: "atom"
30
+
31
+ response.should_not be_success
32
+ response.should_not be_redirect
33
+ end
34
+
35
+ it "should get error if key is wrong" do
36
+ get :index, format: "atom", secret: "MEOW"
37
+
38
+ response.should_not be_success
39
+ response.should_not be_redirect
40
+ end
41
+
42
+ it "should get data if key is right" do
43
+ get :index, format: "atom", secret: OpenConferenceWare::CommentsController::SECRET
44
+
45
+ response.should be_success
46
+ comments = assigns(:comments)
47
+ struct = Hash.from_xml(response.body)
48
+ struct['feed']['entry'].size.should == comments.size
49
+ end
50
+ end
51
+ end
52
+
53
+ describe "anonymous user" do
54
+ it_should_behave_like "shared forbidden index behaviors"
55
+ it_should_behave_like "shared allowed index behaviors"
56
+ end
57
+
58
+ describe "mortal user" do
59
+ before do
60
+ login_as :quentin
61
+ end
62
+
63
+ it_should_behave_like "shared forbidden index behaviors"
64
+ it_should_behave_like "shared allowed index behaviors"
65
+ end
66
+
67
+ describe "admin user" do
68
+ before do
69
+ login_as :aaron
70
+ end
71
+
72
+ after do
73
+ logout
74
+ end
75
+
76
+ it_should_behave_like "shared allowed index behaviors"
77
+
78
+ describe "HTML" do
79
+ before do
80
+ get :index
81
+ end
82
+
83
+ it "should display comments" do
84
+ response.should be_success
85
+ assigns(:comments).size.should > 0
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ describe "create" do
92
+ it "should reject comments from bots" do
93
+ post :create, proposal_id: @proposal.to_param, quagmire: "omg"
94
+
95
+ flash[:failure].should match(/robot/i)
96
+ response.should be_redirect
97
+ end
98
+
99
+ it "should fail on empty comment" do
100
+ email = "bubba@smith.com"
101
+ message = "Yo"
102
+ post :create, proposal_id: @proposal.to_param, comment: {email: "bubba@smith.com"}
103
+
104
+ flash.keys.should include(:failure)
105
+ assigns(:comment).should_not be_valid
106
+ end
107
+
108
+ it "should fail on incomplete comment" do
109
+ post :create, proposal_id: @proposal.to_param, comment: {email: "bubba@smith.com", message: ""}
110
+
111
+ flash.keys.should include(:failure)
112
+ assigns(:comment).should_not be_valid
113
+ end
114
+
115
+ it "should create new comment" do
116
+ email = "bubba@smith.com"
117
+ message = "Yo"
118
+ post :create, proposal_id: @proposal.to_param, comment: {email: email, message: message}
119
+
120
+ assigns(:comment).should be_valid
121
+ flash.keys.should_not include(:failure)
122
+ response.should redirect_to(proposal_url(@proposal, commented: true))
123
+ end
124
+
125
+ it "should assign email if logged in" do
126
+ login_as :quentin
127
+ post :create, proposal_id: @proposal.to_param, comment: {message: "Yo"}
128
+
129
+ comment = assigns(:comment)
130
+ comment.email.should == users(:quentin).email
131
+ end
132
+ end
133
+
134
+ describe "destroy" do
135
+ it "should destroy" do
136
+ login_as :aaron
137
+ comment = comments(:clio_chupacabras_fbi)
138
+ Comment.should_receive(:find).with(comment.to_param).and_return(comment)
139
+ comment.should_receive(:destroy)
140
+ delete :destroy, id: comment.to_param, format: :html
141
+
142
+ flash.keys.should include(:success)
143
+ # response.should be_redirect # TODO why not?
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ describe OpenConferenceWare::EventsController, "when displaying events" do
4
+ render_views
5
+ fixtures :all
6
+ routes { OpenConferenceWare::Engine.routes }
7
+
8
+ describe "index" do
9
+ it "should display error if there's no current event" do
10
+ Event.should_receive(:current).at_least(:once).and_return(nil)
11
+ get :index
12
+
13
+ response.should be_success
14
+ flash.keys.should include(:failure)
15
+ end
16
+
17
+ it "should display a list of events" do
18
+ get :index
19
+
20
+ response.should be_success
21
+ assigns(:events).should_not be_blank
22
+ end
23
+ end
24
+
25
+ describe "show" do
26
+ describe "non-existent event" do
27
+ before do
28
+ get :show, id: -1
29
+ end
30
+
31
+ it "should display error" do
32
+ flash.keys.should include(:failure)
33
+ end
34
+
35
+ it "should redirect to current event" do
36
+ response.should redirect_to(event_path(events(:open)))
37
+ end
38
+ end
39
+
40
+ describe "extant event" do
41
+ before do
42
+ @event = events(:closed)
43
+ get :show, id: @event.slug
44
+ end
45
+
46
+ it "should display event" do
47
+ response.should redirect_to(event_proposals_path(@event))
48
+ end
49
+ end
50
+ end
51
+
52
+ describe "speakers" do
53
+ before do
54
+ @event = events(:open)
55
+ stub_current_event!(event: @event)
56
+ end
57
+
58
+ describe "before proposals statuses published" do
59
+ before do
60
+ @event.stub(:proposal_status_published? => false)
61
+
62
+ get :speakers, id: @event.to_param
63
+ end
64
+
65
+ it "should redirect to event's proposals" do
66
+ response.should redirect_to(event_proposals_path(@event))
67
+ end
68
+
69
+ it "should display a flash error" do
70
+ flash[:failure].should_not be_blank
71
+ end
72
+
73
+ end
74
+
75
+ describe "after proposals statuses published" do
76
+ before do
77
+ @event.stub(:proposal_status_published? => true)
78
+ @event.stub(:schedule_published? => true)
79
+
80
+ get :speakers, id: @event.to_param
81
+ end
82
+
83
+ it "should get a speaker's page" do
84
+ response.should be_success
85
+ end
86
+
87
+ it "should see speakers" do
88
+ response.body.should have_selector(".fn", text: /#{users(:quentin).fullname}/)
89
+ end
90
+
91
+ it "should see sessions" do
92
+ response.body.should have_selector(".summary", text: proposals(:postgresql_session).title)
93
+ end
94
+
95
+ it "should not see non-confirmed proposals" do
96
+ response.body.should_not have_selector(".summary", text: proposals(:clio_chupacabras).title)
97
+ end
98
+ end
99
+
100
+ end
101
+ end
102
+
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+
3
+ describe OpenConferenceWare::Manage::EventsController do
4
+ render_views
5
+ fixtures :all
6
+ routes { OpenConferenceWare::Engine.routes }
7
+
8
+ before(:each) do
9
+ @event = events(:open)
10
+ login_as users(:aaron)
11
+ end
12
+
13
+ it "should retreive event show page" do
14
+ get :index, id: @event.slug
15
+
16
+ response.should be_success
17
+ end
18
+
19
+ it "should retrieve new event form" do
20
+ get :new, id: @event.slug
21
+
22
+ response.should be_success
23
+ end
24
+
25
+ it "should retrieve edit event form" do
26
+ get :edit, id: @event.slug
27
+
28
+ response.should be_success
29
+ end
30
+
31
+ it "should update event" do
32
+ stub_current_event!(event: @event)
33
+ attributes = { "title" => "omgwtfbbq" }
34
+ @event.should_receive(:assign_attributes).with(attributes)
35
+
36
+ put :update, id: @event.slug, event: attributes
37
+
38
+ response.should be_redirect
39
+ flash[:notice].should_not be_blank
40
+ end
41
+
42
+ it "should create event"
43
+
44
+ it "should destroy event"
45
+
46
+ def setup_proposals(&block)
47
+ @proposal_ids = ""
48
+ @proposals = []
49
+ %w[aaron_aardvarks quentin_widgets].each do |slug|
50
+ proposal = proposals(slug)
51
+ block.call proposal
52
+ @proposal_ids << "#{proposal.id},"
53
+ @proposals << proposal
54
+ end
55
+ end
56
+
57
+ def assert_notified
58
+ response.should be_redirect
59
+ flash[:success].should =~ /aaron@example.com/
60
+ flash[:success].should =~ /quentin@example.com/
61
+ end
62
+
63
+ it "should raise an error if trying to notify speakers other than accepted or rejected" do
64
+ setup_proposals { |proposal| proposal.accept! }
65
+ lambda { post :notify_speakers, { id: @event.slug, proposal_ids: @proposal_ids } }.should raise_error(ArgumentError)
66
+ end
67
+
68
+ it "should skip proposals that don't exist" do
69
+ setup_proposals { |proposal| proposal.accept! }
70
+ post :notify_speakers, { id: @event.slug, proposal_ids: @proposal_ids+',999', proposal_status: 'accepted' }
71
+
72
+ assert_notified
73
+ end
74
+
75
+ it "should skip proposals that have already been notified" do
76
+ setup_proposals do |proposal|
77
+ proposal.accept!
78
+ proposal.notified_at = Time.now
79
+ proposal.save
80
+ end
81
+ post :notify_speakers, { id: @event.slug, proposal_ids: @proposal_ids, proposal_status: 'accepted' }
82
+
83
+ assert_notified
84
+ flash[:success].should =~ /none/
85
+ flash[:success].should =~ /already been notified/
86
+ end
87
+
88
+ it "should notify accepted speakers" do
89
+ setup_proposals { |proposal| proposal.accept! }
90
+ post :notify_speakers, { id: @event.slug, proposal_ids: @proposal_ids, proposal_status: 'accepted' }
91
+
92
+ assert_notified
93
+ flash[:success].should_not =~ /already been notified/
94
+ end
95
+
96
+ it "should notify rejected speakers" do
97
+ setup_proposals { |proposal| proposal.reject! }
98
+ post :notify_speakers, { id: @event.slug, proposal_ids: @proposal_ids, proposal_status: 'rejected' }
99
+
100
+ assert_notified
101
+ flash[:success].should_not =~ /already been notified/
102
+ end
103
+ end
@@ -0,0 +1,1273 @@
1
+ require 'spec_helper'
2
+
3
+ describe OpenConferenceWare::ProposalsController do
4
+ render_views
5
+ fixtures :all
6
+ routes { OpenConferenceWare::Engine.routes }
7
+
8
+ # Return an array of Proposal objects extracted from the response body.
9
+ def extract_proposals
10
+ return assert_select('.proposal_row').map{|n| Proposal.find(n.attributes['id'].gsub(/^proposal_row_/, ''))}
11
+ end
12
+
13
+ before do
14
+ @event = events(:open)
15
+ end
16
+
17
+ describe "index" do
18
+ describe "when returning HTML" do
19
+ before do
20
+ get :index, event_id: @event.slug
21
+ end
22
+
23
+ it "should be successful" do
24
+ response.should be_success
25
+ end
26
+
27
+ it "should assign an event" do
28
+ assigns(:event).should == @event
29
+ end
30
+
31
+ it "should assign proposals" do
32
+ assigns(:proposals).should_not be_blank
33
+ end
34
+ end
35
+
36
+ describe "when returning CSV" do
37
+
38
+ def get_csv_index
39
+ OpenConferenceWare.stub(have_user_profiles: true)
40
+ OpenConferenceWare.stub(have_multiple_presenters: true)
41
+ stub_current_event!(event: @event)
42
+
43
+ get :index, event_id: @event.to_param, format: "csv"
44
+
45
+ @rows = CSV.parse(response.body)
46
+ @header = @rows.shift
47
+ end
48
+
49
+ shared_examples_for "shared CSV behaviors" do
50
+
51
+ it "should return CSV" do
52
+ @rows.should be_a_kind_of(Array)
53
+ end
54
+
55
+ it "should see public fields" do
56
+ @header.should include("Title")
57
+ end
58
+ end
59
+
60
+ shared_examples_for "shared non-admin CSV behaviors" do
61
+ it "should not see private fields" do
62
+ @header.should_not include("Emails")
63
+ end
64
+ end
65
+
66
+ describe "anonymous user" do
67
+ before do
68
+ logout
69
+ end
70
+
71
+ describe "with visible schedule" do
72
+ before do
73
+ @controller.stub(:schedule_visible? => true)
74
+ get_csv_index
75
+ end
76
+
77
+ it_should_behave_like "shared CSV behaviors"
78
+
79
+ it_should_behave_like "shared non-admin CSV behaviors"
80
+
81
+ it "should see schedule fields" do
82
+ @header.should include("Start Time")
83
+ end
84
+ end
85
+
86
+ describe "without visible schedule" do
87
+ before do
88
+ @controller.stub(:schedule_visible? => false)
89
+ get_csv_index
90
+ end
91
+
92
+ it_should_behave_like "shared CSV behaviors"
93
+
94
+ it_should_behave_like "shared non-admin CSV behaviors"
95
+
96
+ it "should not see schedule fields" do
97
+ @header.should_not include("Start Time")
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ describe "mortal user" do
104
+ before do
105
+ login_as(:quentin)
106
+ get_csv_index
107
+ end
108
+
109
+ it_should_behave_like "shared CSV behaviors"
110
+
111
+ it_should_behave_like "shared non-admin CSV behaviors"
112
+ end
113
+
114
+ describe "admin user" do
115
+ before do
116
+ controller.stub(:admin?).and_return(true)
117
+ get_csv_index
118
+ end
119
+
120
+ it_should_behave_like "shared CSV behaviors"
121
+
122
+ it "should see private fields" do
123
+ @header.should include("Emails")
124
+ end
125
+ end
126
+ end
127
+
128
+ shared_examples_for "when exporting" do
129
+ # Expects following to be set by implementor's #before block:
130
+ # - @proposals
131
+ # - @records
132
+ # - @record
133
+
134
+ it "should assign multiple items" do
135
+ @proposals.size.should >= 1
136
+ end
137
+
138
+ it "should export same number of items as assigned" do
139
+ @records.size.should == @proposals.size
140
+ end
141
+
142
+ it "should export presenter" do
143
+ @record.keys.should include('presenter')
144
+ end
145
+
146
+ it "should not export email" do
147
+ @record.keys.should_not include('email')
148
+ end
149
+
150
+ it "should not export private notes" do
151
+ @record.keys.should_not include('note_to_organizers')
152
+ end
153
+ end
154
+
155
+ describe "when returning XML" do
156
+ before(:each) do
157
+ get :index, event_id: @event.slug, format: "xml"
158
+
159
+ @proposals = assigns(:proposals)
160
+ @struct = Hash.from_xml(response.body)
161
+ @records = @struct['open_conference_ware_proposals']
162
+ @record = @records.first
163
+ end
164
+
165
+ it_should_behave_like "when exporting"
166
+ end
167
+
168
+ describe "when returning JSON" do
169
+ before(:each) do
170
+ get :index, event_id: @event.slug, format: "json"
171
+
172
+ @proposals = assigns(:proposals)
173
+ @struct = ActiveSupport::JSON.decode(response.body)
174
+ @records = @struct
175
+ @record = @records.first["proposal"]
176
+ end
177
+
178
+ it_should_behave_like "when exporting"
179
+ end
180
+
181
+ describe "when sorting" do
182
+ it "should sort proposals by title" do
183
+ get :index, sort: "title", event_id: @event.to_param
184
+ extracted = extract_proposals
185
+
186
+ extracted.size.should > 0
187
+ values = extracted.map(&:title)
188
+ expected = values.sort_by(&:downcase)
189
+ values.should == expected
190
+ end
191
+
192
+ it "should sort proposals by track" do
193
+ get :index, sort: "track", event_id: @event.to_param
194
+ proposals = extract_proposals
195
+
196
+ proposals.size.should > 0
197
+
198
+ tracks_returned = proposals.map{|proposal| proposal.track.title}
199
+ tracks_expected = tracks_returned.sort_by(&:downcase)
200
+ tracks_returned.should == tracks_expected
201
+ end
202
+
203
+ it "should sort proposals by title descending" do
204
+ get :index, sort: "title", dir: "desc", event_id: @event.to_param
205
+ proposals = extract_proposals
206
+
207
+ proposals.size.should > 0
208
+
209
+ titles_returned = proposals.map(&:title)
210
+ titles_expected = titles_returned.sort_by(&:downcase).reverse
211
+ titles_returned.should == titles_expected
212
+ end
213
+
214
+ it "should sort proposals by start time" do
215
+ get :sessions_index, sort: "start_time", event_id: @event.to_param
216
+ proposals = extract_proposals
217
+
218
+ proposals.size.should > 0
219
+
220
+ values = proposals.map(&:start_time)
221
+ expected = values.sort
222
+ values.should == expected
223
+ end
224
+
225
+ it "should not sort proposals by forbidden field" do
226
+ Proposal.any_instance.should_not_receive(:destroy)
227
+ get :index, sort: "destroy", event_id: @event.to_param
228
+
229
+ # default to sorting by submitted_at
230
+ proposals = extract_proposals
231
+ proposals.size.should > 0
232
+ values = proposals.map(&:submitted_at)
233
+ expected = values.sort
234
+ values.should == expected
235
+ end
236
+
237
+ end
238
+
239
+ describe "when returning ATOM" do
240
+ def get_entry(proposal_symbol)
241
+ title = proposals(proposal_symbol).title
242
+ return @doc.xpath("//entry/title[text()='#{title}']")
243
+ end
244
+
245
+ describe "for /proposals.atom" do
246
+ before do
247
+ get :index, format: "atom"
248
+ @doc = Nokogiri.parse(response.body)
249
+ end
250
+
251
+ it "should include proposals from multiple events" do
252
+ get_entry(:clio_chupacabras).should_not be_empty
253
+ get_entry(:aaron_aardvarks).should_not be_empty
254
+ end
255
+ end
256
+
257
+ describe "for /events/:event_id/proposals.atom" do
258
+ before do
259
+ get :index, format: "atom", event_id: @event.slug
260
+ @doc = Nokogiri.parse(response.body)
261
+ end
262
+
263
+ it "should include proposals from this event" do
264
+ get_entry(:aaron_aardvarks).should_not be_empty
265
+ end
266
+
267
+ it "should not include proposals from other events" do
268
+ get_entry(:clio_chupacabras).should be_empty
269
+ end
270
+ end
271
+ end
272
+
273
+ end
274
+
275
+ describe "sessions" do
276
+ it "should display session_text" do
277
+ event = stub_model(Event,
278
+ :proposal_status_published? => true,
279
+ id: 1234,
280
+ slug: 'event_slug',
281
+ session_text: "MySessionText",
282
+ populated_sessions: []
283
+ )
284
+ stub_current_event!(event: event)
285
+
286
+ get :sessions_index, event_id: event.to_param
287
+ response.body.should have_selector(".event_text.session_text") do |text|
288
+ text.should contain("MySessionText")
289
+ end
290
+ end
291
+
292
+ it "should display a list of sessions" do
293
+ proposal = stub_model(Proposal, state: "confirmed", users: [])
294
+ proposals = [proposal]
295
+ event = stub_model(Event,
296
+ :proposal_status_published? => true,
297
+ id: 1234,
298
+ slug: 'event_slug',
299
+ populated_sessions: proposals
300
+ )
301
+
302
+ stub_current_event!(event: event)
303
+
304
+ # Bypass #fetch_object because it can't cache our singleton doubles.
305
+ Proposal.stub(:fetch_object).and_return do |slug, callback|
306
+ callback.call
307
+ end
308
+
309
+ get :sessions_index, event_id: event.to_param
310
+ expect(assigns(:proposals)).to be_a_kind_of(proposals.class)
311
+ end
312
+
313
+ it "should redirect to proposals unless the proposal status is published" do
314
+ event = stub_model(Event, :proposal_status_published? => false, id: 1234, slug: 'event_slug')
315
+ stub_current_event!(event: event)
316
+ get :sessions_index, event_id: event.to_param
317
+
318
+ response.should redirect_to(event_proposals_url(event))
319
+ end
320
+
321
+ it "should redirect /sessions to proposals unless proposal status is published" do
322
+ event = stub_model(Event, :proposal_status_published? => false, id: 1234, slug: 'event_slug')
323
+ stub_current_event!(event: event, status: :assigned_to_current)
324
+ get :sessions_index, format: :html
325
+
326
+ response.should redirect_to(event_proposals_url(event))
327
+ end
328
+
329
+ it "should normalize /sessions if proposal status is published" do
330
+ event = stub_model(Event, :proposal_status_published? => true, id: 1234, slug: 'event_slug')
331
+ stub_current_event!(event: event, status: :assigned_to_current)
332
+ get :sessions_index, format: :html
333
+
334
+ response.should redirect_to(event_sessions_path(event))
335
+ end
336
+
337
+ it "should normalize /schedule if proposal status is published" do
338
+ event = stub_model(Event, :proposal_status_published? => true, :schedule_published? => true, id: 1234, slug: 'event_slug')
339
+ stub_current_event!(event: event, status: :assigned_to_current)
340
+ get :schedule, format: :html
341
+
342
+ response.should redirect_to(event_schedule_path(event))
343
+ end
344
+ end
345
+
346
+ describe "show" do
347
+ it "should display extant proposal" do
348
+ proposal = proposals(:quentin_widgets)
349
+ get :show, id: proposal.id
350
+
351
+ response.should be_success
352
+ assigns(:proposal).should == proposal
353
+ end
354
+
355
+ it "should redirect back to proposals list if asked to display a non-existent proposal" do
356
+ get :show, id: -1
357
+
358
+ flash[:failure].should_not be_blank
359
+ response.should redirect_to(proposals_url)
360
+ end
361
+
362
+ it "should redirect back to proposals list if asked to display a proposal without an event" do
363
+ proposal = proposals(:quentin_widgets)
364
+ proposal.stub(event: nil)
365
+ Proposal.stub(find_by_id: proposal, find: proposal, lookup: proposal)
366
+
367
+ get :show, id: proposal.id
368
+
369
+ flash[:failure].should_not be_blank
370
+ response.should redirect_to(event_proposals_path(events(:open)))
371
+ end
372
+
373
+ describe "redirect" do
374
+ # Options:
375
+ # * published: Are proposal statuses published for this event?
376
+ # * confirmed: Is this proposal confirmed?
377
+ # * session: Is this proposal being accessed via a sessions#show route?
378
+ # * redirect: Redirect to where? (:proposal, :session, nil)
379
+ def assert_show(opts={}, &block)
380
+ @key = 123
381
+ @event.stub(:proposal_status_published?).and_return(opts[:published])
382
+ stub_current_event!(event: @event)
383
+
384
+ @users = []
385
+ @users.stub(:by_name).and_return([])
386
+
387
+ @proposal = stub_model(Proposal, id: @key, event: @event, track: @event.tracks.empty? ? nil : Track.first, users: @users)
388
+ @proposal.stub(:confirmed?).and_return(opts[:confirmed])
389
+ controller.stub(:get_proposal_and_assignment_status).and_return([@proposal, :assigned_via_param])
390
+ get opts[:session] ? :session_show : :show, id: @key
391
+ case opts[:redirect]
392
+ when :proposal
393
+ response.should redirect_to(proposal_path(@key))
394
+ when :session
395
+ response.should redirect_to(session_path(@key))
396
+ when nil, false
397
+ response.should be_success
398
+ else
399
+ end
400
+ end
401
+
402
+ describe "when status published" do
403
+ it "should redirect confirmed proposal to session" do
404
+ assert_show published: true, confirmed: true, session: false, redirect: :session
405
+ end
406
+
407
+ it "should redirect non-session to proposal" do
408
+ assert_show published: true, confirmed: false, session: true, redirect: :proposal
409
+ end
410
+
411
+ it "should display session" do
412
+ assert_show published: true, confirmed: true, session: true, redirect: false
413
+ end
414
+
415
+ it "should display proposal" do
416
+ assert_show published: true, confirmed: false, session: false, redirect: false
417
+ end
418
+ end
419
+
420
+ describe "when status not published" do
421
+ it "should allow admin to view sessions" do
422
+ login_as :aaron
423
+ assert_show published: false, confirmed: true, session: true, redirect: false
424
+ end
425
+
426
+ it "should redirect confirmed proposal to proposals" do
427
+ assert_show published: false, confirmed: true, session: true, redirect: :proposal
428
+ end
429
+
430
+ it "should redirect non-session to proposal" do
431
+ assert_show published: false, confirmed: false, session: true, redirect: :proposal
432
+ end
433
+
434
+ it "should display confirmed proposal" do
435
+ assert_show published: false, confirmed: true, session: false, redirect: false
436
+ end
437
+
438
+ it "should display session as proposal" do
439
+ assert_show published: false, confirmed: false, session: false, redirect: false
440
+ end
441
+ end
442
+
443
+ describe "non-current event" do
444
+ render_views false
445
+ it "should not redirect a published session of an old event if current event isn't publishing sesions" do
446
+ current_event = stub_model(Event, slug: 'new', proposal_status_published: false)
447
+ old_event = stub_model(Event, slug: 'old', proposal_status_published: true)
448
+ old_session_user = users(:clio)
449
+ old_session = stub_model(Proposal, status: 'confirmed', event: old_event)
450
+ old_session.users << old_session_user
451
+
452
+ Proposal.stub(find: old_session, find_by_id: old_session, lookup: old_session)
453
+ Event.stub(current: current_event)
454
+
455
+ get :session_show, id: old_session.id
456
+ response.should be_success
457
+ end
458
+ end
459
+ end
460
+
461
+ describe "accepted proposal" do
462
+ before do
463
+ @proposal = proposals(:quentin_widgets)
464
+ @proposal.accept!
465
+ end
466
+
467
+ it "should notify owners of acceptance" do
468
+ login_as(users(:quentin))
469
+ get :show, id: @proposal.id
470
+ response.body.should have_selector("h3", text: 'Congratulations')
471
+ end
472
+
473
+ it "should not notify non-owners of acceptance" do
474
+ get :show, id: @proposal.id
475
+ response.body.should_not have_selector("h3", text: 'Congratulations')
476
+ end
477
+
478
+ it "should not notify owners of acceptance if proposal confirmation controls are not visible" do
479
+ event = @proposal.event
480
+ event.stub(:show_proposal_confirmation_controls? => false)
481
+ Proposal.stub(lookup: @proposal)
482
+
483
+ login_as(users(:quentin))
484
+
485
+ get :show, id: @proposal.id
486
+ response.body.should_not have_selector("h3", text: 'Congratulations')
487
+ end
488
+ end
489
+
490
+ describe "not-accepted proposal" do
491
+ before do
492
+ login_as(users(:quentin))
493
+ @proposal = proposals(:quentin_widgets)
494
+ end
495
+
496
+ it "should not notify proposed proposal owners of acceptance" do
497
+ get :show, id: @proposal.id
498
+ response.body.should_not have_selector("h3", text: 'Congratulations')
499
+ end
500
+
501
+ it "should not notify rejected proposal owners of acceptance" do
502
+ @proposal.reject!
503
+ get :show, id: @proposal.id
504
+ response.body.should_not have_selector("h3", text: 'Congratulations')
505
+ end
506
+
507
+ it "should not notify junk proposal owners of acceptance" do
508
+ @proposal.mark_as_junk!
509
+ get :show, id: @proposal.id
510
+ response.body.should_not have_selector("h3", text: 'Congratulations')
511
+ end
512
+ end
513
+
514
+ end
515
+
516
+ describe "new" do
517
+ describe "for open event" do
518
+ describe "with user_profiles?" do
519
+ before(:each) do
520
+ OpenConferenceWare.stub(have_user_profiles: true)
521
+ end
522
+
523
+ it "should redirect incomplete profiles to user edit form" do
524
+ user = users(:incognito)
525
+ login_as(user)
526
+ get :new, event_id: events(:open).slug
527
+
528
+ flash.keys.should include(:notice)
529
+ response.should redirect_to(edit_user_path(user, require_complete_profile: true))
530
+ end
531
+
532
+ it "should allow users with complete profiles" do
533
+ login_as(:quentin)
534
+ get :new, event_id: events(:open).slug
535
+
536
+ flash.keys.should_not include(:failure)
537
+ response.should be_success
538
+ end
539
+ end
540
+
541
+ describe "without user_profiles?" do
542
+ before(:each) do
543
+ OpenConferenceWare.stub(have_user_profiles: false)
544
+ end
545
+
546
+ describe "with anonymous_proposals" do
547
+ before(:each) do
548
+ OpenConferenceWare.stub(have_anonymous_proposals: true)
549
+ end
550
+
551
+ it "should display form for open events" do
552
+ get :new, event_id: events(:open).slug
553
+
554
+ response.should be_success
555
+ assigns(:proposal).should be_true
556
+ end
557
+
558
+ it "should not assign presenter if anonymous" do
559
+ logout
560
+ get :new, event_id: events(:open).slug
561
+
562
+ response.should be_success
563
+ proposal = assigns(:proposal)
564
+ proposal.presenter.should be_blank
565
+ end
566
+ end
567
+
568
+ describe "without anonymous_proposals" do
569
+ before(:each) do
570
+ OpenConferenceWare.stub(have_anonymous_proposals: false)
571
+ end
572
+
573
+ it "should redirect anonymous user to login" do
574
+ get :new, event_id: events(:open).slug
575
+
576
+ flash.keys.should include(:notice)
577
+ response.should redirect_to(sign_in_path)
578
+ end
579
+ end
580
+
581
+ it "should assign presenter if logged in" do
582
+ user = users(:quentin)
583
+ login_as(user)
584
+ get :new, event_id: events(:open).slug
585
+
586
+ response.should be_success
587
+ proposal = assigns(:proposal)
588
+ proposal.presenter.should == user.fullname
589
+ end
590
+
591
+ describe "when an event can have tracks" do
592
+ it "should assign a track if there's only one" do
593
+ event = create(:event)
594
+ event.session_types << build(:session_type)
595
+ track = create(:track, event: event)
596
+ user = create(:user)
597
+ login_as(user)
598
+
599
+ get :new, event_id: event.slug
600
+
601
+ flash[:failure].should be_nil
602
+ assigns(:proposal).track.should == track
603
+ end
604
+
605
+ it "should not assign a track if there's more than one" do
606
+ event = create(:event)
607
+ event.session_types << build(:session_type)
608
+ track1 = create(:track, event: event)
609
+ track2 = create(:track, event: event)
610
+ user = create(:user)
611
+ login_as(user)
612
+
613
+ get :new, event_id: event.slug
614
+
615
+ flash[:failure].should be_nil
616
+ assigns(:proposal).track.should be_nil
617
+ end
618
+ end
619
+
620
+ describe "when event can have session types" do
621
+ it "should assign a session type if there's only one" do
622
+ event = create(:event)
623
+ event.tracks << build(:track)
624
+ session_type = create(:session_type, event: event)
625
+ user = create(:user)
626
+ login_as(user)
627
+
628
+ get :new, event_id: event.slug
629
+
630
+ assigns(:proposal).session_type.should == session_type
631
+ end
632
+
633
+ it "should not assign a session type if there's more than one" do
634
+ event = create(:event)
635
+ event.tracks << build(:track)
636
+ session_type1 = create(:session_type, event: event)
637
+ session_type2 = create(:session_type, event: event)
638
+ user = create(:user)
639
+ login_as(user)
640
+
641
+ get :new, event_id: event.slug
642
+
643
+ assigns(:proposal).session_type.should be_nil
644
+ end
645
+ end
646
+ end
647
+ end
648
+
649
+ describe "with closed event" do
650
+ it "should not display form" do
651
+ login_as(users(:quentin))
652
+ event = events(:closed)
653
+ get :new, event_id: event.to_param
654
+
655
+ response.should redirect_to(event_proposals_path(event))
656
+ end
657
+ end
658
+ end
659
+
660
+ describe "edit" do
661
+ before do
662
+ @proposal = proposals(:quentin_widgets)
663
+ end
664
+
665
+ shared_examples_for "shared allowed edit behaviors" do
666
+ it "should not redirect with failure" do
667
+ get :edit, id: @proposal.id, event_id: @event.to_param
668
+ flash.keys.should_not include(:failure)
669
+ response.should be_success
670
+ end
671
+ end
672
+
673
+ shared_examples_for "shared forbidden edit behaviors" do
674
+ it "should redirect with failure" do
675
+ get :edit, id: @proposal.id, event_id: @event.to_param
676
+ flash.keys.should include(:failure)
677
+ response.should redirect_to(proposal_path(@proposal))
678
+ end
679
+ end
680
+
681
+ describe "anonymous user" do
682
+ before(){ logout }
683
+
684
+ it "should redirect to login" do
685
+ get :edit, id: @proposal.id, event_id: @event.to_param
686
+ response.should redirect_to(sign_in_path)
687
+ end
688
+ end
689
+
690
+ describe "non-owner mortal user" do
691
+ before(){ login_as :clio }
692
+ it_should_behave_like "shared forbidden edit behaviors"
693
+ end
694
+
695
+ describe "owner mortal user" do
696
+ before(){ login_as :quentin }
697
+ it_should_behave_like "shared allowed edit behaviors"
698
+ end
699
+
700
+ describe "admin user" do
701
+ before { login_as :aaron }
702
+ it_should_behave_like "shared allowed edit behaviors"
703
+ end
704
+
705
+ describe "when closed" do
706
+ it "should redirect if owner tries to edit proposal for closed event" do
707
+ proposal = proposals(:clio_chupacabras)
708
+ login_as :clio
709
+ get :edit, id: proposal.id
710
+
711
+ pending "FIXME when should people not be able to edit proposals?"
712
+ response.should redirect_to(event_proposals_path(proposal.event))
713
+ end
714
+
715
+ it "should allow admin to edit" do
716
+ proposal = proposals(:clio_chupacabras)
717
+ login_as :aaron
718
+ get :edit, id: proposal.id
719
+
720
+ response.should be_success
721
+ assigns(:proposal).should == proposal
722
+ end
723
+ end
724
+ end
725
+
726
+ describe "create" do
727
+ # Try to create a proposal.
728
+ #
729
+ # Arguments:
730
+ # * login: User to login as, can be nil for none, symbol or user object.
731
+ # * inputs: Hash of properties to create a proposal from.
732
+ def assert_create(login=nil, inputs={}, &block)
733
+ login ? login_as(login) : logout
734
+ # TODO extract :commit into separate argument
735
+ post :create, inputs.reverse_merge(commit: 'really')
736
+ @record = assigns(:proposal)
737
+ block.call
738
+ end
739
+
740
+ before do
741
+ # TODO test other settings combinations
742
+ OpenConferenceWare.stub(have_proposal_excerpts: false)
743
+ OpenConferenceWare.stub(have_multiple_presenters: false)
744
+ OpenConferenceWare.stub(have_user_profiles: false)
745
+
746
+ @inputs = proposals(:quentin_widgets).attributes
747
+ @record = nil
748
+ end
749
+
750
+ describe "with user_profiles?" do
751
+ before(:each) do
752
+ OpenConferenceWare.stub(have_user_profiles: true)
753
+ end
754
+
755
+ it "should fail to create proposal without a complete user" do
756
+ user = users(:quentin)
757
+ user.should_receive(:complete_profile?).at_least(:once).and_return(false)
758
+ User.should_receive(:find).and_return(user)
759
+ proposal = Proposal.new(@inputs)
760
+ proposal.users << user
761
+ Proposal.should_receive(:new).and_return(proposal)
762
+ assert_create(user, event_id: @event.slug, proposal: @inputs) do
763
+ response.should be_success
764
+ proposal = assigns(:proposal)
765
+ proposal.should_not be_valid
766
+ end
767
+ end
768
+ end
769
+
770
+ describe "without user_profiles?" do
771
+ before(:each) do
772
+ OpenConferenceWare.stub(have_user_profiles: false)
773
+ end
774
+
775
+ describe "with anonymous proposals" do
776
+ before(:each) do
777
+ OpenConferenceWare.stub(have_anonymous_proposals: true)
778
+ end
779
+
780
+ it "should create proposal for anonymous user" do
781
+ assert_create(nil, event_id: @event.slug, proposal: @inputs) do
782
+ proposal = assigns(:proposal)
783
+ proposal.should be_valid
784
+ proposal.id.should_not be_nil
785
+ end
786
+ end
787
+
788
+ it "should preview proposal for anonymous user" do
789
+ @inputs['title'] = ''
790
+ assert_create(nil, event_id: @event.slug, proposal: @inputs, submit: nil, preview: 'Preview') do
791
+ proposal = assigns(:proposal)
792
+ proposal.errors.should_not be_empty
793
+ proposal.should_not be_valid
794
+ proposal.id.should be_nil
795
+ end
796
+ end
797
+ end
798
+
799
+ describe "without anonymous proposals" do
800
+ before(:each) do
801
+ OpenConferenceWare.stub(have_anonymous_proposals: false)
802
+ end
803
+
804
+ it "should not create proposal for anonymous user" do
805
+ assert_create(nil, event_id: @event.slug, proposal: @inputs) do
806
+ response.should redirect_to(sign_in_path)
807
+ end
808
+ end
809
+ end
810
+
811
+ it "should create proposal for mortal user" do
812
+ assert_create(:quentin, event_id: @event.slug, proposal: @inputs) do
813
+ proposal = assigns(:proposal)
814
+ proposal.should be_valid
815
+ proposal.id.should_not be_nil
816
+ end
817
+ end
818
+
819
+ it "should fail to create proposal without a presenter" do
820
+ inputs = @inputs.clone
821
+ inputs['presenter'] = nil
822
+ assert_create(:quentin, event_id: @event.slug, proposal: inputs) do
823
+ response.should be_success
824
+ proposal = assigns(:proposal)
825
+ proposal.should_not be_valid
826
+ end
827
+ end
828
+ end
829
+
830
+ describe "success page" do
831
+ before(:each) do
832
+ login_as(:quentin)
833
+ @proposal = stub_model(Proposal, id: 123)
834
+ @proposal.should_receive(:save).and_return(true)
835
+ @proposal.should_receive(:add_user).and_return(true)
836
+ Proposal.should_receive(:new).and_return(@proposal)
837
+ end
838
+
839
+ it "should display success page" do
840
+ @controller.should_receive(:render).and_return("My HTML here")
841
+
842
+ post :create, commit: "Create", proposal: {foo: 'bar'}
843
+ end
844
+ end
845
+
846
+ end
847
+
848
+ describe "update" do
849
+ def assert_update(login=nil, inputs={}, optional_params={}, &block)
850
+ login ? login_as(login) : logout
851
+ optional_params.reverse_merge commit: 'really'
852
+ put :update, { id: ( inputs['id'] || inputs[:id] ), proposal: inputs }.merge(optional_params)
853
+ block.call
854
+ end
855
+
856
+ before do
857
+ @user = users(:quentin)
858
+ @proposal = proposals(:quentin_widgets)
859
+ @inputs = @proposal.attributes
860
+ end
861
+
862
+ it "should prevent editing of title when proposal titles are locked" do
863
+ @event = stub_current_event!
864
+ @event.stub(:proposal_titles_locked?).and_return(true)
865
+ @controller.stub(:get_proposal_and_assignment_status).and_return(@proposal)
866
+ @proposal.stub(:event).and_return(@event)
867
+
868
+ assert_update(:quentin, id: @proposal.id, title: 'OMG') do
869
+ @proposal.reload
870
+ @proposal.title.should_not == 'OMG'
871
+ end
872
+ end
873
+
874
+ it "should redirect anonymous user to login" do
875
+ assert_update(nil, @inputs) do
876
+ response.should redirect_to(sign_in_path)
877
+ end
878
+ end
879
+
880
+ it "should reject non-owner mortal user" do
881
+ assert_update(:clio, @inputs) do
882
+ flash.keys.should include(:failure)
883
+ response.should redirect_to(proposal_url(@proposal))
884
+ end
885
+ end
886
+
887
+ describe "when settings status" do
888
+ it "should allow admin to change status" do
889
+ @inputs[:transition] = 'accept'
890
+ @controller.should_receive(:get_proposal_and_assignment_status).and_return([@proposal, :assigned_via_param])
891
+ @proposal.should_receive(:accept!)
892
+ assert_update(:aaron, @inputs) do
893
+ # Everything is done through the should_receive
894
+ end
895
+ end
896
+
897
+ it "should not allow non-admin to change status" do
898
+ @inputs[:transition] = 'accept'
899
+ @controller.should_receive(:get_proposal_and_assignment_status).and_return([@proposal, :assigned_via_param])
900
+ @proposal.should_not_receive(:accept!)
901
+ assert_update(:quentin, @inputs) do
902
+ end
903
+ end
904
+ end
905
+
906
+ describe "with user_profiles?" do
907
+ before(:each) do
908
+ OpenConferenceWare.stub(have_user_profiles: true)
909
+ end
910
+
911
+ it "should specify update behavior"
912
+ end
913
+
914
+ describe "without user_profiles?" do
915
+ before(:each) do
916
+ OpenConferenceWare.stub(have_user_profiles: false)
917
+ end
918
+
919
+ it "should display edit form if fields are invalid" do
920
+ inputs = @inputs.clone
921
+ inputs['presenter'] = nil
922
+ assert_update(:quentin, inputs) do
923
+ response.should be_success
924
+ response.should render_template('edit')
925
+ end
926
+ end
927
+
928
+ it "should allow owner mortal user" do
929
+ assert_update(:quentin, @inputs) do
930
+ flash.keys.should include(:success)
931
+ response.should redirect_to(proposal_url(@proposal))
932
+ end
933
+ end
934
+
935
+ it "should display preview" do
936
+ assert_update(:quentin, @inputs, { commit: nil, preview: 'Preview' }) do
937
+ response.should be_success
938
+ response.should render_template('edit')
939
+ end
940
+ end
941
+
942
+ it "should allow admin user" do
943
+ assert_update(:aaron, @inputs) do
944
+ flash.keys.should include(:success)
945
+ response.should redirect_to(proposal_url(@proposal))
946
+ end
947
+ end
948
+ end
949
+ end
950
+
951
+ describe "delete" do
952
+ before do
953
+ @proposal = proposals(:quentin_widgets)
954
+ Proposal.stub(:lookup).and_return(@proposal)
955
+ end
956
+
957
+ def assert_delete(login=nil, &block)
958
+ login ? login_as(login) : logout
959
+ delete :destroy, id: @proposal.id
960
+ block.call
961
+ end
962
+
963
+ it "should ask anonymous to login" do
964
+ @proposal.should_not_receive(:destroy)
965
+ assert_delete do
966
+ response.should redirect_to(sign_in_path)
967
+ end
968
+ end
969
+
970
+ it "should reject non-owner mortal user" do
971
+ @proposal.should_not_receive(:destroy)
972
+ assert_delete(:clio) do
973
+ flash.keys.should include(:failure)
974
+ response.should redirect_to(proposal_url(@proposal))
975
+ end
976
+ end
977
+
978
+ it "should allow owner mortal user" do
979
+ @proposal.should_receive(:destroy)
980
+ assert_delete(:quentin) do
981
+ flash.keys.should include(:success)
982
+ response.should redirect_to(event_proposals_url(@proposal.event))
983
+ end
984
+ end
985
+
986
+ it "should allow admin user" do
987
+ @proposal.should_receive(:destroy)
988
+ assert_delete(:quentin) do
989
+ flash.keys.should include(:success)
990
+ response.should redirect_to(event_proposals_url(@proposal.event))
991
+ end
992
+ end
993
+ end
994
+
995
+ describe "schedule" do
996
+ it "should not fail like a whale" do
997
+ @controller.stub(:schedule_visible?).and_return(true)
998
+ item = proposals(:postgresql_session)
999
+
1000
+ get :schedule, event_id: @event.slug
1001
+
1002
+ response.should be_success
1003
+ response.body.should have_selector(".summary", text: item.title)
1004
+ end
1005
+
1006
+ it "should not fail like a whale with iCalendar" do
1007
+ @controller.stub(:schedule_visible?).and_return(true)
1008
+ item = proposals(:postgresql_session)
1009
+
1010
+ get :schedule, event_id: @event.slug, format: "ics"
1011
+
1012
+ response.should be_success
1013
+ calendar = Vpim::Icalendar.decode(response.body).first
1014
+ component = calendar.find{|t| t.summary == item.title}
1015
+
1016
+ dtstart = Time.parse(component.dtstart.strftime('%Y-%m-%d %H:%M:%S UTC'))
1017
+ dtend = Time.parse(component.dtend.strftime('%Y-%m-%d %H:%M:%S UTC'))
1018
+
1019
+ component.should_not be_nil
1020
+ dtstart.should == item.start_time
1021
+ dtend.should == item.end_time
1022
+ component.summary.should == item.title
1023
+ component.description.should == (item.respond_to?(:users) ?
1024
+ "#{item.users.map(&:fullname).join(', ')}: #{item.excerpt}" :
1025
+ item.excerpt)
1026
+ component.url == session_url(item)
1027
+ end
1028
+ end
1029
+
1030
+ describe "manage speakers" do
1031
+ before(:each) do
1032
+ OpenConferenceWare.stub(have_user_profiles: true)
1033
+ @bubba = stub_model(User, fullname: "Bubba Smith")
1034
+ @billy = stub_model(User, fullname: "Billy Jack")
1035
+ @sue = stub_model(User, fullname: "Sue Smith")
1036
+ @proposal = stub_model(Proposal)
1037
+ @proposal.users = [@bubba, @billy]
1038
+ @event = stub_current_event!
1039
+ controller.stub(:assign_get_proposal_for_speaker_manager)
1040
+ controller.stub(:get_proposal_for_speaker_manager).and_return(@proposal)
1041
+ end
1042
+
1043
+ it "should list" do
1044
+ get :manage_speakers, speakers: "#{@bubba.id},#{@billy.id}", id: @proposal.to_param
1045
+ response.body.should have_selector(".speaker_id[name='speaker_ids[#{@bubba.id}]']")
1046
+ response.body.should have_selector(".speaker_id[name='speaker_ids[#{@billy.id}]']")
1047
+ response.body.should_not have_selector(".speaker_id[name='speaker_ids[#{@sue.id}]']")
1048
+ end
1049
+
1050
+ it "should add user" do
1051
+ User.should_receive(:find).and_return(@sue)
1052
+ get :manage_speakers, speakers: "#{@bubba.id},#{@billy.id}", add: @sue.id, id: @proposal.to_param
1053
+ response.body.should have_selector(".speaker_id[name='speaker_ids[#{@bubba.id}]']")
1054
+ response.body.should have_selector(".speaker_id[name='speaker_ids[#{@billy.id}]']")
1055
+ response.body.should have_selector(".speaker_id[name='speaker_ids[#{@sue.id}]']")
1056
+ end
1057
+
1058
+ it "should remove user" do
1059
+ User.should_receive(:find).and_return(@billy)
1060
+ get :manage_speakers, speakers: "#{@bubba.id},#{@billy.id}", remove: @billy.id, id: @proposal.to_param
1061
+ response.body.should have_selector(".speaker_id[name='speaker_ids[#{@bubba.id}]']")
1062
+ response.body.should_not have_selector(".speaker_id[name='speaker_ids[#{@billy.id}]']")
1063
+ end
1064
+ end
1065
+
1066
+ describe "search speakers" do
1067
+ before(:each) do
1068
+ @proposal = stub_model(Proposal)
1069
+
1070
+ @bubba = stub_model(User, fullname: "Bubba Smith")
1071
+ @billy = stub_model(User, fullname: "Billy Smith")
1072
+ @john = stub_model(User, fullname: "John Doe")
1073
+
1074
+ @params = {
1075
+ search: "smith",
1076
+ speakers: "IGNORED",
1077
+ }
1078
+
1079
+ User.should_receive(:complete_profiles).and_return([@bubba, @john, @billy])
1080
+ end
1081
+
1082
+ describe "new record" do
1083
+ before(:each) do
1084
+ @params[:id] = "new_record"
1085
+ Proposal.should_receive(:new).and_return(@proposal)
1086
+ @proposal.should_receive(:add_user)
1087
+ end
1088
+
1089
+ it "should match users that aren't in the proposal" do
1090
+ @proposal.should_receive(:users).and_return([])
1091
+ post :search_speakers, @params
1092
+ assigns(:matches).should == [@bubba, @billy]
1093
+ end
1094
+
1095
+ it "should not match users that are in the proposal" do
1096
+ @proposal.should_receive(:users).and_return([@bubba])
1097
+ post :search_speakers, @params
1098
+ assigns(:matches).should == [@billy]
1099
+ end
1100
+ end
1101
+
1102
+ describe "existing record" do
1103
+ before(:each) do
1104
+ @proposal.id = 123
1105
+ @proposal.event = events(:open)
1106
+ @params[:id] = @proposal.id
1107
+ Proposal.stub(:find).and_return(@proposal)
1108
+ end
1109
+
1110
+ it "should match users that aren't in the proposal" do
1111
+ @proposal.should_receive(:users).and_return([])
1112
+ post :search_speakers, @params
1113
+ assigns(:matches).should == [@bubba, @billy]
1114
+ end
1115
+
1116
+ it "should not match users that are in the proposal" do
1117
+ @proposal.should_receive(:users).and_return([@bubba])
1118
+ post :search_speakers, @params
1119
+ assigns(:matches).should == [@billy]
1120
+ end
1121
+ end
1122
+ end
1123
+
1124
+ def assert_confirmed
1125
+ post :speaker_confirm, id: @proposal.id
1126
+ @proposal.reload
1127
+ @proposal.status.should == 'confirmed'
1128
+ flash[:success].should =~ /Updated/
1129
+ end
1130
+
1131
+ def assert_not_confirmed
1132
+ post :speaker_confirm, id: @proposal.id
1133
+ @proposal.reload
1134
+ @proposal.status.should_not == 'confirmed'
1135
+ flash[:success].should_not =~ /Updated/
1136
+ end
1137
+
1138
+ describe "proposal login required" do
1139
+ it "should redirect to login if not logged in" do
1140
+ proposal = proposals(:quentin_widgets)
1141
+ get :proposal_login_required, proposal_id: proposal.id
1142
+ response.should redirect_to(sign_in_path)
1143
+ end
1144
+
1145
+ it "should redirect to proposal if logged in" do
1146
+ login_as(users(:quentin))
1147
+ proposal = proposals(:quentin_widgets)
1148
+ get :proposal_login_required, proposal_id: proposal.id
1149
+ response.should redirect_to(proposal_path(proposal))
1150
+ end
1151
+ end
1152
+
1153
+ describe "speaker confirm" do
1154
+ describe "accepted proposal" do
1155
+ before(:each) do
1156
+ @proposal = proposals(:quentin_widgets)
1157
+ @proposal.accept!
1158
+ end
1159
+
1160
+ it "should confirm for owners of the proposal" do
1161
+ login_as(users(:quentin))
1162
+ assert_confirmed
1163
+ end
1164
+ it "should not confirm for non-owners of the proposal" do
1165
+ login_as(users(:aaron))
1166
+ assert_not_confirmed
1167
+ end
1168
+ end
1169
+
1170
+ describe "not-accepted proposal" do
1171
+ before(:each) do
1172
+ @proposal = proposals(:quentin_widgets)
1173
+ end
1174
+
1175
+ it "should not confirm for owners of the proposal" do
1176
+ login_as(users(:quentin))
1177
+ lambda { post :speaker_confirm, id: @proposal.id }.should raise_error(AASM::InvalidTransition)
1178
+ end
1179
+ it "should not confirm for non-owners of the proposal" do
1180
+ login_as(users(:aaron))
1181
+ assert_not_confirmed
1182
+ end
1183
+ end
1184
+ end
1185
+
1186
+ def assert_declined
1187
+ post :speaker_decline, id: @proposal.id
1188
+ @proposal.reload
1189
+ @proposal.status.should == 'declined'
1190
+ flash[:success].should =~ /Updated/
1191
+ end
1192
+
1193
+ def assert_not_declined
1194
+ post :speaker_decline, id: @proposal.id
1195
+ @proposal.reload
1196
+ @proposal.status.should_not == 'declined'
1197
+ flash[:success].should_not =~ /Updated/
1198
+ end
1199
+
1200
+ describe "speaker decline" do
1201
+ describe "accepted proposal" do
1202
+ before(:each) do
1203
+ @proposal = proposals(:quentin_widgets)
1204
+ @proposal.accept!
1205
+ end
1206
+
1207
+ it "should decline for owners of the proposal" do
1208
+ login_as(users(:quentin))
1209
+ assert_declined
1210
+ end
1211
+ it "should not decline for non-owners of the proposal" do
1212
+ login_as(users(:aaron))
1213
+ assert_not_declined
1214
+ end
1215
+ end
1216
+
1217
+ describe "not-accepted proposal" do
1218
+ before(:each) do
1219
+ @proposal = proposals(:quentin_widgets)
1220
+ end
1221
+
1222
+ it "should not decline for owners of the proposal" do
1223
+ login_as(users(:quentin))
1224
+ lambda { post :speaker_decline, id: @proposal.id }.should raise_error(AASM::InvalidTransition)
1225
+ end
1226
+ it "should not decline for non-owners of the proposal" do
1227
+ login_as(users(:aaron))
1228
+ assert_not_declined
1229
+ end
1230
+ end
1231
+ end
1232
+
1233
+ describe "get_proposal_and_assignment_status" do
1234
+ it "should return a status of :invalid_proposal when no proposal id is given" do
1235
+ @controller.stub(:params).and_return({ id: nil })
1236
+ @controller.send(:get_proposal_and_assignment_status).should == [nil, :invalid_proposal]
1237
+ end
1238
+
1239
+ it "should return a status of :invalid_event when a proposal doesn't have a valid event" do
1240
+ proposal = stub_model(Proposal, state: "confirmed", event: nil)
1241
+ Proposal.stub(:lookup).and_return(proposal)
1242
+ @controller.stub(:params).and_return({ id: 1000 })
1243
+ @controller.send(:get_proposal_and_assignment_status).should == [proposal, :invalid_event]
1244
+ end
1245
+ end
1246
+
1247
+ describe "assign_proposal_and_event" do
1248
+
1249
+ it "should return false and not redirect when proposal and its event are successfully found" do
1250
+ proposal = stub_model(Proposal, state: "confirmed", event: @event)
1251
+ @controller.should_receive(:get_proposal_and_assignment_status).and_return([proposal, :assigned_via_param])
1252
+ @controller.send(:assign_proposal_and_event).should == false
1253
+ flash[:failure].should be_nil
1254
+ end
1255
+
1256
+ it "should redirect when proposal assignment status is :invalid_proposal" do
1257
+ proposal = stub_model(Proposal, event: @event)
1258
+ @controller.should_receive(:get_proposal_and_assignment_status).and_return([proposal, :invalid_proposal])
1259
+ @controller.should_receive(:redirect_to)
1260
+ @controller.send(:assign_proposal_and_event)
1261
+ flash[:failure].should == "Sorry, that presentation proposal doesn't exist or has been deleted."
1262
+ end
1263
+
1264
+ it "should redirect when proposal assignment status is :invalid_event" do
1265
+ proposal = stub_model(Proposal, state: "confirmed", event: nil, id: 1)
1266
+ @controller.should_receive(:get_proposal_and_assignment_status).and_return([proposal, :invalid_event])
1267
+ @controller.should_receive(:redirect_to)
1268
+ @controller.send(:assign_proposal_and_event)
1269
+ flash[:failure].should == "Sorry, no event was associated with proposal #1"
1270
+ end
1271
+ end
1272
+
1273
+ end