xrono 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (520) hide show
  1. data/.gitignore +19 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +20 -0
  4. data/Gemfile +30 -0
  5. data/Gemfile.lock +336 -0
  6. data/LICENSE +21 -0
  7. data/README.md +64 -0
  8. data/RELEASE_NOTES.md +83 -0
  9. data/Rakefile +15 -0
  10. data/XRONO.md +64 -0
  11. data/app/assets/images/chosen-sprite.png +0 -0
  12. data/app/assets/images/grid.png +0 -0
  13. data/app/assets/images/isotope_logo.png +0 -0
  14. data/app/assets/images/logo.png +0 -0
  15. data/app/assets/images/logo_122x40.png +0 -0
  16. data/app/assets/images/logo_97x40.png +0 -0
  17. data/app/assets/images/main_bg.jpg +0 -0
  18. data/app/assets/images/nav_bg.jpg +0 -0
  19. data/app/assets/images/rails.png +0 -0
  20. data/app/assets/images/top_bg.jpg +0 -0
  21. data/app/assets/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  22. data/app/assets/images/ui-bg_flat_0_fbf9ee_40x100.png +0 -0
  23. data/app/assets/images/ui-bg_flat_55_fbec88_40x100.png +0 -0
  24. data/app/assets/images/ui-bg_flat_65_ffffff_40x100.png +0 -0
  25. data/app/assets/images/ui-bg_flat_75_cccccc_40x100.png +0 -0
  26. data/app/assets/images/ui-bg_flat_75_dadada_40x100.png +0 -0
  27. data/app/assets/images/ui-bg_flat_75_e6e6e6_40x100.png +0 -0
  28. data/app/assets/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  29. data/app/assets/images/ui-bg_flat_95_fef1ec_40x100.png +0 -0
  30. data/app/assets/images/ui-bg_glass_75_d0e5f5_1x400.png +0 -0
  31. data/app/assets/images/ui-bg_glass_85_dfeffc_1x400.png +0 -0
  32. data/app/assets/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  33. data/app/assets/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png +0 -0
  34. data/app/assets/images/ui-bg_inset-hard_100_f5f8f9_1x100.png +0 -0
  35. data/app/assets/images/ui-bg_inset-hard_100_fcfdfd_1x100.png +0 -0
  36. data/app/assets/images/ui-icons_217bc0_256x240.png +0 -0
  37. data/app/assets/images/ui-icons_222222_256x240.png +0 -0
  38. data/app/assets/images/ui-icons_2e83ff_256x240.png +0 -0
  39. data/app/assets/images/ui-icons_454545_256x240.png +0 -0
  40. data/app/assets/images/ui-icons_469bdd_256x240.png +0 -0
  41. data/app/assets/images/ui-icons_6da8d5_256x240.png +0 -0
  42. data/app/assets/images/ui-icons_888888_256x240.png +0 -0
  43. data/app/assets/images/ui-icons_cd0a0a_256x240.png +0 -0
  44. data/app/assets/images/ui-icons_d8e7f3_256x240.png +0 -0
  45. data/app/assets/images/ui-icons_f9bd01_256x240.png +0 -0
  46. data/app/assets/javascripts/add_taxonomy.js.erb +307 -0
  47. data/app/assets/javascripts/admin_invoices.js +15 -0
  48. data/app/assets/javascripts/api/v1/clients.js.coffee +3 -0
  49. data/app/assets/javascripts/api/v1/tokens.js.coffee +3 -0
  50. data/app/assets/javascripts/application.js.erb +25 -0
  51. data/app/assets/javascripts/bootstrap-dropdown.js +92 -0
  52. data/app/assets/javascripts/bootstrap-tabs.js +80 -0
  53. data/app/assets/javascripts/bootstrap.js.coffee +4 -0
  54. data/app/assets/javascripts/calendar.js +25 -0
  55. data/app/assets/javascripts/chosen.jquery.js +898 -0
  56. data/app/assets/javascripts/client_login/clients.js.coffee +3 -0
  57. data/app/assets/javascripts/client_login/reports.js.coffee +3 -0
  58. data/app/assets/javascripts/comments.js +16 -0
  59. data/app/assets/javascripts/css_browser_selector.js +8 -0
  60. data/app/assets/javascripts/dashboard.js +3 -0
  61. data/app/assets/javascripts/data_vaults.js.coffee +3 -0
  62. data/app/assets/javascripts/file_attachments.js +9 -0
  63. data/app/assets/javascripts/jquery-tagselector.js +50 -0
  64. data/app/assets/javascripts/jquery-ui-1.8.16.custom.min.js +791 -0
  65. data/app/assets/javascripts/jquery.jeditable.js +543 -0
  66. data/app/assets/javascripts/jquery_ujs.js +336 -0
  67. data/app/assets/javascripts/schedule_modal.js +25 -0
  68. data/app/assets/javascripts/specify_project.js +21 -0
  69. data/app/assets/javascripts/ticket_board.js +104 -0
  70. data/app/assets/stylesheets/_jquery-ui-1.8.16.custom.css +568 -0
  71. data/app/assets/stylesheets/application.css +7 -0
  72. data/app/assets/stylesheets/bootstrap_and_overrides.css.scss +41 -0
  73. data/app/assets/stylesheets/chosen.css +367 -0
  74. data/app/assets/stylesheets/client_login/clients.css.scss +3 -0
  75. data/app/assets/stylesheets/client_login/reports.css.scss +3 -0
  76. data/app/assets/stylesheets/data_vaults.css.scss +3 -0
  77. data/app/assets/stylesheets/jquery-tagselector.css +49 -0
  78. data/app/assets/stylesheets/responsive.scss +650 -0
  79. data/app/assets/stylesheets/screen.scss +303 -0
  80. data/app/controllers/admin/base_controller.rb +18 -0
  81. data/app/controllers/admin/invoices_controller.rb +42 -0
  82. data/app/controllers/admin/payroll_controller.rb +29 -0
  83. data/app/controllers/admin/site_settings_controller.rb +19 -0
  84. data/app/controllers/admin/unentered_time_report_controller.rb +26 -0
  85. data/app/controllers/admin/users_controller.rb +86 -0
  86. data/app/controllers/admin/weekly_time_report_controller.rb +11 -0
  87. data/app/controllers/api/base_controller.rb +14 -0
  88. data/app/controllers/api/v1/base_controller.rb +2 -0
  89. data/app/controllers/api/v1/clients_controller.rb +10 -0
  90. data/app/controllers/api/v1/credentials_controller.rb +10 -0
  91. data/app/controllers/api/v1/projects_controller.rb +27 -0
  92. data/app/controllers/api/v1/tickets_controller.rb +46 -0
  93. data/app/controllers/api/v1/tokens_controller.rb +30 -0
  94. data/app/controllers/api/v1/work_units_controller.rb +23 -0
  95. data/app/controllers/client_login/base_controller.rb +3 -0
  96. data/app/controllers/client_login/clients_controller.rb +35 -0
  97. data/app/controllers/client_login/contacts_controller.rb +53 -0
  98. data/app/controllers/client_login/projects_controller.rb +24 -0
  99. data/app/controllers/client_login/reports_controller.rb +23 -0
  100. data/app/controllers/client_login/tickets_controller.rb +41 -0
  101. data/app/controllers/client_login/work_units_controller.rb +11 -0
  102. data/app/controllers/clients_controller.rb +103 -0
  103. data/app/controllers/comments_controller.rb +75 -0
  104. data/app/controllers/contacts_controller.rb +54 -0
  105. data/app/controllers/dashboard/base_controller.rb +79 -0
  106. data/app/controllers/data_vaults_controller.rb +12 -0
  107. data/app/controllers/doorkeeper/application_controller.rb +33 -0
  108. data/app/controllers/file_attachments_controller.rb +60 -0
  109. data/app/controllers/projects_controller.rb +62 -0
  110. data/app/controllers/tickets_controller.rb +48 -0
  111. data/app/controllers/users_controller.rb +57 -0
  112. data/app/controllers/work_units_controller.rb +114 -0
  113. data/app/controllers/xrono/application_controller.rb +65 -0
  114. data/app/helpers/admin/unentered_time_report_helper.rb +9 -0
  115. data/app/helpers/admin_base_helper.rb +14 -0
  116. data/app/helpers/clients_helper.rb +5 -0
  117. data/app/helpers/dashboard_helper.rb +9 -0
  118. data/app/helpers/gravatar_helper.rb +5 -0
  119. data/app/helpers/refurl_helper.rb +9 -0
  120. data/app/helpers/xrono/application_helper.rb +54 -0
  121. data/app/mailers/notifier.rb +18 -0
  122. data/app/models/chart_data.rb +20 -0
  123. data/app/models/client.rb +98 -0
  124. data/app/models/comment.rb +8 -0
  125. data/app/models/contact.rb +10 -0
  126. data/app/models/data_vault.rb +6 -0
  127. data/app/models/file_attachment.rb +8 -0
  128. data/app/models/project.rb +76 -0
  129. data/app/models/role.rb +3 -0
  130. data/app/models/roles_user.rb +4 -0
  131. data/app/models/site_settings.rb +4 -0
  132. data/app/models/ticket.rb +85 -0
  133. data/app/models/user.rb +147 -0
  134. data/app/models/weekly_time_report.rb +26 -0
  135. data/app/models/work_unit.rb +99 -0
  136. data/app/models/work_unit/finders.rb +67 -0
  137. data/app/views/admin/_nav.html.haml +1 -0
  138. data/app/views/admin/base/index.html.haml +3 -0
  139. data/app/views/admin/base/reports.html.haml +3 -0
  140. data/app/views/admin/invoices/index.html.haml +19 -0
  141. data/app/views/admin/invoices/show.html.haml +57 -0
  142. data/app/views/admin/payroll/index.html.haml +18 -0
  143. data/app/views/admin/payroll/show.html.haml +56 -0
  144. data/app/views/admin/site_settings/edit.html.haml +32 -0
  145. data/app/views/admin/unentered_time_report/_week_navigation.html.haml +6 -0
  146. data/app/views/admin/unentered_time_report/show.html.haml +16 -0
  147. data/app/views/admin/users/_form.html.haml +48 -0
  148. data/app/views/admin/users/_locked_users.html.haml +31 -0
  149. data/app/views/admin/users/_users_table.html.haml +25 -0
  150. data/app/views/admin/users/edit.html.haml +1 -0
  151. data/app/views/admin/users/index.html.haml +14 -0
  152. data/app/views/admin/users/locked_clients.html.haml +13 -0
  153. data/app/views/admin/users/locked_users.html.haml +13 -0
  154. data/app/views/admin/users/new.html.haml +1 -0
  155. data/app/views/admin/users/projects.html.haml +25 -0
  156. data/app/views/admin/users/unlocked_clients.html.haml +13 -0
  157. data/app/views/admin/weekly_time_report/_week_pagination.html.haml +11 -0
  158. data/app/views/admin/weekly_time_report/show.html.haml +41 -0
  159. data/app/views/client_login/_work_unit_index.haml +16 -0
  160. data/app/views/client_login/base/index.html.haml +18 -0
  161. data/app/views/client_login/clients/show.html.haml +32 -0
  162. data/app/views/client_login/contacts/_form.html.haml +34 -0
  163. data/app/views/client_login/contacts/edit.html.haml +4 -0
  164. data/app/views/client_login/contacts/index.html.haml +30 -0
  165. data/app/views/client_login/contacts/new.html.haml +5 -0
  166. data/app/views/client_login/contacts/show.html.haml +26 -0
  167. data/app/views/client_login/projects/_tickets.html.haml +31 -0
  168. data/app/views/client_login/projects/show.html.haml +8 -0
  169. data/app/views/client_login/reports/index.html.haml +4 -0
  170. data/app/views/client_login/reports/work_units.html.haml +96 -0
  171. data/app/views/client_login/tickets/_form.html.haml +33 -0
  172. data/app/views/client_login/tickets/new.html.haml +1 -0
  173. data/app/views/client_login/tickets/show.html.haml +23 -0
  174. data/app/views/client_login/work_units/show.html.haml +18 -0
  175. data/app/views/clients/_breadcrumbs.html.haml +2 -0
  176. data/app/views/clients/_clients_table.html.haml +24 -0
  177. data/app/views/clients/_form.html.haml +34 -0
  178. data/app/views/clients/_projects.html.haml +31 -0
  179. data/app/views/clients/edit.html.haml +1 -0
  180. data/app/views/clients/inactive_clients.html.haml +9 -0
  181. data/app/views/clients/index.html.haml +8 -0
  182. data/app/views/clients/new.html.haml +1 -0
  183. data/app/views/clients/show.html.haml +34 -0
  184. data/app/views/clients/suspended_clients.html.haml +8 -0
  185. data/app/views/comments/_comment.html.haml +46 -0
  186. data/app/views/comments/_comment_form.haml +29 -0
  187. data/app/views/comments/_comments.html.haml +3 -0
  188. data/app/views/contacts/_form.html.haml +34 -0
  189. data/app/views/contacts/edit.html.haml +1 -0
  190. data/app/views/contacts/index.html.haml +23 -0
  191. data/app/views/contacts/new.html.haml +1 -0
  192. data/app/views/contacts/show.html.haml +26 -0
  193. data/app/views/dashboard/base/_two_week_time_report.html.haml +13 -0
  194. data/app/views/dashboard/base/_week_pagination.html.haml +10 -0
  195. data/app/views/dashboard/base/calendar.html.haml +1 -0
  196. data/app/views/dashboard/base/calendar.js.erb +3 -0
  197. data/app/views/dashboard/base/index.html.haml +18 -0
  198. data/app/views/devise/confirmations/new.html.haml +9 -0
  199. data/app/views/devise/mailer/confirmation_instructions.html.haml +4 -0
  200. data/app/views/devise/mailer/reset_password_instructions.html.haml +6 -0
  201. data/app/views/devise/mailer/unlock_instructions.html.haml +5 -0
  202. data/app/views/devise/passwords/edit.html.haml +17 -0
  203. data/app/views/devise/passwords/new.html.haml +12 -0
  204. data/app/views/devise/registrations/edit.html.haml +36 -0
  205. data/app/views/devise/registrations/new.html.haml +32 -0
  206. data/app/views/devise/sessions/new.html.haml +22 -0
  207. data/app/views/devise/shared/_links.haml +15 -0
  208. data/app/views/devise/unlocks/new.html.haml +9 -0
  209. data/app/views/file_attachments/_file_attachment.html.haml +10 -0
  210. data/app/views/file_attachments/_file_attachment_combined.html.haml +30 -0
  211. data/app/views/file_attachments/_file_attachments.html.haml +5 -0
  212. data/app/views/file_attachments/new.html.haml +17 -0
  213. data/app/views/layouts/application.html.haml +32 -0
  214. data/app/views/layouts/doorkeeper.html.haml +31 -0
  215. data/app/views/notifier/daily.text.erb +8 -0
  216. data/app/views/notifier/work_unit_notification.text.erb +17 -0
  217. data/app/views/projects/_breadcrumbs.html.haml +5 -0
  218. data/app/views/projects/_form.html.haml +94 -0
  219. data/app/views/projects/_pie_chart_metrics.html.haml +4 -0
  220. data/app/views/projects/_specify.html.haml +11 -0
  221. data/app/views/projects/_ticket_table.html.haml +36 -0
  222. data/app/views/projects/edit.html.haml +2 -0
  223. data/app/views/projects/new.html.haml +2 -0
  224. data/app/views/projects/show.html.haml +57 -0
  225. data/app/views/shared/_calendar.html.haml +86 -0
  226. data/app/views/shared/_data_vault.html.haml +16 -0
  227. data/app/views/shared/_files_and_comments.html.haml +21 -0
  228. data/app/views/shared/_flashes.html.haml +10 -0
  229. data/app/views/shared/_javascripts.html.haml +8 -0
  230. data/app/views/shared/_menu.html.haml +14 -0
  231. data/app/views/shared/_user_roles.html.haml +7 -0
  232. data/app/views/shared/_work_unit_index.html.haml +24 -0
  233. data/app/views/tickets/_add_ticket.html.haml +27 -0
  234. data/app/views/tickets/_breadcrumbs.html.haml +8 -0
  235. data/app/views/tickets/_form.html.haml +40 -0
  236. data/app/views/tickets/_ticket_detail.html.haml +18 -0
  237. data/app/views/tickets/edit.html.haml +2 -0
  238. data/app/views/tickets/modal_new.html.haml +28 -0
  239. data/app/views/tickets/new.html.haml +1 -0
  240. data/app/views/tickets/show.html.haml +28 -0
  241. data/app/views/tickets/ticket_detail.html.haml +18 -0
  242. data/app/views/users/_change_password_form.haml +13 -0
  243. data/app/views/users/_user_head.html.haml +7 -0
  244. data/app/views/users/edit.haml +34 -0
  245. data/app/views/users/historical_time.html.haml +5 -0
  246. data/app/views/users/index.html.haml +18 -0
  247. data/app/views/users/show.html.haml +13 -0
  248. data/app/views/work_units/_add_work_unit.html.haml +81 -0
  249. data/app/views/work_units/_breadcrumbs.html.haml +11 -0
  250. data/app/views/work_units/_dashboard_line.html.haml +6 -0
  251. data/app/views/work_units/_edit_work_unit.haml +44 -0
  252. data/app/views/work_units/_shared_form.haml +40 -0
  253. data/app/views/work_units/edit.html.haml +2 -0
  254. data/app/views/work_units/index.html.haml +7 -0
  255. data/app/views/work_units/new.html.haml +6 -0
  256. data/app/views/work_units/show.html.haml +25 -0
  257. data/app/workers/daily_notifier_worker.rb +7 -0
  258. data/app/workers/work_unit_notifier_worker.rb +7 -0
  259. data/config.ru +7 -0
  260. data/config/admin_navigation.rb +71 -0
  261. data/config/admin_reports_navigation.rb +6 -0
  262. data/config/boot.rb +6 -0
  263. data/config/client_navigation.rb +64 -0
  264. data/config/cucumber.yml +8 -0
  265. data/config/initializers/00_requirements.rb +2 -0
  266. data/config/initializers/backtrace_silencers.rb +7 -0
  267. data/config/initializers/devise.rb +144 -0
  268. data/config/initializers/doorkeeper.rb +32 -0
  269. data/config/initializers/doorkeeper_monkeypatch.rb +66 -0
  270. data/config/initializers/github_concern.rb +4 -0
  271. data/config/initializers/haml.rb +13 -0
  272. data/config/initializers/inflections.rb +10 -0
  273. data/config/initializers/load_controller_mixins.rb +1 -0
  274. data/config/initializers/mime_types.rb +5 -0
  275. data/config/initializers/paper_clip.rb +1 -0
  276. data/config/initializers/simple_navigation.rb +1 -0
  277. data/config/initializers/wrap_parameters.rb +14 -0
  278. data/config/locales/devise.en.yml +39 -0
  279. data/config/locales/doorkeeper.en.yml +23 -0
  280. data/config/locales/en.yml +201 -0
  281. data/config/locales/faker.en.yml +48 -0
  282. data/config/navigation.rb +78 -0
  283. data/config/rackspace_cloudfiles.yml +14 -0
  284. data/config/routes.rb +113 -0
  285. data/config/secondary_navigation.rb +65 -0
  286. data/config/trinidad.yml +4 -0
  287. data/config/warble.rb +144 -0
  288. data/db/migrate/20100825165612_create_clients.rb +14 -0
  289. data/db/migrate/20100825185313_create_projects.rb +14 -0
  290. data/db/migrate/20100825223943_create_tickets.rb +16 -0
  291. data/db/migrate/20100902174920_create_work_units.rb +14 -0
  292. data/db/migrate/20100902190129_add_missing_fields_to_work_units.rb +13 -0
  293. data/db/migrate/20100904173839_add_guid_to_clients_projects_tickets_and_work_orders.rb +13 -0
  294. data/db/migrate/20100922181642_create_comments.rb +19 -0
  295. data/db/migrate/20100927180254_devise_create_users.rb +32 -0
  296. data/db/migrate/20100927184505_add_details_to_users.rb +13 -0
  297. data/db/migrate/20100927191329_add_user_to_work_units.rb +9 -0
  298. data/db/migrate/20100928163356_create_roles.rb +22 -0
  299. data/db/migrate/20100928203745_create_addresses.rb +19 -0
  300. data/db/migrate/20100928204544_create_contacts.rb +17 -0
  301. data/db/migrate/20101007175626_add_lockable_to_users.rb +11 -0
  302. data/db/migrate/20101020162216_add_paid_at_to_work_units_table.rb +9 -0
  303. data/db/migrate/20101020162516_add_check_number_to_work_units_table.rb +9 -0
  304. data/db/migrate/20101020215135_add_paid_field_to_work_units_table.rb +9 -0
  305. data/db/migrate/20101020215226_add_invoiced_field_to_work_units_table.rb +9 -0
  306. data/db/migrate/20101020215457_remove_paid_at_from_work_units_table.rb +9 -0
  307. data/db/migrate/20101020215624_remove_check_number_from_work_units_table.rb +9 -0
  308. data/db/migrate/20101025161443_add_invoiced_at_to_work_units_table.rb +9 -0
  309. data/db/migrate/20101026151900_add_initials_to_clients.rb +9 -0
  310. data/db/migrate/20101029145059_create_file_attachments.rb +18 -0
  311. data/db/migrate/20101030142615_add_guid_to_users.rb +9 -0
  312. data/db/migrate/20101102145137_add_paid_at_to_work_units.rb +9 -0
  313. data/db/migrate/20101112151337_add_active_to_comments.rb +9 -0
  314. data/db/migrate/20101119172727_add_recieves_email_to_contact.rb +10 -0
  315. data/db/migrate/20101122225423_projects_users.rb +11 -0
  316. data/db/migrate/20101129214537_remove_projects_users_table.rb +11 -0
  317. data/db/migrate/20101130225328_contact_receives_email.rb +9 -0
  318. data/db/migrate/20101202203051_add_address_to_contacts.rb +19 -0
  319. data/db/migrate/20101202214746_remove_address_table.rb +17 -0
  320. data/db/migrate/20101216214304_add_hours_type_to_work_unit.rb +9 -0
  321. data/db/migrate/20101217152455_add_effective_hours_to_work_units.rb +9 -0
  322. data/db/migrate/20101228154820_add_overtime_multiplier_to_clients_table.rb +9 -0
  323. data/db/migrate/20101228154951_add_overtime_multiplier_to_projects_table.rb +9 -0
  324. data/db/migrate/20101228155122_create_site_settings_table.rb +11 -0
  325. data/db/migrate/20110107190927_add_site_logo_columns_to_site_settings.rb +15 -0
  326. data/db/migrate/20110111155513_add_total_yearly_pto_per_user_to_site_settings.rb +9 -0
  327. data/db/migrate/20110111220015_add_not_valid_to_file_attachment.rb +9 -0
  328. data/db/migrate/20110125153818_add_fullwidth_to_users.rb +9 -0
  329. data/db/migrate/20110222162404_add_daily_target_hours_to_users.rb +9 -0
  330. data/db/migrate/20110310021732_add_client_to_site_settings.rb +9 -0
  331. data/db/migrate/20110630194044_add_expanded_calendar_to_user.rb +9 -0
  332. data/db/migrate/20110922175742_add_state_to_tickets.rb +9 -0
  333. data/db/migrate/20111212193345_add_estimated_hours_to_tickets.rb +5 -0
  334. data/db/migrate/20111216152557_add_client_boolean_to_user.rb +5 -0
  335. data/db/migrate/20111220193307_add_repo_and_branch_to_projects_and_ticket.rb +6 -0
  336. data/db/migrate/20111220193619_create_git_pushes.rb +9 -0
  337. data/db/migrate/20111220193620_create_git_commits.rb +10 -0
  338. data/db/migrate/20111220193621_add_user_id_to_git_push.rb +5 -0
  339. data/db/migrate/20111220193622_create_github_concernable_git_pushes.rb +11 -0
  340. data/db/migrate/20111227140859_add_token_to_user.rb +7 -0
  341. data/db/migrate/20120102185406_add_completed_boolean_to_ticket.rb +5 -0
  342. data/db/migrate/20120104132121_add_completed_to_project.rb +5 -0
  343. data/db/migrate/20120124184915_modify_project_for_new_git_integration.rb +11 -0
  344. data/db/migrate/20120125145833_add_rates_to_project.rb +6 -0
  345. data/db/migrate/20120126151009_create_data_vaults.rb +11 -0
  346. data/db/migrate/20120126151341_create_versions.rb +18 -0
  347. data/db/migrate/20120126154403_rename_column_data_to_encrypted_data.rb +8 -0
  348. data/db/migrate/20120221193022_add_index_to_user_id_on_work_units.rb +5 -0
  349. data/db/migrate/20120221195345_acts_as_taggable_on_migration.rb +30 -0
  350. data/db/migrate/20120320212313_remove_timestamps_from_roles_users_table.rb +11 -0
  351. data/db/migrate/20120710034831_create_doorkeeper_tables.rb +42 -0
  352. data/db/migrate/20121021122002_add_missing_indices.rb +21 -0
  353. data/doc/website/favicon.ico +0 -0
  354. data/doc/website/images/clock_box.png +0 -0
  355. data/doc/website/images/demo.png +0 -0
  356. data/doc/website/images/download.png +0 -0
  357. data/doc/website/images/fork_me.png +0 -0
  358. data/doc/website/images/lightbox-blank.gif +0 -0
  359. data/doc/website/images/lightbox-btn-close.gif +0 -0
  360. data/doc/website/images/lightbox-btn-next.gif +0 -0
  361. data/doc/website/images/lightbox-btn-prev.gif +0 -0
  362. data/doc/website/images/lightbox-ico-loading.gif +0 -0
  363. data/doc/website/images/main_bg.png +0 -0
  364. data/doc/website/images/nav_bg.png +0 -0
  365. data/doc/website/images/screen_1.png +0 -0
  366. data/doc/website/images/screen_2.png +0 -0
  367. data/doc/website/images/screen_3.png +0 -0
  368. data/doc/website/images/screen_4.png +0 -0
  369. data/doc/website/images/screen_5.png +0 -0
  370. data/doc/website/images/screen_6.png +0 -0
  371. data/doc/website/images/support.png +0 -0
  372. data/doc/website/images/thumb_1.png +0 -0
  373. data/doc/website/images/thumb_2.png +0 -0
  374. data/doc/website/images/thumb_3.png +0 -0
  375. data/doc/website/images/thumb_4.png +0 -0
  376. data/doc/website/images/thumb_5.png +0 -0
  377. data/doc/website/images/thumb_6.png +0 -0
  378. data/doc/website/images/xrono_logo.png +0 -0
  379. data/doc/website/index.html +274 -0
  380. data/doc/website/javascripts/application.js +18 -0
  381. data/doc/website/javascripts/greyscalebasic_regular.typeface.js +1 -0
  382. data/doc/website/javascripts/jquery.lightbox-0.5.min.js +42 -0
  383. data/doc/website/javascripts/jquery.min.js +32 -0
  384. data/doc/website/javascripts/jquery.tweet.js +181 -0
  385. data/doc/website/javascripts/typeface.js +873 -0
  386. data/doc/website/javascripts/verdana_regular.typeface.js +1 -0
  387. data/doc/website/stylesheets/jquery.lightbox.css +104 -0
  388. data/doc/website/stylesheets/jquery.tweet.css +38 -0
  389. data/doc/website/stylesheets/reset-fonts-grids.css +32 -0
  390. data/doc/website/stylesheets/screen.css +228 -0
  391. data/doc/xrono_dash.png +0 -0
  392. data/doc/xrono_ticket.png +0 -0
  393. data/features/admin/invoices.feature +37 -0
  394. data/features/admin/manage_users.feature +17 -0
  395. data/features/admin/payroll.feature +64 -0
  396. data/features/admin/roles.feature +9 -0
  397. data/features/admin/site_settings.feature +19 -0
  398. data/features/admin/unentered_time_report.feature +38 -0
  399. data/features/admin/weekly_time_report.feature +14 -0
  400. data/features/api/v1/clients_controller.feature +58 -0
  401. data/features/authorization-admin.feature +34 -0
  402. data/features/authorization-non-admin.feature +38 -0
  403. data/features/client_login.feature +15 -0
  404. data/features/collaborative_work_units.feature +112 -0
  405. data/features/daily_emails.feature +11 -0
  406. data/features/dashboard.feature +74 -0
  407. data/features/manage_clients.feature +116 -0
  408. data/features/manage_comments.feature +30 -0
  409. data/features/manage_contacts.feature +56 -0
  410. data/features/manage_file_attachments.feature +51 -0
  411. data/features/manage_projects.feature +92 -0
  412. data/features/manage_self.feature +48 -0
  413. data/features/manage_tickets.feature +74 -0
  414. data/features/manage_users.feature +76 -0
  415. data/features/manage_work_units.feature +115 -0
  416. data/features/mockups/images/asset-tracker-client-show.png +0 -0
  417. data/features/mockups/images/asset-tracker.png +0 -0
  418. data/features/mockups/images/ixi - asset tracker v3 homepage - no time entered modal.png +0 -0
  419. data/features/mockups/images/ixi - asset tracker v3 homepage - schedule on modal.png +0 -0
  420. data/features/mockups/images/ixi - asset tracker v3 homepage.png +0 -0
  421. data/features/mockups/ixi - asset tracker v3 homepage - no time entered modal.bmml +194 -0
  422. data/features/mockups/ixi - asset tracker v3 homepage - schedule on modal.bmml +208 -0
  423. data/features/mockups/ixi - asset tracker v3 homepage.bmml +172 -0
  424. data/features/project_assignment.feature +69 -0
  425. data/features/search_work_units.feature +38 -0
  426. data/features/step_definitions/calendar_expansion_steps.rb +7 -0
  427. data/features/step_definitions/client_login_steps.rb +8 -0
  428. data/features/step_definitions/client_steps.rb +29 -0
  429. data/features/step_definitions/comment_steps.rb +4 -0
  430. data/features/step_definitions/contact_steps.rb +22 -0
  431. data/features/step_definitions/debug_steps.rb +5 -0
  432. data/features/step_definitions/devise_steps.rb +26 -0
  433. data/features/step_definitions/email_steps.rb +74 -0
  434. data/features/step_definitions/file_attachment_steps.rb +8 -0
  435. data/features/step_definitions/modified_web_steps.rb +25 -0
  436. data/features/step_definitions/pickle_steps.rb +101 -0
  437. data/features/step_definitions/project_steps.rb +32 -0
  438. data/features/step_definitions/search_steps.rb +3 -0
  439. data/features/step_definitions/ticket_board_steps.rb +39 -0
  440. data/features/step_definitions/ticket_detail_steps.rb +9 -0
  441. data/features/step_definitions/ticket_steps.rb +12 -0
  442. data/features/step_definitions/unentered_time_report_steps.rb +3 -0
  443. data/features/step_definitions/user_steps.rb +43 -0
  444. data/features/step_definitions/web_steps.rb +274 -0
  445. data/features/step_definitions/weekly_time_report_steps.rb +7 -0
  446. data/features/step_definitions/work_unit_index_steps.rb +8 -0
  447. data/features/step_definitions/work_unit_steps.rb +39 -0
  448. data/features/support/email.rb +21 -0
  449. data/features/support/env.rb +80 -0
  450. data/features/support/paths.rb +96 -0
  451. data/features/support/pickle.rb +26 -0
  452. data/features/support/selectors.rb +51 -0
  453. data/features/support/xrono.rb +19 -0
  454. data/features/work_unit_notifier.feature +11 -0
  455. data/lib/controller_mixins.rb +5 -0
  456. data/lib/controller_mixins/authorization.rb +15 -0
  457. data/lib/controller_mixins/contacts.rb +37 -0
  458. data/lib/controller_mixins/generic.rb +15 -0
  459. data/lib/controller_mixins/tickets.rb +35 -0
  460. data/lib/controller_mixins/work_units.rb +13 -0
  461. data/lib/date_ext.rb +16 -0
  462. data/lib/guid_referenced.rb +19 -0
  463. data/lib/tasks/.gitkeep +0 -0
  464. data/lib/tasks/acts_as_commentable_tasks.rake +4 -0
  465. data/lib/tasks/cucumber.rake +65 -0
  466. data/lib/tasks/git_repo_tasks.rake +47 -0
  467. data/lib/tasks/notifications.rake +8 -0
  468. data/lib/tasks/paperclip_tasks.rake +79 -0
  469. data/lib/xrono.rb +1 -0
  470. data/lib/xrono/engine.rb +45 -0
  471. data/public/404.html +26 -0
  472. data/public/422.html +26 -0
  473. data/public/500.html +26 -0
  474. data/public/favicon.ico +0 -0
  475. data/public/favicon.psd +0 -0
  476. data/public/robots.txt +5 -0
  477. data/public/stylesheets/.gitkeep +0 -0
  478. data/public/stylesheets/ie.css +53 -0
  479. data/public/stylesheets/print.css +57 -0
  480. data/public/stylesheets/screen.css +182 -0
  481. data/script/cucumber +10 -0
  482. data/script/rails +6 -0
  483. data/spec/blueprints.rb +86 -0
  484. data/spec/controllers/api/v1/clients_controller_spec.rb +41 -0
  485. data/spec/controllers/api/v1/projects_controller_spec.rb +54 -0
  486. data/spec/controllers/api/v1/tickets_controller_spec.rb +122 -0
  487. data/spec/controllers/api/v1/tokens_controller_spec.rb +52 -0
  488. data/spec/controllers/client_login/clients_controller_spec.rb +5 -0
  489. data/spec/controllers/client_login/reports_controller_spec.rb +5 -0
  490. data/spec/controllers/data_vaults_controller_spec.rb +5 -0
  491. data/spec/internal/app/controllers/application_controller.rb +5 -0
  492. data/spec/internal/config/admin_navigation.rb +71 -0
  493. data/spec/internal/config/database.yml +14 -0
  494. data/spec/internal/config/database.yml.example +20 -0
  495. data/spec/internal/config/database.yml.mysql +20 -0
  496. data/spec/internal/config/database.yml.mysql.jruby +20 -0
  497. data/spec/internal/config/database.yml.postgres +20 -0
  498. data/spec/internal/config/database.yml.postgres.jruby +20 -0
  499. data/spec/internal/config/navigation.rb +80 -0
  500. data/spec/internal/config/routes.rb +3 -0
  501. data/spec/internal/config/secondary_navigation.rb +65 -0
  502. data/spec/internal/db/schema.rb +296 -0
  503. data/spec/internal/log/.gitignore +1 -0
  504. data/spec/internal/public/favicon.ico +0 -0
  505. data/spec/internal/public/system/attachment_files/2/original/tmp.txt +1 -0
  506. data/spec/internal/public/system/attachment_files/3/original/tmp.txt +1 -0
  507. data/spec/lib/date_ext_spec.rb +40 -0
  508. data/spec/mailers/notifier_spec.rb +9 -0
  509. data/spec/models/client_spec.rb +167 -0
  510. data/spec/models/contact_spec.rb +29 -0
  511. data/spec/models/data_vault_spec.rb +5 -0
  512. data/spec/models/project_spec.rb +115 -0
  513. data/spec/models/ticket_spec.rb +223 -0
  514. data/spec/models/user_spec.rb +258 -0
  515. data/spec/models/work_unit_spec.rb +467 -0
  516. data/spec/spec_helper.rb +44 -0
  517. data/spec/support/xrono/macros.rb +15 -0
  518. data/vendor/plugins/.gitkeep +0 -0
  519. data/xrono.gemspec +50 -0
  520. metadata +1209 -0
