eactionpack 2.1.2

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 (338) hide show
  1. data/CHANGELOG +7 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +469 -0
  4. data/RUNNING_UNIT_TESTS +24 -0
  5. data/Rakefile +146 -0
  6. data/install.rb +30 -0
  7. data/lib/action_controller.rb +79 -0
  8. data/lib/action_controller/assertions.rb +69 -0
  9. data/lib/action_controller/assertions/dom_assertions.rb +39 -0
  10. data/lib/action_controller/assertions/model_assertions.rb +20 -0
  11. data/lib/action_controller/assertions/response_assertions.rb +172 -0
  12. data/lib/action_controller/assertions/routing_assertions.rb +146 -0
  13. data/lib/action_controller/assertions/selector_assertions.rb +491 -0
  14. data/lib/action_controller/assertions/tag_assertions.rb +130 -0
  15. data/lib/action_controller/base.rb +1288 -0
  16. data/lib/action_controller/benchmarking.rb +94 -0
  17. data/lib/action_controller/caching.rb +72 -0
  18. data/lib/action_controller/caching/actions.rb +144 -0
  19. data/lib/action_controller/caching/fragments.rb +138 -0
  20. data/lib/action_controller/caching/pages.rb +154 -0
  21. data/lib/action_controller/caching/sql_cache.rb +18 -0
  22. data/lib/action_controller/caching/sweeping.rb +97 -0
  23. data/lib/action_controller/cgi_ext.rb +16 -0
  24. data/lib/action_controller/cgi_ext/cookie.rb +110 -0
  25. data/lib/action_controller/cgi_ext/query_extension.rb +22 -0
  26. data/lib/action_controller/cgi_ext/session.rb +73 -0
  27. data/lib/action_controller/cgi_ext/stdinput.rb +24 -0
  28. data/lib/action_controller/cgi_process.rb +223 -0
  29. data/lib/action_controller/components.rb +166 -0
  30. data/lib/action_controller/cookies.rb +96 -0
  31. data/lib/action_controller/dispatcher.rb +162 -0
  32. data/lib/action_controller/filters.rb +642 -0
  33. data/lib/action_controller/flash.rb +172 -0
  34. data/lib/action_controller/headers.rb +31 -0
  35. data/lib/action_controller/helpers.rb +221 -0
  36. data/lib/action_controller/http_authentication.rb +124 -0
  37. data/lib/action_controller/integration.rb +634 -0
  38. data/lib/action_controller/layout.rb +309 -0
  39. data/lib/action_controller/mime_responds.rb +173 -0
  40. data/lib/action_controller/mime_type.rb +186 -0
  41. data/lib/action_controller/mime_types.rb +20 -0
  42. data/lib/action_controller/polymorphic_routes.rb +191 -0
  43. data/lib/action_controller/record_identifier.rb +102 -0
  44. data/lib/action_controller/request.rb +764 -0
  45. data/lib/action_controller/request_forgery_protection.rb +140 -0
  46. data/lib/action_controller/request_profiler.rb +169 -0
  47. data/lib/action_controller/rescue.rb +258 -0
  48. data/lib/action_controller/resources.rb +572 -0
  49. data/lib/action_controller/response.rb +76 -0
  50. data/lib/action_controller/routing.rb +387 -0
  51. data/lib/action_controller/routing/builder.rb +203 -0
  52. data/lib/action_controller/routing/optimisations.rb +120 -0
  53. data/lib/action_controller/routing/recognition_optimisation.rb +162 -0
  54. data/lib/action_controller/routing/route.rb +240 -0
  55. data/lib/action_controller/routing/route_set.rb +436 -0
  56. data/lib/action_controller/routing/routing_ext.rb +46 -0
  57. data/lib/action_controller/routing/segments.rb +283 -0
  58. data/lib/action_controller/session/active_record_store.rb +340 -0
  59. data/lib/action_controller/session/cookie_store.rb +166 -0
  60. data/lib/action_controller/session/drb_server.rb +32 -0
  61. data/lib/action_controller/session/drb_store.rb +35 -0
  62. data/lib/action_controller/session/mem_cache_store.rb +98 -0
  63. data/lib/action_controller/session_management.rb +158 -0
  64. data/lib/action_controller/status_codes.rb +88 -0
  65. data/lib/action_controller/streaming.rb +155 -0
  66. data/lib/action_controller/templates/rescues/_request_and_response.erb +24 -0
  67. data/lib/action_controller/templates/rescues/_trace.erb +26 -0
  68. data/lib/action_controller/templates/rescues/diagnostics.erb +11 -0
  69. data/lib/action_controller/templates/rescues/layout.erb +29 -0
  70. data/lib/action_controller/templates/rescues/missing_template.erb +2 -0
  71. data/lib/action_controller/templates/rescues/routing_error.erb +10 -0
  72. data/lib/action_controller/templates/rescues/template_error.erb +21 -0
  73. data/lib/action_controller/templates/rescues/unknown_action.erb +2 -0
  74. data/lib/action_controller/test_case.rb +83 -0
  75. data/lib/action_controller/test_process.rb +526 -0
  76. data/lib/action_controller/url_rewriter.rb +142 -0
  77. data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
  78. data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
  79. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +173 -0
  80. data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
  81. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
  82. data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
  83. data/lib/action_controller/verification.rb +130 -0
  84. data/lib/action_pack.rb +24 -0
  85. data/lib/action_pack/version.rb +9 -0
  86. data/lib/action_view.rb +44 -0
  87. data/lib/action_view/base.rb +335 -0
  88. data/lib/action_view/helpers/active_record_helper.rb +276 -0
  89. data/lib/action_view/helpers/asset_tag_helper.rb +599 -0
  90. data/lib/action_view/helpers/atom_feed_helper.rb +143 -0
  91. data/lib/action_view/helpers/benchmark_helper.rb +33 -0
  92. data/lib/action_view/helpers/cache_helper.rb +40 -0
  93. data/lib/action_view/helpers/capture_helper.rb +161 -0
  94. data/lib/action_view/helpers/date_helper.rb +711 -0
  95. data/lib/action_view/helpers/debug_helper.rb +31 -0
  96. data/lib/action_view/helpers/form_helper.rb +767 -0
  97. data/lib/action_view/helpers/form_options_helper.rb +458 -0
  98. data/lib/action_view/helpers/form_tag_helper.rb +458 -0
  99. data/lib/action_view/helpers/javascript_helper.rb +148 -0
  100. data/lib/action_view/helpers/number_helper.rb +186 -0
  101. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  102. data/lib/action_view/helpers/record_tag_helper.rb +59 -0
  103. data/lib/action_view/helpers/sanitize_helper.rb +229 -0
  104. data/lib/action_view/helpers/tag_helper.rb +134 -0
  105. data/lib/action_view/helpers/text_helper.rb +507 -0
  106. data/lib/action_view/helpers/url_helper.rb +573 -0
  107. data/lib/action_view/inline_template.rb +20 -0
  108. data/lib/action_view/partial_template.rb +70 -0
  109. data/lib/action_view/partials.rb +158 -0
  110. data/lib/action_view/template.rb +125 -0
  111. data/lib/action_view/template_error.rb +110 -0
  112. data/lib/action_view/template_finder.rb +176 -0
  113. data/lib/action_view/template_handler.rb +34 -0
  114. data/lib/action_view/template_handlers/builder.rb +27 -0
  115. data/lib/action_view/template_handlers/compilable.rb +128 -0
  116. data/lib/action_view/template_handlers/erb.rb +56 -0
  117. data/lib/action_view/test_case.rb +58 -0
  118. data/lib/actionpack.rb +1 -0
  119. data/test/abstract_unit.rb +36 -0
  120. data/test/active_record_unit.rb +105 -0
  121. data/test/activerecord/active_record_store_test.rb +141 -0
  122. data/test/activerecord/render_partial_with_record_identification_test.rb +191 -0
  123. data/test/adv_attr_test.rb +20 -0
  124. data/test/controller/action_pack_assertions_test.rb +543 -0
  125. data/test/controller/addresses_render_test.rb +43 -0
  126. data/test/controller/assert_select_test.rb +331 -0
  127. data/test/controller/base_test.rb +219 -0
  128. data/test/controller/benchmark_test.rb +32 -0
  129. data/test/controller/caching_test.rb +581 -0
  130. data/test/controller/capture_test.rb +89 -0
  131. data/test/controller/cgi_test.rb +116 -0
  132. data/test/controller/components_test.rb +140 -0
  133. data/test/controller/content_type_test.rb +139 -0
  134. data/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb +0 -0
  135. data/test/controller/controller_fixtures/app/controllers/user_controller.rb +0 -0
  136. data/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb +0 -0
  137. data/test/controller/cookie_test.rb +146 -0
  138. data/test/controller/custom_handler_test.rb +45 -0
  139. data/test/controller/deprecation/deprecated_base_methods_test.rb +37 -0
  140. data/test/controller/dispatcher_test.rb +105 -0
  141. data/test/controller/fake_controllers.rb +33 -0
  142. data/test/controller/fake_models.rb +11 -0
  143. data/test/controller/filter_params_test.rb +49 -0
  144. data/test/controller/filters_test.rb +881 -0
  145. data/test/controller/flash_test.rb +146 -0
  146. data/test/controller/header_test.rb +14 -0
  147. data/test/controller/helper_test.rb +210 -0
  148. data/test/controller/html-scanner/cdata_node_test.rb +15 -0
  149. data/test/controller/html-scanner/document_test.rb +148 -0
  150. data/test/controller/html-scanner/node_test.rb +89 -0
  151. data/test/controller/html-scanner/sanitizer_test.rb +269 -0
  152. data/test/controller/html-scanner/tag_node_test.rb +238 -0
  153. data/test/controller/html-scanner/text_node_test.rb +50 -0
  154. data/test/controller/html-scanner/tokenizer_test.rb +131 -0
  155. data/test/controller/http_authentication_test.rb +54 -0
  156. data/test/controller/integration_test.rb +252 -0
  157. data/test/controller/integration_upload_test.rb +43 -0
  158. data/test/controller/layout_test.rb +255 -0
  159. data/test/controller/mime_responds_test.rb +514 -0
  160. data/test/controller/mime_type_test.rb +84 -0
  161. data/test/controller/new_render_test.rb +843 -0
  162. data/test/controller/polymorphic_routes_test.rb +174 -0
  163. data/test/controller/record_identifier_test.rb +139 -0
  164. data/test/controller/redirect_test.rb +289 -0
  165. data/test/controller/render_test.rb +484 -0
  166. data/test/controller/request_forgery_protection_test.rb +305 -0
  167. data/test/controller/request_test.rb +928 -0
  168. data/test/controller/rescue_test.rb +517 -0
  169. data/test/controller/resources_test.rb +873 -0
  170. data/test/controller/routing_test.rb +2464 -0
  171. data/test/controller/selector_test.rb +628 -0
  172. data/test/controller/send_file_test.rb +138 -0
  173. data/test/controller/session/cookie_store_test.rb +258 -0
  174. data/test/controller/session/mem_cache_store_test.rb +181 -0
  175. data/test/controller/session_fixation_test.rb +89 -0
  176. data/test/controller/session_management_test.rb +178 -0
  177. data/test/controller/test_test.rb +695 -0
  178. data/test/controller/url_rewriter_test.rb +310 -0
  179. data/test/controller/verification_test.rb +270 -0
  180. data/test/controller/view_paths_test.rb +140 -0
  181. data/test/controller/webservice_test.rb +229 -0
  182. data/test/fixtures/addresses/list.erb +1 -0
  183. data/test/fixtures/bad_customers/_bad_customer.html.erb +1 -0
  184. data/test/fixtures/companies.yml +24 -0
  185. data/test/fixtures/company.rb +10 -0
  186. data/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml +1 -0
  187. data/test/fixtures/content_type/render_default_for_js.js.erb +1 -0
  188. data/test/fixtures/content_type/render_default_for_rhtml.rhtml +1 -0
  189. data/test/fixtures/content_type/render_default_for_rxml.rxml +1 -0
  190. data/test/fixtures/customers/_customer.html.erb +1 -0
  191. data/test/fixtures/db_definitions/sqlite.sql +49 -0
  192. data/test/fixtures/developer.rb +9 -0
  193. data/test/fixtures/developers.yml +21 -0
  194. data/test/fixtures/developers_projects.yml +13 -0
  195. data/test/fixtures/fun/games/hello_world.erb +1 -0
  196. data/test/fixtures/functional_caching/_partial.erb +3 -0
  197. data/test/fixtures/functional_caching/fragment_cached.html.erb +2 -0
  198. data/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb +1 -0
  199. data/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs +1 -0
  200. data/test/fixtures/good_customers/_good_customer.html.erb +1 -0
  201. data/test/fixtures/helpers/abc_helper.rb +5 -0
  202. data/test/fixtures/helpers/fun/games_helper.rb +3 -0
  203. data/test/fixtures/helpers/fun/pdf_helper.rb +3 -0
  204. data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
  205. data/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml +1 -0
  206. data/test/fixtures/layout_tests/layouts/item.rhtml +1 -0
  207. data/test/fixtures/layout_tests/layouts/layout_test.rhtml +1 -0
  208. data/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb +1 -0
  209. data/test/fixtures/layout_tests/layouts/third_party_template_library.mab +1 -0
  210. data/test/fixtures/layout_tests/views/hello.rhtml +1 -0
  211. data/test/fixtures/layouts/block_with_layout.erb +3 -0
  212. data/test/fixtures/layouts/builder.builder +3 -0
  213. data/test/fixtures/layouts/partial_with_layout.erb +3 -0
  214. data/test/fixtures/layouts/standard.erb +1 -0
  215. data/test/fixtures/layouts/talk_from_action.erb +2 -0
  216. data/test/fixtures/layouts/yield.erb +2 -0
  217. data/test/fixtures/mascot.rb +3 -0
  218. data/test/fixtures/mascots.yml +4 -0
  219. data/test/fixtures/mascots/_mascot.html.erb +1 -0
  220. data/test/fixtures/multipart/binary_file +0 -0
  221. data/test/fixtures/multipart/boundary_problem_file +10 -0
  222. data/test/fixtures/multipart/bracketed_param +5 -0
  223. data/test/fixtures/multipart/large_text_file +10 -0
  224. data/test/fixtures/multipart/mixed_files +0 -0
  225. data/test/fixtures/multipart/mona_lisa.jpg +0 -0
  226. data/test/fixtures/multipart/single_parameter +5 -0
  227. data/test/fixtures/multipart/text_file +10 -0
  228. data/test/fixtures/override/test/hello_world.erb +1 -0
  229. data/test/fixtures/override2/layouts/test/sub.erb +1 -0
  230. data/test/fixtures/post_test/layouts/post.html.erb +1 -0
  231. data/test/fixtures/post_test/layouts/super_post.iphone.erb +1 -0
  232. data/test/fixtures/post_test/post/index.html.erb +1 -0
  233. data/test/fixtures/post_test/post/index.iphone.erb +1 -0
  234. data/test/fixtures/post_test/super_post/index.html.erb +1 -0
  235. data/test/fixtures/post_test/super_post/index.iphone.erb +1 -0
  236. data/test/fixtures/project.rb +3 -0
  237. data/test/fixtures/projects.yml +7 -0
  238. data/test/fixtures/public/404.html +1 -0
  239. data/test/fixtures/public/500.html +1 -0
  240. data/test/fixtures/public/images/rails.png +0 -0
  241. data/test/fixtures/public/javascripts/application.js +1 -0
  242. data/test/fixtures/public/javascripts/bank.js +1 -0
  243. data/test/fixtures/public/javascripts/robber.js +1 -0
  244. data/test/fixtures/public/javascripts/version.1.0.js +1 -0
  245. data/test/fixtures/public/stylesheets/bank.css +1 -0
  246. data/test/fixtures/public/stylesheets/robber.css +1 -0
  247. data/test/fixtures/public/stylesheets/version.1.0.css +1 -0
  248. data/test/fixtures/replies.yml +15 -0
  249. data/test/fixtures/reply.rb +7 -0
  250. data/test/fixtures/respond_to/all_types_with_layout.html.erb +1 -0
  251. data/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb +1 -0
  252. data/test/fixtures/respond_to/iphone_with_html_response_type.html.erb +1 -0
  253. data/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb +1 -0
  254. data/test/fixtures/respond_to/layouts/missing.html.erb +1 -0
  255. data/test/fixtures/respond_to/layouts/standard.html.erb +1 -0
  256. data/test/fixtures/respond_to/layouts/standard.iphone.erb +1 -0
  257. data/test/fixtures/respond_to/using_defaults.html.erb +1 -0
  258. data/test/fixtures/respond_to/using_defaults.js.rjs +1 -0
  259. data/test/fixtures/respond_to/using_defaults.xml.builder +1 -0
  260. data/test/fixtures/respond_to/using_defaults_with_type_list.html.erb +1 -0
  261. data/test/fixtures/respond_to/using_defaults_with_type_list.js.rjs +1 -0
  262. data/test/fixtures/respond_to/using_defaults_with_type_list.xml.builder +1 -0
  263. data/test/fixtures/scope/test/modgreet.erb +1 -0
  264. data/test/fixtures/shared.html.erb +1 -0
  265. data/test/fixtures/symlink_parent/symlinked_layout.erb +5 -0
  266. data/test/fixtures/test/_customer.erb +1 -0
  267. data/test/fixtures/test/_customer_counter.erb +1 -0
  268. data/test/fixtures/test/_customer_greeting.erb +1 -0
  269. data/test/fixtures/test/_form.erb +1 -0
  270. data/test/fixtures/test/_hash_greeting.erb +1 -0
  271. data/test/fixtures/test/_hash_object.erb +2 -0
  272. data/test/fixtures/test/_hello.builder +1 -0
  273. data/test/fixtures/test/_labelling_form.erb +1 -0
  274. data/test/fixtures/test/_layout_for_partial.html.erb +3 -0
  275. data/test/fixtures/test/_partial.erb +1 -0
  276. data/test/fixtures/test/_partial.html.erb +1 -0
  277. data/test/fixtures/test/_partial.js.erb +1 -0
  278. data/test/fixtures/test/_partial_for_use_in_layout.html.erb +1 -0
  279. data/test/fixtures/test/_partial_only.erb +1 -0
  280. data/test/fixtures/test/_person.erb +2 -0
  281. data/test/fixtures/test/_raise.html.erb +1 -0
  282. data/test/fixtures/test/action_talk_to_layout.erb +2 -0
  283. data/test/fixtures/test/block_content_for.erb +2 -0
  284. data/test/fixtures/test/calling_partial_with_layout.html.erb +1 -0
  285. data/test/fixtures/test/capturing.erb +4 -0
  286. data/test/fixtures/test/content_for.erb +2 -0
  287. data/test/fixtures/test/content_for_concatenated.erb +3 -0
  288. data/test/fixtures/test/content_for_with_parameter.erb +2 -0
  289. data/test/fixtures/test/delete_with_js.rjs +2 -0
  290. data/test/fixtures/test/dot.directory/render_file_with_ivar.erb +1 -0
  291. data/test/fixtures/test/enum_rjs_test.rjs +6 -0
  292. data/test/fixtures/test/erb_content_for.erb +2 -0
  293. data/test/fixtures/test/formatted_html_erb.html.erb +1 -0
  294. data/test/fixtures/test/formatted_xml_erb.builder +1 -0
  295. data/test/fixtures/test/formatted_xml_erb.html.erb +1 -0
  296. data/test/fixtures/test/formatted_xml_erb.xml.erb +1 -0
  297. data/test/fixtures/test/greeting.erb +1 -0
  298. data/test/fixtures/test/greeting.js.rjs +1 -0
  299. data/test/fixtures/test/hello.builder +4 -0
  300. data/test/fixtures/test/hello_world.erb +1 -0
  301. data/test/fixtures/test/hello_world_container.builder +3 -0
  302. data/test/fixtures/test/hello_world_from_rxml.builder +4 -0
  303. data/test/fixtures/test/hello_world_with_layout_false.erb +1 -0
  304. data/test/fixtures/test/hello_xml_world.builder +11 -0
  305. data/test/fixtures/test/list.erb +1 -0
  306. data/test/fixtures/test/non_erb_block_content_for.builder +4 -0
  307. data/test/fixtures/test/potential_conflicts.erb +4 -0
  308. data/test/fixtures/test/render_file_from_template.html.erb +1 -0
  309. data/test/fixtures/test/render_file_with_ivar.erb +1 -0
  310. data/test/fixtures/test/render_file_with_locals.erb +1 -0
  311. data/test/fixtures/test/render_to_string_test.erb +1 -0
  312. data/test/fixtures/test/update_element_with_capture.erb +9 -0
  313. data/test/fixtures/test/using_layout_around_block.html.erb +1 -0
  314. data/test/fixtures/topic.rb +3 -0
  315. data/test/fixtures/topics.yml +22 -0
  316. data/test/fixtures/topics/_topic.html.erb +1 -0
  317. data/test/template/active_record_helper_test.rb +268 -0
  318. data/test/template/asset_tag_helper_test.rb +514 -0
  319. data/test/template/atom_feed_helper_test.rb +179 -0
  320. data/test/template/benchmark_helper_test.rb +60 -0
  321. data/test/template/date_helper_test.rb +1791 -0
  322. data/test/template/deprecated_erb_variable_test.rb +9 -0
  323. data/test/template/erb_util_test.rb +24 -0
  324. data/test/template/form_helper_test.rb +885 -0
  325. data/test/template/form_options_helper_test.rb +1333 -0
  326. data/test/template/form_tag_helper_test.rb +272 -0
  327. data/test/template/javascript_helper_test.rb +73 -0
  328. data/test/template/number_helper_test.rb +97 -0
  329. data/test/template/record_tag_helper_test.rb +54 -0
  330. data/test/template/sanitize_helper_test.rb +48 -0
  331. data/test/template/tag_helper_test.rb +77 -0
  332. data/test/template/template_finder_test.rb +73 -0
  333. data/test/template/template_object_test.rb +95 -0
  334. data/test/template/test_test.rb +56 -0
  335. data/test/template/text_helper_test.rb +367 -0
  336. data/test/template/url_helper_test.rb +544 -0
  337. data/test/testing_sandbox.rb +15 -0
  338. metadata +469 -0
