incline 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (303) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/Gemfile +17 -0
  4. data/Gemfile.lock +186 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.rdoc +208 -0
  7. data/Rakefile +37 -0
  8. data/app/assets/fonts/incline/.keep +0 -0
  9. data/app/assets/images/incline/.keep +0 -0
  10. data/app/assets/images/incline/barcode-B.svg +181 -0
  11. data/app/assets/javascripts/incline/activate_classed_items.js +11 -0
  12. data/app/assets/javascripts/incline/application.js +30 -0
  13. data/app/assets/javascripts/incline/bootstrap-datepicker.js +1800 -0
  14. data/app/assets/javascripts/incline/datatables.js +22193 -0
  15. data/app/assets/javascripts/incline/escapeHtml.js +10 -0
  16. data/app/assets/javascripts/incline/inline_actions.js +479 -0
  17. data/app/assets/javascripts/incline/jquery.doubleScroll.js +112 -0
  18. data/app/assets/javascripts/incline/jquery.number.js +764 -0
  19. data/app/assets/javascripts/incline/regexMask.js +27 -0
  20. data/app/assets/javascripts/incline/select2/i18n/en.js +3 -0
  21. data/app/assets/javascripts/incline/select2/select2.full.js +6436 -0
  22. data/app/assets/stylesheets/incline/application.css +18 -0
  23. data/app/assets/stylesheets/incline/bootstrap-theme.min.css +5 -0
  24. data/app/assets/stylesheets/incline/custom.scss +279 -0
  25. data/app/assets/stylesheets/incline/datatables.css +494 -0
  26. data/app/assets/stylesheets/incline/datepicker3.css +790 -0
  27. data/app/assets/stylesheets/incline/select2.css +484 -0
  28. data/app/controllers/incline/access_groups_controller.rb +127 -0
  29. data/app/controllers/incline/access_test_controller.rb +30 -0
  30. data/app/controllers/incline/account_activations_controller.rb +28 -0
  31. data/app/controllers/incline/application_controller.rb +11 -0
  32. data/app/controllers/incline/contact_controller.rb +34 -0
  33. data/app/controllers/incline/password_resets_controller.rb +113 -0
  34. data/app/controllers/incline/security_controller.rb +100 -0
  35. data/app/controllers/incline/sessions_controller.rb +50 -0
  36. data/app/controllers/incline/users_controller.rb +304 -0
  37. data/app/controllers/incline/welcome_controller.rb +19 -0
  38. data/app/helpers/incline/.keep +0 -0
  39. data/app/mailers/incline/application_mailer_base.rb +11 -0
  40. data/app/mailers/incline/contact_form.rb +19 -0
  41. data/app/mailers/incline/user_mailer.rb +45 -0
  42. data/app/models/incline/access_group.rb +121 -0
  43. data/app/models/incline/access_group_group_member.rb +12 -0
  44. data/app/models/incline/access_group_user_member.rb +10 -0
  45. data/app/models/incline/action_group.rb +12 -0
  46. data/app/models/incline/action_security.rb +222 -0
  47. data/app/models/incline/contact_message.rb +37 -0
  48. data/app/models/incline/disable_info.rb +20 -0
  49. data/app/models/incline/password_reset.rb +14 -0
  50. data/app/models/incline/password_reset_request.rb +14 -0
  51. data/app/models/incline/user.rb +437 -0
  52. data/app/models/incline/user_login_history.rb +30 -0
  53. data/app/views/incline/access_groups/_details.json.jbuilder +10 -0
  54. data/app/views/incline/access_groups/_form.html.erb +19 -0
  55. data/app/views/incline/access_groups/_list.html.erb +60 -0
  56. data/app/views/incline/access_groups/_messages.json.jbuilder +6 -0
  57. data/app/views/incline/access_groups/edit.html.erb +2 -0
  58. data/app/views/incline/access_groups/index.html.erb +6 -0
  59. data/app/views/incline/access_groups/index.json.jbuilder +16 -0
  60. data/app/views/incline/access_groups/new.html.erb +2 -0
  61. data/app/views/incline/access_groups/show.html.erb +9 -0
  62. data/app/views/incline/access_groups/show.json.jbuilder +11 -0
  63. data/app/views/incline/contact/new.html.erb +22 -0
  64. data/app/views/incline/contact_form/contact.html.erb +16 -0
  65. data/app/views/incline/contact_form/contact.text.erb +13 -0
  66. data/app/views/incline/password_resets/edit.html.erb +16 -0
  67. data/app/views/incline/password_resets/new.html.erb +12 -0
  68. data/app/views/incline/security/_details.json.jbuilder +7 -0
  69. data/app/views/incline/security/_form.html.erb +20 -0
  70. data/app/views/incline/security/_list.html.erb +89 -0
  71. data/app/views/incline/security/_messages.json.jbuilder +6 -0
  72. data/app/views/incline/security/edit.html.erb +2 -0
  73. data/app/views/incline/security/index.html.erb +6 -0
  74. data/app/views/incline/security/index.json.jbuilder +16 -0
  75. data/app/views/incline/security/show.html.erb +31 -0
  76. data/app/views/incline/security/show.json.jbuilder +11 -0
  77. data/app/views/incline/sessions/new.html.erb +26 -0
  78. data/app/views/incline/user_mailer/account_activation.html.erb +7 -0
  79. data/app/views/incline/user_mailer/account_activation.text.erb +6 -0
  80. data/app/views/incline/user_mailer/invalid_password_reset.html.erb +3 -0
  81. data/app/views/incline/user_mailer/invalid_password_reset.text.erb +5 -0
  82. data/app/views/incline/user_mailer/password_reset.html.erb +8 -0
  83. data/app/views/incline/user_mailer/password_reset.text.erb +7 -0
  84. data/app/views/incline/users/_details.json.jbuilder +32 -0
  85. data/app/views/incline/users/_form.html.erb +21 -0
  86. data/app/views/incline/users/_list.html.erb +102 -0
  87. data/app/views/incline/users/_messages.json.jbuilder +6 -0
  88. data/app/views/incline/users/disable_confirm.html.erb +19 -0
  89. data/app/views/incline/users/edit.html.erb +5 -0
  90. data/app/views/incline/users/index.html.erb +6 -0
  91. data/app/views/incline/users/index.json.jbuilder +16 -0
  92. data/app/views/incline/users/new.html.erb +5 -0
  93. data/app/views/incline/users/show.html.erb +12 -0
  94. data/app/views/incline/users/show.json.jbuilder +11 -0
  95. data/app/views/incline/welcome/home.html.erb +5 -0
  96. data/app/views/layouts/application.html.erb +1 -0
  97. data/app/views/layouts/incline/_account_menu.html.erb +18 -0
  98. data/app/views/layouts/incline/_app_menu_anon.html.erb +1 -0
  99. data/app/views/layouts/incline/_app_menu_authenticated.html.erb +1 -0
  100. data/app/views/layouts/incline/_footer.html.erb +13 -0
  101. data/app/views/layouts/incline/_header.html.erb +21 -0
  102. data/app/views/layouts/incline/_html_mailer.html.erb +5 -0
  103. data/app/views/layouts/incline/_incline_app.html.erb +25 -0
  104. data/app/views/layouts/incline/_messages.html.erb +3 -0
  105. data/app/views/layouts/incline/_shim.html.erb +3 -0
  106. data/app/views/layouts/incline/_text_mailer.text.erb +1 -0
  107. data/app/views/layouts/incline/application.html.erb +1 -0
  108. data/app/views/layouts/mailer.html.erb +2 -0
  109. data/app/views/layouts/mailer.text.erb +2 -0
  110. data/bin/rails +12 -0
  111. data/bin/test_scaffold.sh +10 -0
  112. data/config/routes.rb +61 -0
  113. data/db/migrate/20170511230126_create_incline_users.rb +26 -0
  114. data/db/migrate/20170515003052_create_incline_access_groups.rb +10 -0
  115. data/db/migrate/20170515003221_create_incline_user_login_histories.rb +12 -0
  116. data/db/migrate/20170515150908_create_incline_access_group_user_members.rb +11 -0
  117. data/db/migrate/20170515151058_create_incline_access_group_group_members.rb +11 -0
  118. data/db/migrate/20170517193432_add_comments_to_incline_user.rb +5 -0
  119. data/db/migrate/20170622132700_create_incline_action_securities.rb +16 -0
  120. data/db/migrate/20170622172712_create_incline_action_groups.rb +11 -0
  121. data/db/migrate/20170622195742_add_non_standard_to_action_security.rb +5 -0
  122. data/db/migrate/20170622230422_add_visible_to_action_security.rb +5 -0
  123. data/db/seeds.rb +81 -0
  124. data/exe/new_incline_app +42 -0
  125. data/lib/generators/incline/install_generator.rb +259 -0
  126. data/lib/generators/incline/templates/_app_menu_anon.html.erb +1 -0
  127. data/lib/generators/incline/templates/_app_menu_authenticated.html.erb +1 -0
  128. data/lib/generators/incline/templates/incline_application.css +17 -0
  129. data/lib/generators/incline/templates/incline_application.html.erb +1 -0
  130. data/lib/generators/incline/templates/incline_application.js +12 -0
  131. data/lib/generators/incline/templates/incline_database.yml +25 -0
  132. data/lib/generators/incline/templates/incline_email.yml +20 -0
  133. data/lib/generators/incline/templates/incline_mailer.html.erb +2 -0
  134. data/lib/generators/incline/templates/incline_mailer.text.erb +2 -0
  135. data/lib/generators/incline/templates/incline_users.yml +64 -0
  136. data/lib/generators/incline/templates/incline_version.rb +3 -0
  137. data/lib/incline/auth_engine_base.rb +52 -0
  138. data/lib/incline/data_tables_request.rb +336 -0
  139. data/lib/incline/date_time_formats.rb +6 -0
  140. data/lib/incline/engine.rb +212 -0
  141. data/lib/incline/errors.rb +15 -0
  142. data/lib/incline/extensions/action_controller_base.rb +526 -0
  143. data/lib/incline/extensions/action_mailer_base.rb +66 -0
  144. data/lib/incline/extensions/action_view_base.rb +489 -0
  145. data/lib/incline/extensions/active_record_base.rb +308 -0
  146. data/lib/incline/extensions/application.rb +137 -0
  147. data/lib/incline/extensions/application_configuration.rb +50 -0
  148. data/lib/incline/extensions/connection_adapter.rb +55 -0
  149. data/lib/incline/extensions/date_time_value.rb +123 -0
  150. data/lib/incline/extensions/date_value.rb +77 -0
  151. data/lib/incline/extensions/decimal_value.rb +55 -0
  152. data/lib/incline/extensions/erb_scaffold_generator.rb +31 -0
  153. data/lib/incline/extensions/float_value.rb +59 -0
  154. data/lib/incline/extensions/form_builder.rb +617 -0
  155. data/lib/incline/extensions/integer_value.rb +54 -0
  156. data/lib/incline/extensions/jbuilder_generator.rb +38 -0
  157. data/lib/incline/extensions/jbuilder_template.rb +39 -0
  158. data/lib/incline/extensions/main_app.rb +40 -0
  159. data/lib/incline/extensions/numeric.rb +63 -0
  160. data/lib/incline/extensions/object.rb +31 -0
  161. data/lib/incline/extensions/resource_route_generator.rb +53 -0
  162. data/lib/incline/extensions/session.rb +113 -0
  163. data/lib/incline/extensions/string.rb +50 -0
  164. data/lib/incline/extensions/test_case.rb +764 -0
  165. data/lib/incline/extensions/time_zone_converter.rb +40 -0
  166. data/lib/incline/global_status.rb +236 -0
  167. data/lib/incline/helpers/route_hash_formatter.rb +46 -0
  168. data/lib/incline/json_log_formatter.rb +96 -0
  169. data/lib/incline/json_logger.rb +17 -0
  170. data/lib/incline/log.rb +153 -0
  171. data/lib/incline/number_formats.rb +17 -0
  172. data/lib/incline/recaptcha.rb +346 -0
  173. data/lib/incline/user_manager.rb +212 -0
  174. data/lib/incline/validators/email_validator.rb +45 -0
  175. data/lib/incline/validators/ip_address_validator.rb +32 -0
  176. data/lib/incline/validators/recaptcha_validator.rb +37 -0
  177. data/lib/incline/validators/safe_name_validator.rb +31 -0
  178. data/lib/incline/version.rb +3 -0
  179. data/lib/incline/work_path.rb +75 -0
  180. data/lib/incline.rb +197 -0
  181. data/lib/tasks/incline_tasks.rake +4 -0
  182. data/lib/templates/erb/scaffold/_form.html.erb +43 -0
  183. data/lib/templates/erb/scaffold/_list.html.erb +81 -0
  184. data/lib/templates/erb/scaffold/edit.html.erb +1 -0
  185. data/lib/templates/erb/scaffold/index.html.erb +6 -0
  186. data/lib/templates/erb/scaffold/new.html.erb +1 -0
  187. data/lib/templates/erb/scaffold/show.html.erb +34 -0
  188. data/lib/templates/jbuilder/scaffold/_details.json.jbuilder +20 -0
  189. data/lib/templates/jbuilder/scaffold/index.json.jbuilder +16 -0
  190. data/lib/templates/jbuilder/scaffold/show.json.jbuilder +16 -0
  191. data/lib/templates/rails/scaffold_controller/controller.rb +128 -0
  192. data/test/controllers/incline/access_groups_controller_test.rb +65 -0
  193. data/test/controllers/incline/access_test_controller_test.rb +53 -0
  194. data/test/controllers/incline/contact_controller_test.rb +32 -0
  195. data/test/controllers/incline/security_controller_test.rb +39 -0
  196. data/test/controllers/incline/welcome_controller_test.rb +16 -0
  197. data/test/dummy/README.rdoc +28 -0
  198. data/test/dummy/Rakefile +6 -0
  199. data/test/dummy/app/assets/images/.keep +0 -0
  200. data/test/dummy/app/assets/javascripts/application.js +12 -0
  201. data/test/dummy/app/assets/stylesheets/application.css +17 -0
  202. data/test/dummy/app/controllers/application_controller.rb +5 -0
  203. data/test/dummy/app/controllers/concerns/.keep +0 -0
  204. data/test/dummy/app/helpers/application_helper.rb +2 -0
  205. data/test/dummy/app/mailers/.keep +0 -0
  206. data/test/dummy/app/models/.keep +0 -0
  207. data/test/dummy/app/models/concerns/.keep +0 -0
  208. data/test/dummy/app/views/layouts/application.html.erb +1 -0
  209. data/test/dummy/app/views/layouts/incline/_app_menu_anon.html.erb +1 -0
  210. data/test/dummy/app/views/layouts/incline/_app_menu_authenticated.html.erb +1 -0
  211. data/test/dummy/app/views/layouts/mailer.html.erb +2 -0
  212. data/test/dummy/app/views/layouts/mailer.text.erb +2 -0
  213. data/test/dummy/bin/bundle +3 -0
  214. data/test/dummy/bin/rails +4 -0
  215. data/test/dummy/bin/rake +4 -0
  216. data/test/dummy/bin/setup +29 -0
  217. data/test/dummy/config/application.rb +38 -0
  218. data/test/dummy/config/boot.rb +5 -0
  219. data/test/dummy/config/database.yml +34 -0
  220. data/test/dummy/config/email.yml +24 -0
  221. data/test/dummy/config/environment.rb +5 -0
  222. data/test/dummy/config/environments/development.rb +45 -0
  223. data/test/dummy/config/environments/production.rb +85 -0
  224. data/test/dummy/config/environments/test.rb +44 -0
  225. data/test/dummy/config/initializers/assets.rb +11 -0
  226. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  227. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  228. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  229. data/test/dummy/config/initializers/inflections.rb +16 -0
  230. data/test/dummy/config/initializers/mime_types.rb +4 -0
  231. data/test/dummy/config/initializers/session_store.rb +3 -0
  232. data/test/dummy/config/initializers/to_time_preserves_timezone.rb +10 -0
  233. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  234. data/test/dummy/config/locales/en.yml +23 -0
  235. data/test/dummy/config/routes.rb +6 -0
  236. data/test/dummy/config.ru +4 -0
  237. data/test/dummy/db/schema.rb +108 -0
  238. data/test/dummy/lib/assets/.keep +0 -0
  239. data/test/dummy/log/.keep +0 -0
  240. data/test/dummy/public/404.html +67 -0
  241. data/test/dummy/public/422.html +67 -0
  242. data/test/dummy/public/500.html +66 -0
  243. data/test/dummy/public/favicon.ico +0 -0
  244. data/test/extensions/action_controller_base_extensions_test.rb +21 -0
  245. data/test/extensions/action_mailer_base_extensions_test.rb +20 -0
  246. data/test/extensions/action_view_base_extensions_test.rb +267 -0
  247. data/test/extensions/active_record_extensions_test.rb +173 -0
  248. data/test/extensions/application_configuration_extensions_test.rb +46 -0
  249. data/test/extensions/application_extensions_test.rb +23 -0
  250. data/test/extensions/connection_adapter_extensions_test.rb +54 -0
  251. data/test/extensions/date_time_value_extensions_test.rb +104 -0
  252. data/test/extensions/date_value_extensions_test.rb +102 -0
  253. data/test/extensions/decimal_value_extensions_test.rb +85 -0
  254. data/test/extensions/erb_scaffold_generator_extensions_test.rb +17 -0
  255. data/test/extensions/float_value_extensions_test.rb +78 -0
  256. data/test/extensions/form_builder_extensions_test.rb +28 -0
  257. data/test/extensions/integer_value_extensions_test.rb +78 -0
  258. data/test/extensions/jbuilder_generator_extensions_test.rb +21 -0
  259. data/test/extensions/jbuilder_template_extensions_test.rb +47 -0
  260. data/test/extensions/main_app_extensions_test.rb +55 -0
  261. data/test/extensions/numeric_extensions_test.rb +76 -0
  262. data/test/extensions/object_extensions_test.rb +104 -0
  263. data/test/extensions/session_extensions_test.rb +69 -0
  264. data/test/extensions/string_extensions_test.rb +32 -0
  265. data/test/extensions/test_case_extensions_test.rb +538 -0
  266. data/test/extensions/time_zone_converter_extensions_test.rb +10 -0
  267. data/test/fixtures/incline/access_group_group_members.yml +1 -0
  268. data/test/fixtures/incline/access_group_user_members.yml +1 -0
  269. data/test/fixtures/incline/access_groups.yml +13 -0
  270. data/test/fixtures/incline/action_groups.yml +6 -0
  271. data/test/fixtures/incline/action_securities.yml +18 -0
  272. data/test/fixtures/incline/user_login_histories.yml +1 -0
  273. data/test/fixtures/incline/users.yml +64 -0
  274. data/test/incline_test.rb +63 -0
  275. data/test/integration/incline/users_edit_test.rb +180 -0
  276. data/test/integration/incline/users_login_test.rb +105 -0
  277. data/test/integration/incline/users_signup_test.rb +147 -0
  278. data/test/integration/navigation_test.rb +11 -0
  279. data/test/lib/data_tables_request_test.rb +245 -0
  280. data/test/lib/date_time_formats_test.rb +111 -0
  281. data/test/lib/global_status_test.rb +89 -0
  282. data/test/lib/json_log_formatter_test.rb +43 -0
  283. data/test/lib/log_test.rb +36 -0
  284. data/test/lib/recaptcha_test.rb +75 -0
  285. data/test/lib/user_manager_test.rb +47 -0
  286. data/test/lib/work_path_test.rb +18 -0
  287. data/test/models/incline/access_group_group_member_test.rb +30 -0
  288. data/test/models/incline/access_group_test.rb +60 -0
  289. data/test/models/incline/access_group_user_member_test.rb +29 -0
  290. data/test/models/incline/action_group_test.rb +27 -0
  291. data/test/models/incline/action_security_test.rb +176 -0
  292. data/test/models/incline/contact_message_test.rb +66 -0
  293. data/test/models/incline/disable_info_test.rb +29 -0
  294. data/test/models/incline/password_reset_request_test.rb +35 -0
  295. data/test/models/incline/password_reset_test.rb +51 -0
  296. data/test/models/incline/user_login_history_test.rb +31 -0
  297. data/test/models/incline/user_test.rb +91 -0
  298. data/test/test_helper.rb +42 -0
  299. data/test/validators/email_validator_test.rb +102 -0
  300. data/test/validators/ip_address_validator_test.rb +107 -0
  301. data/test/validators/recaptcha_validator_test.rb +57 -0
  302. data/test/validators/safe_name_validator_test.rb +101 -0
  303. metadata +584 -0