@@ -0,0 +1,8 @@
1
+ Given /^the following file attachments:$/ do |file_attachments|
2
+ FileAttachment.create!(clients.hashes)
3
+ end
4
+
5
+ When /^I attach a file$/ do
6
+ attach_file("file_attachment[attachment_file]", File.join(::Rails.root.to_s,'..', '..', 'features', 'step_definitions', 'file_attachment_steps.rb'))
7
+ end
8
+
@@ -0,0 +1,25 @@
1
+ Then /^the "([^"]*)" field(?: under (.*))? should hold "([^"]*)"$/ do |field, parent, value|
2
+ with_scope(parent) do
3
+ field = find_field(field)
4
+ field_value = (field.tag_name == 'textarea') ? field.text : field.value
5
+ if field_value.respond_to? :should
6
+ field_value.should =~ /#{value}/
7
+ else
8
+ assert_match(/#{value}/, field_value)
9
+ end
10
+ end
11
+ end
12
+
13
+ Then /^"([^"]*)" should be seen within "([^"]*)"$/ do |value, field|
14
+ assert page.has_xpath?("//option[contains(string(), '#{value}')]")
15
+ end
16
+
17
+ Then /^"([^\"]+)" should not be visible$/ do |text|
18
+ paths = [
19
+ "//*[@class='hidden']/*[contains(.,'#{text}')]",
20
+ "//*[@class='invisible']/*[contains(.,'#{text}')]",
21
+ "//*[@style='display: none;']/*[contains(.,'#{text}')]"
22
+ ]
23
+ xpath = paths.join '|'
24
+ page.should have_xpath(xpath)
25
+ end
@@ -0,0 +1,101 @@
1
+ # -*- coding: utf-8 -*-
2
+ # this file generated by script/generate pickle
3
+
4
+ # create a model
5
+ Given(/^#{capture_model} exists?(?: with #{capture_fields})?$/) do |name, fields|
6
+ create_model(name, fields)
7
+ end
8
+
9
+ # create n models
10
+ Given(/^(\d+) #{capture_plural_factory} exist(?: with #{capture_fields})?$/) do |count, plural_factory, fields|
11
+ count.to_i.times { create_model(plural_factory.singularize, fields) }
12
+ end
13
+
14
+ # create models from a table
15
+ Given(/^the following #{capture_plural_factory} exists?:?$/) do |plural_factory, table|
16
+ create_models_from_table(plural_factory, table)
17
+ end
18
+
19
+ # find a model
20
+ Then(/^#{capture_model} should exist(?: with #{capture_fields})?$/) do |name, fields|
21
+ find_model!(name, fields)
22
+ end
23
+
24
+ # not find a model
25
+ Then(/^#{capture_model} should not exist(?: with #{capture_fields})?$/) do |name, fields|
26
+ find_model(name, fields).should be_nil
27
+ end
28
+
29
+ # find models with a table
30
+ Then(/^the following #{capture_plural_factory} should exists?:?$/) do |plural_factory, table|
31
+ find_models_from_table(plural_factory, table).should_not be_any(&:nil?)
32
+ end
33
+
34
+ # find exactly n models
35
+ Then(/^(\d+) #{capture_plural_factory} should exist(?: with #{capture_fields})?$/) do |count, plural_factory, fields|
36
+ find_models(plural_factory.singularize, fields).size.should == count.to_i
37
+ end
38
+
39
+ # assert equality of models
40
+ Then(/^#{capture_model} should be #{capture_model}$/) do |a, b|
41
+ model!(a).should == model!(b)
42
+ end
43
+
44
+ # assert model is in another model's has_many assoc
45
+ Then(/^#{capture_model} should be (?:in|one of|amongst) #{capture_model}(?:'s)? (\w+)$/) do |target, owner, association|
46
+ model!(owner).send(association).should include(model!(target))
47
+ end
48
+
49
+ # assert model is not in another model's has_many assoc
50
+ Then(/^#{capture_model} should not be (?:in|one of|amongst) #{capture_model}(?:'s)? (\w+)$/) do |target, owner, association|
51
+ model!(owner).send(association).should_not include(model!(target))
52
+ end
53
+
54
+ # assert model is another model's has_one/belongs_to assoc
55
+ Then(/^#{capture_model} should be #{capture_model}(?:'s)? (\w+)$/) do |target, owner, association|
56
+ model!(owner).send(association).should == model!(target)
57
+ end
58
+
59
+ # assert model is not another model's has_one/belongs_to assoc
60
+ Then(/^#{capture_model} should not be #{capture_model}(?:'s)? (\w+)$/) do |target, owner, association|
61
+ model!(owner).send(association).should_not == model!(target)
62
+ end
63
+
64
+ # assert model.predicate?
65
+ Then(/^#{capture_model} should (?:be|have) (?:an? )?#{capture_predicate}$/) do |name, predicate|
66
+ if model!(name).respond_to?("has_#{predicate.gsub(' ', '_')}")
67
+ model!(name).should send("have_#{predicate.gsub(' ', '_')}")
68
+ else
69
+ model!(name).should send("be_#{predicate.gsub(' ', '_')}")
70
+ end
71
+ end
72
+
73
+ # assert not model.predicate?
74
+ Then(/^#{capture_model} should not (?:be|have) (?:an? )?#{capture_predicate}$/) do |name, predicate|
75
+ if model!(name).respond_to?("has_#{predicate.gsub(' ', '_')}")
76
+ model!(name).should_not send("have_#{predicate.gsub(' ', '_')}")
77
+ else
78
+ model!(name).should_not send("be_#{predicate.gsub(' ', '_')}")
79
+ end
80
+ end
81
+
82
+ # model.attribute.should eql(value)
83
+ # model.attribute.should_not eql(value)
84
+ Then(/^#{capture_model}'s (\w+) (should(?: not)?) be #{capture_value}$/) do |name, attribute, expectation, expected|
85
+ actual_value = model(name).send(attribute)
86
+ expectation = expectation.gsub(' ', '_')
87
+
88
+ case expected
89
+ when 'nil', 'true', 'false'
90
+ actual_value.send(expectation, send("be_#{expected}"))
91
+ when /^[+-]?[0-9_]+(\.\d+)?$/
92
+ actual_value.send(expectation, eql(expected.to_f))
93
+ else
94
+ actual_value.to_s.send(expectation, eql(eval(expected)))
95
+ end
96
+ end
97
+
98
+ # assert size of association
99
+ Then /^#{capture_model} should have (\d+) (\w+)$/ do |name, size, association|
100
+ model!(name).send(association).size.should == size.to_i
101
+ end
@@ -0,0 +1,32 @@
1
+ Given /^I am assigned to the project$/ do
2
+ User.last.has_role!(:developer, Project.last)
3
+ end
4
+
5
+ Given /^the following projects:$/ do |projects|
6
+ client = Client.create(:name => 'test', :status => 'test')
7
+ projects.hashes.each do |hash|
8
+ Project.create(hash.merge(:client_id => client.id))
9
+ end
10
+ end
11
+
12
+ When /^I visit the projects index page for a given client$/ do
13
+ client = Client.create(:name => 'test', :status => 'test')
14
+ visit projects_path
15
+ end
16
+
17
+ When /^(?:|I )visit the new project page for a given client$/ do
18
+ client = Client.create(:name => 'test', :status => 'test')
19
+ visit new_client_project_path(client)
20
+ end
21
+
22
+ When /^I delete the (\d+)(?:st|nd|rd|th) project$/ do |pos|
23
+ visit projects_path
24
+ within("table tbody tr:nth-child(#{pos.to_i})") do
25
+ click_link "Destroy"
26
+ end
27
+ end
28
+
29
+ Then /^I should see the following projects:$/ do |expected_projects_table|
30
+ expected_projects_table.diff!(find('table').all('tr').map { |row| row.all('th, td').map { |cell| cell.text.strip } })
31
+ end
32
+
@@ -0,0 +1,3 @@
1
+ Given /^I can't access the search page for a work unit$/ do
2
+ visit "/work_units"
3
+ end
@@ -0,0 +1,39 @@
1
+ When /^I drag "([^"]*)" fridge ticket to "([^"]*)"$/ do |arg1, arg2|
2
+ ticket = page.find_by_id('fridge_ul').find('li')
3
+ development_bucket = page.find_by_id('development_ul')
4
+ ticket.drag_to(development_bucket)
5
+ end
6
+
7
+ Then /^the ticket named "([^"]*)" should have a state of "([^"]*)"$/ do |ticket_name, state|
8
+ ticket = Ticket.find_by_name ticket_name
9
+ ticket.state.should == state
10
+ end
11
+
12
+ Then /^ticket "([^"]*)" should appear in "([^"]*)" bucket$/ do |ticket_name, bucket|
13
+ ticket = page.find_by_id("#{bucket}_ul").find('li')
14
+ end
15
+
16
+ When /^I drag "([^"]*)" development ticket to "([^"]*)"$/ do |arg1, arg2|
17
+ ticket = page.find_by_id('development_ul').find('li')
18
+ peer_review_bucket = page.find_by_id('peer_review_ul')
19
+ ticket.drag_to(peer_review_bucket)
20
+ end
21
+
22
+ When /^I drag "([^"]*)" peer_review ticket to "([^"]*)"$/ do |arg1, arg2|
23
+ ticket = page.find_by_id('peer_review_ul').find('li')
24
+ user_acceptance_bucket = page.find_by_id('user_acceptance_ul')
25
+ ticket.drag_to(user_acceptance_bucket)
26
+ end
27
+
28
+ When /^I drag "([^"]*)" user_acceptance ticket to "([^"]*)"$/ do |arg1, arg2|
29
+ ticket = page.find_by_id('user_acceptance_ul').find('li')
30
+ archived_bucket = page.find_by_id('archived_ul')
31
+ ticket.drag_to(archived_bucket)
32
+ end
33
+
34
+ Then /^the ticket named "([^"]*)" in "([^"]*)" should have html id equal to ActiveRecord model id$/ do |ticket_name, bucket|
35
+ ticket_AR = Ticket.find_by_name ticket_name
36
+ ticket_HTML = page.find_by_id("#{bucket}_ul").find('li')
37
+ ticket_HTML["id"].should == ticket_AR.id.to_s
38
+ end
39
+
@@ -0,0 +1,9 @@
1
+ When /^(?:|I )double[-\s]?click "([^"]*)"$/ do |locator|
2
+ element = page.find_by_id('fridge_ul').find('li')
3
+ page.driver.browser.mouse.double_click(element.native)
4
+ end
5
+
6
+ Given /^ticket "([^"]*)" has a work unit with description: "([^"]*)"$/ do |ticket_name, desc|
7
+ ticket = Ticket.find_by_name ticket_name
8
+ WorkUnit.create!(:hours_type => "Normal", :description => desc, :ticket_id => ticket.id, :hours => 8, :scheduled_at => "2010-10-01 12:00:00", :user_id => @current_user.id)
9
+ end
@@ -0,0 +1,12 @@
1
+ Then /^I should see the following tickets:$/ do |expected_tickets_table|
2
+ expected_tickets_table.diff!(find('table').all('tr').map { |row| row.all('th, td').map { |cell| cell.text.strip } })
3
+ end
4
+
5
+ Then /^I should be able to create a new ticket$/ do
6
+ step %{I follow "New Ticket"}
7
+ step %{I fill in "Name" with "test"}
8
+ step %{I fill in "Description" with "test description"}
9
+ step %{I press "Create Ticket"}
10
+ step %{I should see "Ticket created successfully"}
11
+ end
12
+
@@ -0,0 +1,3 @@
1
+ Given /^user is assigned to the project$/ do
2
+ User.last.has_role!(:developer, Project.last)
3
+ end
@@ -0,0 +1,43 @@
1
+ Given /^the following user records:$/ do |users|
2
+ users.hashes.each do |user|
3
+ the_role = user.delete("role")
4
+ u = User.create(user)
5
+ # add role
6
+ u.has_role!(the_role.to_sym)
7
+ end
8
+ end
9
+
10
+ Given /^the user "([^"]*)" has (?:a|an) "([^"]*)" role(?: on #{capture_model})?$/ do |email, role, model_name|
11
+ if model_name
12
+ User.find_by_email(email).has_role!(role.to_sym, find_model(model_name))
13
+ else
14
+ User.find_by_email(email).has_role!(role.to_sym)
15
+ end
16
+ end
17
+
18
+ When /^I delete the (\d+)(?:st|nd|rd|th) user$/ do |pos|
19
+ visit admin_users_path
20
+ within("table tbody tr:nth-child(#{pos.to_i})") do
21
+ click_button "Destroy"
22
+ end
23
+ end
24
+
25
+ Then /^there should be a user with a client login in the database$/ do
26
+ email = "client_login@user.com"
27
+ User.find(:all, :conditions => ["email = ? AND client = ?", email, true]).any?.should == true
28
+ end
29
+
30
+ Given /^I am an authenticated client on an existing project page$/ do
31
+ visit destroy_user_session_path
32
+ current_user = User.make(:email => "current_user@example.com", :password => "password", :password_confirmation => "password")
33
+ current_user.has_role!("client")
34
+ c = Client.create(:name => "Test Client", :status => "Active")
35
+ p = Project.create(:name => "Test Project", :client_id => c.id)
36
+ current_user.has_role!(:client, p)
37
+ visit new_user_session_path
38
+ step %{I fill in "user_email" with "current_user@example.com"}
39
+ step %{I fill in "user_password" with "password"}
40
+ step %{I press "Sign in"}
41
+ visit client_login_project_path(p)
42
+ end
43
+
@@ -0,0 +1,274 @@
1
+ # TL;DR: YOU SHOULD DELETE THIS FILE
2
+ #
3
+ # This file was generated by Cucumber-Rails and is only here to get you a head start
4
+ # These step definitions are thin wrappers around the Capybara/Webrat API that lets you
5
+ # visit pages, interact with widgets and make assertions about page content.
6
+ #
7
+ # If you use these step definitions as basis for your features you will quickly end up
8
+ # with features that are:
9
+ #
10
+ # * Hard to maintain
11
+ # * Verbose to read
12
+ #
13
+ # A much better approach is to write your own higher level step definitions, following
14
+ # the advice in the following blog posts:
15
+ #
16
+ # * http://benmabey.com/2008/05/19/imperative-vs-declarative-scenarios-in-user-stories.html
17
+ # * http://dannorth.net/2011/01/31/whose-domain-is-it-anyway/
18
+ # * http://elabs.se/blog/15-you-re-cuking-it-wrong
19
+ #
20
+
21
+
22
+ require 'uri'
23
+ require 'cgi'
24
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths"))
25
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "selectors"))
26
+
27
+ module WithinHelpers
28
+ def with_scope(locator)
29
+ locator ? within(*selector_for(locator)) { yield } : yield
30
+ end
31
+ end
32
+ World(WithinHelpers)
33
+
34
+ # Single-line step scoper
35
+ When /^(.*) within (.*[^:])$/ do |step_name, parent|
36
+ with_scope(parent) { step step_name }
37
+ end
38
+
39
+ # Multi-line step scoper
40
+ When /^(.*) within (.*[^:]):$/ do |step_name, parent, table_or_string|
41
+ with_scope(parent) { step "#{step_name}:", table_or_string }
42
+ end
43
+
44
+ Given /^(?:|I )am on (.+)$/ do |page_name|
45
+ visit path_to(page_name)
46
+ end
47
+
48
+ When /^(?:|I )go to (.+)$/ do |page_name|
49
+ visit path_to(page_name)
50
+ end
51
+
52
+ Given /^(?:|I )visit (.+)$/ do |path|
53
+ visit path
54
+ end
55
+
56
+ When /^(?:|I )press "([^"]*)"$/ do |button|
57
+ click_button(button)
58
+ end
59
+
60
+ When /^(?:|I )follow "([^"]*)"$/ do |link|
61
+ click_link(link)
62
+ end
63
+
64
+ When /^(?:|I )follow ([^"]*)$/ do |link|
65
+ find(*selector_for(link)).click
66
+ end
67
+
68
+ When /^(?:|I )fill in "([^"]*)" with "([^"]*)"$/ do |field, value|
69
+ fill_in(field, :with => value)
70
+ end
71
+
72
+ When /^(?:|I )fill in "([^"]*)" for "([^"]*)"$/ do |value, field|
73
+ fill_in(field, :with => value)
74
+ end
75
+
76
+ # Use this to fill in an entire form with data from a table. Example:
77
+ #
78
+ # When I fill in the following:
79
+ # | Account Number | 5002 |
80
+ # | Expiry date | 2009-11-01 |
81
+ # | Note | Nice guy |
82
+ # | Wants Email? | |
83
+ #
84
+ # TODO: Add support for checkbox, select or option
85
+ # based on naming conventions.
86
+ #
87
+ When /^(?:|I )fill in the following:$/ do |fields|
88
+ fields.rows_hash.each do |name, value|
89
+ step %{I fill in "#{name}" with "#{value}"}
90
+ end
91
+ end
92
+
93
+ When /^(?:|I )select "([^"]*)" from "([^"]*)"$/ do |value, field|
94
+ select(value, :from => field)
95
+ end
96
+
97
+ When /^(?:|I )check "([^"]*)"$/ do |field|
98
+ check(field)
99
+ end
100
+
101
+ When /^(?:|I )uncheck "([^"]*)"$/ do |field|
102
+ uncheck(field)
103
+ end
104
+
105
+ When /^(?:|I )choose "([^"]*)"$/ do |field|
106
+ choose(field)
107
+ end
108
+
109
+ When /^(?:|I )attach the file "([^"]*)" to "([^"]*)"$/ do |path, field|
110
+ attach_file(field, File.expand_path(path))
111
+ end
112
+
113
+ Then /^(?:|I )should see "([^"]*)"$/ do |text|
114
+ if page.respond_to? :should
115
+ page.should have_content(text)
116
+ else
117
+ assert page.has_content?(text)
118
+ end
119
+ end
120
+
121
+ Then /^(?:|I )should see \/([^\/]*)\/$/ do |regexp|
122
+ regexp = Regexp.new(regexp)
123
+
124
+ if page.respond_to? :should
125
+ page.should have_xpath('//*', :text => regexp)
126
+ else
127
+ assert page.has_xpath?('//*', :text => regexp)
128
+ end
129
+ end
130
+
131
+ Then /^(?:|I )should not see "([^"]*)"$/ do |text|
132
+ if page.respond_to? :should
133
+ page.should have_no_content(text)
134
+ else
135
+ assert page.has_no_content?(text)
136
+ end
137
+ end
138
+
139
+ Then /^(?:|I )should not see \/([^\/]*)\/$/ do |regexp|
140
+ regexp = Regexp.new(regexp)
141
+
142
+ if page.respond_to? :should
143
+ page.should have_no_xpath('//*', :text => regexp)
144
+ else
145
+ assert page.has_no_xpath?('//*', :text => regexp)
146
+ end
147
+ end
148
+
149
+ Then /^the "([^"]*)" field(?: under (.*))? should contain "([^"]*)"$/ do |field, parent, value|
150
+ with_scope(parent) do
151
+ field = find_field(field)
152
+ field_value = (field.tag_name == 'textarea') ? field.text : field.value
153
+ if field_value.respond_to? :should
154
+ field_value.should =~ /#{value}/
155
+ else
156
+ assert_match(/#{value}/, field_value)
157
+ end
158
+ end
159
+ end
160
+
161
+ Then /^the "([^"]*)" field(?: under (.*))? should not contain "([^"]*)"$/ do |field, parent, value|
162
+ with_scope(parent) do
163
+ field = find_field(field)
164
+ field_value = (field.tag_name == 'textarea') ? field.text : field.value
165
+ if field_value.respond_to? :should_not
166
+ field_value.should_not =~ /#{value}/
167
+ else
168
+ assert_no_match(/#{value}/, field_value)
169
+ end
170
+ end
171
+ end
172
+
173
+ Then /^the "([^"]*)" field should have the error "([^"]*)"$/ do |field, error_message|
174
+ element = find_field(field)
175
+ classes = element.find(:xpath, '..')[:class].split(' ')
176
+
177
+ form_for_input = element.find(:xpath, 'ancestor::form[1]')
178
+ using_formtastic = form_for_input[:class].include?('formtastic')
179
+ error_class = using_formtastic ? 'error' : 'field_with_errors'
180
+
181
+ if classes.respond_to? :should
182
+ classes.should include(error_class)
183
+ else
184
+ assert classes.include?(error_class)
185
+ end
186
+
187
+ if page.respond_to?(:should)
188
+ if using_formtastic
189
+ error_paragraph = element.find(:xpath, '../*[@class="inline-errors"][1]')
190
+ error_paragraph.should have_content(error_message)
191
+ else
192
+ page.should have_content("#{field.titlecase} #{error_message}")
193
+ end
194
+ else
195
+ if using_formtastic
196
+ error_paragraph = element.find(:xpath, '../*[@class="inline-errors"][1]')
197
+ assert error_paragraph.has_content?(error_message)
198
+ else
199
+ assert page.has_content?("#{field.titlecase} #{error_message}")
200
+ end
201
+ end
202
+ end
203
+
204
+ Then /^the "([^"]*)" field should have no error$/ do |field|
205
+ element = find_field(field)
206
+ classes = element.find(:xpath, '..')[:class].split(' ')
207
+ if classes.respond_to? :should
208
+ classes.should_not include('field_with_errors')
209
+ classes.should_not include('error')
210
+ else
211
+ assert !classes.include?('field_with_errors')
212
+ assert !classes.include?('error')
213
+ end
214
+ end
215
+
216
+ Then /^the "([^"]*)" checkbox(?: under (.*))? should be checked$/ do |label, parent|
217
+ with_scope(parent) do
218
+ field_checked = find_field(label)['checked']
219
+ if field_checked.respond_to? :should
220
+ field_checked.should be_true
221
+ else
222
+ assert field_checked
223
+ end
224
+ end
225
+ end
226
+
227
+ Then /^the "([^"]*)" checkbox(?: under (.*))? should not be checked$/ do |label, parent|
228
+ with_scope(parent) do
229
+ field_checked = find_field(label)['checked']
230
+ if field_checked.respond_to? :should
231
+ field_checked.should be_false
232
+ else
233
+ assert !field_checked
234
+ end
235
+ end
236
+ end
237
+
238
+ Then /^(?:|I )should be on (.+)$/ do |page_name|
239
+ current_path = URI.parse(current_url).path
240
+ if current_path.respond_to? :should
241
+ current_path.should == path_to(page_name)
242
+ else
243
+ assert_equal path_to(page_name), current_path
244
+ end
245
+ end
246
+
247
+ Then /^I should see a link with text "([^\"]*)"$/ do |text|
248
+ page.should have_link(text)
249
+ end
250
+
251
+ Then /^I should see selector ([^\"]*)$/ do |text|
252
+ page.should have_selector(*selector_for(text))
253
+ end
254
+
255
+ Then /^I should not see a link with text "([^\"]*)"$/ do |text|
256
+ page.should have_no_link(text)
257
+ end
258
+
259
+ Then /^(?:|I )should have the following query string:$/ do |expected_pairs|
260
+ query = URI.parse(current_url).query
261
+ actual_params = query ? CGI.parse(query) : {}
262
+ expected_params = {}
263
+ expected_pairs.rows_hash.each_pair{|k,v| expected_params[k] = v.split(',')}
264
+
265
+ if actual_params.respond_to? :should
266
+ actual_params.should == expected_params
267
+ else
268
+ assert_equal expected_params, actual_params
269
+ end
270
+ end
271
+
272
+ Then /^show me the page$/ do
273
+ save_and_open_page
274
+ end