@@ -0,0 +1,634 @@
1
+ require 'stringio'
2
+ require 'uri'
3
+
4
+ require 'action_controller/dispatcher'
5
+ require 'action_controller/test_process'
6
+
7
+ module ActionController
8
+ module Integration #:nodoc:
9
+ # An integration Session instance represents a set of requests and responses
10
+ # performed sequentially by some virtual user. Becase you can instantiate
11
+ # multiple sessions and run them side-by-side, you can also mimic (to some
12
+ # limited extent) multiple simultaneous users interacting with your system.
13
+ #
14
+ # Typically, you will instantiate a new session using IntegrationTest#open_session,
15
+ # rather than instantiating Integration::Session directly.
16
+ class Session
17
+ include Test::Unit::Assertions
18
+ include ActionController::Assertions
19
+ include ActionController::TestProcess
20
+
21
+ # The integer HTTP status code of the last request.
22
+ attr_reader :status
23
+
24
+ # The status message that accompanied the status code of the last request.
25
+ attr_reader :status_message
26
+
27
+ # The URI of the last request.
28
+ attr_reader :path
29
+
30
+ # The hostname used in the last request.
31
+ attr_accessor :host
32
+
33
+ # The remote_addr used in the last request.
34
+ attr_accessor :remote_addr
35
+
36
+ # The Accept header to send.
37
+ attr_accessor :accept
38
+
39
+ # A map of the cookies returned by the last response, and which will be
40
+ # sent with the next request.
41
+ attr_reader :cookies
42
+
43
+ # A map of the headers returned by the last response.
44
+ attr_reader :headers
45
+
46
+ # A reference to the controller instance used by the last request.
47
+ attr_reader :controller
48
+
49
+ # A reference to the request instance used by the last request.
50
+ attr_reader :request
51
+
52
+ # A reference to the response instance used by the last request.
53
+ attr_reader :response
54
+
55
+ # A running counter of the number of requests processed.
56
+ attr_accessor :request_count
57
+
58
+ class MultiPartNeededException < Exception
59
+ end
60
+
61
+ # Create and initialize a new Session instance.
62
+ def initialize
63
+ reset!
64
+ end
65
+
66
+ # Resets the instance. This can be used to reset the state information
67
+ # in an existing session instance, so it can be used from a clean-slate
68
+ # condition.
69
+ #
70
+ # session.reset!
71
+ def reset!
72
+ @status = @path = @headers = nil
73
+ @result = @status_message = nil
74
+ @https = false
75
+ @cookies = {}
76
+ @controller = @request = @response = nil
77
+ @request_count = 0
78
+
79
+ self.host = "www.example.com"
80
+ self.remote_addr = "127.0.0.1"
81
+ self.accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
82
+
83
+ unless defined? @named_routes_configured
84
+ # install the named routes in this session instance.
85
+ klass = class<<self; self; end
86
+ Routing::Routes.install_helpers(klass)
87
+
88
+ # the helpers are made protected by default--we make them public for
89
+ # easier access during testing and troubleshooting.
90
+ klass.module_eval { public *Routing::Routes.named_routes.helpers }
91
+ @named_routes_configured = true
92
+ end
93
+ end
94
+
95
+ # Specify whether or not the session should mimic a secure HTTPS request.
96
+ #
97
+ # session.https!
98
+ # session.https!(false)
99
+ def https!(flag=true)
100
+ @https = flag
101
+ end
102
+
103
+ # Return +true+ if the session is mimicing a secure HTTPS request.
104
+ #
105
+ # if session.https?
106
+ # ...
107
+ # end
108
+ def https?
109
+ @https
110
+ end
111
+
112
+ # Set the host name to use in the next request.
113
+ #
114
+ # session.host! "www.example.com"
115
+ def host!(name)
116
+ @host = name
117
+ end
118
+
119
+ # Follow a single redirect response. If the last response was not a
120
+ # redirect, an exception will be raised. Otherwise, the redirect is
121
+ # performed on the location header.
122
+ def follow_redirect!
123
+ raise "not a redirect! #{@status} #{@status_message}" unless redirect?
124
+ get(interpret_uri(headers['location'].first))
125
+ status
126
+ end
127
+
128
+ # Performs a request using the specified method, following any subsequent
129
+ # redirect. Note that the redirects are followed until the response is
130
+ # not a redirect--this means you may run into an infinite loop if your
131
+ # redirect loops back to itself.
132
+ def request_via_redirect(http_method, path, parameters = nil, headers = nil)
133
+ send(http_method, path, parameters, headers)
134
+ follow_redirect! while redirect?
135
+ status
136
+ end
137
+
138
+ # Performs a GET request, following any subsequent redirect.
139
+ # See +request_via_redirect+ for more information.
140
+ def get_via_redirect(path, parameters = nil, headers = nil)
141
+ request_via_redirect(:get, path, parameters, headers)
142
+ end
143
+
144
+ # Performs a POST request, following any subsequent redirect.
145
+ # See +request_via_redirect+ for more information.
146
+ def post_via_redirect(path, parameters = nil, headers = nil)
147
+ request_via_redirect(:post, path, parameters, headers)
148
+ end
149
+
150
+ # Performs a PUT request, following any subsequent redirect.
151
+ # See +request_via_redirect+ for more information.
152
+ def put_via_redirect(path, parameters = nil, headers = nil)
153
+ request_via_redirect(:put, path, parameters, headers)
154
+ end
155
+
156
+ # Performs a DELETE request, following any subsequent redirect.
157
+ # See +request_via_redirect+ for more information.
158
+ def delete_via_redirect(path, parameters = nil, headers = nil)
159
+ request_via_redirect(:delete, path, parameters, headers)
160
+ end
161
+
162
+ # Returns +true+ if the last response was a redirect.
163
+ def redirect?
164
+ status/100 == 3
165
+ end
166
+
167
+ # Performs a GET request with the given parameters. The parameters may
168
+ # be +nil+, a Hash, or a string that is appropriately encoded
169
+ # (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
170
+ # The headers should be a hash. The keys will automatically be upcased, with the
171
+ # prefix 'HTTP_' added if needed.
172
+ #
173
+ # You can also perform POST, PUT, DELETE, and HEAD requests with +post+,
174
+ # +put+, +delete+, and +head+.
175
+ def get(path, parameters = nil, headers = nil)
176
+ process :get, path, parameters, headers
177
+ end
178
+
179
+ # Performs a POST request with the given parameters. See get() for more details.
180
+ def post(path, parameters = nil, headers = nil)
181
+ process :post, path, parameters, headers
182
+ end
183
+
184
+ # Performs a PUT request with the given parameters. See get() for more details.
185
+ def put(path, parameters = nil, headers = nil)
186
+ process :put, path, parameters, headers
187
+ end
188
+
189
+ # Performs a DELETE request with the given parameters. See get() for more details.
190
+ def delete(path, parameters = nil, headers = nil)
191
+ process :delete, path, parameters, headers
192
+ end
193
+
194
+ # Performs a HEAD request with the given parameters. See get() for more details.
195
+ def head(path, parameters = nil, headers = nil)
196
+ process :head, path, parameters, headers
197
+ end
198
+
199
+ # Performs an XMLHttpRequest request with the given parameters, mirroring
200
+ # a request from the Prototype library.
201
+ #
202
+ # The request_method is :get, :post, :put, :delete or :head; the
203
+ # parameters are +nil+, a hash, or a url-encoded or multipart string;
204
+ # the headers are a hash. Keys are automatically upcased and prefixed
205
+ # with 'HTTP_' if not already.
206
+ def xml_http_request(request_method, path, parameters = nil, headers = nil)
207
+ headers ||= {}
208
+ headers['X-Requested-With'] = 'XMLHttpRequest'
209
+ headers['Accept'] ||= 'text/javascript, text/html, application/xml, text/xml, */*'
210
+
211
+ process(request_method, path, parameters, headers)
212
+ end
213
+ alias xhr :xml_http_request
214
+
215
+ # Returns the URL for the given options, according to the rules specified
216
+ # in the application's routes.
217
+ def url_for(options)
218
+ controller ? controller.url_for(options) : generic_url_rewriter.rewrite(options)
219
+ end
220
+
221
+ private
222
+ class StubCGI < CGI #:nodoc:
223
+ attr_accessor :stdinput, :stdoutput, :env_table
224
+
225
+ def initialize(env, stdinput = nil)
226
+ self.env_table = env
227
+ self.stdoutput = StringIO.new
228
+
229
+ super
230
+
231
+ stdinput.set_encoding(Encoding::BINARY) if stdinput.respond_to?(:set_encoding)
232
+ stdinput.force_encoding(Encoding::BINARY) if stdinput.respond_to?(:force_encoding)
233
+ @stdinput = stdinput.is_a?(IO) ? stdinput : StringIO.new(stdinput || '')
234
+ end
235
+ end
236
+
237
+ # Tailors the session based on the given URI, setting the HTTPS value
238
+ # and the hostname.
239
+ def interpret_uri(path)
240
+ location = URI.parse(path)
241
+ https! URI::HTTPS === location if location.scheme
242
+ host! location.host if location.host
243
+ location.query ? "#{location.path}?#{location.query}" : location.path
244
+ end
245
+
246
+ # Performs the actual request.
247
+ def process(method, path, parameters = nil, headers = nil)
248
+ data = requestify(parameters)
249
+ path = interpret_uri(path) if path =~ %r{://}
250
+ path = "/#{path}" unless path[0] == ?/
251
+ @path = path
252
+ env = {}
253
+
254
+ if method == :get
255
+ env["QUERY_STRING"] = data
256
+ data = nil
257
+ end
258
+
259
+ env.update(
260
+ "REQUEST_METHOD" => method.to_s.upcase,
261
+ "REQUEST_URI" => path,
262
+ "HTTP_HOST" => host,
263
+ "REMOTE_ADDR" => remote_addr,
264
+ "SERVER_PORT" => (https? ? "443" : "80"),
265
+ "CONTENT_TYPE" => "application/x-www-form-urlencoded",
266
+ "CONTENT_LENGTH" => data ? data.length.to_s : nil,
267
+ "HTTP_COOKIE" => encode_cookies,
268
+ "HTTPS" => https? ? "on" : "off",
269
+ "HTTP_ACCEPT" => accept
270
+ )
271
+
272
+ (headers || {}).each do |key, value|
273
+ key = key.to_s.upcase.gsub(/-/, "_")
274
+ key = "HTTP_#{key}" unless env.has_key?(key) || key =~ /^HTTP_/
275
+ env[key] = value
276
+ end
277
+
278
+ unless ActionController::Base.respond_to?(:clear_last_instantiation!)
279
+ ActionController::Base.module_eval { include ControllerCapture }
280
+ end
281
+
282
+ ActionController::Base.clear_last_instantiation!
283
+
284
+ cgi = StubCGI.new(env, data)
285
+ ActionController::Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, cgi.stdoutput)
286
+ @result = cgi.stdoutput.string
287
+ @request_count += 1
288
+
289
+ @controller = ActionController::Base.last_instantiation
290
+ @request = @controller.request
291
+ @response = @controller.response
292
+
293
+ # Decorate the response with the standard behavior of the TestResponse
294
+ # so that things like assert_response can be used in integration
295
+ # tests.
296
+ @response.extend(TestResponseBehavior)
297
+
298
+ @html_document = nil
299
+
300
+ parse_result
301
+ return status
302
+ rescue MultiPartNeededException
303
+ boundary = "----------XnJLe9ZIbbGUYtzPQJ16u1"
304
+ status = process(method, path, multipart_body(parameters, boundary), (headers || {}).merge({"CONTENT_TYPE" => "multipart/form-data; boundary=#{boundary}"}))
305
+ return status
306
+ end
307
+
308
+ # Parses the result of the response and extracts the various values,
309
+ # like cookies, status, headers, etc.
310
+ def parse_result
311
+ response_headers, result_body = @result.split(/\r\n\r\n/, 2)
312
+
313
+ @headers = Hash.new { |h,k| h[k] = [] }
314
+ response_headers.to_s.each_line do |line|
315
+ key, value = line.strip.split(/:\s*/, 2)
316
+ @headers[key.downcase] << value
317
+ end
318
+
319
+ (@headers['set-cookie'] || [] ).each do |string|
320
+ name, value = string.match(/^([^=]*)=([^;]*);/)[1,2]
321
+ @cookies[name] = value
322
+ end
323
+
324
+ @status, @status_message = @headers["status"].first.to_s.split(/ /)
325
+ @status = @status.to_i
326
+ end
327
+
328
+ # Encode the cookies hash in a format suitable for passing to a
329
+ # request.
330
+ def encode_cookies
331
+ cookies.inject("") do |string, (name, value)|
332
+ string << "#{name}=#{value}; "
333
+ end
334
+ end
335
+
336
+ # Get a temporary URL writer object
337
+ def generic_url_rewriter
338
+ cgi = StubCGI.new('REQUEST_METHOD' => "GET",
339
+ 'QUERY_STRING' => "",
340
+ "REQUEST_URI" => "/",
341
+ "HTTP_HOST" => host,
342
+ "SERVER_PORT" => https? ? "443" : "80",
343
+ "HTTPS" => https? ? "on" : "off")
344
+ ActionController::UrlRewriter.new(ActionController::CgiRequest.new(cgi), {})
345
+ end
346
+
347
+ def name_with_prefix(prefix, name)
348
+ prefix ? "#{prefix}[#{name}]" : name.to_s
349
+ end
350
+
351
+ # Convert the given parameters to a request string. The parameters may
352
+ # be a string, +nil+, or a Hash.
353
+ def requestify(parameters, prefix=nil)
354
+ if TestUploadedFile === parameters
355
+ raise MultiPartNeededException
356
+ elsif Hash === parameters
357
+ return nil if parameters.empty?
358
+ parameters.map { |k,v| requestify(v, name_with_prefix(prefix, k)) }.join("&")
359
+ elsif Array === parameters
360
+ parameters.map { |v| requestify(v, name_with_prefix(prefix, "")) }.join("&")
361
+ elsif prefix.nil?
362
+ parameters
363
+ else
364
+ "#{CGI.escape(prefix)}=#{CGI.escape(parameters.to_s)}"
365
+ end
366
+ end
367
+
368
+ def multipart_requestify(params, first=true)
369
+ returning Hash.new do |p|
370
+ params.each do |key, value|
371
+ k = first ? CGI.escape(key.to_s) : "[#{CGI.escape(key.to_s)}]"
372
+ if Hash === value
373
+ multipart_requestify(value, false).each do |subkey, subvalue|
374
+ p[k + subkey] = subvalue
375
+ end
376
+ else
377
+ p[k] = value
378
+ end
379
+ end
380
+ end
381
+ end
382
+
383
+ def multipart_body(params, boundary)
384
+ multipart_requestify(params).map do |key, value|
385
+ if value.respond_to?(:original_filename)
386
+ File.open(value.path) do |f|
387
+ f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)
388
+
389
+ <<-EOF
390
+ --#{boundary}\r
391
+ Content-Disposition: form-data; name="#{key}"; filename="#{CGI.escape(value.original_filename)}"\r
392
+ Content-Type: #{value.content_type}\r
393
+ Content-Length: #{File.stat(value.path).size}\r
394
+ \r
395
+ #{f.read}\r
396
+ EOF
397
+ end
398
+ else
399
+ <<-EOF
400
+ --#{boundary}\r
401
+ Content-Disposition: form-data; name="#{key}"\r
402
+ \r
403
+ #{value}\r
404
+ EOF
405
+ end
406
+ end.join("")+"--#{boundary}--\r"
407
+ end
408
+ end
409
+
410
+ # A module used to extend ActionController::Base, so that integration tests
411
+ # can capture the controller used to satisfy a request.
412
+ module ControllerCapture #:nodoc:
413
+ def self.included(base)
414
+ base.extend(ClassMethods)
415
+ base.class_eval do
416
+ class << self
417
+ alias_method_chain :new, :capture
418
+ end
419
+ end
420
+ end
421
+
422
+ module ClassMethods #:nodoc:
423
+ mattr_accessor :last_instantiation
424
+
425
+ def clear_last_instantiation!
426
+ self.last_instantiation = nil
427
+ end
428
+
429
+ def new_with_capture(*args)
430
+ controller = new_without_capture(*args)
431
+ self.last_instantiation ||= controller
432
+ controller
433
+ end
434
+ end
435
+ end
436
+
437
+ module Runner
438
+ # Reset the current session. This is useful for testing multiple sessions
439
+ # in a single test case.
440
+ def reset!
441
+ @integration_session = open_session
442
+ end
443
+
444
+ %w(get post put head delete cookies assigns
445
+ xml_http_request get_via_redirect post_via_redirect).each do |method|
446
+ define_method(method) do |*args|
447
+ reset! unless @integration_session
448
+ # reset the html_document variable, but only for new get/post calls
449
+ @html_document = nil unless %w(cookies assigns).include?(method)
450
+ returning @integration_session.send!(method, *args) do
451
+ copy_session_variables!
452
+ end
453
+ end
454
+ end
455
+
456
+ # Open a new session instance. If a block is given, the new session is
457
+ # yielded to the block before being returned.
458
+ #
459
+ # session = open_session do |sess|
460
+ # sess.extend(CustomAssertions)
461
+ # end
462
+ #
463
+ # By default, a single session is automatically created for you, but you
464
+ # can use this method to open multiple sessions that ought to be tested
465
+ # simultaneously.
466
+ def open_session
467
+ session = Integration::Session.new
468
+
469
+ # delegate the fixture accessors back to the test instance
470
+ extras = Module.new { attr_accessor :delegate, :test_result }
471
+ if self.class.respond_to?(:fixture_table_names)
472
+ self.class.fixture_table_names.each do |table_name|
473
+ name = table_name.tr(".", "_")
474
+ next unless respond_to?(name)
475
+ extras.send!(:define_method, name) { |*args| delegate.send(name, *args) }
476
+ end
477
+ end
478
+
479
+ # delegate add_assertion to the test case
480
+ extras.send!(:define_method, :add_assertion) { test_result.add_assertion }
481
+ session.extend(extras)
482
+ session.delegate = self
483
+ session.test_result = @_result
484
+
485
+ yield session if block_given?
486
+ session
487
+ end
488
+
489
+ # Copy the instance variables from the current session instance into the
490
+ # test instance.
491
+ def copy_session_variables! #:nodoc:
492
+ return unless @integration_session
493
+ %w(controller response request).each do |var|
494
+ instance_variable_set("@#{var}", @integration_session.send!(var))
495
+ end
496
+ end
497
+
498
+ # Delegate unhandled messages to the current session instance.
499
+ def method_missing(sym, *args, &block)
500
+ reset! unless @integration_session
501
+ returning @integration_session.send!(sym, *args, &block) do
502
+ copy_session_variables!
503
+ end
504
+ end
505
+ end
506
+ end
507
+
508
+ # An IntegrationTest is one that spans multiple controllers and actions,
509
+ # tying them all together to ensure they work together as expected. It tests
510
+ # more completely than either unit or functional tests do, exercising the
511
+ # entire stack, from the dispatcher to the database.
512
+ #
513
+ # At its simplest, you simply extend IntegrationTest and write your tests
514
+ # using the get/post methods:
515
+ #
516
+ # require "#{File.dirname(__FILE__)}/test_helper"
517
+ #
518
+ # class ExampleTest < ActionController::IntegrationTest
519
+ # fixtures :people
520
+ #
521
+ # def test_login
522
+ # # get the login page
523
+ # get "/login"
524
+ # assert_equal 200, status
525
+ #
526
+ # # post the login and follow through to the home page
527
+ # post "/login", :username => people(:jamis).username,
528
+ # :password => people(:jamis).password
529
+ # follow_redirect!
530
+ # assert_equal 200, status
531
+ # assert_equal "/home", path
532
+ # end
533
+ # end
534
+ #
535
+ # However, you can also have multiple session instances open per test, and
536
+ # even extend those instances with assertions and methods to create a very
537
+ # powerful testing DSL that is specific for your application. You can even
538
+ # reference any named routes you happen to have defined!
539
+ #
540
+ # require "#{File.dirname(__FILE__)}/test_helper"
541
+ #
542
+ # class AdvancedTest < ActionController::IntegrationTest
543
+ # fixtures :people, :rooms
544
+ #
545
+ # def test_login_and_speak
546
+ # jamis, david = login(:jamis), login(:david)
547
+ # room = rooms(:office)
548
+ #
549
+ # jamis.enter(room)
550
+ # jamis.speak(room, "anybody home?")
551
+ #
552
+ # david.enter(room)
553
+ # david.speak(room, "hello!")
554
+ # end
555
+ #
556
+ # private
557
+ #
558
+ # module CustomAssertions
559
+ # def enter(room)
560
+ # # reference a named route, for maximum internal consistency!
561
+ # get(room_url(:id => room.id))
562
+ # assert(...)
563
+ # ...
564
+ # end
565
+ #
566
+ # def speak(room, message)
567
+ # xml_http_request "/say/#{room.id}", :message => message
568
+ # assert(...)
569
+ # ...
570
+ # end
571
+ # end
572
+ #
573
+ # def login(who)
574
+ # open_session do |sess|
575
+ # sess.extend(CustomAssertions)
576
+ # who = people(who)
577
+ # sess.post "/login", :username => who.username,
578
+ # :password => who.password
579
+ # assert(...)
580
+ # end
581
+ # end
582
+ # end
583
+ class IntegrationTest < Test::Unit::TestCase
584
+ include Integration::Runner
585
+
586
+ # Work around a bug in test/unit caused by the default test being named
587
+ # as a symbol (:default_test), which causes regex test filters
588
+ # (like "ruby test.rb -n /foo/") to fail because =~ doesn't work on
589
+ # symbols.
590
+ def initialize(name) #:nodoc:
591
+ super(name.to_s)
592
+ end
593
+
594
+ # Work around test/unit's requirement that every subclass of TestCase have
595
+ # at least one test method. Note that this implementation extends to all
596
+ # subclasses, as well, so subclasses of IntegrationTest may also exist
597
+ # without any test methods.
598
+ def run(*args) #:nodoc:
599
+ return if @method_name == "default_test"
600
+ super
601
+ end
602
+
603
+ # Because of how use_instantiated_fixtures and use_transactional_fixtures
604
+ # are defined, we need to treat them as special cases. Otherwise, users
605
+ # would potentially have to set their values for both Test::Unit::TestCase
606
+ # ActionController::IntegrationTest, since by the time the value is set on
607
+ # TestCase, IntegrationTest has already been defined and cannot inherit
608
+ # changes to those variables. So, we make those two attributes copy-on-write.
609
+
610
+ class << self
611
+ def use_transactional_fixtures=(flag) #:nodoc:
612
+ @_use_transactional_fixtures = true
613
+ @use_transactional_fixtures = flag
614
+ end
615
+
616
+ def use_instantiated_fixtures=(flag) #:nodoc:
617
+ @_use_instantiated_fixtures = true
618
+ @use_instantiated_fixtures = flag
619
+ end
620
+
621
+ def use_transactional_fixtures #:nodoc:
622
+ @_use_transactional_fixtures ?
623
+ @use_transactional_fixtures :
624
+ superclass.use_transactional_fixtures
625
+ end
626
+
627
+ def use_instantiated_fixtures #:nodoc:
628
+ @_use_instantiated_fixtures ?
629
+ @use_instantiated_fixtures :
630
+ superclass.use_instantiated_fixtures
631
+ end
632
+ end
633
+ end
634
+ end