@@ -0,0 +1,764 @@
1
+ require 'active_support'
2
+
3
+ module Incline::Extensions
4
+ ##
5
+ # Adds some extra assertions and methods for use in tests.
6
+ module TestCase
7
+ ##
8
+ # Adds the #access_tests_for method.
9
+ module ClassMethods
10
+ ##
11
+ # This method will generate multiple generic access tests for your controller.
12
+ #
13
+ # The +action+ argument can be one or more actions to test. These can be specified as arguments or an array.
14
+ # access_tests_for :new, :edit, :show
15
+ # access_tests_for [ :new, :edit, :show ]
16
+ #
17
+ # Options are provided after the last action to test. All options will be applied to all actions.
18
+ # The {action}_params option being the only one that is explicitly for a specific action.
19
+ #
20
+ # Valid options:
21
+ # controller::
22
+ # The name of the controller. If not supplied, the controller is inferred from the class name.
23
+ # url_helper::
24
+ # The code used to generate the URL. If not supplied, the helper is inferred from the controller and action name.
25
+ # fixture_helper::
26
+ # A string defining the fixture helper to use. If not supplied the pluralized controller name will be used.
27
+ # fixture_key::
28
+ # The key to use to load a fixture. The default is :one.
29
+ # allow_anon::
30
+ # Determines if anonymous users should be able to access the action. The default is false.
31
+ # allow_any_user::
32
+ # Determines if any authenticated user should be able to access the action. The default is false.
33
+ # allow_groups::
34
+ # Specifies a list of groups that should be able to access the action. The default is nil.
35
+ # deny_groups::
36
+ # Specifies a list of groups that should not be able to access the action. The default is nil.
37
+ # allow_admin::
38
+ # Specifies if a system admin should be able to access the action. The default is true.
39
+ # method::
40
+ # Specifies the method to process the action with. The default is 'get'.
41
+ # success::
42
+ # Determines the result on success. Defauls to :success for 'get' requests, otherwise the pluralized controller helper path.
43
+ # failure::
44
+ # Determines the result on failure for non-anon tests. Defaults to 'main_app.root_path'.
45
+ # anon_failure::
46
+ # Determines the result on failure for anon tests. Defaults to 'incline.login_path'.
47
+ # {action}_params::
48
+ # You can pass params to the action by specifying a hash containing them in this fashion.
49
+ # e.g. - :new_params => { } # params for :new action
50
+ # return_code::
51
+ # If this is set to a true value, the test code is generated, but not executed. The test code will then
52
+ # be returned as a string.
53
+ # If this is not to a true value, the test code will be executed as generated and nil will be returned.
54
+ #
55
+ # access_tests_for :new, controller: 'users', allow_anon: true, allow_any_user: false, allow_admin: false
56
+ #
57
+ def access_tests_for(*actions)
58
+
59
+ options = actions.delete(actions.last) if actions.last.is_a?(::Hash)
60
+ options ||= { }
61
+
62
+ if actions.count == 1 && actions.first.is_a?(::Array)
63
+ actions = actions.first
64
+ end
65
+
66
+ if actions.count > 1
67
+ data = actions.map{|act| access_tests_for(act, options.dup)}
68
+ if options[:return_code]
69
+ return data.join
70
+ else
71
+ return nil
72
+ end
73
+ end
74
+
75
+ action = actions.first
76
+
77
+ options = {
78
+ allow_anon: false,
79
+ allow_any_user: false,
80
+ allow_groups: nil,
81
+ deny_groups: nil,
82
+ allow_admin: true,
83
+ fixture_key: :one,
84
+ failure: 'main_app.root_path',
85
+ anon_failure: 'incline.login_path'
86
+ }.merge((options || {}).symbolize_keys)
87
+
88
+ action = action.to_sym
89
+ params = options[:"#{action}_params"]
90
+ params = params.inspect if params.is_a?(::Hash)
91
+ params = nil unless params.is_a?(::String)
92
+
93
+ # guess at the method to use.
94
+ if options[:method].blank?
95
+ options[:method] =
96
+ if action == :destroy
97
+ 'delete'
98
+ elsif action == :update
99
+ 'patch'
100
+ elsif action == :create
101
+ 'post'
102
+ else
103
+ 'get'
104
+ end
105
+ end
106
+ options[:method] = options[:method].to_sym
107
+
108
+ if options[:controller].blank?
109
+ # only works with controller tests (eg - UsersControllerTest => users_controller_test => users_controller)
110
+ options[:controller] = self.name.underscore.rpartition('_')[0]
111
+ else
112
+ options[:controller] = options[:controller].to_s.underscore
113
+ end
114
+
115
+ if options[:controller] =~ /_controller$/
116
+ options[:controller] = options[:controller].rpartition('_')[0]
117
+ end
118
+
119
+ if options[:fixture_helper].blank?
120
+ options[:fixture_helper] = options[:controller].pluralize
121
+ end
122
+
123
+ if options[:url_helper].blank?
124
+ fix_val = "#{options[:fixture_helper]}(#{options[:fixture_key].inspect})"
125
+ options[:url_helper] =
126
+ case action
127
+ when :show, :update, :destroy then "#{options[:controller].singularize}_path(#{fix_val})"
128
+ when :edit then "edit_#{options[:controller].singularize}_path(#{fix_val})"
129
+ when :new then "new_#{options[:controller].singularize}_path"
130
+ else "#{options[:controller].pluralize}_path"
131
+ end
132
+ end
133
+
134
+ if options[:success].blank?
135
+ if options[:method] == :get
136
+ options[:success] = :success
137
+ else
138
+ options[:success] = "#{options[:controller].pluralize}_path"
139
+ end
140
+ end
141
+
142
+
143
+ method = options[:method]
144
+ url_helper = options[:url_helper]
145
+
146
+ tests = [
147
+ # label result user group success_override failure_override
148
+ [ 'anonymous', options[:allow_anon], nil, nil, nil, options[:anon_failure] ],
149
+ [ 'any user', options[:allow_any_user], :basic ],
150
+ [ 'admin user', options[:allow_admin], :admin ]
151
+ ]
152
+
153
+ unless options[:allow_groups].blank?
154
+ if options[:allow_groups].is_a?(::String)
155
+ options[:allow_groups] = options[:allow_groups].gsub(',', ';').split(';').map{|v| v.strip}
156
+ end
157
+ options[:allow_groups].each do |group|
158
+ tests << [ "#{group} member", true, :basic, group ]
159
+ end
160
+ end
161
+
162
+ unless options[:deny_groups].blank?
163
+ if options[:deny_groups].is_a?(::String)
164
+ options[:deny_groups] = options[:deny_groups].gsub(',', ';').split(';').map{|v| v.strip}
165
+ end
166
+ options[:deny_groups].each do |group|
167
+ tests << [ "#{group} member", false, :basic, group ]
168
+ end
169
+ end
170
+
171
+ all_code = ''
172
+
173
+ tests.each do |(label, result, user, group, success_override, failure_override)|
174
+ expected_result = result ? (success_override || options[:success]) : (failure_override || options[:failure])
175
+
176
+ # build the code block
177
+ test_code = "test \"should #{result ? '' : 'not '}allow access to #{action} for #{label}\" do\n"
178
+
179
+ if user
180
+ test_code += " user = incline_users(#{user.inspect})\n"
181
+ if group
182
+ test_code += " group = Incline::AccessGroup.find_or_create_by(name: #{group.inspect})\n"
183
+ test_code += " user.groups << group\n"
184
+ end
185
+ test_code += " log_in_as user\n"
186
+ end
187
+
188
+ test_code += " path = #{url_helper}\n"
189
+
190
+ if params.blank?
191
+ test_code += " #{method}(path)\n"
192
+ else
193
+ test_code += " #{method}(path, #{params})\n"
194
+ end
195
+
196
+ if expected_result.is_a?(::Symbol)
197
+ test_code += " assert_response #{expected_result.inspect}\n"
198
+ else
199
+ test_code += " assert_redirected_to #{expected_result}\n"
200
+ end
201
+
202
+ test_code += "end\n"
203
+
204
+ all_code += test_code
205
+
206
+ unless options[:return_code]
207
+ Incline::Log::debug test_code
208
+ eval test_code
209
+ end
210
+ end
211
+
212
+ options[:return_code] ? all_code : nil
213
+
214
+ end
215
+
216
+ end
217
+
218
+ ##
219
+ # Make sure main_app is available and working correctly.
220
+ def main_app
221
+ Rails.application.class.routes.url_helpers
222
+ end
223
+
224
+ ##
225
+ # Determines if a user is logged into the test session
226
+ def is_logged_in?
227
+ !session[:user_id].nil?
228
+ end
229
+
230
+ ##
231
+ # Logs in a test user
232
+ def log_in_as(user, options = {})
233
+ password = options[:password] || 'Password123'
234
+ remember_me = options[:remember_me] || '1'
235
+ if integration_test?
236
+ post incline.login_path, session: { email: user.email, password: password, remember_me: remember_me }
237
+ else
238
+ session[:user_id] = user.id
239
+ end
240
+ end
241
+
242
+
243
+ ##
244
+ # Tests a specific field for presence validation.
245
+ #
246
+ # model::
247
+ # This must respond to _attribute_ and _attribute=_ as well as _valid?_ and _errors_.
248
+ #
249
+ # attribute::
250
+ # This must provide the name of a valid attribute in the model.
251
+ #
252
+ # message::
253
+ # This is optional, but if provided it will be postfixed with the failure reason.
254
+ #
255
+ # regex::
256
+ # This is the regex to match against the error message to ensure that the failure is for the correct reason.
257
+ #
258
+ def assert_required(model, attribute, message = nil, regex = /can't be blank/i)
259
+ original_value = model.send(attribute)
260
+ assert model.valid?, 'Model should be valid to start.'
261
+ is_string = original_value.is_a?(::String)
262
+ setter = :"#{attribute}="
263
+ model.send setter, nil
264
+ assert_not model.valid?, message ? (message + ': (nil)') : "Should not allow #{attribute} to be set to nil."
265
+ assert model.errors[attribute].to_s =~ regex, message ? (message + ': (error message)') : 'Did not fail for expected reason.'
266
+ if is_string
267
+ model.send setter, ''
268
+ assert_not model.valid?, message ? (message + ": ('')") : "Should not allow #{attribute} to be set to empty string."
269
+ assert model.errors[attribute].to_s =~ regex, message ? (message + ': (error message)') : 'Did not fail for expected reason.'
270
+ model.send setter, ' '
271
+ assert_not model.valid?, message ? (message + ": (' ')") : "Should not allow #{attribute} to be set to blank string."
272
+ assert model.errors[attribute].to_s =~ regex, message ? (message + ': (error message)') : 'Did not fail for expected reason.'
273
+ end
274
+ model.send setter, original_value
275
+ assert model.valid?, message ? (message + ": !(#{original_value.inspect})") : "Should allow #{attribute} to be set back to '#{original_value.inspect}'."
276
+ end
277
+
278
+ ##
279
+ # Tests a specific field for maximum length restriction.
280
+ #
281
+ # model::
282
+ # This must respond to _attribute_ and _attribute=_ as well as _valid?_ and _errors_.
283
+ #
284
+ # attribute::
285
+ # This must provide the name of a valid attribute in the model.
286
+ #
287
+ # max_length::
288
+ # This is the maximum valid length for the field.
289
+ #
290
+ # message::
291
+ # This is optional, but if provided it will be postfixed with the failure reason.
292
+ #
293
+ # regex::
294
+ # This is the regex to match against the error message to ensure that the failure is for the correct reason.
295
+ #
296
+ # options::
297
+ # This is a list of options for the validation.
298
+ # Currently :start_with and :end_with are recognized.
299
+ # Use :start_with to specify a prefix for the tested string.
300
+ # Use :end_with to specify a postfix for the tested string.
301
+ # This would be most useful when you value has to follow a format (eg - email address :end_with => '@example.com')
302
+ #
303
+ def assert_max_length(model, attribute, max_length, message = nil, regex = /is too long/i, options = {})
304
+ original_value = model.send(attribute)
305
+ assert model.valid?, 'Model should be valid to start.'
306
+ setter = :"#{attribute}="
307
+
308
+ if message.is_a?(::Hash)
309
+ options = message.merge(options || {})
310
+ message = nil
311
+ end
312
+
313
+ if regex.is_a?(::Hash)
314
+ options = regex.merge(options || {})
315
+ regex = /is too long/i
316
+ end
317
+
318
+ pre = options[:start_with].to_s
319
+ post = options[:end_with].to_s
320
+ len = max_length - pre.length - post.length
321
+
322
+ # try with maximum valid length.
323
+ value = pre + ('a' * len) + post
324
+ model.send setter, value
325
+ assert model.valid?, message ? (message + ": !(#{value.length})") : "Should allow a string of #{value.length} characters."
326
+
327
+ # try with one extra character.
328
+ value = pre + ('a' * (len + 1)) + post
329
+ model.send setter, value
330
+ assert_not model.valid?, message ? (message + ": (#{value.length})") : "Should not allow a string of #{value.length} characters."
331
+ assert model.errors[attribute].to_s =~ regex, message ? (message + ': (error message)') : 'Did not fail for expected reason.'
332
+
333
+ model.send setter, original_value
334
+ assert model.valid?, message ? (message + ": !(#{original_value.inspect})") : "Should allow #{attribute} to be set back to '#{original_value.inspect}'."
335
+ end
336
+
337
+ ##
338
+ # Tests a specific field for maximum length restriction.
339
+ #
340
+ # model::
341
+ # This must respond to _attribute_ and _attribute=_ as well as _valid?_ and _errors_.
342
+ #
343
+ # attribute::
344
+ # This must provide the name of a valid attribute in the model.
345
+ #
346
+ # min_length::
347
+ # This is the minimum valid length for the field.
348
+ #
349
+ # message::
350
+ # This is optional, but if provided it will be postfixed with the failure reason.
351
+ #
352
+ # regex::
353
+ # This is the regex to match against the error message to ensure that the failure is for the correct reason.
354
+ #
355
+ # options::
356
+ # This is a list of options for the validation.
357
+ # Currently :start_with and :end_with are recognized.
358
+ # Use :start_with to specify a prefix for the tested string.
359
+ # Use :end_with to specify a postfix for the tested string.
360
+ # This would be most useful when you value has to follow a format (eg - email address :end_with => '@example.com')
361
+ #
362
+ def assert_min_length(model, attribute, min_length, message = nil, regex = /is too short/i, options = {})
363
+ original_value = model.send(attribute)
364
+ assert model.valid?, 'Model should be valid to start.'
365
+ setter = :"#{attribute}="
366
+
367
+ if message.is_a?(::Hash)
368
+ options = message.merge(options || {})
369
+ message = nil
370
+ end
371
+
372
+ if regex.is_a?(::Hash)
373
+ options = regex.merge(options || {})
374
+ regex = /is too short/i
375
+ end
376
+
377
+ pre = options[:start_with].to_s
378
+ post = options[:end_with].to_s
379
+ len = min_length - pre.length - post.length
380
+
381
+ # try with minimum valid length.
382
+ value = pre + ('a' * len) + post
383
+ model.send setter, value
384
+ assert model.valid?, message ? (message + ": !(#{value.length})") : "Should allow a string of #{value.length} characters."
385
+
386
+ # try with one extra character.
387
+ value = pre + ('a' * (len - 1)) + post
388
+ model.send setter, value
389
+ assert_not model.valid?, message ? (message + ": (#{value.length})") : "Should not allow a string of #{value.length} characters."
390
+ assert model.errors[attribute].to_s =~ regex, message ? (message + ': (error message)') : 'Did not fail for expected reason.'
391
+
392
+ model.send setter, original_value
393
+ assert model.valid?, message ? (message + ": !(#{original_value.inspect})") : "Should allow #{attribute} to be set back to '#{original_value.inspect}'."
394
+ end
395
+
396
+ ##
397
+ # Tests a specific field for uniqueness.
398
+ #
399
+ # model::
400
+ # This must respond to _attribute_ and _attribute=_ as well as _valid?_, _errors_, and _save!_.
401
+ # The model will be saved to perform uniqueness testing.
402
+ #
403
+ # attribute::
404
+ # This must provide the name of a valid attribute in the model.
405
+ #
406
+ # case_sensitive::
407
+ # This determines if changing case should change validation.
408
+ #
409
+ # message::
410
+ # This is optional, but if provided it will be postfixed with the failure reason.
411
+ #
412
+ # regex::
413
+ # This is the regex to match against the error message to ensure that the failure is for the correct reason.
414
+ #
415
+ #
416
+ # alternate_scopes::
417
+ # This is also optional. If provided the keys of the hash will be used to
418
+ # set additional attributes on the model. When these attributes are changed to the alternate
419
+ # values, the model should once again be valid.
420
+ # The alternative scopes are processed one at a time and the original values are restored
421
+ # before moving onto the next scope.
422
+ #
423
+ def assert_uniqueness(model, attribute, case_sensitive = false, message = nil, regex = /has already been taken/i, alternate_scopes = {})
424
+ setter = :"#{attribute}="
425
+ original_value = model.send(attribute)
426
+
427
+ assert model.valid?, 'Model should be valid to start.'
428
+
429
+ if case_sensitive.is_a?(::Hash)
430
+ alternate_scopes = case_sensitive.merge(alternate_scopes || {})
431
+ case_sensitive = false
432
+ end
433
+ if message.is_a?(::Hash)
434
+ alternate_scopes = message.merge(alternate_scopes || {})
435
+ message = nil
436
+ end
437
+ if regex.is_a?(::Hash)
438
+ alternate_scopes = regex.merge(alternate_scopes || {})
439
+ regex = /has already been taken/i
440
+ end
441
+
442
+ model.save!
443
+ copy = model.dup
444
+
445
+ assert_not copy.valid?, message ? (message + ": (#{copy.send(attribute).inspect})") : "Duplicate model with #{attribute}=#{copy.send(attribute).inspect} should not be valid."
446
+ assert copy.errors[attribute].to_s =~ regex, message ? (message + ': (error message)') : "Did not fail for expected reason"
447
+ if original_value.is_a?(::String)
448
+ unless case_sensitive
449
+ copy.send(setter, original_value.upcase)
450
+ assert_not copy.valid?, message ? (message + ": (#{copy.send(attribute).inspect})") : "Duplicate model with #{attribute}=#{copy.send(attribute).inspect} should not be valid."
451
+ assert copy.errors[attribute].to_s =~ regex, message ? (message + ': (error message)') : "Did not fail for expected reason"
452
+ copy.send(setter, original_value.downcase)
453
+ assert_not copy.valid?, message ? (message + ": (#{copy.send(attribute).inspect})") : "Duplicate model with #{attribute}=#{copy.send(attribute).inspect} should not be valid."
454
+ assert copy.errors[attribute].to_s =~ regex, message ? (message + ': (error message)') : "Did not fail for expected reason"
455
+ end
456
+ end
457
+
458
+ unless alternate_scopes.blank?
459
+ copy.send(setter, original_value)
460
+ assert_not copy.valid?, message ? (message + ": (#{copy.send(attribute).inspect})") : "Duplicate model with #{attribute}=#{copy.send(attribute).inspect} should not be valid."
461
+ assert copy.errors[attribute].to_s =~ regex, message ? (message + ': (error message)') : "Did not fail for expected reason"
462
+ alternate_scopes.each do |k,v|
463
+ kset = :"#{k}="
464
+ vorig = copy.send(k)
465
+ copy.send(kset, v)
466
+ assert copy.valid?, message ? (message + ": !#{k}(#{v})") : "Duplicate model with #{k}=#{v.inspect} should be valid with #{attribute}=#{copy.send(attribute).inspect}."
467
+ copy.send(kset, vorig)
468
+ assert_not copy.valid?, message ? (message + ": (#{copy.send(attribute).inspect})") : "Duplicate model with #{attribute}=#{copy.send(attribute).inspect} should not be valid."
469
+ assert copy.errors[attribute].to_s =~ regex, message ? (message + ': (error message)') : "Did not fail for expected reason"
470
+ end
471
+ end
472
+ end
473
+
474
+ ##
475
+ # Tests a specific field for reCAPTCHA validation.
476
+ #
477
+ # During testing reCAPTCHA is disabled, but there is a special response that is expected.
478
+ #
479
+ # model::
480
+ # This must respond to _attribute_ and _attribute=_ as well as _valid?_ and _errors_.
481
+ #
482
+ # attribute::
483
+ # This must provide the name of a valid attribute in the model.
484
+ #
485
+ # message::
486
+ # This is optional, but if provided it will be postfixed with the failure reason.
487
+ #
488
+ # regex::
489
+ # This is the regex to match against the error message to ensure that the failure is for the correct reason.
490
+ # The default value of nil uses two regular expressions to match the two failure cases.
491
+ #
492
+ def assert_recaptcha_validation(model, attribute, message = nil, regex = nil)
493
+ assert model.valid?, 'Model should be valid to start.'
494
+ setter = :"#{attribute}="
495
+
496
+ # no response, just an ip address.
497
+ model.send setter, '127.0.0.1'
498
+ assert_not model.valid?, message ? (message + ': (accepted without response)') : 'Should not have accepted without response.'
499
+ r = regex || /requires recaptcha challenge to be completed/i
500
+ assert model.errors[:base].to_s =~ r, message ? (message + ': (error message)') : 'Did not fail for expected reason.'
501
+
502
+ @item.recaptcha = '127.0.0.1|invalid'
503
+ assert_not @item.valid?, message ? (message + ': (accepted invalid response)') : 'Should not have accepted invalid response.'
504
+ r = regex || /invalid response from recaptcha challenge/i
505
+ assert model.errors[:base].to_s =~ r, message ? (message + ': (error message)') : 'Did not fail for expected reason.'
506
+
507
+ # since recaptcha is disabled for testing, the following string should validate.
508
+ @item.recaptcha = '127.0.0.1|disabled'
509
+ assert @item.valid?, message ? (message + ': (rejected valid response)') : 'Should have accepted valid response.'
510
+ end
511
+
512
+ ##
513
+ # Tests a specific field for email verification.
514
+ #
515
+ # model::
516
+ # This must respond to _attribute_ and _attribute=_ as well as _valid?_ and _errors_.
517
+ #
518
+ # attribute::
519
+ # This must provide the name of a valid attribute in the model.
520
+ #
521
+ # message::
522
+ # This is optional, but if provided it will be postfixed with the failure reason.
523
+ #
524
+ # regex::
525
+ # This is the regex to match against the error message to ensure that the failure is for the correct reason.
526
+ #
527
+ def assert_email_validation(model, attribute, message = nil, regex = /is not a valid email address/i)
528
+ assert model.valid?, 'Model should be valid to start.'
529
+ setter = :"#{attribute}="
530
+ orig = model.send attribute
531
+
532
+ valid = %w(
533
+ user@example.com
534
+ USER@foo.COM
535
+ A_US-ER@foo.bar.org
536
+ first.last@foo.jp
537
+ alice+bob@bax.cn
538
+ )
539
+
540
+ invalid = %w(
541
+ user@example,com
542
+ user_at_foo.org
543
+ user@example.
544
+ user@example.com.
545
+ foo@bar_baz.com
546
+ foo@bar+baz.com
547
+ @example.com
548
+ user@
549
+ user
550
+ user@..com
551
+ user@example..com
552
+ user@.example.com
553
+ user@@example.com
554
+ user@www@example.com
555
+ )
556
+
557
+ valid.each do |addr|
558
+ model.send setter, addr
559
+ assert model.valid?, message ? (message + ': (rejected valid address)') : "Should have accepted #{addr.inspect}."
560
+ end
561
+
562
+ invalid.each do |addr|
563
+ model.send setter, addr
564
+ assert_not model.valid?, message ? (message + ': (accepted invalid address)') : "Should have rejected #{addr.inspect}."
565
+ assert model.errors[attribute].to_s =~ regex, message ? (message + ': (error message)') : 'Did not fail for expected reason.'
566
+ end
567
+
568
+ model.send setter, orig
569
+ assert model.valid?, message ? (message + ': (rejected original value)') : "Should have accepted original value of #{orig.inspect}."
570
+
571
+ end
572
+
573
+
574
+ ##
575
+ # Tests a specific field for IP address verification.
576
+ #
577
+ # model::
578
+ # This must respond to _attribute_ and _attribute=_ as well as _valid?_ and _errors_.
579
+ #
580
+ # attribute::
581
+ # This must provide the name of a valid attribute in the model.
582
+ #
583
+ # mask::
584
+ # This can be one of :allow_mask, :require_mask, or :deny_mask. The default is :allow_mask.
585
+ #
586
+ # message::
587
+ # This is optional, but if provided it will be postfixed with the failure reason.
588
+ #
589
+ # regex::
590
+ # This is the regex to match against the error message to ensure that the failure is for the correct reason.
591
+ # The default value is nil to test for the various default messages.
592
+ #
593
+ def assert_ip_validation(model, attribute, mask = :allow_mask, message = nil, regex = nil)
594
+ assert model.valid?, 'Model should be valid to start.'
595
+ setter = :"#{attribute}="
596
+ orig = model.send attribute
597
+
598
+ valid = %w(
599
+ 0.0.0.0
600
+ 1.2.3.4
601
+ 10.20.30.40
602
+ 255.255.255.255
603
+ 10:20::30:40
604
+ ::1
605
+ 1:2:3:4:5:6:7:8
606
+ A:B:C:D:E:F::
607
+ )
608
+
609
+ invalid = %w(
610
+ localhost
611
+ 100.200.300.400
612
+ 12345::abcde
613
+ 1.2.3.4.5
614
+ 1.2.3
615
+ 0
616
+ 1:2:3:4:5:6:7:8:9:0
617
+ a:b:c:d:e:f:g:h
618
+ )
619
+
620
+ valid.each do |addr|
621
+ if mask == :require_mask
622
+ if addr.index(':')
623
+ addr += '/128'
624
+ else
625
+ addr += '/32'
626
+ end
627
+ end
628
+ model.send setter, addr
629
+ assert model.valid?, message ? (message + ': (rejected valid address)') : "Should have accepted #{addr.inspect}."
630
+ end
631
+
632
+ r = regex ? regex : /is not a valid ip address/i
633
+ invalid.each do |addr|
634
+ if mask == :require_mask
635
+ if addr.index(':')
636
+ addr += '/128'
637
+ else
638
+ addr += '/32'
639
+ end
640
+ end
641
+ model.send setter, addr
642
+ assert_not model.valid?, message ? (message + ': (accepted invalid address)') : "Should have rejected #{addr.inspect}."
643
+ assert model.errors[attribute].to_s =~ r, message ? (message + ': (error message)') : 'Did not fail for expected reason.'
644
+ end
645
+
646
+ if mask == :allow_mask || mask == :require_mask
647
+ address = '127.0.0.0/8'
648
+ model.send setter, address
649
+ assert model.valid?, message ? (message + ': (rejected masked address)') : "Should have accepted #{address.inspect}."
650
+ end
651
+
652
+ if mask == :allow_mask || mask == :deny_mask
653
+ address = '127.0.0.1'
654
+ model.send setter, address
655
+ assert model.valid?, message ? (message + ': (rejected unmasked address)') : "Should have accepted #{address.inspect}."
656
+ end
657
+
658
+ if mask == :require_mask
659
+ r = regex ? regex : /must contain a mask/i
660
+ address = '127.0.0.1'
661
+ model.send setter, address
662
+ assert_not model.valid?, message ? (message + ': (accepted unmasked address)') : "Should have rejected #{address.inspect} for no mask."
663
+ assert model.errors[attribute].to_s =~ r, message ? (message + ': (error message)') : 'Did not fail for expected reason.'
664
+ end
665
+
666
+ if mask == :deny_mask
667
+ r = regex ? regex : /must not contain a mask/i
668
+ address = '127.0.0.0/8'
669
+ model.send setter, address
670
+ assert_not model.valid? message ? (message + ': (accepted masked address)') : "Should have rejected #{address.inspect} for mask."
671
+ assert model.errors[attribute].to_s =~ r, message ? (message + ': (error message)') : 'Did not fail for expected reason.'
672
+ end
673
+
674
+ model.send setter, orig
675
+ assert model.valid?, message ? (message + ': (rejected original value)') : "Should have accepted original value of #{orig.inspect}."
676
+ end
677
+
678
+ ##
679
+ # Tests a specific field for safe name verification.
680
+ #
681
+ # model::
682
+ # This must respond to _attribute_ and _attribute=_ as well as _valid?_ and _errors_.
683
+ #
684
+ # attribute::
685
+ # This must provide the name of a valid attribute in the model.
686
+ #
687
+ # length::
688
+ # The length of the string to test. Must be greater than 2. Default is 6.
689
+ #
690
+ # message::
691
+ # This is optional, but if provided it will be postfixed with the failure reason.
692
+ #
693
+ # regex::
694
+ # This is the regex to match against the error message to ensure that the failure is for the correct reason.
695
+ # The default value is nil to test for the various default messages.
696
+ #
697
+ def assert_safe_name_validation(model, attribute, length = 6, message = nil, regex = nil)
698
+ assert model.valid?, 'Model should be valid to start.'
699
+ setter = :"#{attribute}="
700
+ orig = model.send attribute
701
+
702
+ assert length > 2, message ? (message + ': (field is too short to test)') : 'Requires a field length greater than 2 to perform tests.'
703
+
704
+ # valid tests.
705
+ mid_length = length - 2
706
+ mid = '' # _z_z_z_z_z_
707
+ while mid.length < mid_length
708
+ if mid.length + 1 < mid_length
709
+ mid += '_z'
710
+ else
711
+ mid += '_'
712
+ end
713
+ end
714
+
715
+ [
716
+ 'a' * length,
717
+ 'a' + ('1' * (length - 1)),
718
+ 'a' + mid + 'a',
719
+ 'a' + mid + '1'
720
+ ].each do |val|
721
+ model.send setter, val
722
+ assert model.valid?, message ? (message + ': (rejected valid string)') : "Should have accepted #{val.inspect}."
723
+ val.upcase!
724
+ model.send setter, val
725
+ assert model.valid?, message ? (message + ': (rejected valid string)') : "Should have accepted #{val.inspect}."
726
+ end
727
+
728
+ # invalid tests.
729
+ {
730
+ '_' + ('a' * (length - 1)) => /must start with a letter/i,
731
+ '1' + ('a' * (length - 1)) => /must start with a letter/i,
732
+ ('a' * (length - 1)) + '_' => /must not end with an underscore/i,
733
+ ('a' * (length - 2)) + '-' + 'a' => /must contain only letters, numbers, and underscore/i,
734
+ ('a' * (length - 2)) + '#' + 'a' => /must contain only letters, numbers, and underscore/i,
735
+ ('a' * (length - 2)) + ' ' + 'a' => /must contain only letters, numbers, and underscore/i
736
+ }.each do |val, reg|
737
+ r = regex ? regex : reg
738
+ model.send setter, val
739
+ assert_not model.valid?, message ? (message + ': (accepted invalid string)') : "Should have rejected #{val.inspect}."
740
+ assert model.errors[attribute].to_s =~ r, message ? (message + ': (error message)') : "Did not fail for expected reason on #{val.inspect}."
741
+ end
742
+
743
+ model.send setter, orig
744
+ assert model.valid?, message ? (message + ': (rejected original value)') : "Should have accepted original value of #{orig.inspect}."
745
+ end
746
+
747
+ ##
748
+ # Includes the class methods into the including object.
749
+ def self.included(base)
750
+ base.extend ClassMethods
751
+ end
752
+
753
+ private
754
+
755
+ # returns true inside an integration test
756
+ def integration_test?
757
+ defined?(post_via_redirect)
758
+ end
759
+
760
+
761
+ end
762
+ end
763
+
764
+ ActiveSupport::TestCase.include Incline::Extensions::TestCase