diff options
Diffstat (limited to 'actionpack/test/controller')
61 files changed, 4595 insertions, 5173 deletions
diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index b94f45bfe7..d0dfbfbc74 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -1,5 +1,4 @@ require 'abstract_unit' -require 'action_view/vendor/html-scanner' require 'controller/fake_controllers' class ActionPackAssertionsController < ActionController::Base @@ -39,6 +38,8 @@ class ActionPackAssertionsController < ActionController::Base def redirect_external() redirect_to "http://www.rubyonrails.org"; end + def redirect_external_protocol_relative() redirect_to "//www.rubyonrails.org"; end + def response404() head '404 AWOL' end def response500() head '500 Sorry' end @@ -96,6 +97,14 @@ class ActionPackAssertionsController < ActionController::Base raise "post" if request.post? render :text => "request method: #{request.env['REQUEST_METHOD']}" end + + def render_file_absolute_path + render :file => File.expand_path('../../../README.rdoc', __FILE__) + end + + def render_file_relative_path + render :file => 'README.rdoc' + end end # Used to test that assert_response includes the exception message @@ -137,32 +146,37 @@ end class ActionPackAssertionsControllerTest < ActionController::TestCase - def test_assert_tag_and_url_for - get :render_url - assert_tag :content => "/action_pack_assertions/flash_me" + def test_render_file_absolute_path + get :render_file_absolute_path + assert_match(/\A= Action Pack/, @response.body) + end + + def test_render_file_relative_path + get :render_file_relative_path + assert_match(/\A= Action Pack/, @response.body) end def test_get_request assert_raise(RuntimeError) { get :raise_exception_on_get } get :raise_exception_on_post - assert_equal @response.body, 'request method: GET' + assert_equal 'request method: GET', @response.body end def test_post_request assert_raise(RuntimeError) { post :raise_exception_on_post } post :raise_exception_on_get - assert_equal @response.body, 'request method: POST' + assert_equal 'request method: POST', @response.body end def test_get_post_request_switch post :raise_exception_on_get - assert_equal @response.body, 'request method: POST' + assert_equal 'request method: POST', @response.body get :raise_exception_on_post - assert_equal @response.body, 'request method: GET' + assert_equal 'request method: GET', @response.body post :raise_exception_on_get - assert_equal @response.body, 'request method: POST' + assert_equal 'request method: POST', @response.body get :raise_exception_on_post - assert_equal @response.body, 'request method: GET' + assert_equal 'request method: GET', @response.body end def test_string_constraint @@ -240,6 +254,19 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase end end + def test_assert_redirect_failure_message_with_protocol_relative_url + begin + process :redirect_external_protocol_relative + assert_redirected_to "/foo" + rescue ActiveSupport::TestCase::Assertion => ex + assert_no_match( + /#{request.protocol}#{request.host}\/\/www.rubyonrails.org/, + ex.message, + 'protocol relative url was incorrectly normalized' + ) + end + end + def test_template_objects_exist process :assign_this assert !@controller.instance_variable_defined?(:"@hi") @@ -259,7 +286,7 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase def test_flash_exist process :flash_me assert flash.any? - assert_present flash['hello'] + assert flash['hello'].present? end def test_flash_does_not_exist @@ -269,7 +296,7 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase def test_session_exist process :session_stuffing - assert_equal session['xmas'], 'turkey' + assert_equal 'turkey', session['xmas'] end def session_does_not_exist @@ -291,6 +318,9 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase process :redirect_external assert_equal 'http://www.rubyonrails.org', @response.redirect_url + + process :redirect_external_protocol_relative + assert_equal '//www.rubyonrails.org', @response.redirect_url end def test_no_redirect_url @@ -408,22 +438,18 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase def test_assert_response_uses_exception_message @controller = AssertResponseWithUnexpectedErrorController.new - get :index + e = assert_raise RuntimeError, 'Expected non-success response' do + get :index + end assert_response :success - flunk 'Expected non-success response' - rescue RuntimeError => e - assert e.message.include?('FAIL') + assert_includes 'FAIL', e.message end def test_assert_response_failure_response_with_no_exception @controller = AssertResponseWithUnexpectedErrorController.new get :show - assert_response :success - flunk 'Expected non-success response' - rescue ActiveSupport::TestCase::Assertion - # success - rescue - flunk "assert_response failed to handle failure response with missing, but optional, exception." + assert_response 500 + assert_equal 'Boom', response.body end end @@ -441,6 +467,28 @@ class AssertTemplateTest < ActionController::TestCase assert_template :partial => '_partial' end + def test_file_with_absolute_path_success + get :render_file_absolute_path + assert_template :file => File.expand_path('../../../README.rdoc', __FILE__) + end + + def test_file_with_relative_path_success + get :render_file_relative_path + assert_template :file => 'README.rdoc' + end + + def test_with_file_failure + get :render_file_absolute_path + assert_raise(ActiveSupport::TestCase::Assertion) do + assert_template :file => 'test/hello_world' + end + + get :render_file_absolute_path + assert_raise(ActiveSupport::TestCase::Assertion) do + assert_template file: nil + end + end + def test_with_nil_passes_when_no_template_rendered get :nothing assert_template nil @@ -527,6 +575,13 @@ class AssertTemplateTest < ActionController::TestCase end end + def test_fails_expecting_not_known_layout + get :render_with_layout + assert_raise(ArgumentError) do + assert_template :layout => 1 + end + end + def test_passes_with_correct_layout get :render_with_layout assert_template :layout => "layouts/standard" @@ -563,6 +618,24 @@ class AssertTemplateTest < ActionController::TestCase get :nothing assert_template nil + + get :partial + assert_template partial: 'test/_partial' + + get :nothing + assert_template partial: nil + + get :render_with_layout + assert_template layout: 'layouts/standard' + + get :nothing + assert_template layout: nil + + get :render_file_relative_path + assert_template file: 'README.rdoc' + + get :nothing + assert_template file: nil end end diff --git a/actionpack/test/controller/assert_select_test.rb b/actionpack/test/controller/assert_select_test.rb deleted file mode 100644 index 3d667f0a2f..0000000000 --- a/actionpack/test/controller/assert_select_test.rb +++ /dev/null @@ -1,348 +0,0 @@ -# encoding: utf-8 -#-- -# Copyright (c) 2006 Assaf Arkin (http://labnotes.org) -# Under MIT and/or CC By license. -#++ - -require 'abstract_unit' -require 'controller/fake_controllers' - -require 'action_mailer' -ActionMailer::Base.view_paths = FIXTURE_LOAD_PATH - -class AssertSelectTest < ActionController::TestCase - Assertion = ActiveSupport::TestCase::Assertion - - class AssertSelectMailer < ActionMailer::Base - def test(html) - mail :body => html, :content_type => "text/html", - :subject => "Test e-mail", :from => "test@test.host", :to => "test <test@test.host>" - end - end - - class AssertMultipartSelectMailer < ActionMailer::Base - def test(options) - mail :subject => "Test e-mail", :from => "test@test.host", :to => "test <test@test.host>" do |format| - format.text { render :text => options[:text] } - format.html { render :text => options[:html] } - end - end - end - - class AssertSelectController < ActionController::Base - def response_with=(content) - @content = content - end - - def response_with(&block) - @update = block - end - - def html() - render :text=>@content, :layout=>false, :content_type=>Mime::HTML - @content = nil - end - - def xml() - render :text=>@content, :layout=>false, :content_type=>Mime::XML - @content = nil - end - end - - tests AssertSelectController - - def setup - super - ActionMailer::Base.delivery_method = :test - ActionMailer::Base.perform_deliveries = true - ActionMailer::Base.deliveries = [] - end - - def teardown - super - ActionMailer::Base.deliveries.clear - end - - def assert_failure(message, &block) - e = assert_raise(Assertion, &block) - assert_match(message, e.message) if Regexp === message - assert_equal(message, e.message) if String === message - end - - # - # Test assert select. - # - - def test_assert_select - render_html %Q{<div id="1"></div><div id="2"></div>} - assert_select "div", 2 - assert_failure(/Expected at least 1 element matching \"p\", found 0/) { assert_select "p" } - end - - def test_equality_integer - render_html %Q{<div id="1"></div><div id="2"></div>} - assert_failure(/Expected exactly 3 elements matching \"div\", found 2/) { assert_select "div", 3 } - assert_failure(/Expected exactly 0 elements matching \"div\", found 2/) { assert_select "div", 0 } - end - - def test_equality_true_false - render_html %Q{<div id="1"></div><div id="2"></div>} - assert_nothing_raised { assert_select "div" } - assert_raise(Assertion) { assert_select "p" } - assert_nothing_raised { assert_select "div", true } - assert_raise(Assertion) { assert_select "p", true } - assert_raise(Assertion) { assert_select "div", false } - assert_nothing_raised { assert_select "p", false } - end - - def test_equality_false_message - render_html %Q{<div id="1"></div><div id="2"></div>} - assert_failure(/Expected exactly 0 elements matching \"div\", found 2/) { assert_select "div", false } - end - - def test_equality_string_and_regexp - render_html %Q{<div id="1">foo</div><div id="2">foo</div>} - assert_nothing_raised { assert_select "div", "foo" } - assert_raise(Assertion) { assert_select "div", "bar" } - assert_nothing_raised { assert_select "div", :text=>"foo" } - assert_raise(Assertion) { assert_select "div", :text=>"bar" } - assert_nothing_raised { assert_select "div", /(foo|bar)/ } - assert_raise(Assertion) { assert_select "div", /foobar/ } - assert_nothing_raised { assert_select "div", :text=>/(foo|bar)/ } - assert_raise(Assertion) { assert_select "div", :text=>/foobar/ } - assert_raise(Assertion) { assert_select "p", :text=>/foobar/ } - end - - def test_equality_of_html - render_html %Q{<p>\n<em>"This is <strong>not</strong> a big problem,"</em> he said.\n</p>} - text = "\"This is not a big problem,\" he said." - html = "<em>\"This is <strong>not</strong> a big problem,\"</em> he said." - assert_nothing_raised { assert_select "p", text } - assert_raise(Assertion) { assert_select "p", html } - assert_nothing_raised { assert_select "p", :html=>html } - assert_raise(Assertion) { assert_select "p", :html=>text } - # No stripping for pre. - render_html %Q{<pre>\n<em>"This is <strong>not</strong> a big problem,"</em> he said.\n</pre>} - text = "\n\"This is not a big problem,\" he said.\n" - html = "\n<em>\"This is <strong>not</strong> a big problem,\"</em> he said.\n" - assert_nothing_raised { assert_select "pre", text } - assert_raise(Assertion) { assert_select "pre", html } - assert_nothing_raised { assert_select "pre", :html=>html } - assert_raise(Assertion) { assert_select "pre", :html=>text } - end - - def test_strip_textarea - render_html %Q{<textarea>\n\nfoo\n</textarea>} - assert_select "textarea", "\nfoo\n" - render_html %Q{<textarea>\nfoo</textarea>} - assert_select "textarea", "foo" - end - - def test_counts - render_html %Q{<div id="1">foo</div><div id="2">foo</div>} - assert_nothing_raised { assert_select "div", 2 } - assert_failure(/Expected exactly 3 elements matching \"div\", found 2/) do - assert_select "div", 3 - end - assert_nothing_raised { assert_select "div", 1..2 } - assert_failure(/Expected between 3 and 4 elements matching \"div\", found 2/) do - assert_select "div", 3..4 - end - assert_nothing_raised { assert_select "div", :count=>2 } - assert_failure(/Expected exactly 3 elements matching \"div\", found 2/) do - assert_select "div", :count=>3 - end - assert_nothing_raised { assert_select "div", :minimum=>1 } - assert_nothing_raised { assert_select "div", :minimum=>2 } - assert_failure(/Expected at least 3 elements matching \"div\", found 2/) do - assert_select "div", :minimum=>3 - end - assert_nothing_raised { assert_select "div", :maximum=>2 } - assert_nothing_raised { assert_select "div", :maximum=>3 } - assert_failure(/Expected at most 1 element matching \"div\", found 2/) do - assert_select "div", :maximum=>1 - end - assert_nothing_raised { assert_select "div", :minimum=>1, :maximum=>2 } - assert_failure(/Expected between 3 and 4 elements matching \"div\", found 2/) do - assert_select "div", :minimum=>3, :maximum=>4 - end - end - - def test_substitution_values - render_html %Q{<div id="1">foo</div><div id="2">foo</div>} - assert_select "div#?", /\d+/ do |elements| - assert_equal 2, elements.size - end - assert_select "div" do - assert_select "div#?", /\d+/ do |elements| - assert_equal 2, elements.size - assert_select "#1" - assert_select "#2" - end - end - end - - def test_nested_assert_select - render_html %Q{<div id="1">foo</div><div id="2">foo</div>} - assert_select "div" do |elements| - assert_equal 2, elements.size - assert_select elements[0], "#1" - assert_select elements[1], "#2" - end - assert_select "div" do - assert_select "div" do |elements| - assert_equal 2, elements.size - # Testing in a group is one thing - assert_select "#1,#2" - # Testing individually is another. - assert_select "#1" - assert_select "#2" - assert_select "#3", false - end - end - - assert_failure(/Expected at least 1 element matching \"#4\", found 0\./) do - assert_select "div" do - assert_select "#4" - end - end - end - - def test_assert_select_text_match - render_html %Q{<div id="1"><span>foo</span></div><div id="2"><span>bar</span></div>} - assert_select "div" do - assert_nothing_raised { assert_select "div", "foo" } - assert_nothing_raised { assert_select "div", "bar" } - assert_nothing_raised { assert_select "div", /\w*/ } - assert_nothing_raised { assert_select "div", :text => /\w*/, :count=>2 } - assert_raise(Assertion) { assert_select "div", :text=>"foo", :count=>2 } - assert_nothing_raised { assert_select "div", :html=>"<span>bar</span>" } - assert_nothing_raised { assert_select "div", :html=>"<span>bar</span>" } - assert_nothing_raised { assert_select "div", :html=>/\w*/ } - assert_nothing_raised { assert_select "div", :html=>/\w*/, :count=>2 } - assert_raise(Assertion) { assert_select "div", :html=>"<span>foo</span>", :count=>2 } - end - end - - def test_elect_with_xml_namespace_attributes - render_html %Q{<link xlink:href="http://nowhere.com"></link>} - assert_nothing_raised { assert_select "link[xlink:href=http://nowhere.com]" } - end - - # - # Test css_select. - # - - def test_css_select - render_html %Q{<div id="1"></div><div id="2"></div>} - assert_equal 2, css_select("div").size - assert_equal 0, css_select("p").size - end - - def test_nested_css_select - render_html %Q{<div id="1">foo</div><div id="2">foo</div>} - assert_select "div#?", /\d+/ do |elements| - assert_equal 1, css_select(elements[0], "div").size - assert_equal 1, css_select(elements[1], "div").size - end - assert_select "div" do - assert_equal 2, css_select("div").size - css_select("div").each do |element| - # Testing as a group is one thing - assert !css_select("#1,#2").empty? - # Testing individually is another - assert !css_select("#1").empty? - assert !css_select("#2").empty? - end - end - end - - def test_feed_item_encoded - render_xml <<-EOF -<rss version="2.0"> - <channel> - <item> - <description> - <![CDATA[ - <p>Test 1</p> - ]]> - </description> - </item> - <item> - <description> - <![CDATA[ - <p>Test 2</p> - ]]> - </description> - </item> - </channel> -</rss> -EOF - assert_select "channel item description" do - # Test element regardless of wrapper. - assert_select_encoded do - assert_select "p", :count=>2, :text=>/Test/ - end - # Test through encoded wrapper. - assert_select_encoded do - assert_select "encoded p", :count=>2, :text=>/Test/ - end - # Use :root instead (recommended) - assert_select_encoded do - assert_select ":root p", :count=>2, :text=>/Test/ - end - # Test individually. - assert_select "description" do |elements| - assert_select_encoded elements[0] do - assert_select "p", "Test 1" - end - assert_select_encoded elements[1] do - assert_select "p", "Test 2" - end - end - end - - # Test that we only un-encode element itself. - assert_select "channel item" do - assert_select_encoded do - assert_select "p", 0 - end - end - end - - # - # Test assert_select_email - # - - def test_assert_select_email - assert_raise(Assertion) { assert_select_email {} } - AssertSelectMailer.test("<div><p>foo</p><p>bar</p></div>").deliver - assert_select_email do - assert_select "div:root" do - assert_select "p:first-child", "foo" - assert_select "p:last-child", "bar" - end - end - end - - def test_assert_select_email_multipart - AssertMultipartSelectMailer.test(:html => "<div><p>foo</p><p>bar</p></div>", :text => 'foo bar').deliver - assert_select_email do - assert_select "div:root" do - assert_select "p:first-child", "foo" - assert_select "p:last-child", "bar" - end - end - end - - protected - def render_html(html) - @controller.response_with = html - get :html - end - - def render_xml(xml) - @controller.response_with = xml - get :xml - end -end diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb index 9b42e7631f..950788743e 100644 --- a/actionpack/test/controller/base_test.rb +++ b/actionpack/test/controller/base_test.rb @@ -61,11 +61,14 @@ class UrlOptionsController < ActionController::Base end end -class RecordIdentifierController < ActionController::Base +class RecordIdentifierIncludedController < ActionController::Base + include ActionView::RecordIdentifier end -class RecordIdentifierWithoutDeprecationController < ActionController::Base - include ActionView::RecordIdentifier +class ActionMissingController < ActionController::Base + def action_missing(action) + render :text => "Response for #{action}" + end end class ControllerClassTests < ActiveSupport::TestCase @@ -82,43 +85,20 @@ class ControllerClassTests < ActiveSupport::TestCase assert_equal 'contained_empty', Submodule::ContainedEmptyController.controller_name end - def test_record_identifier - assert_respond_to RecordIdentifierController.new, :dom_id - assert_respond_to RecordIdentifierController.new, :dom_class - end - - def test_record_identifier_is_deprecated - record = Comment.new - record.save - - dom_id = nil - assert_deprecated 'dom_id method will no longer' do - dom_id = RecordIdentifierController.new.dom_id(record) - end - - assert_equal 'comment_1', dom_id - - dom_class = nil - assert_deprecated 'dom_class method will no longer' do - dom_class = RecordIdentifierController.new.dom_class(record) - end - assert_equal 'comment', dom_class - end - def test_no_deprecation_when_action_view_record_identifier_is_included record = Comment.new record.save dom_id = nil assert_not_deprecated do - dom_id = RecordIdentifierWithoutDeprecationController.new.dom_id(record) + dom_id = RecordIdentifierIncludedController.new.dom_id(record) end assert_equal 'comment_1', dom_id dom_class = nil assert_not_deprecated do - dom_class = RecordIdentifierWithoutDeprecationController.new.dom_class(record) + dom_class = RecordIdentifierIncludedController.new.dom_class(record) end assert_equal 'comment', dom_class end @@ -178,7 +158,7 @@ class PerformActionTest < ActionController::TestCase exception = assert_raise AbstractController::ActionNotFound do get :non_existent end - assert_equal exception.message, "The action 'non_existent' could not be found for EmptyController" + assert_equal "The action 'non_existent' could not be found for EmptyController", exception.message end def test_get_on_hidden_should_fail @@ -186,6 +166,12 @@ class PerformActionTest < ActionController::TestCase assert_raise(AbstractController::ActionNotFound) { get :hidden_action } assert_raise(AbstractController::ActionNotFound) { get :another_hidden_action } end + + def test_action_missing_should_work + use_controller ActionMissingController + get :arbitrary_action + assert_equal "Response for arbitrary_action", @response.body + end end class UrlOptionsTest < ActionController::TestCase diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 2428cd7433..c0e6a2ebd1 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -1,6 +1,5 @@ require 'fileutils' require 'abstract_unit' -require 'active_record_unit' CACHE_DIR = 'test_cache' # Don't change '/../temp/' cavalierly or you might hose something you don't want hosed @@ -21,7 +20,7 @@ class FragmentCachingMetalTest < ActionController::TestCase @controller = FragmentCachingMetalTestController.new @controller.perform_caching = true @controller.cache_store = @store - @params = { controller: 'posts', action: 'index'} + @params = { controller: 'posts', action: 'index' } @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new @controller.params = @params @@ -41,7 +40,7 @@ class CachingController < ActionController::Base end class FragmentCachingTestController < CachingController - def some_action; end; + def some_action; end end class FragmentCachingTest < ActionController::TestCase @@ -165,6 +164,13 @@ class FunctionalCachingController < CachingController end end + def formatted_fragment_cached_with_variant + respond_to do |format| + format.html.phone + format.html + end + end + def fragment_cached_without_digest end end @@ -191,7 +197,7 @@ CACHED assert_equal expected_body, @response.body assert_equal "This bit's fragment cached", - @store.read("views/test.host/functional_caching/fragment_cached/#{template_digest("functional_caching/fragment_cached", "html")}") + @store.read("views/test.host/functional_caching/fragment_cached/#{template_digest("functional_caching/fragment_cached")}") end def test_fragment_caching_in_partials @@ -200,7 +206,7 @@ CACHED assert_match(/Old fragment caching in a partial/, @response.body) assert_match("Old fragment caching in a partial", - @store.read("views/test.host/functional_caching/html_fragment_cached_with_partial/#{template_digest("functional_caching/_partial", "html")}")) + @store.read("views/test.host/functional_caching/html_fragment_cached_with_partial/#{template_digest("functional_caching/_partial")}")) end def test_skipping_fragment_cache_digesting @@ -218,7 +224,23 @@ CACHED assert_match(/Some inline content/, @response.body) assert_match(/Some cached content/, @response.body) assert_match("Some cached content", - @store.read("views/test.host/functional_caching/inline_fragment_cached/#{template_digest("functional_caching/inline_fragment_cached", "html")}")) + @store.read("views/test.host/functional_caching/inline_fragment_cached/#{template_digest("functional_caching/inline_fragment_cached")}")) + end + + def test_fragment_cache_instrumentation + payload = nil + + subscriber = proc do |*args| + event = ActiveSupport::Notifications::Event.new(*args) + payload = event.payload + end + + ActiveSupport::Notifications.subscribed(subscriber, "read_fragment.action_controller") do + get :inline_fragment_cached + end + + assert_equal "functional_caching", payload[:controller] + assert_equal "inline_fragment_cached", payload[:action] end def test_html_formatted_fragment_caching @@ -229,7 +251,7 @@ CACHED assert_equal expected_body, @response.body assert_equal "<p>ERB</p>", - @store.read("views/test.host/functional_caching/formatted_fragment_cached/#{template_digest("functional_caching/formatted_fragment_cached", "html")}") + @store.read("views/test.host/functional_caching/formatted_fragment_cached/#{template_digest("functional_caching/formatted_fragment_cached")}") end def test_xml_formatted_fragment_caching @@ -240,12 +262,26 @@ CACHED assert_equal expected_body, @response.body assert_equal " <p>Builder</p>\n", - @store.read("views/test.host/functional_caching/formatted_fragment_cached/#{template_digest("functional_caching/formatted_fragment_cached", "xml")}") + @store.read("views/test.host/functional_caching/formatted_fragment_cached/#{template_digest("functional_caching/formatted_fragment_cached")}") + end + + + def test_fragment_caching_with_variant + @request.variant = :phone + + get :formatted_fragment_cached_with_variant, :format => "html" + assert_response :success + expected_body = "<body>\n<p>PHONE</p>\n</body>\n" + + assert_equal expected_body, @response.body + + assert_equal "<p>PHONE</p>", + @store.read("views/test.host/functional_caching/formatted_fragment_cached_with_variant/#{template_digest("functional_caching/formatted_fragment_cached_with_variant")}") end private - def template_digest(name, format) - ActionView::Digestor.digest(name, format, @controller.lookup_context) + def template_digest(name) + ActionView::Digestor.digest(name: name, finder: @controller.lookup_context) end end @@ -296,17 +332,20 @@ class CacheHelperOutputBufferTest < ActionController::TestCase end end -class DeprecatedPageCacheExtensionTest < ActiveSupport::TestCase - def test_page_cache_extension_binds_default_static_extension - deprecation_behavior = ActiveSupport::Deprecation.behavior - ActiveSupport::Deprecation.behavior = :silence - old_extension = ActionController::Base.default_static_extension +class ViewCacheDependencyTest < ActionController::TestCase + class NoDependenciesController < ActionController::Base + end + + class HasDependenciesController < ActionController::Base + view_cache_dependency { "trombone" } + view_cache_dependency { "flute" } + end - ActionController::Base.page_cache_extension = '.rss' + def test_view_cache_dependencies_are_empty_by_default + assert NoDependenciesController.new.view_cache_dependencies.empty? + end - assert_equal '.rss', ActionController::Base.default_static_extension - ensure - ActiveSupport::Deprecation.behavior = deprecation_behavior - ActionController::Base.default_static_extension = old_extension + def test_view_cache_dependencies_are_listed_in_declaration_order + assert_equal %w(trombone flute), HasDependenciesController.new.view_cache_dependencies end end diff --git a/actionpack/test/controller/capture_test.rb b/actionpack/test/controller/capture_test.rb deleted file mode 100644 index 72263156d9..0000000000 --- a/actionpack/test/controller/capture_test.rb +++ /dev/null @@ -1,79 +0,0 @@ -require 'abstract_unit' -require 'active_support/logger' - -class CaptureController < ActionController::Base - def self.controller_name; "test"; end - def self.controller_path; "test"; end - - def content_for - @title = nil - render :layout => "talk_from_action" - end - - def content_for_with_parameter - @title = nil - render :layout => "talk_from_action" - end - - def content_for_concatenated - @title = nil - render :layout => "talk_from_action" - end - - def non_erb_block_content_for - @title = nil - render :layout => "talk_from_action" - end - - def proper_block_detection - @todo = "some todo" - end -end - -class CaptureTest < ActionController::TestCase - tests CaptureController - - def setup - super - # enable a logger so that (e.g.) the benchmarking stuff runs, so we can get - # a more accurate simulation of what happens in "real life". - @controller.logger = ActiveSupport::Logger.new(nil) - - @request.host = "www.nextangle.com" - end - - def test_simple_capture - get :capturing - assert_equal "Dreamy days", @response.body.strip - end - - def test_content_for - get :content_for - assert_equal expected_content_for_output, @response.body - end - - def test_should_concatentate_content_for - get :content_for_concatenated - assert_equal expected_content_for_output, @response.body - end - - def test_should_set_content_for_with_parameter - get :content_for_with_parameter - assert_equal expected_content_for_output, @response.body - end - - def test_non_erb_block_content_for - get :non_erb_block_content_for - assert_equal expected_content_for_output, @response.body - end - - def test_proper_block_detection - get :proper_block_detection - assert_equal "some todo", @response.body - end - - private - def expected_content_for_output - "<title>Putting stuff in the title!</title>\nGreat stuff!" - end -end diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb index 03d5d27cf4..89667df3a4 100644 --- a/actionpack/test/controller/content_type_test.rb +++ b/actionpack/test/controller/content_type_test.rb @@ -68,12 +68,11 @@ class ContentTypeTest < ActionController::TestCase end def test_render_changed_charset_default - ActionDispatch::Response.default_charset = "utf-16" - get :render_defaults - assert_equal "utf-16", @response.charset - assert_equal Mime::HTML, @response.content_type - ensure - ActionDispatch::Response.default_charset = "utf-8" + with_default_charset "utf-16" do + get :render_defaults + assert_equal "utf-16", @response.charset + assert_equal Mime::HTML, @response.content_type + end end # :ported: @@ -105,12 +104,11 @@ class ContentTypeTest < ActionController::TestCase end def test_nil_default_for_erb - ActionDispatch::Response.default_charset = nil - get :render_default_for_erb - assert_equal Mime::HTML, @response.content_type - assert_nil @response.charset, @response.headers.inspect - ensure - ActionDispatch::Response.default_charset = "utf-8" + with_default_charset nil do + get :render_default_for_erb + assert_equal Mime::HTML, @response.content_type + assert_nil @response.charset, @response.headers.inspect + end end def test_default_for_erb @@ -130,6 +128,16 @@ class ContentTypeTest < ActionController::TestCase assert_equal Mime::HTML, @response.content_type assert_equal "utf-8", @response.charset end + + private + + def with_default_charset(charset) + old_default_charset = ActionDispatch::Response.default_charset + ActionDispatch::Response.default_charset = charset + yield + ensure + ActionDispatch::Response.default_charset = old_default_charset + end end class AcceptBasedContentTypeTest < ActionController::TestCase diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb index 1c59dd5953..2e08a6af9f 100644 --- a/actionpack/test/controller/filters_test.rb +++ b/actionpack/test/controller/filters_test.rb @@ -2,15 +2,15 @@ require 'abstract_unit' class ActionController::Base class << self - %w(append_around_filter prepend_after_filter prepend_around_filter prepend_before_filter skip_after_filter skip_before_filter skip_filter).each do |pending| + %w(append_around_action prepend_after_action prepend_around_action prepend_before_action skip_after_action skip_before_action skip_action_callback).each do |pending| define_method(pending) do |*args| $stderr.puts "#{pending} unimplemented: #{args.inspect}" end unless method_defined?(pending) end - def before_filters + def before_actions filters = _process_action_callbacks.select { |c| c.kind == :before } - filters.map! { |c| c.instance_variable_get(:@raw_filter) } + filters.map!(&:raw_filter) end end @@ -28,8 +28,8 @@ end class FilterTest < ActionController::TestCase class TestController < ActionController::Base - before_filter :ensure_login - after_filter :clean_up + before_action :ensure_login + after_action :clean_up def show render :inline => "ran action" @@ -42,13 +42,13 @@ class FilterTest < ActionController::TestCase end def clean_up - @ran_after_filter ||= [] - @ran_after_filter << "clean_up" + @ran_after_action ||= [] + @ran_after_action << "clean_up" end end class ChangingTheRequirementsController < TestController - before_filter :ensure_login, :except => [:go_wild] + before_action :ensure_login, :except => [:go_wild] def go_wild render :text => "gobble" @@ -56,9 +56,9 @@ class FilterTest < ActionController::TestCase end class TestMultipleFiltersController < ActionController::Base - before_filter :try_1 - before_filter :try_2 - before_filter :try_3 + before_action :try_1 + before_action :try_2 + before_action :try_3 (1..3).each do |i| define_method "fail_#{i}" do @@ -78,8 +78,8 @@ class FilterTest < ActionController::TestCase end class RenderingController < ActionController::Base - before_filter :before_filter_rendering - after_filter :unreached_after_filter + before_action :before_action_rendering + after_action :unreached_after_action def show @ran_action = true @@ -87,29 +87,29 @@ class FilterTest < ActionController::TestCase end private - def before_filter_rendering + def before_action_rendering @ran_filter ||= [] - @ran_filter << "before_filter_rendering" + @ran_filter << "before_action_rendering" render :inline => "something else" end - def unreached_after_filter - @ran_filter << "unreached_after_filter_after_render" + def unreached_after_action + @ran_filter << "unreached_after_action_after_render" end end - class RenderingForPrependAfterFilterController < RenderingController - prepend_after_filter :unreached_prepend_after_filter + class RenderingForPrependAfterActionController < RenderingController + prepend_after_action :unreached_prepend_after_action private - def unreached_prepend_after_filter - @ran_filter << "unreached_preprend_after_filter_after_render" + def unreached_prepend_after_action + @ran_filter << "unreached_preprend_after_action_after_render" end end - class BeforeFilterRedirectionController < ActionController::Base - before_filter :before_filter_redirects - after_filter :unreached_after_filter + class BeforeActionRedirectionController < ActionController::Base + before_action :before_action_redirects + after_action :unreached_after_action def show @ran_action = true @@ -122,23 +122,23 @@ class FilterTest < ActionController::TestCase end private - def before_filter_redirects + def before_action_redirects @ran_filter ||= [] - @ran_filter << "before_filter_redirects" + @ran_filter << "before_action_redirects" redirect_to(:action => 'target_of_redirection') end - def unreached_after_filter - @ran_filter << "unreached_after_filter_after_redirection" + def unreached_after_action + @ran_filter << "unreached_after_action_after_redirection" end end - class BeforeFilterRedirectionForPrependAfterFilterController < BeforeFilterRedirectionController - prepend_after_filter :unreached_prepend_after_filter_after_redirection + class BeforeActionRedirectionForPrependAfterActionController < BeforeActionRedirectionController + prepend_after_action :unreached_prepend_after_action_after_redirection private - def unreached_prepend_after_filter_after_redirection - @ran_filter << "unreached_prepend_after_filter_after_redirection" + def unreached_prepend_after_action_after_redirection + @ran_filter << "unreached_prepend_after_action_after_redirection" end end @@ -151,8 +151,8 @@ class FilterTest < ActionController::TestCase render :inline => "ran action" end - def show_without_filter - render :inline => "ran action without filter" + def show_without_action + render :inline => "ran action without action" end private @@ -168,54 +168,70 @@ class FilterTest < ActionController::TestCase end class ConditionalCollectionFilterController < ConditionalFilterController - before_filter :ensure_login, :except => [ :show_without_filter, :another_action ] + before_action :ensure_login, :except => [ :show_without_action, :another_action ] end class OnlyConditionSymController < ConditionalFilterController - before_filter :ensure_login, :only => :show + before_action :ensure_login, :only => :show end class ExceptConditionSymController < ConditionalFilterController - before_filter :ensure_login, :except => :show_without_filter + before_action :ensure_login, :except => :show_without_action end class BeforeAndAfterConditionController < ConditionalFilterController - before_filter :ensure_login, :only => :show - after_filter :clean_up_tmp, :only => :show + before_action :ensure_login, :only => :show + after_action :clean_up_tmp, :only => :show end class OnlyConditionProcController < ConditionalFilterController - before_filter(:only => :show) {|c| c.instance_variable_set(:"@ran_proc_filter", true) } + before_action(:only => :show) {|c| c.instance_variable_set(:"@ran_proc_action", true) } end class ExceptConditionProcController < ConditionalFilterController - before_filter(:except => :show_without_filter) {|c| c.instance_variable_set(:"@ran_proc_filter", true) } + before_action(:except => :show_without_action) {|c| c.instance_variable_set(:"@ran_proc_action", true) } end class ConditionalClassFilter - def self.before(controller) controller.instance_variable_set(:"@ran_class_filter", true) end + def self.before(controller) controller.instance_variable_set(:"@ran_class_action", true) end end class OnlyConditionClassController < ConditionalFilterController - before_filter ConditionalClassFilter, :only => :show + before_action ConditionalClassFilter, :only => :show end class ExceptConditionClassController < ConditionalFilterController - before_filter ConditionalClassFilter, :except => :show_without_filter + before_action ConditionalClassFilter, :except => :show_without_action end class AnomolousYetValidConditionController < ConditionalFilterController - before_filter(ConditionalClassFilter, :ensure_login, Proc.new {|c| c.instance_variable_set(:"@ran_proc_filter1", true)}, :except => :show_without_filter) { |c| c.instance_variable_set(:"@ran_proc_filter2", true)} + before_action(ConditionalClassFilter, :ensure_login, Proc.new {|c| c.instance_variable_set(:"@ran_proc_action1", true)}, :except => :show_without_action) { |c| c.instance_variable_set(:"@ran_proc_action2", true)} + end + + class OnlyConditionalOptionsFilter < ConditionalFilterController + before_action :ensure_login, :only => :index, :if => Proc.new {|c| c.instance_variable_set(:"@ran_conditional_index_proc", true) } end class ConditionalOptionsFilter < ConditionalFilterController - before_filter :ensure_login, :if => Proc.new { |c| true } - before_filter :clean_up_tmp, :if => Proc.new { |c| false } + before_action :ensure_login, :if => Proc.new { |c| true } + before_action :clean_up_tmp, :if => Proc.new { |c| false } + end + + class ConditionalOptionsSkipFilter < ConditionalFilterController + before_action :ensure_login + before_action :clean_up_tmp + + skip_before_action :ensure_login, if: -> { false } + skip_before_action :clean_up_tmp, if: -> { true } + end + + class ClassController < ConditionalFilterController + before_action ConditionalClassFilter end class PrependingController < TestController - prepend_before_filter :wonderful_life - # skip_before_filter :fire_flash + prepend_before_action :wonderful_life + # skip_before_action :fire_flash private def wonderful_life @@ -225,8 +241,8 @@ class FilterTest < ActionController::TestCase end class SkippingAndLimitedController < TestController - skip_before_filter :ensure_login - before_filter :ensure_login, :only => :index + skip_before_action :ensure_login + before_action :ensure_login, :only => :index def index render :text => 'ok' @@ -238,9 +254,9 @@ class FilterTest < ActionController::TestCase end class SkippingAndReorderingController < TestController - skip_before_filter :ensure_login - before_filter :find_record - before_filter :ensure_login + skip_before_action :ensure_login + before_action :find_record + before_action :ensure_login def index render :text => 'ok' @@ -254,10 +270,10 @@ class FilterTest < ActionController::TestCase end class ConditionalSkippingController < TestController - skip_before_filter :ensure_login, :only => [ :login ] - skip_after_filter :clean_up, :only => [ :login ] + skip_before_action :ensure_login, :only => [ :login ] + skip_after_action :clean_up, :only => [ :login ] - before_filter :find_user, :only => [ :change_password ] + before_action :find_user, :only => [ :change_password ] def login render :inline => "ran action" @@ -275,8 +291,8 @@ class FilterTest < ActionController::TestCase end class ConditionalParentOfConditionalSkippingController < ConditionalFilterController - before_filter :conditional_in_parent_before, :only => [:show, :another_action] - after_filter :conditional_in_parent_after, :only => [:show, :another_action] + before_action :conditional_in_parent_before, :only => [:show, :another_action] + after_action :conditional_in_parent_after, :only => [:show, :another_action] private @@ -292,20 +308,20 @@ class FilterTest < ActionController::TestCase end class ChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController - skip_before_filter :conditional_in_parent_before, :only => :another_action - skip_after_filter :conditional_in_parent_after, :only => :another_action + skip_before_action :conditional_in_parent_before, :only => :another_action + skip_after_action :conditional_in_parent_after, :only => :another_action end class AnotherChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController - skip_before_filter :conditional_in_parent_before, :only => :show + skip_before_action :conditional_in_parent_before, :only => :show end class ProcController < PrependingController - before_filter(proc { |c| c.instance_variable_set(:"@ran_proc_filter", true) }) + before_action(proc { |c| c.instance_variable_set(:"@ran_proc_action", true) }) end class ImplicitProcController < PrependingController - before_filter { |c| c.instance_variable_set(:"@ran_proc_filter", true) } + before_action { |c| c.instance_variable_set(:"@ran_proc_action", true) } end class AuditFilter @@ -351,7 +367,7 @@ class FilterTest < ActionController::TestCase end class AuditController < ActionController::Base - before_filter(AuditFilter) + before_action(AuditFilter) def show render :text => "hello" @@ -359,14 +375,14 @@ class FilterTest < ActionController::TestCase end class AroundFilterController < PrependingController - around_filter AroundFilter.new + around_action AroundFilter.new end class BeforeAfterClassFilterController < PrependingController begin filter = AroundFilter.new - before_filter filter - after_filter filter + before_action filter + after_action filter end end @@ -378,18 +394,18 @@ class FilterTest < ActionController::TestCase super() end - before_filter { |c| c.class.execution_log << " before procfilter " } - prepend_around_filter AroundFilter.new + before_action { |c| c.class.execution_log << " before procfilter " } + prepend_around_action AroundFilter.new - after_filter { |c| c.class.execution_log << " after procfilter " } - append_around_filter AppendedAroundFilter.new + after_action { |c| c.class.execution_log << " after procfilter " } + append_around_action AppendedAroundFilter.new end class MixedSpecializationController < ActionController::Base class OutOfOrder < StandardError; end - before_filter :first - before_filter :second, :only => :foo + before_action :first + before_action :second, :only => :foo def foo render :text => 'foo' @@ -410,7 +426,7 @@ class FilterTest < ActionController::TestCase end class DynamicDispatchController < ActionController::Base - before_filter :choose + before_action :choose %w(foo bar baz).each do |action| define_method(action) { render :text => action } @@ -423,9 +439,9 @@ class FilterTest < ActionController::TestCase end class PrependingBeforeAndAfterController < ActionController::Base - prepend_before_filter :before_all - prepend_after_filter :after_all - before_filter :between_before_all_and_after_all + prepend_before_action :before_all + prepend_after_action :after_all + before_action :between_before_all_and_after_all def before_all @ran_filter ||= [] @@ -450,16 +466,14 @@ class FilterTest < ActionController::TestCase class RescuingAroundFilterWithBlock def around(controller) - begin - yield - rescue ErrorToRescue => ex - controller.__send__ :render, :text => "I rescued this: #{ex.inspect}" - end + yield + rescue ErrorToRescue => ex + controller.__send__ :render, :text => "I rescued this: #{ex.inspect}" end end class RescuedController < ActionController::Base - around_filter RescuingAroundFilterWithBlock.new + around_action RescuingAroundFilterWithBlock.new def show raise ErrorToRescue.new("Something made the bad noise.") @@ -468,10 +482,10 @@ class FilterTest < ActionController::TestCase class NonYieldingAroundFilterController < ActionController::Base - before_filter :filter_one - around_filter :non_yielding_filter - before_filter :filter_two - after_filter :filter_three + before_action :filter_one + around_action :non_yielding_action + before_action :action_two + after_action :action_three def index render :inline => "index" @@ -484,24 +498,23 @@ class FilterTest < ActionController::TestCase @filters << "filter_one" end - def filter_two - @filters << "filter_two" + def action_two + @filters << "action_two" end - def non_yielding_filter + def non_yielding_action @filters << "it didn't yield" - @filter_return_value end - def filter_three - @filters << "filter_three" + def action_three + @filters << "action_three" end end class ImplicitActionsController < ActionController::Base - before_filter :find_only, :only => :edit - before_filter :find_except, :except => :edit + before_action :find_only, :only => :edit + before_action :find_except, :except => :edit private @@ -514,80 +527,63 @@ class FilterTest < ActionController::TestCase end end - def test_non_yielding_around_filters_not_returning_false_do_not_raise + def test_non_yielding_around_actions_do_not_raise controller = NonYieldingAroundFilterController.new - controller.instance_variable_set "@filter_return_value", true assert_nothing_raised do test_process(controller, "index") end end - def test_non_yielding_around_filters_returning_false_do_not_raise - controller = NonYieldingAroundFilterController.new - controller.instance_variable_set "@filter_return_value", false - assert_nothing_raised do - test_process(controller, "index") - end - end - - def test_after_filters_are_not_run_if_around_filter_returns_false - controller = NonYieldingAroundFilterController.new - controller.instance_variable_set "@filter_return_value", false - test_process(controller, "index") - assert_equal ["filter_one", "it didn't yield"], controller.assigns['filters'] - end - - def test_after_filters_are_not_run_if_around_filter_does_not_yield + def test_after_actions_are_not_run_if_around_action_does_not_yield controller = NonYieldingAroundFilterController.new - controller.instance_variable_set "@filter_return_value", true test_process(controller, "index") assert_equal ["filter_one", "it didn't yield"], controller.assigns['filters'] end - def test_added_filter_to_inheritance_graph - assert_equal [ :ensure_login ], TestController.before_filters + def test_added_action_to_inheritance_graph + assert_equal [ :ensure_login ], TestController.before_actions end def test_base_class_in_isolation - assert_equal [ ], ActionController::Base.before_filters + assert_equal [ ], ActionController::Base.before_actions end - def test_prepending_filter - assert_equal [ :wonderful_life, :ensure_login ], PrependingController.before_filters + def test_prepending_action + assert_equal [ :wonderful_life, :ensure_login ], PrependingController.before_actions end - def test_running_filters + def test_running_actions test_process(PrependingController) assert_equal %w( wonderful_life ensure_login ), assigns["ran_filter"] end - def test_running_filters_with_proc + def test_running_actions_with_proc test_process(ProcController) - assert assigns["ran_proc_filter"] + assert assigns["ran_proc_action"] end - def test_running_filters_with_implicit_proc + def test_running_actions_with_implicit_proc test_process(ImplicitProcController) - assert assigns["ran_proc_filter"] + assert assigns["ran_proc_action"] end - def test_running_filters_with_class + def test_running_actions_with_class test_process(AuditController) assert assigns["was_audited"] end - def test_running_anomolous_yet_valid_condition_filters + def test_running_anomolous_yet_valid_condition_actions test_process(AnomolousYetValidConditionController) assert_equal %w( ensure_login ), assigns["ran_filter"] - assert assigns["ran_class_filter"] - assert assigns["ran_proc_filter1"] - assert assigns["ran_proc_filter2"] + assert assigns["ran_class_action"] + assert assigns["ran_proc_action1"] + assert assigns["ran_proc_action2"] - test_process(AnomolousYetValidConditionController, "show_without_filter") + test_process(AnomolousYetValidConditionController, "show_without_action") assert_nil assigns["ran_filter"] - assert !assigns["ran_class_filter"] - assert !assigns["ran_proc_filter1"] - assert !assigns["ran_proc_filter2"] + assert !assigns["ran_class_action"] + assert !assigns["ran_proc_action1"] + assert !assigns["ran_proc_action2"] end def test_running_conditional_options @@ -595,113 +591,135 @@ class FilterTest < ActionController::TestCase assert_equal %w( ensure_login ), assigns["ran_filter"] end - def test_running_collection_condition_filters + def test_running_conditional_skip_options + test_process(ConditionalOptionsSkipFilter) + assert_equal %w( ensure_login ), assigns["ran_filter"] + end + + def test_skipping_class_actions + test_process(ClassController) + assert_equal true, assigns["ran_class_action"] + + skipping_class_controller = Class.new(ClassController) do + skip_before_action ConditionalClassFilter + end + + test_process(skipping_class_controller) + assert_nil assigns['ran_class_action'] + end + + def test_running_collection_condition_actions test_process(ConditionalCollectionFilterController) assert_equal %w( ensure_login ), assigns["ran_filter"] - test_process(ConditionalCollectionFilterController, "show_without_filter") + test_process(ConditionalCollectionFilterController, "show_without_action") assert_nil assigns["ran_filter"] test_process(ConditionalCollectionFilterController, "another_action") assert_nil assigns["ran_filter"] end - def test_running_only_condition_filters + def test_running_only_condition_actions test_process(OnlyConditionSymController) assert_equal %w( ensure_login ), assigns["ran_filter"] - test_process(OnlyConditionSymController, "show_without_filter") + test_process(OnlyConditionSymController, "show_without_action") assert_nil assigns["ran_filter"] test_process(OnlyConditionProcController) - assert assigns["ran_proc_filter"] - test_process(OnlyConditionProcController, "show_without_filter") - assert !assigns["ran_proc_filter"] + assert assigns["ran_proc_action"] + test_process(OnlyConditionProcController, "show_without_action") + assert !assigns["ran_proc_action"] test_process(OnlyConditionClassController) - assert assigns["ran_class_filter"] - test_process(OnlyConditionClassController, "show_without_filter") - assert !assigns["ran_class_filter"] + assert assigns["ran_class_action"] + test_process(OnlyConditionClassController, "show_without_action") + assert !assigns["ran_class_action"] end - def test_running_except_condition_filters + def test_running_except_condition_actions test_process(ExceptConditionSymController) assert_equal %w( ensure_login ), assigns["ran_filter"] - test_process(ExceptConditionSymController, "show_without_filter") + test_process(ExceptConditionSymController, "show_without_action") assert_nil assigns["ran_filter"] test_process(ExceptConditionProcController) - assert assigns["ran_proc_filter"] - test_process(ExceptConditionProcController, "show_without_filter") - assert !assigns["ran_proc_filter"] + assert assigns["ran_proc_action"] + test_process(ExceptConditionProcController, "show_without_action") + assert !assigns["ran_proc_action"] test_process(ExceptConditionClassController) - assert assigns["ran_class_filter"] - test_process(ExceptConditionClassController, "show_without_filter") - assert !assigns["ran_class_filter"] + assert assigns["ran_class_action"] + test_process(ExceptConditionClassController, "show_without_action") + assert !assigns["ran_class_action"] + end + + def test_running_only_condition_and_conditional_options + test_process(OnlyConditionalOptionsFilter, "show") + assert_not assigns["ran_conditional_index_proc"] end - def test_running_before_and_after_condition_filters + def test_running_before_and_after_condition_actions test_process(BeforeAndAfterConditionController) assert_equal %w( ensure_login clean_up_tmp), assigns["ran_filter"] - test_process(BeforeAndAfterConditionController, "show_without_filter") + test_process(BeforeAndAfterConditionController, "show_without_action") assert_nil assigns["ran_filter"] end - def test_around_filter + def test_around_action test_process(AroundFilterController) assert assigns["before_ran"] assert assigns["after_ran"] end - def test_before_after_class_filter + def test_before_after_class_action test_process(BeforeAfterClassFilterController) assert assigns["before_ran"] assert assigns["after_ran"] end - def test_having_properties_in_around_filter + def test_having_properties_in_around_action test_process(AroundFilterController) assert_equal "before and after", assigns["execution_log"] end - def test_prepending_and_appending_around_filter + def test_prepending_and_appending_around_action test_process(MixedFilterController) assert_equal " before aroundfilter before procfilter before appended aroundfilter " + " after appended aroundfilter after procfilter after aroundfilter ", MixedFilterController.execution_log end - def test_rendering_breaks_filtering_chain + def test_rendering_breaks_actioning_chain response = test_process(RenderingController) assert_equal "something else", response.body assert !assigns["ran_action"] end - def test_before_filter_rendering_breaks_filtering_chain_for_after_filter + def test_before_action_rendering_breaks_actioning_chain_for_after_action test_process(RenderingController) - assert_equal %w( before_filter_rendering ), assigns["ran_filter"] + assert_equal %w( before_action_rendering ), assigns["ran_filter"] assert !assigns["ran_action"] end - def test_before_filter_redirects_breaks_filtering_chain_for_after_filter - test_process(BeforeFilterRedirectionController) + def test_before_action_redirects_breaks_actioning_chain_for_after_action + test_process(BeforeActionRedirectionController) assert_response :redirect - assert_equal "http://test.host/filter_test/before_filter_redirection/target_of_redirection", redirect_to_url - assert_equal %w( before_filter_redirects ), assigns["ran_filter"] + assert_equal "http://test.host/filter_test/before_action_redirection/target_of_redirection", redirect_to_url + assert_equal %w( before_action_redirects ), assigns["ran_filter"] end - def test_before_filter_rendering_breaks_filtering_chain_for_preprend_after_filter - test_process(RenderingForPrependAfterFilterController) - assert_equal %w( before_filter_rendering ), assigns["ran_filter"] + def test_before_action_rendering_breaks_actioning_chain_for_preprend_after_action + test_process(RenderingForPrependAfterActionController) + assert_equal %w( before_action_rendering ), assigns["ran_filter"] assert !assigns["ran_action"] end - def test_before_filter_redirects_breaks_filtering_chain_for_preprend_after_filter - test_process(BeforeFilterRedirectionForPrependAfterFilterController) + def test_before_action_redirects_breaks_actioning_chain_for_preprend_after_action + test_process(BeforeActionRedirectionForPrependAfterActionController) assert_response :redirect - assert_equal "http://test.host/filter_test/before_filter_redirection_for_prepend_after_filter/target_of_redirection", redirect_to_url - assert_equal %w( before_filter_redirects ), assigns["ran_filter"] + assert_equal "http://test.host/filter_test/before_action_redirection_for_prepend_after_action/target_of_redirection", redirect_to_url + assert_equal %w( before_action_redirects ), assigns["ran_filter"] end - def test_filters_with_mixed_specialization_run_in_order + def test_actions_with_mixed_specialization_run_in_order assert_nothing_raised do response = test_process(MixedSpecializationController, 'bar') assert_equal 'bar', response.body @@ -722,7 +740,7 @@ class FilterTest < ActionController::TestCase end end - def test_running_prepended_before_and_after_filter + def test_running_prepended_before_and_after_action test_process(PrependingBeforeAndAfterController) assert_equal %w( before_all between_before_all_and_after_all after_all ), assigns["ran_filter"] end @@ -739,26 +757,26 @@ class FilterTest < ActionController::TestCase assert_equal %w( find_record ensure_login ), assigns["ran_filter"] end - def test_conditional_skipping_of_filters + def test_conditional_skipping_of_actions test_process(ConditionalSkippingController, "login") assert_nil assigns["ran_filter"] test_process(ConditionalSkippingController, "change_password") assert_equal %w( ensure_login find_user ), assigns["ran_filter"] test_process(ConditionalSkippingController, "login") - assert !@controller.instance_variable_defined?("@ran_after_filter") + assert !@controller.instance_variable_defined?("@ran_after_action") test_process(ConditionalSkippingController, "change_password") - assert_equal %w( clean_up ), @controller.instance_variable_get("@ran_after_filter") + assert_equal %w( clean_up ), @controller.instance_variable_get("@ran_after_action") end - def test_conditional_skipping_of_filters_when_parent_filter_is_also_conditional + def test_conditional_skipping_of_actions_when_parent_action_is_also_conditional test_process(ChildOfConditionalParentController) assert_equal %w( conditional_in_parent_before conditional_in_parent_after ), assigns['ran_filter'] test_process(ChildOfConditionalParentController, 'another_action') assert_nil assigns['ran_filter'] end - def test_condition_skipping_of_filters_when_siblings_also_have_conditions + def test_condition_skipping_of_actions_when_siblings_also_have_conditions test_process(ChildOfConditionalParentController) assert_equal %w( conditional_in_parent_before conditional_in_parent_after ), assigns['ran_filter'] test_process(AnotherChildOfConditionalParentController) @@ -772,7 +790,7 @@ class FilterTest < ActionController::TestCase assert_nil assigns['ran_filter'] end - def test_a_rescuing_around_filter + def test_a_rescuing_around_action response = nil assert_nothing_raised do response = test_process(RescuedController) @@ -782,7 +800,7 @@ class FilterTest < ActionController::TestCase assert_equal("I rescued this: #<FilterTest::ErrorToRescue: Something made the bad noise.>", response.body) end - def test_filters_obey_only_and_except_for_implicit_actions + def test_actions_obey_only_and_except_for_implicit_actions test_process(ImplicitActionsController, 'show') assert_equal 'Except', assigns(:except) assert_nil assigns(:only) @@ -816,7 +834,7 @@ class PostsController < ActionController::Base include AroundExceptions end - module_eval %w(raises_before raises_after raises_both no_raise no_filter).map { |action| "def #{action}; default_action end" }.join("\n") + module_eval %w(raises_before raises_after raises_both no_raise no_action).map { |action| "def #{action}; default_action end" }.join("\n") private def default_action @@ -825,9 +843,9 @@ class PostsController < ActionController::Base end class ControllerWithSymbolAsFilter < PostsController - around_filter :raise_before, :only => :raises_before - around_filter :raise_after, :only => :raises_after - around_filter :without_exception, :only => :no_raise + around_action :raise_before, :only => :raises_before + around_action :raise_after, :only => :raises_after + around_action :without_exception, :only => :no_raise private def raise_before @@ -859,7 +877,7 @@ class ControllerWithFilterClass < PostsController end end - around_filter YieldingFilter, :only => :raises_after + around_action YieldingFilter, :only => :raises_after end class ControllerWithFilterInstance < PostsController @@ -870,22 +888,11 @@ class ControllerWithFilterInstance < PostsController end end - around_filter YieldingFilter.new, :only => :raises_after -end - -class ControllerWithFilterMethod < PostsController - class YieldingFilter < DefaultFilter - def around(controller) - yield - raise After - end - end - - around_filter YieldingFilter.new.method(:around), :only => :raises_after + around_action YieldingFilter.new, :only => :raises_after end class ControllerWithProcFilter < PostsController - around_filter(:only => :no_raise) do |c,b| + around_action(:only => :no_raise) do |c,b| c.instance_variable_set(:"@before", true) b.call c.instance_variable_set(:"@after", true) @@ -893,14 +900,14 @@ class ControllerWithProcFilter < PostsController end class ControllerWithNestedFilters < ControllerWithSymbolAsFilter - around_filter :raise_before, :raise_after, :without_exception, :only => :raises_both + around_action :raise_before, :raise_after, :without_exception, :only => :raises_both end class ControllerWithAllTypesOfFilters < PostsController - before_filter :before - around_filter :around - after_filter :after - around_filter :around_again + before_action :before + around_action :around + after_action :after + around_action :around_again private def before @@ -926,8 +933,8 @@ class ControllerWithAllTypesOfFilters < PostsController end class ControllerWithTwoLessFilters < ControllerWithAllTypesOfFilters - skip_filter :around_again - skip_filter :after + skip_action_callback :around_again + skip_action_callback :after end class YieldingAroundFiltersTest < ActionController::TestCase @@ -938,7 +945,7 @@ class YieldingAroundFiltersTest < ActionController::TestCase assert_nothing_raised { test_process(controller,'no_raise') } assert_nothing_raised { test_process(controller,'raises_before') } assert_nothing_raised { test_process(controller,'raises_after') } - assert_nothing_raised { test_process(controller,'no_filter') } + assert_nothing_raised { test_process(controller,'no_action') } end def test_with_symbol @@ -967,7 +974,7 @@ class YieldingAroundFiltersTest < ActionController::TestCase assert assigns['after'] end - def test_nested_filters + def test_nested_actions controller = ControllerWithNestedFilters assert_nothing_raised do begin @@ -983,34 +990,34 @@ class YieldingAroundFiltersTest < ActionController::TestCase end end - def test_filter_order_with_all_filter_types + def test_action_order_with_all_action_types test_process(ControllerWithAllTypesOfFilters,'no_raise') assert_equal 'before around (before yield) around_again (before yield) around_again (after yield) after around (after yield)', assigns['ran_filter'].join(' ') end - def test_filter_order_with_skip_filter_method + def test_action_order_with_skip_action_method test_process(ControllerWithTwoLessFilters,'no_raise') assert_equal 'before around (before yield) around (after yield)', assigns['ran_filter'].join(' ') end - def test_first_filter_in_multiple_before_filter_chain_halts + def test_first_action_in_multiple_before_action_chain_halts controller = ::FilterTest::TestMultipleFiltersController.new response = test_process(controller, 'fail_1') - assert_equal ' ', response.body + assert_equal '', response.body assert_equal 1, controller.instance_variable_get(:@try) end - def test_second_filter_in_multiple_before_filter_chain_halts + def test_second_action_in_multiple_before_action_chain_halts controller = ::FilterTest::TestMultipleFiltersController.new response = test_process(controller, 'fail_2') - assert_equal ' ', response.body + assert_equal '', response.body assert_equal 2, controller.instance_variable_get(:@try) end - def test_last_filter_in_multiple_before_filter_chain_halts + def test_last_action_in_multiple_before_action_chain_halts controller = ::FilterTest::TestMultipleFiltersController.new response = test_process(controller, 'fail_3') - assert_equal ' ', response.body + assert_equal '', response.body assert_equal 3, controller.instance_variable_get(:@try) end diff --git a/actionpack/test/controller/flash_hash_test.rb b/actionpack/test/controller/flash_hash_test.rb index 5490d9394b..d979b561f2 100644 --- a/actionpack/test/controller/flash_hash_test.rb +++ b/actionpack/test/controller/flash_hash_test.rb @@ -29,6 +29,15 @@ module ActionDispatch assert_equal 'world', @hash['hello'] end + def test_key + @hash['foo'] = 'bar' + + assert @hash.key?('foo') + assert @hash.key?(:foo) + assert_not @hash.key?('bar') + assert_not @hash.key?(:bar) + end + def test_delete @hash['foo'] = 'bar' @hash.delete 'foo' @@ -67,6 +76,16 @@ module ActionDispatch assert_equal({'flashes' => {'message' => 'Hello'}, 'discard' => %w[message]}, hash.to_session_value) end + def test_from_session_value_on_json_serializer + decrypted_data = "{ \"session_id\":\"d98bdf6d129618fc2548c354c161cfb5\", \"flash\":{\"discard\":[], \"flashes\":{\"message\":\"hey you\"}} }" + session = ActionDispatch::Cookies::JsonSerializer.load(decrypted_data) + hash = Flash::FlashHash.from_session_value(session['flash']) + + assert_equal({'discard' => %w[message], 'flashes' => { 'message' => 'hey you'}}, hash.to_session_value) + assert_equal "hey you", hash[:message] + assert_equal "hey you", hash["message"] + end + def test_empty? assert @hash.empty? @hash['zomg'] = 'bears' diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb index 9d4356f546..3720a920d0 100644 --- a/actionpack/test/controller/flash_test.rb +++ b/actionpack/test/controller/flash_test.rb @@ -1,5 +1,4 @@ require 'abstract_unit' -# FIXME remove DummyKeyGenerator and this require in 4.1 require 'active_support/key_generator' class FlashTest < ActionController::TestCase @@ -175,14 +174,14 @@ class FlashTest < ActionController::TestCase flash.update(:foo => :foo_indeed, :bar => :bar_indeed) assert_equal(:foo_indeed, flash.discard(:foo)) # valid key passed - assert_nil flash.discard(:unknown) # non existant key passed - assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard().to_hash) # nothing passed - assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard(nil).to_hash) # nothing passed + assert_nil flash.discard(:unknown) # non existent key passed + assert_equal({"foo" => :foo_indeed, "bar" => :bar_indeed}, flash.discard().to_hash) # nothing passed + assert_equal({"foo" => :foo_indeed, "bar" => :bar_indeed}, flash.discard(nil).to_hash) # nothing passed assert_equal(:foo_indeed, flash.keep(:foo)) # valid key passed - assert_nil flash.keep(:unknown) # non existant key passed - assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep().to_hash) # nothing passed - assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep(nil).to_hash) # nothing passed + assert_nil flash.keep(:unknown) # non existent key passed + assert_equal({"foo" => :foo_indeed, "bar" => :bar_indeed}, flash.keep().to_hash) # nothing passed + assert_equal({"foo" => :foo_indeed, "bar" => :bar_indeed}, flash.keep(nil).to_hash) # nothing passed end def test_redirect_to_with_alert @@ -211,15 +210,36 @@ class FlashTest < ActionController::TestCase end def test_redirect_to_with_adding_flash_types - @controller.class.add_flash_types :foo + original_controller = @controller + test_controller_with_flash_type_foo = Class.new(TestController) do + add_flash_types :foo + end + @controller = test_controller_with_flash_type_foo.new get :redirect_with_foo_flash assert_equal "for great justice", @controller.send(:flash)[:foo] + ensure + @controller = original_controller + end + + def test_add_flash_type_to_subclasses + test_controller_with_flash_type_foo = Class.new(TestController) do + add_flash_types :foo + end + subclass_controller_with_no_flash_type = Class.new(test_controller_with_flash_type_foo) + assert subclass_controller_with_no_flash_type._flash_types.include?(:foo) + end + + def test_does_not_add_flash_type_to_parent_class + Class.new(TestController) do + add_flash_types :bar + end + assert_not TestController._flash_types.include?(:bar) end end class FlashIntegrationTest < ActionDispatch::IntegrationTest SessionKey = '_myapp_session' - Generator = ActiveSupport::DummyKeyGenerator.new('b3c631c314c0bbca50c1b2843150fe33') + Generator = ActiveSupport::LegacyKeyGenerator.new('b3c631c314c0bbca50c1b2843150fe33') class TestController < ActionController::Base add_flash_types :bar diff --git a/actionpack/test/controller/force_ssl_test.rb b/actionpack/test/controller/force_ssl_test.rb index 6758668b7a..00d4612ac9 100644 --- a/actionpack/test/controller/force_ssl_test.rb +++ b/actionpack/test/controller/force_ssl_test.rb @@ -14,8 +14,42 @@ class ForceSSLControllerLevel < ForceSSLController force_ssl end -class ForceSSLCustomDomain < ForceSSLController - force_ssl :host => "secure.test.host" +class ForceSSLCustomOptions < ForceSSLController + force_ssl :host => "secure.example.com", :only => :redirect_host + force_ssl :port => 8443, :only => :redirect_port + force_ssl :subdomain => 'secure', :only => :redirect_subdomain + force_ssl :domain => 'secure.com', :only => :redirect_domain + force_ssl :path => '/foo', :only => :redirect_path + force_ssl :status => :found, :only => :redirect_status + force_ssl :flash => { :message => 'Foo, Bar!' }, :only => :redirect_flash + force_ssl :alert => 'Foo, Bar!', :only => :redirect_alert + force_ssl :notice => 'Foo, Bar!', :only => :redirect_notice + + def force_ssl_action + render :text => action_name + end + + alias_method :redirect_host, :force_ssl_action + alias_method :redirect_port, :force_ssl_action + alias_method :redirect_subdomain, :force_ssl_action + alias_method :redirect_domain, :force_ssl_action + alias_method :redirect_path, :force_ssl_action + alias_method :redirect_status, :force_ssl_action + alias_method :redirect_flash, :force_ssl_action + alias_method :redirect_alert, :force_ssl_action + alias_method :redirect_notice, :force_ssl_action + + def use_flash + render :text => flash[:message] + end + + def use_alert + render :text => flash[:alert] + end + + def use_notice + render :text => flash[:notice] + end end class ForceSSLOnlyAction < ForceSSLController @@ -59,8 +93,6 @@ class RedirectToSSL < ForceSSLController end class ForceSSLControllerLevelTest < ActionController::TestCase - tests ForceSSLControllerLevel - def test_banana_redirects_to_https get :banana assert_response 301 @@ -80,25 +112,79 @@ class ForceSSLControllerLevelTest < ActionController::TestCase end end -class ForceSSLCustomDomainTest < ActionController::TestCase - tests ForceSSLCustomDomain +class ForceSSLCustomOptionsTest < ActionController::TestCase + def setup + @request.env['HTTP_HOST'] = 'www.example.com:80' + end - def test_banana_redirects_to_https_with_custom_host - get :banana + def test_redirect_to_custom_host + get :redirect_host assert_response 301 - assert_equal "https://secure.test.host/force_ssl_custom_domain/banana", redirect_to_url + assert_equal "https://secure.example.com/force_ssl_custom_options/redirect_host", redirect_to_url end - def test_cheeseburger_redirects_to_https_with_custom_host - get :cheeseburger + def test_redirect_to_custom_port + get :redirect_port + assert_response 301 + assert_equal "https://www.example.com:8443/force_ssl_custom_options/redirect_port", redirect_to_url + end + + def test_redirect_to_custom_subdomain + get :redirect_subdomain assert_response 301 - assert_equal "https://secure.test.host/force_ssl_custom_domain/cheeseburger", redirect_to_url + assert_equal "https://secure.example.com/force_ssl_custom_options/redirect_subdomain", redirect_to_url + end + + def test_redirect_to_custom_domain + get :redirect_domain + assert_response 301 + assert_equal "https://www.secure.com/force_ssl_custom_options/redirect_domain", redirect_to_url + end + + def test_redirect_to_custom_path + get :redirect_path + assert_response 301 + assert_equal "https://www.example.com/foo", redirect_to_url + end + + def test_redirect_to_custom_status + get :redirect_status + assert_response 302 + assert_equal "https://www.example.com/force_ssl_custom_options/redirect_status", redirect_to_url + end + + def test_redirect_to_custom_flash + get :redirect_flash + assert_response 301 + assert_equal "https://www.example.com/force_ssl_custom_options/redirect_flash", redirect_to_url + + get :use_flash + assert_response 200 + assert_equal "Foo, Bar!", @response.body + end + + def test_redirect_to_custom_alert + get :redirect_alert + assert_response 301 + assert_equal "https://www.example.com/force_ssl_custom_options/redirect_alert", redirect_to_url + + get :use_alert + assert_response 200 + assert_equal "Foo, Bar!", @response.body + end + + def test_redirect_to_custom_notice + get :redirect_notice + assert_response 301 + assert_equal "https://www.example.com/force_ssl_custom_options/redirect_notice", redirect_to_url + + get :use_notice + assert_response 200 + assert_equal "Foo, Bar!", @response.body end end class ForceSSLOnlyActionTest < ActionController::TestCase - tests ForceSSLOnlyAction - def test_banana_not_redirects_to_https get :banana assert_response 200 @@ -112,8 +198,6 @@ class ForceSSLOnlyActionTest < ActionController::TestCase end class ForceSSLExceptActionTest < ActionController::TestCase - tests ForceSSLExceptAction - def test_banana_not_redirects_to_https get :banana assert_response 200 @@ -127,8 +211,6 @@ class ForceSSLExceptActionTest < ActionController::TestCase end class ForceSSLIfConditionTest < ActionController::TestCase - tests ForceSSLIfCondition - def test_banana_not_redirects_to_https get :banana assert_response 200 @@ -142,25 +224,85 @@ class ForceSSLIfConditionTest < ActionController::TestCase end class ForceSSLFlashTest < ActionController::TestCase - tests ForceSSLFlash - def test_cheeseburger_redirects_to_https get :set_flash assert_response 302 assert_equal "http://test.host/force_ssl_flash/cheeseburger", redirect_to_url + # FIXME: AC::TestCase#build_request_uri doesn't build a new uri if PATH_INFO exists + @request.env.delete('PATH_INFO') + get :cheeseburger assert_response 301 assert_equal "https://test.host/force_ssl_flash/cheeseburger", redirect_to_url + # FIXME: AC::TestCase#build_request_uri doesn't build a new uri if PATH_INFO exists + @request.env.delete('PATH_INFO') + get :use_flash assert_equal "hello", assigns["flash_copy"]["that"] assert_equal "hello", assigns["flashy"] end end +class ForceSSLDuplicateRoutesTest < ActionController::TestCase + tests ForceSSLControllerLevel + + def test_force_ssl_redirects_to_same_path + with_routing do |set| + set.draw do + get '/foo', :to => 'force_ssl_controller_level#banana' + get '/bar', :to => 'force_ssl_controller_level#banana' + end + + @request.env['PATH_INFO'] = '/bar' + + get :banana + assert_response 301 + assert_equal 'https://test.host/bar', redirect_to_url + end + end +end + +class ForceSSLFormatTest < ActionController::TestCase + tests ForceSSLControllerLevel + + def test_force_ssl_redirects_to_same_format + with_routing do |set| + set.draw do + get '/foo', :to => 'force_ssl_controller_level#banana' + end + + get :banana, :format => :json + assert_response 301 + assert_equal 'https://test.host/foo.json', redirect_to_url + end + end +end + +class ForceSSLOptionalSegmentsTest < ActionController::TestCase + tests ForceSSLControllerLevel + + def test_force_ssl_redirects_to_same_format + with_routing do |set| + set.draw do + scope '(:locale)' do + defaults :locale => 'en' do + get '/foo', :to => 'force_ssl_controller_level#banana' + end + end + end + + @request.env['PATH_INFO'] = '/en/foo' + get :banana, :locale => 'en' + assert_equal 'en', @controller.params[:locale] + assert_response 301 + assert_equal 'https://test.host/en/foo', redirect_to_url + end + end +end + class RedirectToSSLTest < ActionController::TestCase - tests RedirectToSSL def test_banana_redirects_to_https_if_not_https get :banana assert_response 301 @@ -179,4 +321,4 @@ class RedirectToSSLTest < ActionController::TestCase assert_response 200 assert_equal 'ihaz', response.body end -end
\ No newline at end of file +end diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb index 248c81193e..936b8c2450 100644 --- a/actionpack/test/controller/helper_test.rb +++ b/actionpack/test/controller/helper_test.rb @@ -60,6 +60,12 @@ class HelpersPathsController < ActionController::Base end end +class HelpersTypoController < ActionController::Base + path = File.expand_path('../../fixtures/helpers_typo', __FILE__) + $:.unshift(path) + self.helpers_path = path +end + module LocalAbcHelper def a() end def b() end @@ -82,6 +88,22 @@ class HelperPathsTest < ActiveSupport::TestCase end end +class HelpersTypoControllerTest < ActiveSupport::TestCase + def setup + @autoload_paths = ActiveSupport::Dependencies.autoload_paths + ActiveSupport::Dependencies.autoload_paths = Array(HelpersTypoController.helpers_path) + end + + def test_helper_typo_error_message + e = assert_raise(NameError) { HelpersTypoController.helper 'admin/users' } + assert_equal "Couldn't find Admin::UsersHelper, expected it to be defined in helpers/admin/users_helper.rb", e.message + end + + def teardown + ActiveSupport::Dependencies.autoload_paths = @autoload_paths + end +end + class HelperTest < ActiveSupport::TestCase class TestController < ActionController::Base attr_accessor :delegate_attr @@ -201,6 +223,12 @@ class HelperTest < ActiveSupport::TestCase # fun/pdf_helper.rb assert methods.include?(:foobar) end + + def test_helper_proxy_config + AllHelpersController.config.my_var = 'smth' + + assert_equal 'smth', AllHelpersController.helpers.config.my_var + end private def expected_helper_methods diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb index 90548d4294..9052fc6962 100644 --- a/actionpack/test/controller/http_basic_authentication_test.rb +++ b/actionpack/test/controller/http_basic_authentication_test.rb @@ -129,6 +129,13 @@ class HttpBasicAuthenticationTest < ActionController::TestCase assert_response :unauthorized end + test "authentication request with wrong scheme" do + header = 'Bearer ' + encode_credentials('David', 'Goliath').split(' ', 2)[1] + @request.env['HTTP_AUTHORIZATION'] = header + get :search + assert_response :unauthorized + end + private def encode_credentials(username, password) diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index 537de7a2dd..52a0bc9aa3 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -1,5 +1,4 @@ require 'abstract_unit' -# FIXME remove DummyKeyGenerator and this require in 4.1 require 'active_support/key_generator' class HttpDigestAuthenticationTest < ActionController::TestCase @@ -22,7 +21,7 @@ class HttpDigestAuthenticationTest < ActionController::TestCase def authenticate authenticate_or_request_with_http_digest("SuperSecret") do |username| - # Return the password + # Returns the password USERS[username] end end @@ -43,7 +42,7 @@ class HttpDigestAuthenticationTest < ActionController::TestCase setup do # Used as secret in generating nonce to prevent tampering of timestamp @secret = "4fb45da9e4ab4ddeb7580d6a35503d99" - @request.env["action_dispatch.key_generator"] = ActiveSupport::DummyKeyGenerator.new(@secret) + @request.env["action_dispatch.key_generator"] = ActiveSupport::LegacyKeyGenerator.new(@secret) end teardown do @@ -249,6 +248,14 @@ class HttpDigestAuthenticationTest < ActionController::TestCase assert_equal 'Definitely Maybe', @response.body end + test "when sent a basic auth header, returns Unauthorized" do + @request.env['HTTP_AUTHORIZATION'] = 'Basic Gwf2aXq8ZLF3Hxq=' + + get :display + + assert_response :unauthorized + end + private def encode_credentials(options) diff --git a/actionpack/test/controller/http_token_authentication_test.rb b/actionpack/test/controller/http_token_authentication_test.rb index ebf6d224aa..a758df2ec6 100644 --- a/actionpack/test/controller/http_token_authentication_test.rb +++ b/actionpack/test/controller/http_token_authentication_test.rb @@ -21,7 +21,7 @@ class HttpTokenAuthenticationTest < ActionController::TestCase private def authenticate - authenticate_or_request_with_http_token do |token, options| + authenticate_or_request_with_http_token do |token, _| token == 'lifo' end end @@ -132,13 +132,69 @@ class HttpTokenAuthenticationTest < ActionController::TestCase assert_equal(expected, actual) end - private + test "token_and_options returns empty string with empty token" do + token = '' + actual = ActionController::HttpAuthentication::Token.token_and_options(sample_request(token)).first + expected = token + assert_equal(expected, actual) + end + + test "token_and_options returns correct token with nounce option" do + token = "rcHu+HzSFw89Ypyhn/896A=" + nonce_hash = {nonce: "123abc"} + actual = ActionController::HttpAuthentication::Token.token_and_options(sample_request(token, nonce_hash)) + expected_token = token + expected_nonce = {"nonce" => nonce_hash[:nonce]} + assert_equal(expected_token, actual.first) + assert_equal(expected_nonce, actual.last) + end - def sample_request(token) - @sample_request ||= OpenStruct.new authorization: %{Token token="#{token}"} + test "token_and_options returns nil with no value after the equal sign" do + actual = ActionController::HttpAuthentication::Token.token_and_options(malformed_request).first + expected = nil + assert_equal(expected, actual) end - def encode_credentials(token, options = {}) - ActionController::HttpAuthentication::Token.encode_credentials(token, options) + test "raw_params returns a tuple of two key value pair strings" do + auth = sample_request("rcHu+HzSFw89Ypyhn/896A=").authorization.to_s + actual = ActionController::HttpAuthentication::Token.raw_params(auth) + expected = ["token=\"rcHu+HzSFw89Ypyhn/896A=\"", "nonce=\"def\""] + assert_equal(expected, actual) + end + + test "token_and_options returns right token when token key is not specified in header" do + token = "rcHu+HzSFw89Ypyhn/896A=" + + actual = ActionController::HttpAuthentication::Token.token_and_options( + sample_request_without_token_key(token) + ).first + + expected = token + assert_equal(expected, actual) end + + private + + def sample_request(token, options = {nonce: "def"}) + authorization = options.inject([%{Token token="#{token}"}]) do |arr, (k, v)| + arr << "#{k}=\"#{v}\"" + end.join(", ") + mock_authorization_request(authorization) + end + + def malformed_request + mock_authorization_request(%{Token token=}) + end + + def sample_request_without_token_key(token) + mock_authorization_request(%{Token #{token}}) + end + + def mock_authorization_request(authorization) + OpenStruct.new(authorization: authorization) + end + + def encode_credentials(token, options = {}) + ActionController::HttpAuthentication::Token.encode_credentials(token, options) + end end diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index cf561d913a..5535c7ae78 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'controller/fake_controllers' -require 'action_view/vendor/html-scanner' +require 'rails/engine' class SessionTest < ActiveSupport::TestCase StubApp = lambda { |env| @@ -117,12 +117,6 @@ class SessionTest < ActiveSupport::TestCase @session.head(path,params,headers) end - def test_options - path = "/index"; params = "blah"; headers = {:location => 'blah'} - @session.expects(:process).with(:options,path,params,headers) - @session.options(path,params,headers) - end - def test_xml_http_request_get path = "/index"; params = "blah"; headers = {:location => 'blah'} headers_after_xhr = headers.merge( @@ -183,16 +177,6 @@ class SessionTest < ActiveSupport::TestCase @session.xml_http_request(:head,path,params,headers) end - def test_xml_http_request_options - path = "/index"; params = "blah"; headers = {:location => 'blah'} - headers_after_xhr = headers.merge( - "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", - "HTTP_ACCEPT" => "text/javascript, text/html, application/xml, text/xml, */*" - ) - @session.expects(:process).with(:options,path,params,headers_after_xhr) - @session.xml_http_request(:options,path,params,headers) - end - def test_xml_http_request_override_accept path = "/index"; params = "blah"; headers = {:location => 'blah', "HTTP_ACCEPT" => "application/xml"} headers_after_xhr = headers.merge( @@ -250,7 +234,7 @@ class IntegrationTestUsesCorrectClass < ActionDispatch::IntegrationTest @integration_session.stubs(:generic_url_rewriter) @integration_session.stubs(:process) - %w( get post head patch put delete options ).each do |verb| + %w( get post head patch put delete ).each do |verb| assert_nothing_raised("'#{verb}' should use integration test methods") { __send__(verb, '/') } end end @@ -293,7 +277,7 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest end def redirect - redirect_to :action => "get" + redirect_to action_url('get') end end @@ -308,7 +292,7 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest assert_equal({}, cookies.to_hash) assert_equal "OK", body assert_equal "OK", response.body - assert_kind_of HTML::Document, html_document + assert_kind_of Nokogiri::HTML::Document, html_document assert_equal 1, request_count end end @@ -324,7 +308,7 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest assert_equal({}, cookies.to_hash) assert_equal "Created", body assert_equal "Created", response.body - assert_kind_of HTML::Document, html_document + assert_kind_of Nokogiri::HTML::Document, html_document assert_equal 1, request_count end end @@ -384,12 +368,16 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest assert_response :redirect assert_response :found assert_equal "<html><body>You are being <a href=\"http://www.example.com/get\">redirected</a>.</body></html>", response.body - assert_kind_of HTML::Document, html_document + assert_kind_of Nokogiri::HTML::Document, html_document assert_equal 1, request_count follow_redirect! assert_response :success assert_equal "/get", path + + get '/moved' + assert_response :redirect + assert_redirected_to '/method' end end @@ -466,6 +454,58 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest assert_equal 'http://www.example.com/foo', url_for(:controller => "foo") end + def test_port_via_host! + with_test_route_set do + host! 'www.example.com:8080' + get '/get' + assert_equal 8080, request.port + end + end + + def test_port_via_process + with_test_route_set do + get 'http://www.example.com:8080/get' + assert_equal 8080, request.port + end + end + + def test_https_and_port_via_host_and_https! + with_test_route_set do + host! 'www.example.com' + https! true + + get '/get' + assert_equal 443, request.port + assert_equal true, request.ssl? + + host! 'www.example.com:443' + https! true + + get '/get' + assert_equal 443, request.port + assert_equal true, request.ssl? + + host! 'www.example.com:8443' + https! true + + get '/get' + assert_equal 8443, request.port + assert_equal true, request.ssl? + end + end + + def test_https_and_port_via_process + with_test_route_set do + get 'https://www.example.com/get' + assert_equal 443, request.port + assert_equal true, request.ssl? + + get 'https://www.example.com:8443/get' + assert_equal 8443, request.port + assert_equal true, request.ssl? + end + end + private def with_test_route_set with_routing do |set| @@ -475,8 +515,10 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest end set.draw do - match ':action', :to => controller, :via => [:get, :post] - get 'get/:action', :to => controller + get 'moved' => redirect('/method') + + match ':action', :to => controller, :via => [:get, :post], :as => :action + get 'get/:action', :to => controller, :as => :get_action end self.singleton_class.send(:include, set.url_helpers) @@ -521,6 +563,21 @@ class MetalIntegrationTest < ActionDispatch::IntegrationTest def test_generate_url_without_controller assert_equal 'http://www.example.com/foo', url_for(:controller => "foo") end + + def test_pass_headers + get "/success", {}, "Referer" => "http://www.example.com/foo", "Host" => "http://nohost.com" + + assert_equal "http://nohost.com", @request.env["HTTP_HOST"] + assert_equal "http://www.example.com/foo", @request.env["HTTP_REFERER"] + end + + def test_pass_env + get "/success", {}, "HTTP_REFERER" => "http://test.com/", "HTTP_HOST" => "http://test.com" + + assert_equal "http://test.com", @request.env["HTTP_HOST"] + assert_equal "http://test.com/", @request.env["HTTP_REFERER"] + end + end class ApplicationIntegrationTest < ActionDispatch::IntegrationTest @@ -538,7 +595,7 @@ class ApplicationIntegrationTest < ActionDispatch::IntegrationTest @routes ||= ActionDispatch::Routing::RouteSet.new end - class MountedApp + class MountedApp < Rails::Engine def self.routes @routes ||= ActionDispatch::Routing::RouteSet.new end @@ -558,6 +615,8 @@ class ApplicationIntegrationTest < ActionDispatch::IntegrationTest get 'bar', :to => 'application_integration_test/test#index', :as => :bar mount MountedApp => '/mounted', :as => "mounted" + get 'fooz' => proc { |env| [ 200, {'X-Cascade' => 'pass'}, [ "omg" ] ] }, :anchor => false + get 'fooz', :to => 'application_integration_test/test#index' end def app @@ -574,6 +633,12 @@ class ApplicationIntegrationTest < ActionDispatch::IntegrationTest assert_equal '/mounted/baz', mounted.baz_path end + test "path after cascade pass" do + get '/fooz' + assert_equal 'index', response.body + assert_equal '/fooz', path + end + test "route helpers after controller access" do get '/' assert_equal '/', empty_string_path @@ -699,13 +764,17 @@ class UrlOptionsIntegrationTest < ActionDispatch::IntegrationTest assert_equal "http://bar.com/foo", foos_url end - test "test can override default url options" do + def test_can_override_default_url_options + original_host = default_url_options.dup + default_url_options[:host] = "foobar.com" assert_equal "http://foobar.com/foo", foos_url get "/bar" assert_response :success assert_equal "http://foobar.com/foo", foos_url + ensure + ActionDispatch::Integration::Session.default_url_options = self.default_url_options = original_host end test "current request path parameters are recalled" do @@ -714,3 +783,94 @@ class UrlOptionsIntegrationTest < ActionDispatch::IntegrationTest assert_equal "/foo/1/edit", url_for(:action => 'edit', :only_path => true) end end + +class HeadWithStatusActionIntegrationTest < ActionDispatch::IntegrationTest + class FooController < ActionController::Base + def status + head :ok + end + end + + def self.routes + @routes ||= ActionDispatch::Routing::RouteSet.new + end + + def self.call(env) + routes.call(env) + end + + def app + self.class + end + + routes.draw do + get "/foo/status" => 'head_with_status_action_integration_test/foo#status' + end + + test "get /foo/status with head result does not cause stack overflow error" do + assert_nothing_raised do + get '/foo/status' + end + assert_response :ok + end +end + +class IntegrationWithRoutingTest < ActionDispatch::IntegrationTest + class FooController < ActionController::Base + def index + render plain: 'ok' + end + end + + def test_with_routing_resets_session + klass_namespace = self.class.name.underscore + + with_routing do |routes| + routes.draw do + namespace klass_namespace do + resources :foo, path: '/with' + end + end + + get '/integration_with_routing_test/with' + assert_response 200 + assert_equal 'ok', response.body + end + + with_routing do |routes| + routes.draw do + namespace klass_namespace do + resources :foo, path: '/routing' + end + end + + get '/integration_with_routing_test/routing' + assert_response 200 + assert_equal 'ok', response.body + end + end +end + +# to work in contexts like rspec before(:all) +class IntegrationRequestsWithoutSetup < ActionDispatch::IntegrationTest + self._setup_callbacks = [] + self._teardown_callbacks = [] + + class FooController < ActionController::Base + def ok + cookies[:key] = 'ok' + render plain: 'ok' + end + end + + def test_request + with_routing do |routes| + routes.draw { get ':action' => FooController } + get '/ok' + + assert_response 200 + assert_equal 'ok', response.body + assert_equal 'ok', cookies['key'] + end + end +end diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb deleted file mode 100644 index 94a8d2f180..0000000000 --- a/actionpack/test/controller/layout_test.rb +++ /dev/null @@ -1,237 +0,0 @@ -require 'abstract_unit' -require 'rbconfig' - -# The view_paths array must be set on Base and not LayoutTest so that LayoutTest's inherited -# method has access to the view_paths array when looking for a layout to automatically assign. -old_load_paths = ActionController::Base.view_paths - -ActionView::Template::register_template_handler :mab, - lambda { |template| template.source.inspect } - -ActionController::Base.view_paths = [ File.dirname(__FILE__) + '/../fixtures/layout_tests/' ] - -class LayoutTest < ActionController::Base - def self.controller_path; 'views' end - def self._implied_layout_name; to_s.underscore.gsub(/_controller$/, '') ; end - self.view_paths = ActionController::Base.view_paths.dup -end - -# Restore view_paths to previous value -ActionController::Base.view_paths = old_load_paths - -class ProductController < LayoutTest -end - -class ItemController < LayoutTest -end - -class ThirdPartyTemplateLibraryController < LayoutTest -end - -module ControllerNameSpace -end - -class ControllerNameSpace::NestedController < LayoutTest -end - -class MultipleExtensions < LayoutTest -end - -class LayoutAutoDiscoveryTest < ActionController::TestCase - def setup - super - @request.host = "www.nextangle.com" - end - - def test_application_layout_is_default_when_no_controller_match - @controller = ProductController.new - get :hello - assert_equal 'layout_test.erb hello.erb', @response.body - end - - def test_controller_name_layout_name_match - @controller = ItemController.new - get :hello - assert_equal 'item.erb hello.erb', @response.body - end - - def test_third_party_template_library_auto_discovers_layout - @controller = ThirdPartyTemplateLibraryController.new - get :hello - assert_response :success - assert_equal 'layouts/third_party_template_library.mab', @response.body - end - - def test_namespaced_controllers_auto_detect_layouts1 - @controller = ControllerNameSpace::NestedController.new - get :hello - assert_equal 'controller_name_space/nested.erb hello.erb', @response.body - end - - def test_namespaced_controllers_auto_detect_layouts2 - @controller = MultipleExtensions.new - get :hello - assert_equal 'multiple_extensions.html.erb hello.erb', @response.body.strip - end -end - -class DefaultLayoutController < LayoutTest -end - -class StreamingLayoutController < LayoutTest - def render(*args) - options = args.extract_options! || {} - super(*args, options.merge(:stream => true)) - end -end - -class AbsolutePathLayoutController < LayoutTest - layout File.expand_path(File.expand_path(__FILE__) + '/../../fixtures/layout_tests/layouts/layout_test') -end - -class HasOwnLayoutController < LayoutTest - layout 'item' -end - -class PrependsViewPathController < LayoutTest - def hello - prepend_view_path File.dirname(__FILE__) + '/../fixtures/layout_tests/alt/' - render :layout => 'alt' - end -end - -class OnlyLayoutController < LayoutTest - layout 'item', :only => "hello" -end - -class ExceptLayoutController < LayoutTest - layout 'item', :except => "goodbye" -end - -class SetsLayoutInRenderController < LayoutTest - def hello - render :layout => 'third_party_template_library' - end -end - -class RendersNoLayoutController < LayoutTest - def hello - render :layout => false - end -end - -class LayoutSetInResponseTest < ActionController::TestCase - include ActionView::Template::Handlers - - def test_layout_set_when_using_default_layout - @controller = DefaultLayoutController.new - get :hello - assert_template :layout => "layouts/layout_test" - end - - def test_layout_set_when_using_streaming_layout - @controller = StreamingLayoutController.new - get :hello - assert_template :hello - end - - def test_layout_set_when_set_in_controller - @controller = HasOwnLayoutController.new - get :hello - assert_template :layout => "layouts/item" - end - - def test_layout_only_exception_when_included - @controller = OnlyLayoutController.new - get :hello - assert_template :layout => "layouts/item" - end - - def test_layout_only_exception_when_excepted - @controller = OnlyLayoutController.new - get :goodbye - assert !@response.body.include?("item.erb"), "#{@response.body.inspect} included 'item.erb'" - end - - def test_layout_except_exception_when_included - @controller = ExceptLayoutController.new - get :hello - assert_template :layout => "layouts/item" - end - - def test_layout_except_exception_when_excepted - @controller = ExceptLayoutController.new - get :goodbye - assert !@response.body.include?("item.erb"), "#{@response.body.inspect} included 'item.erb'" - end - - def test_layout_set_when_using_render - @controller = SetsLayoutInRenderController.new - get :hello - assert_template :layout => "layouts/third_party_template_library" - end - - def test_layout_is_not_set_when_none_rendered - @controller = RendersNoLayoutController.new - get :hello - assert_template :layout => nil - end - - def test_layout_is_picked_from_the_controller_instances_view_path - @controller = PrependsViewPathController.new - get :hello - assert_template :layout => /layouts\/alt/ - end - - def test_absolute_pathed_layout - @controller = AbsolutePathLayoutController.new - get :hello - assert_equal "layout_test.erb hello.erb", @response.body.strip - end -end - -class RenderWithTemplateOptionController < LayoutTest - def hello - render :template => 'alt/hello' - end -end - -class SetsNonExistentLayoutFile < LayoutTest - layout "nofile" -end - -class LayoutExceptionRaisedTest < ActionController::TestCase - def test_exception_raised_when_layout_file_not_found - @controller = SetsNonExistentLayoutFile.new - assert_raise(ActionView::MissingTemplate) { get :hello } - end -end - -class LayoutStatusIsRendered < LayoutTest - def hello - render :status => 401 - end -end - -class LayoutStatusIsRenderedTest < ActionController::TestCase - def test_layout_status_is_rendered - @controller = LayoutStatusIsRendered.new - get :hello - assert_response 401 - end -end - -unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ - class LayoutSymlinkedTest < LayoutTest - layout "symlinked/symlinked_layout" - end - - class LayoutSymlinkedIsRenderedTest < ActionController::TestCase - def test_symlinked_layout_is_rendered - @controller = LayoutSymlinkedTest.new - get :hello - assert_response 200 - assert_template :layout => "layouts/symlinked/symlinked_layout" - end - end -end diff --git a/actionpack/test/controller/live_stream_test.rb b/actionpack/test/controller/live_stream_test.rb index 3b1a07d7af..7fd1276e98 100644 --- a/actionpack/test/controller/live_stream_test.rb +++ b/actionpack/test/controller/live_stream_test.rb @@ -1,8 +1,114 @@ require 'abstract_unit' require 'active_support/concurrency/latch' +Thread.abort_on_exception = true module ActionController + class SSETest < ActionController::TestCase + class SSETestController < ActionController::Base + include ActionController::Live + + def basic_sse + response.headers['Content-Type'] = 'text/event-stream' + sse = SSE.new(response.stream) + sse.write("{\"name\":\"John\"}") + sse.write({ name: "Ryan" }) + ensure + sse.close + end + + def sse_with_event + sse = SSE.new(response.stream, event: "send-name") + sse.write("{\"name\":\"John\"}") + sse.write({ name: "Ryan" }) + ensure + sse.close + end + + def sse_with_retry + sse = SSE.new(response.stream, retry: 1000) + sse.write("{\"name\":\"John\"}") + sse.write({ name: "Ryan" }, retry: 1500) + ensure + sse.close + end + + def sse_with_id + sse = SSE.new(response.stream) + sse.write("{\"name\":\"John\"}", id: 1) + sse.write({ name: "Ryan" }, id: 2) + ensure + sse.close + end + + def sse_with_multiple_line_message + sse = SSE.new(response.stream) + sse.write("first line.\nsecond line.") + ensure + sse.close + end + end + + tests SSETestController + + def wait_for_response_stream_close + response.body + end + + def test_basic_sse + get :basic_sse + + wait_for_response_stream_close + assert_match(/data: {\"name\":\"John\"}/, response.body) + assert_match(/data: {\"name\":\"Ryan\"}/, response.body) + end + + def test_sse_with_event_name + get :sse_with_event + + wait_for_response_stream_close + assert_match(/data: {\"name\":\"John\"}/, response.body) + assert_match(/data: {\"name\":\"Ryan\"}/, response.body) + assert_match(/event: send-name/, response.body) + end + + def test_sse_with_retry + get :sse_with_retry + + wait_for_response_stream_close + first_response, second_response = response.body.split("\n\n") + assert_match(/data: {\"name\":\"John\"}/, first_response) + assert_match(/retry: 1000/, first_response) + + assert_match(/data: {\"name\":\"Ryan\"}/, second_response) + assert_match(/retry: 1500/, second_response) + end + + def test_sse_with_id + get :sse_with_id + + wait_for_response_stream_close + first_response, second_response = response.body.split("\n\n") + assert_match(/data: {\"name\":\"John\"}/, first_response) + assert_match(/id: 1/, first_response) + + assert_match(/data: {\"name\":\"Ryan\"}/, second_response) + assert_match(/id: 2/, second_response) + end + + def test_sse_with_multiple_line_message + get :sse_with_multiple_line_message + + wait_for_response_stream_close + first_response, second_response = response.body.split("\n") + assert_match(/data: first line/, first_response) + assert_match(/data: second line/, second_response) + end + end + class LiveStreamTest < ActionController::TestCase + class Exception < StandardError + end + class TestController < ActionController::Base include ActionController::Live @@ -12,6 +118,12 @@ module ActionController 'test' end + def set_cookie + cookies[:hello] = "world" + response.stream.write "hello world" + response.close + end + def render_text render :text => 'zomg' end @@ -48,18 +160,106 @@ module ActionController end response.stream.close end + + def with_stale + render text: 'stale' if stale?(etag: "123", template: false) + end + + def exception_in_view + render 'doesntexist' + end + + def exception_in_view_after_commit + response.stream.write "" + render 'doesntexist' + end + + def exception_with_callback + response.headers['Content-Type'] = 'text/event-stream' + + response.stream.on_error do + response.stream.write %(data: "500 Internal Server Error"\n\n) + response.stream.close + end + + response.stream.write "" # make sure the response is committed + raise 'An exception occurred...' + end + + def exception_in_controller + raise Exception, 'Exception in controller' + end + + def bad_request_error + raise ActionController::BadRequest + end + + def exception_in_exception_callback + response.headers['Content-Type'] = 'text/event-stream' + response.stream.on_error do + raise 'We need to go deeper.' + end + response.stream.write '' + response.stream.write params[:widget][:didnt_check_for_nil] + end + + def overfill_buffer_and_die + # Write until the buffer is full. It doesn't expose that + # information directly, so we must hard-code its size: + 10.times do + response.stream.write '.' + end + # .. plus one more, because the #each frees up a slot: + response.stream.write '.' + + latch.release + + # This write will block, and eventually raise + response.stream.write 'x' + + 20.times do + response.stream.write '.' + end + end + + def ignore_client_disconnect + response.stream.ignore_disconnect = true + + response.stream.write '' # commit + + # These writes will be ignored + 15.times do + response.stream.write 'x' + end + + logger.info 'Work complete' + latch.release + end end tests TestController - class TestResponse < Live::Response - def recycle! - initialize + def assert_stream_closed + assert response.stream.closed?, 'stream should be closed' + assert response.sent?, 'stream should be sent' + end + + def capture_log_output + output = StringIO.new + old_logger, ActionController::Base.logger = ActionController::Base.logger, ActiveSupport::Logger.new(output) + + begin + yield output + ensure + ActionController::Base.logger = old_logger end end - def build_response - TestResponse.new + def test_set_cookie + @controller = TestController.new + get :set_cookie + assert_equal({'hello' => 'world'}, @response.cookies) + assert_equal "hello world", @response.body end def test_set_response! @@ -83,6 +283,7 @@ module ActionController @controller.response = @response t = Thread.new(@response) { |resp| + resp.await_commit resp.stream.each do |part| assert_equal parts.shift, part ol = @controller.latch @@ -93,7 +294,63 @@ module ActionController @controller.process :blocking_stream - assert t.join + assert t.join(3), 'timeout expired before the thread terminated' + end + + def test_abort_with_full_buffer + @controller.latch = ActiveSupport::Concurrency::Latch.new + + @request.parameters[:format] = 'plain' + @controller.request = @request + @controller.response = @response + + got_error = ActiveSupport::Concurrency::Latch.new + @response.stream.on_error do + ActionController::Base.logger.warn 'Error while streaming' + got_error.release + end + + t = Thread.new(@response) { |resp| + resp.await_commit + _, _, body = resp.to_a + body.each do |part| + @controller.latch.await + body.close + break + end + } + + capture_log_output do |output| + @controller.process :overfill_buffer_and_die + t.join + got_error.await + assert_match 'Error while streaming', output.rewind && output.read + end + end + + def test_ignore_client_disconnect + @controller.latch = ActiveSupport::Concurrency::Latch.new + + @controller.request = @request + @controller.response = @response + + t = Thread.new(@response) { |resp| + resp.await_commit + _, _, body = resp.to_a + body.each do |part| + body.close + break + end + } + + capture_log_output do |output| + @controller.process :ignore_client_disconnect + t.join + Timeout.timeout(3) do + @controller.latch.await + end + assert_match 'Work complete', output.rewind && output.read + end end def test_thread_locals_get_copied @@ -115,7 +372,83 @@ module ActionController def test_render_text get :render_text assert_equal 'zomg', response.body - assert response.stream.closed?, 'stream should be closed' + assert_stream_closed + end + + def test_exception_handling_html + assert_raises(ActionView::MissingTemplate) do + get :exception_in_view + end + + capture_log_output do |output| + get :exception_in_view_after_commit + assert_match %r((window\.location = "/500\.html"</script></html>)$), response.body + assert_match 'Missing template test/doesntexist', output.rewind && output.read + assert_stream_closed + end + assert response.body + assert_stream_closed + end + + def test_exception_handling_plain_text + assert_raises(ActionView::MissingTemplate) do + get :exception_in_view, format: :json + end + + capture_log_output do |output| + get :exception_in_view_after_commit, format: :json + assert_equal '', response.body + assert_match 'Missing template test/doesntexist', output.rewind && output.read + assert_stream_closed + end + end + + def test_exception_callback_when_committed + capture_log_output do |output| + get :exception_with_callback, format: 'text/event-stream' + assert_equal %(data: "500 Internal Server Error"\n\n), response.body + assert_match 'An exception occurred...', output.rewind && output.read + assert_stream_closed + end + end + + def test_exception_in_controller_before_streaming + assert_raises(ActionController::LiveStreamTest::Exception) do + get :exception_in_controller, format: 'text/event-stream' + end + end + + def test_bad_request_in_controller_before_streaming + assert_raises(ActionController::BadRequest) do + get :bad_request_error, format: 'text/event-stream' + end + end + + def test_exceptions_raised_handling_exceptions_and_committed + capture_log_output do |output| + get :exception_in_exception_callback, format: 'text/event-stream' + assert_equal '', response.body + assert_match 'We need to go deeper', output.rewind && output.read + assert_stream_closed + end + end + + def test_stale_without_etag + get :with_stale + assert_equal 200, @response.status.to_i + end + + def test_stale_with_etag + @request.if_none_match = Digest::MD5.hexdigest("123") + get :with_stale + assert_equal 304, @response.status.to_i + end + end + + class BufferTest < ActionController::TestCase + def test_nil_callback + buf = ActionController::Live::Buffer.new nil + assert buf.call_on_error end end end diff --git a/actionpack/test/controller/localized_templates_test.rb b/actionpack/test/controller/localized_templates_test.rb index 41ff2f3809..2be947c648 100644 --- a/actionpack/test/controller/localized_templates_test.rb +++ b/actionpack/test/controller/localized_templates_test.rb @@ -8,10 +8,18 @@ end class LocalizedTemplatesTest < ActionController::TestCase tests LocalizedController + setup do + @old_locale = I18n.locale + end + + teardown do + I18n.locale = @old_locale + end + def test_localized_template_is_used I18n.locale = :de get :hello_world - assert_equal "Gutten Tag", @response.body + assert_equal "Guten Tag", @response.body end def test_default_locale_template_is_used_when_locale_is_missing @@ -19,4 +27,20 @@ class LocalizedTemplatesTest < ActionController::TestCase get :hello_world assert_equal "Hello World", @response.body end -end
\ No newline at end of file + + def test_use_fallback_locales + I18n.locale = :"de-AT" + I18n.backend.class.send(:include, I18n::Backend::Fallbacks) + I18n.fallbacks[:"de-AT"] = [:de] + + get :hello_world + assert_equal "Guten Tag", @response.body + end + + def test_localized_template_has_correct_header_with_no_format_in_template_name + I18n.locale = :it + get :hello_world + assert_equal "Ciao Mondo", @response.body + assert_equal "text/html", @response.content_type + end +end diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb index 075347be52..864c6ee130 100644 --- a/actionpack/test/controller/log_subscriber_test.rb +++ b/actionpack/test/controller/log_subscriber_test.rb @@ -73,6 +73,16 @@ module Another def with_action_not_found raise AbstractController::ActionNotFound end + + def append_info_to_payload(payload) + super + payload[:test_key] = "test_value" + @last_payload = payload + end + + def last_payload + @last_payload + end end end @@ -85,7 +95,7 @@ class ACLogSubscriberTest < ActionController::TestCase @old_logger = ActionController::Base.logger - @cache_path = File.expand_path('../temp/test_cache', File.dirname(__FILE__)) + @cache_path = File.join Dir.tmpdir, Dir::Tmpname.make_tmpname('tmp', 'cache') @controller.cache_store = :file_store, @cache_path ActionController::LogSubscriber.attach_to :action_controller end @@ -137,6 +147,17 @@ class ACLogSubscriberTest < ActionController::TestCase assert_equal 'Parameters: {"id"=>"10"}', logs[1] end + def test_multiple_process_with_parameters + get :show, :id => '10' + get :show, :id => '20' + + wait + + assert_equal 6, logs.size + assert_equal 'Parameters: {"id"=>"10"}', logs[1] + assert_equal 'Parameters: {"id"=>"20"}', logs[4] + end + def test_process_action_with_wrapped_parameters @request.env['CONTENT_TYPE'] = 'application/json' post :show, :id => '10', :name => 'jose' @@ -152,6 +173,16 @@ class ACLogSubscriberTest < ActionController::TestCase assert_match(/\(Views: [\d.]+ms\)/, logs[1]) end + def test_append_info_to_payload_is_called_even_with_exception + begin + get :with_exception + wait + rescue Exception + end + + assert_equal "test_value", @controller.last_payload[:test_key] + end + def test_process_action_with_filter_parameters @request.env["action_dispatch.parameter_filter"] = [:lifo, :amount] diff --git a/actionpack/test/controller/mime/accept_format_test.rb b/actionpack/test/controller/mime/accept_format_test.rb new file mode 100644 index 0000000000..811c507af2 --- /dev/null +++ b/actionpack/test/controller/mime/accept_format_test.rb @@ -0,0 +1,92 @@ +require 'abstract_unit' + +class StarStarMimeController < ActionController::Base + layout nil + + def index + render + end +end + +class StarStarMimeControllerTest < ActionController::TestCase + def test_javascript_with_format + @request.accept = "text/javascript" + get :index, :format => 'js' + assert_match "function addition(a,b){ return a+b; }", @response.body + end + + def test_javascript_with_no_format + @request.accept = "text/javascript" + get :index + assert_match "function addition(a,b){ return a+b; }", @response.body + end + + def test_javascript_with_no_format_only_star_star + @request.accept = "*/*" + get :index + assert_match "function addition(a,b){ return a+b; }", @response.body + end +end + +class AbstractPostController < ActionController::Base + self.view_paths = File.dirname(__FILE__) + "/../../fixtures/post_test/" +end + +# For testing layouts which are set automatically +class PostController < AbstractPostController + around_action :with_iphone + + def index + respond_to(:html, :iphone, :js) + end + +protected + + def with_iphone + request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone" + yield + end +end + +class SuperPostController < PostController +end + +class MimeControllerLayoutsTest < ActionController::TestCase + tests PostController + + def setup + super + @request.host = "www.example.com" + Mime::Type.register_alias("text/html", :iphone) + end + + def teardown + super + Mime::Type.unregister(:iphone) + end + + def test_missing_layout_renders_properly + get :index + assert_equal '<html><div id="html">Hello Firefox</div></html>', @response.body + + @request.accept = "text/iphone" + get :index + assert_equal 'Hello iPhone', @response.body + end + + def test_format_with_inherited_layouts + @controller = SuperPostController.new + + get :index + assert_equal '<html><div id="html">Super Firefox</div></html>', @response.body + + @request.accept = "text/iphone" + get :index + assert_equal '<html><div id="super_iphone">Super iPhone</div></html>', @response.body + end + + def test_non_navigational_format_with_no_template_fallbacks_to_html_template_with_no_layout + get :index, :format => :js + assert_equal "Hello Firefox", @response.body + end +end diff --git a/actionpack/test/controller/mime/respond_to_test.rb b/actionpack/test/controller/mime/respond_to_test.rb new file mode 100644 index 0000000000..66d2fd7716 --- /dev/null +++ b/actionpack/test/controller/mime/respond_to_test.rb @@ -0,0 +1,784 @@ +require 'abstract_unit' + +class RespondToController < ActionController::Base + layout :set_layout + + def html_xml_or_rss + respond_to do |type| + type.html { render :text => "HTML" } + type.xml { render :text => "XML" } + type.rss { render :text => "RSS" } + type.all { render :text => "Nothing" } + end + end + + def js_or_html + respond_to do |type| + type.html { render :text => "HTML" } + type.js { render :text => "JS" } + type.all { render :text => "Nothing" } + end + end + + def json_or_yaml + respond_to do |type| + type.json { render :text => "JSON" } + type.yaml { render :text => "YAML" } + end + end + + def html_or_xml + respond_to do |type| + type.html { render :text => "HTML" } + type.xml { render :text => "XML" } + type.all { render :text => "Nothing" } + end + end + + def json_xml_or_html + respond_to do |type| + type.json { render :text => 'JSON' } + type.xml { render :xml => 'XML' } + type.html { render :text => 'HTML' } + end + end + + + def forced_xml + request.format = :xml + + respond_to do |type| + type.html { render :text => "HTML" } + type.xml { render :text => "XML" } + end + end + + def just_xml + respond_to do |type| + type.xml { render :text => "XML" } + end + end + + def using_defaults + respond_to do |type| + type.html + type.xml + end + end + + def using_defaults_with_type_list + respond_to(:html, :xml) + end + + def using_defaults_with_all + respond_to do |type| + type.html + type.all{ render text: "ALL" } + end + end + + def made_for_content_type + respond_to do |type| + type.rss { render :text => "RSS" } + type.atom { render :text => "ATOM" } + type.all { render :text => "Nothing" } + end + end + + def custom_type_handling + respond_to do |type| + type.html { render :text => "HTML" } + type.custom("application/crazy-xml") { render :text => "Crazy XML" } + type.all { render :text => "Nothing" } + end + end + + + def custom_constant_handling + respond_to do |type| + type.html { render :text => "HTML" } + type.mobile { render :text => "Mobile" } + end + end + + def custom_constant_handling_without_block + respond_to do |type| + type.html { render :text => "HTML" } + type.mobile + end + end + + def handle_any + respond_to do |type| + type.html { render :text => "HTML" } + type.any(:js, :xml) { render :text => "Either JS or XML" } + end + end + + def handle_any_any + respond_to do |type| + type.html { render :text => 'HTML' } + type.any { render :text => 'Whatever you ask for, I got it' } + end + end + + def all_types_with_layout + respond_to do |type| + type.html + end + end + + def json_with_callback + respond_to do |type| + type.json { render :json => 'JS', :callback => 'alert' } + end + end + + def iphone_with_html_response_type + request.format = :iphone if request.env["HTTP_ACCEPT"] == "text/iphone" + + respond_to do |type| + type.html { @type = "Firefox" } + type.iphone { @type = "iPhone" } + end + end + + def iphone_with_html_response_type_without_layout + request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone" + + respond_to do |type| + type.html { @type = "Firefox"; render :action => "iphone_with_html_response_type" } + type.iphone { @type = "iPhone" ; render :action => "iphone_with_html_response_type" } + end + end + + def variant_with_implicit_rendering + end + + def variant_with_format_and_custom_render + request.variant = :mobile + + respond_to do |type| + type.html { render text: "mobile" } + end + end + + def multiple_variants_for_format + respond_to do |type| + type.html do |html| + html.tablet { render text: "tablet" } + html.phone { render text: "phone" } + end + end + end + + def variant_plus_none_for_format + respond_to do |format| + format.html do |variant| + variant.phone { render text: "phone" } + variant.none + end + end + end + + def variant_inline_syntax + respond_to do |format| + format.js { render text: "js" } + format.html.none { render text: "none" } + format.html.phone { render text: "phone" } + end + end + + def variant_inline_syntax_without_block + respond_to do |format| + format.js + format.html.none + format.html.phone + end + end + + def variant_any + respond_to do |format| + format.html do |variant| + variant.any(:tablet, :phablet){ render text: "any" } + variant.phone { render text: "phone" } + end + end + end + + def variant_any_any + respond_to do |format| + format.html do |variant| + variant.any { render text: "any" } + variant.phone { render text: "phone" } + end + end + end + + def variant_inline_any + respond_to do |format| + format.html.any(:tablet, :phablet){ render text: "any" } + format.html.phone { render text: "phone" } + end + end + + def variant_inline_any_any + respond_to do |format| + format.html.phone { render text: "phone" } + format.html.any { render text: "any" } + end + end + + def variant_any_implicit_render + respond_to do |format| + format.html.phone + format.html.any(:tablet, :phablet) + end + end + + def variant_any_with_none + respond_to do |format| + format.html.any(:none, :phone){ render text: "none or phone" } + end + end + + def format_any_variant_any + respond_to do |format| + format.html { render text: "HTML" } + format.any(:js, :xml) do |variant| + variant.phone{ render text: "phone" } + variant.any(:tablet, :phablet){ render text: "tablet" } + end + end + end + + protected + def set_layout + case action_name + when "all_types_with_layout", "iphone_with_html_response_type" + "respond_to/layouts/standard" + when "iphone_with_html_response_type_without_layout" + "respond_to/layouts/missing" + end + end +end + +class RespondToControllerTest < ActionController::TestCase + def setup + super + @request.host = "www.example.com" + Mime::Type.register_alias("text/html", :iphone) + Mime::Type.register("text/x-mobile", :mobile) + end + + def teardown + super + Mime::Type.unregister(:iphone) + Mime::Type.unregister(:mobile) + end + + def test_html + @request.accept = "text/html" + get :js_or_html + assert_equal 'HTML', @response.body + + get :html_or_xml + assert_equal 'HTML', @response.body + + assert_raises(ActionController::UnknownFormat) do + get :just_xml + end + end + + def test_all + @request.accept = "*/*" + get :js_or_html + assert_equal 'HTML', @response.body # js is not part of all + + get :html_or_xml + assert_equal 'HTML', @response.body + + get :just_xml + assert_equal 'XML', @response.body + end + + def test_xml + @request.accept = "application/xml" + get :html_xml_or_rss + assert_equal 'XML', @response.body + end + + def test_js_or_html + @request.accept = "text/javascript, text/html" + xhr :get, :js_or_html + assert_equal 'JS', @response.body + + @request.accept = "text/javascript, text/html" + xhr :get, :html_or_xml + assert_equal 'HTML', @response.body + + @request.accept = "text/javascript, text/html" + + assert_raises(ActionController::UnknownFormat) do + xhr :get, :just_xml + end + end + + def test_json_or_yaml_with_leading_star_star + @request.accept = "*/*, application/json" + get :json_xml_or_html + assert_equal 'HTML', @response.body + + @request.accept = "*/* , application/json" + get :json_xml_or_html + assert_equal 'HTML', @response.body + end + + def test_json_or_yaml + xhr :get, :json_or_yaml + assert_equal 'JSON', @response.body + + get :json_or_yaml, :format => 'json' + assert_equal 'JSON', @response.body + + get :json_or_yaml, :format => 'yaml' + assert_equal 'YAML', @response.body + + { 'YAML' => %w(text/yaml), + 'JSON' => %w(application/json text/x-json) + }.each do |body, content_types| + content_types.each do |content_type| + @request.accept = content_type + get :json_or_yaml + assert_equal body, @response.body + end + end + end + + def test_js_or_anything + @request.accept = "text/javascript, */*" + xhr :get, :js_or_html + assert_equal 'JS', @response.body + + xhr :get, :html_or_xml + assert_equal 'HTML', @response.body + + xhr :get, :just_xml + assert_equal 'XML', @response.body + end + + def test_using_defaults + @request.accept = "*/*" + get :using_defaults + assert_equal "text/html", @response.content_type + assert_equal 'Hello world!', @response.body + + @request.accept = "application/xml" + get :using_defaults + assert_equal "application/xml", @response.content_type + assert_equal "<p>Hello world!</p>\n", @response.body + end + + def test_using_defaults_with_all + @request.accept = "*/*" + get :using_defaults_with_all + assert_equal "HTML!", @response.body.strip + + @request.accept = "text/html" + get :using_defaults_with_all + assert_equal "HTML!", @response.body.strip + + @request.accept = "application/json" + get :using_defaults_with_all + assert_equal "ALL", @response.body + end + + def test_using_defaults_with_type_list + @request.accept = "*/*" + get :using_defaults_with_type_list + assert_equal "text/html", @response.content_type + assert_equal 'Hello world!', @response.body + + @request.accept = "application/xml" + get :using_defaults_with_type_list + assert_equal "application/xml", @response.content_type + assert_equal "<p>Hello world!</p>\n", @response.body + end + + def test_with_atom_content_type + @request.accept = "" + @request.env["CONTENT_TYPE"] = "application/atom+xml" + xhr :get, :made_for_content_type + assert_equal "ATOM", @response.body + end + + def test_with_rss_content_type + @request.accept = "" + @request.env["CONTENT_TYPE"] = "application/rss+xml" + xhr :get, :made_for_content_type + assert_equal "RSS", @response.body + end + + def test_synonyms + @request.accept = "application/javascript" + get :js_or_html + assert_equal 'JS', @response.body + + @request.accept = "application/x-xml" + get :html_xml_or_rss + assert_equal "XML", @response.body + end + + def test_custom_types + @request.accept = "application/crazy-xml" + get :custom_type_handling + assert_equal "application/crazy-xml", @response.content_type + assert_equal 'Crazy XML', @response.body + + @request.accept = "text/html" + get :custom_type_handling + assert_equal "text/html", @response.content_type + assert_equal 'HTML', @response.body + end + + def test_xhtml_alias + @request.accept = "application/xhtml+xml,application/xml" + get :html_or_xml + assert_equal 'HTML', @response.body + end + + def test_firefox_simulation + @request.accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" + get :html_or_xml + assert_equal 'HTML', @response.body + end + + def test_handle_any + @request.accept = "*/*" + get :handle_any + assert_equal 'HTML', @response.body + + @request.accept = "text/javascript" + get :handle_any + assert_equal 'Either JS or XML', @response.body + + @request.accept = "text/xml" + get :handle_any + assert_equal 'Either JS or XML', @response.body + end + + def test_handle_any_any + @request.accept = "*/*" + get :handle_any_any + assert_equal 'HTML', @response.body + end + + def test_handle_any_any_parameter_format + get :handle_any_any, {:format=>'html'} + assert_equal 'HTML', @response.body + end + + def test_handle_any_any_explicit_html + @request.accept = "text/html" + get :handle_any_any + assert_equal 'HTML', @response.body + end + + def test_handle_any_any_javascript + @request.accept = "text/javascript" + get :handle_any_any + assert_equal 'Whatever you ask for, I got it', @response.body + end + + def test_handle_any_any_xml + @request.accept = "text/xml" + get :handle_any_any + assert_equal 'Whatever you ask for, I got it', @response.body + end + + def test_handle_any_any_unkown_format + get :handle_any_any, { format: 'php' } + assert_equal 'Whatever you ask for, I got it', @response.body + end + + def test_browser_check_with_any_any + @request.accept = "application/json, application/xml" + get :json_xml_or_html + assert_equal 'JSON', @response.body + + @request.accept = "application/json, application/xml, */*" + get :json_xml_or_html + assert_equal 'HTML', @response.body + end + + def test_html_type_with_layout + @request.accept = "text/html" + get :all_types_with_layout + assert_equal '<html><div id="html">HTML for all_types_with_layout</div></html>', @response.body + end + + def test_json_with_callback_sets_javascript_content_type + @request.accept = 'application/json' + get :json_with_callback + assert_equal '/**/alert(JS)', @response.body + assert_equal 'text/javascript', @response.content_type + end + + def test_xhr + xhr :get, :js_or_html + assert_equal 'JS', @response.body + end + + def test_custom_constant + get :custom_constant_handling, :format => "mobile" + assert_equal "text/x-mobile", @response.content_type + assert_equal "Mobile", @response.body + end + + def test_custom_constant_handling_without_block + get :custom_constant_handling_without_block, :format => "mobile" + assert_equal "text/x-mobile", @response.content_type + assert_equal "Mobile", @response.body + end + + def test_forced_format + get :html_xml_or_rss + assert_equal "HTML", @response.body + + get :html_xml_or_rss, :format => "html" + assert_equal "HTML", @response.body + + get :html_xml_or_rss, :format => "xml" + assert_equal "XML", @response.body + + get :html_xml_or_rss, :format => "rss" + assert_equal "RSS", @response.body + end + + def test_internally_forced_format + get :forced_xml + assert_equal "XML", @response.body + + get :forced_xml, :format => "html" + assert_equal "XML", @response.body + end + + def test_extension_synonyms + get :html_xml_or_rss, :format => "xhtml" + assert_equal "HTML", @response.body + end + + def test_render_action_for_html + @controller.instance_eval do + def render(*args) + @action = args.first[:action] unless args.empty? + @action ||= action_name + + response.body = "#{@action} - #{formats}" + end + end + + get :using_defaults + assert_equal "using_defaults - #{[:html]}", @response.body + + get :using_defaults, :format => "xml" + assert_equal "using_defaults - #{[:xml]}", @response.body + end + + def test_format_with_custom_response_type + get :iphone_with_html_response_type + assert_equal '<html><div id="html">Hello future from Firefox!</div></html>', @response.body + + get :iphone_with_html_response_type, :format => "iphone" + assert_equal "text/html", @response.content_type + assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body + end + + def test_format_with_custom_response_type_and_request_headers + @request.accept = "text/iphone" + get :iphone_with_html_response_type + assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body + assert_equal "text/html", @response.content_type + end + + def test_invalid_format + assert_raises(ActionController::UnknownFormat) do + get :using_defaults, :format => "invalidformat" + end + end + + def test_invalid_variant + @request.variant = :invalid + assert_raises(ActionView::MissingTemplate) do + get :variant_with_implicit_rendering + end + end + + def test_variant_not_set_regular_template_missing + assert_raises(ActionView::MissingTemplate) do + get :variant_with_implicit_rendering + end + end + + def test_variant_with_implicit_rendering + @request.variant = :mobile + get :variant_with_implicit_rendering + assert_equal "text/html", @response.content_type + assert_equal "mobile", @response.body + end + + def test_variant_with_format_and_custom_render + @request.variant = :phone + get :variant_with_format_and_custom_render + assert_equal "text/html", @response.content_type + assert_equal "mobile", @response.body + end + + def test_multiple_variants_for_format + @request.variant = :tablet + get :multiple_variants_for_format + assert_equal "text/html", @response.content_type + assert_equal "tablet", @response.body + end + + def test_no_variant_in_variant_setup + get :variant_plus_none_for_format + assert_equal "text/html", @response.content_type + assert_equal "none", @response.body + end + + def test_variant_inline_syntax + get :variant_inline_syntax, format: :js + assert_equal "text/javascript", @response.content_type + assert_equal "js", @response.body + + get :variant_inline_syntax + assert_equal "text/html", @response.content_type + assert_equal "none", @response.body + + @request.variant = :phone + get :variant_inline_syntax + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + end + + def test_variant_inline_syntax_without_block + @request.variant = :phone + get :variant_inline_syntax_without_block + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + end + + def test_variant_any + @request.variant = :phone + get :variant_any + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + + @request.variant = :tablet + get :variant_any + assert_equal "text/html", @response.content_type + assert_equal "any", @response.body + + @request.variant = :phablet + get :variant_any + assert_equal "text/html", @response.content_type + assert_equal "any", @response.body + end + + def test_variant_any_any + get :variant_any_any + assert_equal "text/html", @response.content_type + assert_equal "any", @response.body + + @request.variant = :phone + get :variant_any_any + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + + @request.variant = :yolo + get :variant_any_any + assert_equal "text/html", @response.content_type + assert_equal "any", @response.body + end + + def test_variant_inline_any + @request.variant = :phone + get :variant_any + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + + @request.variant = :tablet + get :variant_inline_any + assert_equal "text/html", @response.content_type + assert_equal "any", @response.body + + @request.variant = :phablet + get :variant_inline_any + assert_equal "text/html", @response.content_type + assert_equal "any", @response.body + end + + def test_variant_inline_any_any + @request.variant = :phone + get :variant_inline_any_any + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + + @request.variant = :yolo + get :variant_inline_any_any + assert_equal "text/html", @response.content_type + assert_equal "any", @response.body + end + + def test_variant_any_implicit_render + @request.variant = :tablet + get :variant_any_implicit_render + assert_equal "text/html", @response.content_type + assert_equal "tablet", @response.body + + @request.variant = :phablet + get :variant_any_implicit_render + assert_equal "text/html", @response.content_type + assert_equal "phablet", @response.body + end + + def test_variant_any_with_none + get :variant_any_with_none + assert_equal "text/html", @response.content_type + assert_equal "none or phone", @response.body + + @request.variant = :phone + get :variant_any_with_none + assert_equal "text/html", @response.content_type + assert_equal "none or phone", @response.body + end + + def test_format_any_variant_any + @request.variant = :tablet + get :format_any_variant_any, format: :js + assert_equal "text/javascript", @response.content_type + assert_equal "tablet", @response.body + end + + def test_variant_negotiation_inline_syntax + @request.variant = [:tablet, :phone] + get :variant_inline_syntax_without_block + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + end + + def test_variant_negotiation_block_syntax + @request.variant = [:tablet, :phone] + get :variant_plus_none_for_format + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + end + + def test_variant_negotiation_without_block + @request.variant = [:tablet, :phone] + get :variant_inline_syntax_without_block + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + end +end diff --git a/actionpack/test/controller/mime/responders_test.rb b/actionpack/test/controller/mime/responders_test.rb new file mode 100644 index 0000000000..032b4c0ab1 --- /dev/null +++ b/actionpack/test/controller/mime/responders_test.rb @@ -0,0 +1,32 @@ +require 'abstract_unit' +require 'controller/fake_models' + +class ResponderTest < ActionController::TestCase + def test_class_level_respond_to + e = assert_raises(NoMethodError) do + Class.new(ActionController::Base) do + respond_to :json + end + end + + assert_includes e.message, '`responders` gem' + assert_includes e.message, '~> 2.0' + end + + def test_respond_with + klass = Class.new(ActionController::Base) do + def index + respond_with Customer.new("david", 13) + end + end + + @controller = klass.new + + e = assert_raises(NoMethodError) do + get :index + end + + assert_includes e.message, '`responders` gem' + assert_includes e.message, '~> 2.0' + end +end diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb deleted file mode 100644 index ed013e2185..0000000000 --- a/actionpack/test/controller/mime_responds_test.rb +++ /dev/null @@ -1,1233 +0,0 @@ -require 'abstract_unit' -require 'controller/fake_models' -require 'active_support/core_ext/hash/conversions' - -class StarStarMimeController < ActionController::Base - layout nil - - def index - render - end -end - -class RespondToController < ActionController::Base - layout :set_layout - - def html_xml_or_rss - respond_to do |type| - type.html { render :text => "HTML" } - type.xml { render :text => "XML" } - type.rss { render :text => "RSS" } - type.all { render :text => "Nothing" } - end - end - - def js_or_html - respond_to do |type| - type.html { render :text => "HTML" } - type.js { render :text => "JS" } - type.all { render :text => "Nothing" } - end - end - - def json_or_yaml - respond_to do |type| - type.json { render :text => "JSON" } - type.yaml { render :text => "YAML" } - end - end - - def html_or_xml - respond_to do |type| - type.html { render :text => "HTML" } - type.xml { render :text => "XML" } - type.all { render :text => "Nothing" } - end - end - - def json_xml_or_html - respond_to do |type| - type.json { render :text => 'JSON' } - type.xml { render :xml => 'XML' } - type.html { render :text => 'HTML' } - end - end - - - def forced_xml - request.format = :xml - - respond_to do |type| - type.html { render :text => "HTML" } - type.xml { render :text => "XML" } - end - end - - def just_xml - respond_to do |type| - type.xml { render :text => "XML" } - end - end - - def using_defaults - respond_to do |type| - type.html - type.xml - end - end - - def using_defaults_with_type_list - respond_to(:html, :xml) - end - - def made_for_content_type - respond_to do |type| - type.rss { render :text => "RSS" } - type.atom { render :text => "ATOM" } - type.all { render :text => "Nothing" } - end - end - - def custom_type_handling - respond_to do |type| - type.html { render :text => "HTML" } - type.custom("application/crazy-xml") { render :text => "Crazy XML" } - type.all { render :text => "Nothing" } - end - end - - - def custom_constant_handling - respond_to do |type| - type.html { render :text => "HTML" } - type.mobile { render :text => "Mobile" } - end - end - - def custom_constant_handling_without_block - respond_to do |type| - type.html { render :text => "HTML" } - type.mobile - end - end - - def handle_any - respond_to do |type| - type.html { render :text => "HTML" } - type.any(:js, :xml) { render :text => "Either JS or XML" } - end - end - - def handle_any_any - respond_to do |type| - type.html { render :text => 'HTML' } - type.any { render :text => 'Whatever you ask for, I got it' } - end - end - - def all_types_with_layout - respond_to do |type| - type.html - end - end - - def iphone_with_html_response_type - request.format = :iphone if request.env["HTTP_ACCEPT"] == "text/iphone" - - respond_to do |type| - type.html { @type = "Firefox" } - type.iphone { @type = "iPhone" } - end - end - - def iphone_with_html_response_type_without_layout - request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone" - - respond_to do |type| - type.html { @type = "Firefox"; render :action => "iphone_with_html_response_type" } - type.iphone { @type = "iPhone" ; render :action => "iphone_with_html_response_type" } - end - end - - protected - def set_layout - case action_name - when "all_types_with_layout", "iphone_with_html_response_type" - "respond_to/layouts/standard" - when "iphone_with_html_response_type_without_layout" - "respond_to/layouts/missing" - end - end -end - -class StarStarMimeControllerTest < ActionController::TestCase - tests StarStarMimeController - - def test_javascript_with_format - @request.accept = "text/javascript" - get :index, :format => 'js' - assert_match "function addition(a,b){ return a+b; }", @response.body - end - - def test_javascript_with_no_format - @request.accept = "text/javascript" - get :index - assert_match "function addition(a,b){ return a+b; }", @response.body - end - - def test_javascript_with_no_format_only_star_star - @request.accept = "*/*" - get :index - assert_match "function addition(a,b){ return a+b; }", @response.body - end - -end - -class RespondToControllerTest < ActionController::TestCase - tests RespondToController - - def setup - super - @request.host = "www.example.com" - Mime::Type.register_alias("text/html", :iphone) - Mime::Type.register("text/x-mobile", :mobile) - end - - def teardown - super - Mime::Type.unregister(:iphone) - Mime::Type.unregister(:mobile) - end - - def test_html - @request.accept = "text/html" - get :js_or_html - assert_equal 'HTML', @response.body - - get :html_or_xml - assert_equal 'HTML', @response.body - - assert_raises(ActionController::UnknownFormat) do - get :just_xml - end - end - - def test_all - @request.accept = "*/*" - get :js_or_html - assert_equal 'HTML', @response.body # js is not part of all - - get :html_or_xml - assert_equal 'HTML', @response.body - - get :just_xml - assert_equal 'XML', @response.body - end - - def test_xml - @request.accept = "application/xml" - get :html_xml_or_rss - assert_equal 'XML', @response.body - end - - def test_js_or_html - @request.accept = "text/javascript, text/html" - xhr :get, :js_or_html - assert_equal 'JS', @response.body - - @request.accept = "text/javascript, text/html" - xhr :get, :html_or_xml - assert_equal 'HTML', @response.body - - @request.accept = "text/javascript, text/html" - - assert_raises(ActionController::UnknownFormat) do - xhr :get, :just_xml - end - end - - def test_json_or_yaml_with_leading_star_star - @request.accept = "*/*, application/json" - get :json_xml_or_html - assert_equal 'HTML', @response.body - - @request.accept = "*/* , application/json" - get :json_xml_or_html - assert_equal 'HTML', @response.body - end - - def test_json_or_yaml - xhr :get, :json_or_yaml - assert_equal 'JSON', @response.body - - get :json_or_yaml, :format => 'json' - assert_equal 'JSON', @response.body - - get :json_or_yaml, :format => 'yaml' - assert_equal 'YAML', @response.body - - { 'YAML' => %w(text/yaml), - 'JSON' => %w(application/json text/x-json) - }.each do |body, content_types| - content_types.each do |content_type| - @request.accept = content_type - get :json_or_yaml - assert_equal body, @response.body - end - end - end - - def test_js_or_anything - @request.accept = "text/javascript, */*" - xhr :get, :js_or_html - assert_equal 'JS', @response.body - - xhr :get, :html_or_xml - assert_equal 'HTML', @response.body - - xhr :get, :just_xml - assert_equal 'XML', @response.body - end - - def test_using_defaults - @request.accept = "*/*" - get :using_defaults - assert_equal "text/html", @response.content_type - assert_equal 'Hello world!', @response.body - - @request.accept = "application/xml" - get :using_defaults - assert_equal "application/xml", @response.content_type - assert_equal "<p>Hello world!</p>\n", @response.body - end - - def test_using_defaults_with_type_list - @request.accept = "*/*" - get :using_defaults_with_type_list - assert_equal "text/html", @response.content_type - assert_equal 'Hello world!', @response.body - - @request.accept = "application/xml" - get :using_defaults_with_type_list - assert_equal "application/xml", @response.content_type - assert_equal "<p>Hello world!</p>\n", @response.body - end - - def test_with_atom_content_type - @request.accept = "" - @request.env["CONTENT_TYPE"] = "application/atom+xml" - xhr :get, :made_for_content_type - assert_equal "ATOM", @response.body - end - - def test_with_rss_content_type - @request.accept = "" - @request.env["CONTENT_TYPE"] = "application/rss+xml" - xhr :get, :made_for_content_type - assert_equal "RSS", @response.body - end - - def test_synonyms - @request.accept = "application/javascript" - get :js_or_html - assert_equal 'JS', @response.body - - @request.accept = "application/x-xml" - get :html_xml_or_rss - assert_equal "XML", @response.body - end - - def test_custom_types - @request.accept = "application/crazy-xml" - get :custom_type_handling - assert_equal "application/crazy-xml", @response.content_type - assert_equal 'Crazy XML', @response.body - - @request.accept = "text/html" - get :custom_type_handling - assert_equal "text/html", @response.content_type - assert_equal 'HTML', @response.body - end - - def test_xhtml_alias - @request.accept = "application/xhtml+xml,application/xml" - get :html_or_xml - assert_equal 'HTML', @response.body - end - - def test_firefox_simulation - @request.accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" - get :html_or_xml - assert_equal 'HTML', @response.body - end - - def test_handle_any - @request.accept = "*/*" - get :handle_any - assert_equal 'HTML', @response.body - - @request.accept = "text/javascript" - get :handle_any - assert_equal 'Either JS or XML', @response.body - - @request.accept = "text/xml" - get :handle_any - assert_equal 'Either JS or XML', @response.body - end - - def test_handle_any_any - @request.accept = "*/*" - get :handle_any_any - assert_equal 'HTML', @response.body - end - - def test_handle_any_any_parameter_format - get :handle_any_any, {:format=>'html'} - assert_equal 'HTML', @response.body - end - - def test_handle_any_any_explicit_html - @request.accept = "text/html" - get :handle_any_any - assert_equal 'HTML', @response.body - end - - def test_handle_any_any_javascript - @request.accept = "text/javascript" - get :handle_any_any - assert_equal 'Whatever you ask for, I got it', @response.body - end - - def test_handle_any_any_xml - @request.accept = "text/xml" - get :handle_any_any - assert_equal 'Whatever you ask for, I got it', @response.body - end - - def test_browser_check_with_any_any - @request.accept = "application/json, application/xml" - get :json_xml_or_html - assert_equal 'JSON', @response.body - - @request.accept = "application/json, application/xml, */*" - get :json_xml_or_html - assert_equal 'HTML', @response.body - end - - def test_html_type_with_layout - @request.accept = "text/html" - get :all_types_with_layout - assert_equal '<html><div id="html">HTML for all_types_with_layout</div></html>', @response.body - end - - def test_xhr - xhr :get, :js_or_html - assert_equal 'JS', @response.body - end - - def test_custom_constant - get :custom_constant_handling, :format => "mobile" - assert_equal "text/x-mobile", @response.content_type - assert_equal "Mobile", @response.body - end - - def test_custom_constant_handling_without_block - get :custom_constant_handling_without_block, :format => "mobile" - assert_equal "text/x-mobile", @response.content_type - assert_equal "Mobile", @response.body - end - - def test_forced_format - get :html_xml_or_rss - assert_equal "HTML", @response.body - - get :html_xml_or_rss, :format => "html" - assert_equal "HTML", @response.body - - get :html_xml_or_rss, :format => "xml" - assert_equal "XML", @response.body - - get :html_xml_or_rss, :format => "rss" - assert_equal "RSS", @response.body - end - - def test_internally_forced_format - get :forced_xml - assert_equal "XML", @response.body - - get :forced_xml, :format => "html" - assert_equal "XML", @response.body - end - - def test_extension_synonyms - get :html_xml_or_rss, :format => "xhtml" - assert_equal "HTML", @response.body - end - - def test_render_action_for_html - @controller.instance_eval do - def render(*args) - @action = args.first[:action] unless args.empty? - @action ||= action_name - - response.body = "#{@action} - #{formats}" - end - end - - get :using_defaults - assert_equal "using_defaults - #{[:html].to_s}", @response.body - - get :using_defaults, :format => "xml" - assert_equal "using_defaults - #{[:xml].to_s}", @response.body - end - - def test_format_with_custom_response_type - get :iphone_with_html_response_type - assert_equal '<html><div id="html">Hello future from Firefox!</div></html>', @response.body - - get :iphone_with_html_response_type, :format => "iphone" - assert_equal "text/html", @response.content_type - assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body - end - - def test_format_with_custom_response_type_and_request_headers - @request.accept = "text/iphone" - get :iphone_with_html_response_type - assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body - assert_equal "text/html", @response.content_type - end - - def test_invalid_format - assert_raises(ActionController::UnknownFormat) do - get :using_defaults, :format => "invalidformat" - end - end -end - -class RespondWithController < ActionController::Base - respond_to :html, :json, :touch - respond_to :xml, :except => :using_resource_with_block - respond_to :js, :only => [ :using_resource_with_block, :using_resource, 'using_hash_resource' ] - - def using_resource - respond_with(resource) - end - - def using_hash_resource - respond_with({:result => resource}) - end - - def using_resource_with_block - respond_with(resource) do |format| - format.csv { render :text => "CSV" } - end - end - - def using_resource_with_overwrite_block - respond_with(resource) do |format| - format.html { render :text => "HTML" } - end - end - - def using_resource_with_collection - respond_with([resource, Customer.new("jamis", 9)]) - end - - def using_resource_with_parent - respond_with(Quiz::Store.new("developer?", 11), Customer.new("david", 13)) - end - - def using_resource_with_status_and_location - respond_with(resource, :location => "http://test.host/", :status => :created) - end - - def using_invalid_resource_with_template - respond_with(resource) - end - - def using_options_with_template - @customer = resource - respond_with(@customer, :status => 123, :location => "http://test.host/") - end - - def using_resource_with_responder - responder = proc { |c, r, o| c.render :text => "Resource name is #{r.first.name}" } - respond_with(resource, :responder => responder) - end - - def using_resource_with_action - respond_with(resource, :action => :foo) do |format| - format.html { raise ActionView::MissingTemplate.new([], "bar", ["foo"], {}, false) } - end - end - - def using_responder_with_respond - responder = Class.new(ActionController::Responder) do - def respond; @controller.render :text => "respond #{format}"; end - end - respond_with(resource, :responder => responder) - end - -protected - - def resource - Customer.new("david", request.delete? ? nil : 13) - end -end - -class InheritedRespondWithController < RespondWithController - clear_respond_to - respond_to :xml, :json - - def index - respond_with(resource) do |format| - format.json { render :text => "JSON" } - end - end -end - -class RenderJsonRespondWithController < RespondWithController - clear_respond_to - respond_to :json - - def index - respond_with(resource) do |format| - format.json { render :json => RenderJsonTestException.new('boom') } - end - end - - def create - resource = ValidatedCustomer.new(params[:name], 1) - respond_with(resource) do |format| - format.json do - if resource.errors.empty? - render :json => { :valid => true } - else - render :json => { :valid => false } - end - end - end - end -end - -class EmptyRespondWithController < ActionController::Base - def index - respond_with(Customer.new("david", 13)) - end -end - -class RespondWithControllerTest < ActionController::TestCase - tests RespondWithController - - def setup - super - @request.host = "www.example.com" - Mime::Type.register_alias('text/html', :iphone) - Mime::Type.register_alias('text/html', :touch) - Mime::Type.register('text/x-mobile', :mobile) - end - - def teardown - super - Mime::Type.unregister(:iphone) - Mime::Type.unregister(:touch) - Mime::Type.unregister(:mobile) - end - - def test_using_resource - @request.accept = "application/xml" - get :using_resource - assert_equal "application/xml", @response.content_type - assert_equal "<name>david</name>", @response.body - - @request.accept = "application/json" - assert_raise ActionView::MissingTemplate do - get :using_resource - end - end - - def test_using_resource_with_js_simply_tries_to_render_the_template - @request.accept = "text/javascript" - get :using_resource - assert_equal "text/javascript", @response.content_type - assert_equal "alert(\"Hi\");", @response.body - end - - def test_using_hash_resource_with_js_raises_an_error_if_template_cant_be_found - @request.accept = "text/javascript" - assert_raise ActionView::MissingTemplate do - get :using_hash_resource - end - end - - def test_using_hash_resource - @request.accept = "application/xml" - get :using_hash_resource - assert_equal "application/xml", @response.content_type - assert_equal "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<hash>\n <name>david</name>\n</hash>\n", @response.body - - @request.accept = "application/json" - get :using_hash_resource - assert_equal "application/json", @response.content_type - assert @response.body.include?("result") - assert @response.body.include?('"name":"david"') - assert @response.body.include?('"id":13') - end - - def test_using_hash_resource_with_post - @request.accept = "application/json" - assert_raise ArgumentError, "Nil location provided. Can't build URI." do - post :using_hash_resource - end - end - - def test_using_resource_with_block - @request.accept = "*/*" - get :using_resource_with_block - assert_equal "text/html", @response.content_type - assert_equal 'Hello world!', @response.body - - @request.accept = "text/csv" - get :using_resource_with_block - assert_equal "text/csv", @response.content_type - assert_equal "CSV", @response.body - - @request.accept = "application/xml" - get :using_resource - assert_equal "application/xml", @response.content_type - assert_equal "<name>david</name>", @response.body - end - - def test_using_resource_with_overwrite_block - get :using_resource_with_overwrite_block - assert_equal "text/html", @response.content_type - assert_equal "HTML", @response.body - end - - def test_not_acceptable - @request.accept = "application/xml" - assert_raises(ActionController::UnknownFormat) do - get :using_resource_with_block - end - - @request.accept = "text/javascript" - assert_raises(ActionController::UnknownFormat) do - get :using_resource_with_overwrite_block - end - end - - def test_using_resource_for_post_with_html_redirects_on_success - with_test_route_set do - post :using_resource - assert_equal "text/html", @response.content_type - assert_equal 302, @response.status - assert_equal "http://www.example.com/customers/13", @response.location - assert @response.redirect? - end - end - - def test_using_resource_for_post_with_html_rerender_on_failure - with_test_route_set do - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - post :using_resource - assert_equal "text/html", @response.content_type - assert_equal 200, @response.status - assert_equal "New world!\n", @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_post_with_xml_yields_created_on_success - with_test_route_set do - @request.accept = "application/xml" - post :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 201, @response.status - assert_equal "<name>david</name>", @response.body - assert_equal "http://www.example.com/customers/13", @response.location - end - end - - def test_using_resource_for_post_with_xml_yields_unprocessable_entity_on_failure - with_test_route_set do - @request.accept = "application/xml" - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - post :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 422, @response.status - assert_equal errors.to_xml, @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_post_with_json_yields_unprocessable_entity_on_failure - with_test_route_set do - @request.accept = "application/json" - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - post :using_resource - assert_equal "application/json", @response.content_type - assert_equal 422, @response.status - errors = {:errors => errors} - assert_equal errors.to_json, @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_patch_with_html_redirects_on_success - with_test_route_set do - patch :using_resource - assert_equal "text/html", @response.content_type - assert_equal 302, @response.status - assert_equal "http://www.example.com/customers/13", @response.location - assert @response.redirect? - end - end - - def test_using_resource_for_patch_with_html_rerender_on_failure - with_test_route_set do - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - patch :using_resource - assert_equal "text/html", @response.content_type - assert_equal 200, @response.status - assert_equal "Edit world!\n", @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_patch_with_html_rerender_on_failure_even_on_method_override - with_test_route_set do - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - @request.env["rack.methodoverride.original_method"] = "POST" - patch :using_resource - assert_equal "text/html", @response.content_type - assert_equal 200, @response.status - assert_equal "Edit world!\n", @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_put_with_html_redirects_on_success - with_test_route_set do - put :using_resource - assert_equal "text/html", @response.content_type - assert_equal 302, @response.status - assert_equal "http://www.example.com/customers/13", @response.location - assert @response.redirect? - end - end - - def test_using_resource_for_put_with_html_rerender_on_failure - with_test_route_set do - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - put :using_resource - assert_equal "text/html", @response.content_type - assert_equal 200, @response.status - assert_equal "Edit world!\n", @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_put_with_html_rerender_on_failure_even_on_method_override - with_test_route_set do - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - @request.env["rack.methodoverride.original_method"] = "POST" - put :using_resource - assert_equal "text/html", @response.content_type - assert_equal 200, @response.status - assert_equal "Edit world!\n", @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_put_with_xml_yields_no_content_on_success - @request.accept = "application/xml" - put :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 204, @response.status - assert_equal "", @response.body - end - - def test_using_resource_for_put_with_json_yields_no_content_on_success - Customer.any_instance.stubs(:to_json).returns('{"name": "David"}') - @request.accept = "application/json" - put :using_resource - assert_equal "application/json", @response.content_type - assert_equal 204, @response.status - assert_equal "", @response.body - end - - def test_using_resource_for_put_with_xml_yields_unprocessable_entity_on_failure - @request.accept = "application/xml" - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - put :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 422, @response.status - assert_equal errors.to_xml, @response.body - assert_nil @response.location - end - - def test_using_resource_for_put_with_json_yields_unprocessable_entity_on_failure - @request.accept = "application/json" - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - put :using_resource - assert_equal "application/json", @response.content_type - assert_equal 422, @response.status - errors = {:errors => errors} - assert_equal errors.to_json, @response.body - assert_nil @response.location - end - - def test_using_resource_for_delete_with_html_redirects_on_success - with_test_route_set do - Customer.any_instance.stubs(:destroyed?).returns(true) - delete :using_resource - assert_equal "text/html", @response.content_type - assert_equal 302, @response.status - assert_equal "http://www.example.com/customers", @response.location - end - end - - def test_using_resource_for_delete_with_xml_yields_no_content_on_success - Customer.any_instance.stubs(:destroyed?).returns(true) - @request.accept = "application/xml" - delete :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 204, @response.status - assert_equal "", @response.body - end - - def test_using_resource_for_delete_with_json_yields_no_content_on_success - Customer.any_instance.stubs(:to_json).returns('{"name": "David"}') - Customer.any_instance.stubs(:destroyed?).returns(true) - @request.accept = "application/json" - delete :using_resource - assert_equal "application/json", @response.content_type - assert_equal 204, @response.status - assert_equal "", @response.body - end - - def test_using_resource_for_delete_with_html_redirects_on_failure - with_test_route_set do - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - Customer.any_instance.stubs(:destroyed?).returns(false) - delete :using_resource - assert_equal "text/html", @response.content_type - assert_equal 302, @response.status - assert_equal "http://www.example.com/customers", @response.location - end - end - - def test_using_resource_with_parent_for_get - @request.accept = "application/xml" - get :using_resource_with_parent - assert_equal "application/xml", @response.content_type - assert_equal 200, @response.status - assert_equal "<name>david</name>", @response.body - end - - def test_using_resource_with_parent_for_post - with_test_route_set do - @request.accept = "application/xml" - - post :using_resource_with_parent - assert_equal "application/xml", @response.content_type - assert_equal 201, @response.status - assert_equal "<name>david</name>", @response.body - assert_equal "http://www.example.com/quiz_stores/11/customers/13", @response.location - - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - post :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 422, @response.status - assert_equal errors.to_xml, @response.body - assert_nil @response.location - end - end - - def test_using_resource_with_collection - @request.accept = "application/xml" - get :using_resource_with_collection - assert_equal "application/xml", @response.content_type - assert_equal 200, @response.status - assert_match(/<name>david<\/name>/, @response.body) - assert_match(/<name>jamis<\/name>/, @response.body) - end - - def test_using_resource_with_action - @controller.instance_eval do - def render(params={}) - self.response_body = "#{params[:action]} - #{formats}" - end - end - - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - - post :using_resource_with_action - assert_equal "foo - #{[:html].to_s}", @controller.response.body - end - - def test_respond_as_responder_entry_point - @request.accept = "text/html" - get :using_responder_with_respond - assert_equal "respond html", @response.body - - @request.accept = "application/xml" - get :using_responder_with_respond - assert_equal "respond xml", @response.body - end - - def test_clear_respond_to - @controller = InheritedRespondWithController.new - @request.accept = "text/html" - assert_raises(ActionController::UnknownFormat) do - get :index - end - end - - def test_first_in_respond_to_has_higher_priority - @controller = InheritedRespondWithController.new - @request.accept = "*/*" - get :index - assert_equal "application/xml", @response.content_type - assert_equal "<name>david</name>", @response.body - end - - def test_block_inside_respond_with_is_rendered - @controller = InheritedRespondWithController.new - @request.accept = "application/json" - get :index - assert_equal "JSON", @response.body - end - - def test_render_json_object_responds_to_str_still_produce_json - @controller = RenderJsonRespondWithController.new - @request.accept = "application/json" - get :index, :format => :json - assert_match(/"message":"boom"/, @response.body) - assert_match(/"error":"RenderJsonTestException"/, @response.body) - end - - def test_api_response_with_valid_resource_respect_override_block - @controller = RenderJsonRespondWithController.new - post :create, :name => "sikachu", :format => :json - assert_equal '{"valid":true}', @response.body - end - - def test_api_response_with_invalid_resource_respect_override_block - @controller = RenderJsonRespondWithController.new - post :create, :name => "david", :format => :json - assert_equal '{"valid":false}', @response.body - end - - def test_no_double_render_is_raised - @request.accept = "text/html" - assert_raise ActionView::MissingTemplate do - get :using_resource - end - end - - def test_using_resource_with_status_and_location - @request.accept = "text/html" - post :using_resource_with_status_and_location - assert @response.redirect? - assert_equal "http://test.host/", @response.location - - @request.accept = "application/xml" - get :using_resource_with_status_and_location - assert_equal 201, @response.status - end - - def test_using_resource_with_status_and_location_with_invalid_resource - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - - @request.accept = "text/xml" - - post :using_resource_with_status_and_location - assert_equal errors.to_xml, @response.body - assert_equal 422, @response.status - assert_equal nil, @response.location - - put :using_resource_with_status_and_location - assert_equal errors.to_xml, @response.body - assert_equal 422, @response.status - assert_equal nil, @response.location - end - - def test_using_invalid_resource_with_template - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - - @request.accept = "text/xml" - - post :using_invalid_resource_with_template - assert_equal errors.to_xml, @response.body - assert_equal 422, @response.status - assert_equal nil, @response.location - - put :using_invalid_resource_with_template - assert_equal errors.to_xml, @response.body - assert_equal 422, @response.status - assert_equal nil, @response.location - end - - def test_using_options_with_template - @request.accept = "text/xml" - - post :using_options_with_template - assert_equal "<customer-name>david</customer-name>", @response.body - assert_equal 123, @response.status - assert_equal "http://test.host/", @response.location - - put :using_options_with_template - assert_equal "<customer-name>david</customer-name>", @response.body - assert_equal 123, @response.status - assert_equal "http://test.host/", @response.location - end - - def test_using_resource_with_responder - get :using_resource_with_responder - assert_equal "Resource name is david", @response.body - end - - def test_using_resource_with_set_responder - RespondWithController.responder = proc { |c, r, o| c.render :text => "Resource name is #{r.first.name}" } - get :using_resource - assert_equal "Resource name is david", @response.body - ensure - RespondWithController.responder = ActionController::Responder - end - - def test_error_is_raised_if_no_respond_to_is_declared_and_respond_with_is_called - @controller = EmptyRespondWithController.new - @request.accept = "*/*" - assert_raise RuntimeError do - get :index - end - end - - private - def with_test_route_set - with_routing do |set| - set.draw do - resources :customers - resources :quiz_stores do - resources :customers - end - get ":controller/:action" - end - yield - end - end -end - -class AbstractPostController < ActionController::Base - self.view_paths = File.dirname(__FILE__) + "/../fixtures/post_test/" -end - -# For testing layouts which are set automatically -class PostController < AbstractPostController - around_action :with_iphone - - def index - respond_to(:html, :iphone, :js) - end - -protected - - def with_iphone - request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone" - yield - end -end - -class SuperPostController < PostController -end - -class MimeControllerLayoutsTest < ActionController::TestCase - tests PostController - - def setup - super - @request.host = "www.example.com" - Mime::Type.register_alias("text/html", :iphone) - end - - def teardown - super - Mime::Type.unregister(:iphone) - end - - def test_missing_layout_renders_properly - get :index - assert_equal '<html><div id="html">Hello Firefox</div></html>', @response.body - - @request.accept = "text/iphone" - get :index - assert_equal 'Hello iPhone', @response.body - end - - def test_format_with_inherited_layouts - @controller = SuperPostController.new - - get :index - assert_equal '<html><div id="html">Super Firefox</div></html>', @response.body - - @request.accept = "text/iphone" - get :index - assert_equal '<html><div id="super_iphone">Super iPhone</div></html>', @response.body - end - - def test_non_navigational_format_with_no_template_fallbacks_to_html_template_with_no_layout - get :index, :format => :js - assert_equal "Hello Firefox", @response.body - end -end - -class FlashResponder < ActionController::Responder - def initialize(controller, resources, options={}) - super - end - - def to_html - controller.flash[:notice] = 'Success' - super - end -end - -class FlashResponderController < ActionController::Base - self.responder = FlashResponder - respond_to :html - - def index - respond_with Object.new do |format| - format.html { render :text => 'HTML' } - end - end -end - -class FlashResponderControllerTest < ActionController::TestCase - tests FlashResponderController - - def test_respond_with_block_executed - get :index - assert_equal 'HTML', @response.body - end - - def test_flash_responder_executed - get :index - assert_equal 'Success', flash[:notice] - end -end diff --git a/actionpack/test/controller/new_base/bare_metal_test.rb b/actionpack/test/controller/new_base/bare_metal_test.rb index 7396c850ad..246ba099af 100644 --- a/actionpack/test/controller/new_base/bare_metal_test.rb +++ b/actionpack/test/controller/new_base/bare_metal_test.rb @@ -2,6 +2,8 @@ require "abstract_unit" module BareMetalTest class BareController < ActionController::Metal + include ActionController::RackDelegation + def index self.response_body = "Hello world" end @@ -81,8 +83,8 @@ module BareMetalTest assert_nil headers['Content-Length'] end - test "head :continue (101) does not return a content-type header" do - headers = HeadController.action(:continue).call(Rack::MockRequest.env_for("/")).second + test "head :switching_protocols (101) does not return a content-type header" do + headers = HeadController.action(:switching_protocols).call(Rack::MockRequest.env_for("/")).second assert_nil headers['Content-Type'] assert_nil headers['Content-Length'] end diff --git a/actionpack/test/controller/new_base/render_body_test.rb b/actionpack/test/controller/new_base/render_body_test.rb new file mode 100644 index 0000000000..f4a3db8b41 --- /dev/null +++ b/actionpack/test/controller/new_base/render_body_test.rb @@ -0,0 +1,170 @@ +require 'abstract_unit' + +module RenderBody + class MinimalController < ActionController::Metal + include AbstractController::Rendering + include ActionController::Rendering + + def index + render body: "Hello World!" + end + end + + class SimpleController < ActionController::Base + self.view_paths = [ActionView::FixtureResolver.new] + + def index + render body: "hello david" + end + end + + class WithLayoutController < ::ApplicationController + self.view_paths = [ActionView::FixtureResolver.new( + "layouts/application.erb" => "<%= yield %>, I'm here!", + "layouts/greetings.erb" => "<%= yield %>, I wish thee well.", + "layouts/ivar.erb" => "<%= yield %>, <%= @ivar %>" + )] + + def index + render body: "hello david" + end + + def custom_code + render body: "hello world", status: 404 + end + + def with_custom_code_as_string + render body: "hello world", status: "404 Not Found" + end + + def with_nil + render body: nil + end + + def with_nil_and_status + render body: nil, status: 403 + end + + def with_false + render body: false + end + + def with_layout_true + render body: "hello world", layout: true + end + + def with_layout_false + render body: "hello world", layout: false + end + + def with_layout_nil + render body: "hello world", layout: nil + end + + def with_custom_layout + render body: "hello world", layout: "greetings" + end + + def with_custom_content_type + response.headers['Content-Type'] = 'application/json' + render body: '["troll","face"]' + end + + def with_ivar_in_layout + @ivar = "hello world" + render body: "hello world", layout: "ivar" + end + end + + class RenderBodyTest < Rack::TestCase + test "rendering body from a minimal controller" do + get "/render_body/minimal/index" + assert_body "Hello World!" + assert_status 200 + end + + test "rendering body from an action with default options renders the body with the layout" do + with_routing do |set| + set.draw { get ':controller', action: 'index' } + + get "/render_body/simple" + assert_body "hello david" + assert_status 200 + end + end + + test "rendering body from an action with default options renders the body without the layout" do + with_routing do |set| + set.draw { get ':controller', action: 'index' } + + get "/render_body/with_layout" + + assert_body "hello david" + assert_status 200 + end + end + + test "rendering body, while also providing a custom status code" do + get "/render_body/with_layout/custom_code" + + assert_body "hello world" + assert_status 404 + end + + test "rendering body with nil returns an empty body" do + get "/render_body/with_layout/with_nil" + + assert_body "" + assert_status 200 + end + + test "Rendering body with nil and custom status code returns an empty body and the status" do + get "/render_body/with_layout/with_nil_and_status" + + assert_body "" + assert_status 403 + end + + test "rendering body with false returns the string 'false'" do + get "/render_body/with_layout/with_false" + + assert_body "false" + assert_status 200 + end + + test "rendering body with layout: true" do + get "/render_body/with_layout/with_layout_true" + + assert_body "hello world, I'm here!" + assert_status 200 + end + + test "rendering body with layout: 'greetings'" do + get "/render_body/with_layout/with_custom_layout" + + assert_body "hello world, I wish thee well." + assert_status 200 + end + + test "specified content type should not be removed" do + get "/render_body/with_layout/with_custom_content_type" + + assert_equal %w{ troll face }, JSON.parse(response.body) + assert_equal 'application/json', response.headers['Content-Type'] + end + + test "rendering body with layout: false" do + get "/render_body/with_layout/with_layout_false" + + assert_body "hello world" + assert_status 200 + end + + test "rendering body with layout: nil" do + get "/render_body/with_layout/with_layout_nil" + + assert_body "hello world" + assert_status 200 + end + end +end diff --git a/actionpack/test/controller/new_base/render_file_test.rb b/actionpack/test/controller/new_base/render_file_test.rb index a961cbf849..0c21bb0719 100644 --- a/actionpack/test/controller/new_base/render_file_test.rb +++ b/actionpack/test/controller/new_base/render_file_test.rb @@ -13,15 +13,6 @@ module RenderFile render :file => File.join(File.dirname(__FILE__), '../../fixtures/test/render_file_with_ivar') end - def without_file_key - render File.join(File.dirname(__FILE__), *%w[.. .. fixtures test hello_world]) - end - - def without_file_key_with_instance_variable - @secret = 'in the sauce' - render File.join(File.dirname(__FILE__), '../../fixtures/test/render_file_with_ivar') - end - def relative_path @secret = 'in the sauce' render :file => '../../fixtures/test/render_file_with_ivar' @@ -41,11 +32,6 @@ module RenderFile path = File.join(File.dirname(__FILE__), '../../fixtures/test/render_file_with_locals') render :file => path, :locals => {:secret => 'in the sauce'} end - - def without_file_key_with_locals - path = FIXTURES.join('test/render_file_with_locals').to_s - render path, :locals => {:secret => 'in the sauce'} - end end class TestBasic < Rack::TestCase @@ -61,16 +47,6 @@ module RenderFile assert_response "The secret is in the sauce\n" end - test "rendering path without specifying the :file key" do - get :without_file_key - assert_response "Hello world!" - end - - test "rendering path without specifying the :file key with ivar" do - get :without_file_key_with_instance_variable - assert_response "The secret is in the sauce\n" - end - test "rendering a relative path" do get :relative_path assert_response "The secret is in the sauce\n" @@ -90,10 +66,5 @@ module RenderFile get :with_locals assert_response "The secret is in the sauce\n" end - - test "rendering path without specifying the :file key with locals" do - get :without_file_key_with_locals - assert_response "The secret is in the sauce\n" - end end end diff --git a/actionpack/test/controller/new_base/render_html_test.rb b/actionpack/test/controller/new_base/render_html_test.rb new file mode 100644 index 0000000000..fe11501eeb --- /dev/null +++ b/actionpack/test/controller/new_base/render_html_test.rb @@ -0,0 +1,190 @@ +require 'abstract_unit' + +module RenderHtml + class MinimalController < ActionController::Metal + include AbstractController::Rendering + include ActionController::Rendering + + def index + render html: "Hello World!" + end + end + + class SimpleController < ActionController::Base + self.view_paths = [ActionView::FixtureResolver.new] + + def index + render html: "hello david" + end + end + + class WithLayoutController < ::ApplicationController + self.view_paths = [ActionView::FixtureResolver.new( + "layouts/application.html.erb" => "<%= yield %>, I'm here!", + "layouts/greetings.html.erb" => "<%= yield %>, I wish thee well.", + "layouts/ivar.html.erb" => "<%= yield %>, <%= @ivar %>" + )] + + def index + render html: "hello david" + end + + def custom_code + render html: "hello world", status: 404 + end + + def with_custom_code_as_string + render html: "hello world", status: "404 Not Found" + end + + def with_nil + render html: nil + end + + def with_nil_and_status + render html: nil, status: 403 + end + + def with_false + render html: false + end + + def with_layout_true + render html: "hello world", layout: true + end + + def with_layout_false + render html: "hello world", layout: false + end + + def with_layout_nil + render html: "hello world", layout: nil + end + + def with_custom_layout + render html: "hello world", layout: "greetings" + end + + def with_ivar_in_layout + @ivar = "hello world" + render html: "hello world", layout: "ivar" + end + + def with_unsafe_html_tag + render html: "<p>hello world</p>", layout: nil + end + + def with_safe_html_tag + render html: "<p>hello world</p>".html_safe, layout: nil + end + end + + class RenderHtmlTest < Rack::TestCase + test "rendering text from a minimal controller" do + get "/render_html/minimal/index" + assert_body "Hello World!" + assert_status 200 + end + + test "rendering text from an action with default options renders the text with the layout" do + with_routing do |set| + set.draw { get ':controller', action: 'index' } + + get "/render_html/simple" + assert_body "hello david" + assert_status 200 + end + end + + test "rendering text from an action with default options renders the text without the layout" do + with_routing do |set| + set.draw { get ':controller', action: 'index' } + + get "/render_html/with_layout" + + assert_body "hello david" + assert_status 200 + end + end + + test "rendering text, while also providing a custom status code" do + get "/render_html/with_layout/custom_code" + + assert_body "hello world" + assert_status 404 + end + + test "rendering text with nil returns an empty body" do + get "/render_html/with_layout/with_nil" + + assert_body "" + assert_status 200 + end + + test "Rendering text with nil and custom status code returns an empty body and the status" do + get "/render_html/with_layout/with_nil_and_status" + + assert_body "" + assert_status 403 + end + + test "rendering text with false returns the string 'false'" do + get "/render_html/with_layout/with_false" + + assert_body "false" + assert_status 200 + end + + test "rendering text with layout: true" do + get "/render_html/with_layout/with_layout_true" + + assert_body "hello world, I'm here!" + assert_status 200 + end + + test "rendering text with layout: 'greetings'" do + get "/render_html/with_layout/with_custom_layout" + + assert_body "hello world, I wish thee well." + assert_status 200 + end + + test "rendering text with layout: false" do + get "/render_html/with_layout/with_layout_false" + + assert_body "hello world" + assert_status 200 + end + + test "rendering text with layout: nil" do + get "/render_html/with_layout/with_layout_nil" + + assert_body "hello world" + assert_status 200 + end + + test "rendering html should escape the string if it is not html safe" do + get "/render_html/with_layout/with_unsafe_html_tag" + + assert_body "<p>hello world</p>" + assert_status 200 + end + + test "rendering html should not escape the string if it is html safe" do + get "/render_html/with_layout/with_safe_html_tag" + + assert_body "<p>hello world</p>" + assert_status 200 + end + + test "rendering from minimal controller returns response with text/html content type" do + get "/render_html/minimal/index" + assert_content_type "text/html" + end + + test "rendering from normal controller returns response with text/html content type" do + get "/render_html/simple/index" + assert_content_type "text/html; charset=utf-8" + end + end +end diff --git a/actionpack/test/controller/new_base/render_implicit_action_test.rb b/actionpack/test/controller/new_base/render_implicit_action_test.rb index 1e2191d417..5b4885f7e0 100644 --- a/actionpack/test/controller/new_base/render_implicit_action_test.rb +++ b/actionpack/test/controller/new_base/render_implicit_action_test.rb @@ -6,7 +6,7 @@ module RenderImplicitAction "render_implicit_action/simple/hello_world.html.erb" => "Hello world!", "render_implicit_action/simple/hyphen-ated.html.erb" => "Hello hyphen-ated!", "render_implicit_action/simple/not_implemented.html.erb" => "Not Implemented" - )] + ), ActionView::FileSystemResolver.new(File.expand_path('../../../controller', __FILE__))] def hello_world() end end @@ -33,10 +33,25 @@ module RenderImplicitAction assert_status 200 end + test "render does not traverse the file system" do + assert_raises(AbstractController::ActionNotFound) do + action_name = %w(.. .. fixtures shared).join(File::SEPARATOR) + SimpleController.action(action_name).call(Rack::MockRequest.env_for("/")) + end + end + test "available_action? returns true for implicit actions" do assert SimpleController.new.available_action?(:hello_world) assert SimpleController.new.available_action?(:"hyphen-ated") assert SimpleController.new.available_action?(:not_implemented) end + + test "available_action? does not allow File::SEPARATOR on the name" do + action_name = %w(evil .. .. path).join(File::SEPARATOR) + assert_equal false, SimpleController.new.available_action?(action_name.to_sym) + + action_name = %w(evil path).join(File::SEPARATOR) + assert_equal false, SimpleController.new.available_action?(action_name.to_sym) + end end end diff --git a/actionpack/test/controller/new_base/render_partial_test.rb b/actionpack/test/controller/new_base/render_partial_test.rb index 2f1aa22208..9e5022c9f4 100644 --- a/actionpack/test/controller/new_base/render_partial_test.rb +++ b/actionpack/test/controller/new_base/render_partial_test.rb @@ -5,14 +5,14 @@ module RenderPartial class BasicController < ActionController::Base self.view_paths = [ActionView::FixtureResolver.new( - "render_partial/basic/_basic.html.erb" => "BasicPartial!", - "render_partial/basic/basic.html.erb" => "<%= @test_unchanged = 'goodbye' %><%= render :partial => 'basic' %><%= @test_unchanged %>", - "render_partial/basic/with_json.html.erb" => "<%= render :partial => 'with_json', :formats => [:json] %>", - "render_partial/basic/_with_json.json.erb" => "<%= render :partial => 'final', :formats => [:json] %>", - "render_partial/basic/_final.json.erb" => "{ final: json }", - "render_partial/basic/overriden.html.erb" => "<%= @test_unchanged = 'goodbye' %><%= render :partial => 'overriden' %><%= @test_unchanged %>", - "render_partial/basic/_overriden.html.erb" => "ParentPartial!", - "render_partial/child/_overriden.html.erb" => "OverridenPartial!" + "render_partial/basic/_basic.html.erb" => "BasicPartial!", + "render_partial/basic/basic.html.erb" => "<%= @test_unchanged = 'goodbye' %><%= render :partial => 'basic' %><%= @test_unchanged %>", + "render_partial/basic/with_json.html.erb" => "<%= render :partial => 'with_json', :formats => [:json] %>", + "render_partial/basic/_with_json.json.erb" => "<%= render :partial => 'final', :formats => [:json] %>", + "render_partial/basic/_final.json.erb" => "{ final: json }", + "render_partial/basic/overridden.html.erb" => "<%= @test_unchanged = 'goodbye' %><%= render :partial => 'overridden' %><%= @test_unchanged %>", + "render_partial/basic/_overridden.html.erb" => "ParentPartial!", + "render_partial/child/_overridden.html.erb" => "OverriddenPartial!" )] def html_with_json_inside_json @@ -24,7 +24,7 @@ module RenderPartial render :action => "basic" end - def overriden + def overridden @test_unchanged = 'hello' end end @@ -55,8 +55,8 @@ module RenderPartial end test "partial from child controller gets picked" do - get :overriden - assert_response("goodbyeOverridenPartial!goodbye") + get :overridden + assert_response("goodbyeOverriddenPartial!goodbye") end end diff --git a/actionpack/test/controller/new_base/render_plain_test.rb b/actionpack/test/controller/new_base/render_plain_test.rb new file mode 100644 index 0000000000..0e36d36b50 --- /dev/null +++ b/actionpack/test/controller/new_base/render_plain_test.rb @@ -0,0 +1,168 @@ +require 'abstract_unit' + +module RenderPlain + class MinimalController < ActionController::Metal + include AbstractController::Rendering + include ActionController::Rendering + + def index + render plain: "Hello World!" + end + end + + class SimpleController < ActionController::Base + self.view_paths = [ActionView::FixtureResolver.new] + + def index + render plain: "hello david" + end + end + + class WithLayoutController < ::ApplicationController + self.view_paths = [ActionView::FixtureResolver.new( + "layouts/application.text.erb" => "<%= yield %>, I'm here!", + "layouts/greetings.text.erb" => "<%= yield %>, I wish thee well.", + "layouts/ivar.text.erb" => "<%= yield %>, <%= @ivar %>" + )] + + def index + render plain: "hello david" + end + + def custom_code + render plain: "hello world", status: 404 + end + + def with_custom_code_as_string + render plain: "hello world", status: "404 Not Found" + end + + def with_nil + render plain: nil + end + + def with_nil_and_status + render plain: nil, status: 403 + end + + def with_false + render plain: false + end + + def with_layout_true + render plain: "hello world", layout: true + end + + def with_layout_false + render plain: "hello world", layout: false + end + + def with_layout_nil + render plain: "hello world", layout: nil + end + + def with_custom_layout + render plain: "hello world", layout: "greetings" + end + + def with_ivar_in_layout + @ivar = "hello world" + render plain: "hello world", layout: "ivar" + end + end + + class RenderPlainTest < Rack::TestCase + test "rendering text from a minimal controller" do + get "/render_plain/minimal/index" + assert_body "Hello World!" + assert_status 200 + end + + test "rendering text from an action with default options renders the text with the layout" do + with_routing do |set| + set.draw { get ':controller', action: 'index' } + + get "/render_plain/simple" + assert_body "hello david" + assert_status 200 + end + end + + test "rendering text from an action with default options renders the text without the layout" do + with_routing do |set| + set.draw { get ':controller', action: 'index' } + + get "/render_plain/with_layout" + + assert_body "hello david" + assert_status 200 + end + end + + test "rendering text, while also providing a custom status code" do + get "/render_plain/with_layout/custom_code" + + assert_body "hello world" + assert_status 404 + end + + test "rendering text with nil returns an empty body" do + get "/render_plain/with_layout/with_nil" + + assert_body "" + assert_status 200 + end + + test "Rendering text with nil and custom status code returns an empty body and the status" do + get "/render_plain/with_layout/with_nil_and_status" + + assert_body "" + assert_status 403 + end + + test "rendering text with false returns the string 'false'" do + get "/render_plain/with_layout/with_false" + + assert_body "false" + assert_status 200 + end + + test "rendering text with layout: true" do + get "/render_plain/with_layout/with_layout_true" + + assert_body "hello world, I'm here!" + assert_status 200 + end + + test "rendering text with layout: 'greetings'" do + get "/render_plain/with_layout/with_custom_layout" + + assert_body "hello world, I wish thee well." + assert_status 200 + end + + test "rendering text with layout: false" do + get "/render_plain/with_layout/with_layout_false" + + assert_body "hello world" + assert_status 200 + end + + test "rendering text with layout: nil" do + get "/render_plain/with_layout/with_layout_nil" + + assert_body "hello world" + assert_status 200 + end + + test "rendering from minimal controller returns response with text/plain content type" do + get "/render_plain/minimal/index" + assert_content_type "text/plain" + end + + test "rendering from normal controller returns response with text/plain content type" do + get "/render_plain/simple/index" + assert_content_type "text/plain; charset=utf-8" + end + end +end diff --git a/actionpack/test/controller/new_base/render_streaming_test.rb b/actionpack/test/controller/new_base/render_streaming_test.rb index f4bdd3e1d4..4c9126ca8c 100644 --- a/actionpack/test/controller/new_base/render_streaming_test.rb +++ b/actionpack/test/controller/new_base/render_streaming_test.rb @@ -4,7 +4,7 @@ module RenderStreaming class BasicController < ActionController::Base self.view_paths = [ActionView::FixtureResolver.new( "render_streaming/basic/hello_world.html.erb" => "Hello world", - "render_streaming/basic/boom.html.erb" => "<%= nil.invalid! %>", + "render_streaming/basic/boom.html.erb" => "<%= raise 'Ruby was here!' %>", "layouts/application.html.erb" => "<%= yield %>, I'm here!", "layouts/boom.html.erb" => "<body class=\"<%= nil.invalid! %>\"<%= yield %></body>" )] @@ -90,9 +90,9 @@ module RenderStreaming begin get "/render_streaming/basic/template_exception" io.rewind - assert_match "(undefined method `invalid!' for nil:NilClass)", io.read + assert_match "Ruby was here!", io.read ensure - ActionController::Base.logger = _old + ActionView::Base.logger = _old end end diff --git a/actionpack/test/controller/new_base/render_template_test.rb b/actionpack/test/controller/new_base/render_template_test.rb index 6b2ae2b2a9..42a86b1d0d 100644 --- a/actionpack/test/controller/new_base/render_template_test.rb +++ b/actionpack/test/controller/new_base/render_template_test.rb @@ -9,12 +9,12 @@ module RenderTemplate "locals.html.erb" => "The secret is <%= secret %>", "xml_template.xml.builder" => "xml.html do\n xml.p 'Hello'\nend", "with_raw.html.erb" => "Hello <%=raw '<strong>this is raw</strong>' %>", - "with_implicit_raw.html.erb" => "Hello <%== '<strong>this is also raw</strong>' %> in a html template", + "with_implicit_raw.html.erb" => "Hello <%== '<strong>this is also raw</strong>' %> in an html template", "with_implicit_raw.text.erb" => "Hello <%== '<strong>this is also raw</strong>' %> in a text template", "test/with_json.html.erb" => "<%= render :template => 'test/with_json', :formats => [:json] %>", "test/with_json.json.erb" => "<%= render :template => 'test/final', :formats => [:json] %>", "test/final.json.erb" => "{ final: json }", - "test/with_error.html.erb" => "<%= idontexist %>" + "test/with_error.html.erb" => "<%= raise 'i do not exist' %>" )] def index @@ -45,6 +45,10 @@ module RenderTemplate render :template => "locals", :locals => { :secret => 'area51' } end + def with_locals_without_key + render "locals", :locals => { :secret => 'area51' } + end + def builder_template render :template => "xml_template" end @@ -101,6 +105,11 @@ module RenderTemplate assert_response "The secret is area51" end + test "rendering a template with local variables without key" do + get :with_locals + assert_response "The secret is area51" + end + test "rendering a builder template" do get :builder_template, "format" => "xml" assert_response "<html>\n <p>Hello</p>\n</html>\n" @@ -114,7 +123,7 @@ module RenderTemplate get :with_implicit_raw - assert_body "Hello <strong>this is also raw</strong> in a html template" + assert_body "Hello <strong>this is also raw</strong> in an html template" assert_status 200 get :with_implicit_raw, format: 'text' @@ -132,7 +141,7 @@ module RenderTemplate test "rendering a template with error properly excerts the code" do get :with_error assert_status 500 - assert_match "undefined local variable or method `idontexist", response.body + assert_match "i do not exist", response.body end end diff --git a/actionpack/test/controller/new_base/render_test.rb b/actionpack/test/controller/new_base/render_test.rb index cc7f12ac6d..5635e16234 100644 --- a/actionpack/test/controller/new_base/render_test.rb +++ b/actionpack/test/controller/new_base/render_test.rb @@ -7,10 +7,10 @@ module Render "render/blank_render/access_request.html.erb" => "The request: <%= request.method.to_s.upcase %>", "render/blank_render/access_action_name.html.erb" => "Action Name: <%= action_name %>", "render/blank_render/access_controller_name.html.erb" => "Controller Name: <%= controller_name %>", - "render/blank_render/overriden_with_own_view_paths_appended.html.erb" => "parent content", - "render/blank_render/overriden_with_own_view_paths_prepended.html.erb" => "parent content", - "render/blank_render/overriden.html.erb" => "parent content", - "render/child_render/overriden.html.erb" => "child content" + "render/blank_render/overridden_with_own_view_paths_appended.html.erb" => "parent content", + "render/blank_render/overridden_with_own_view_paths_prepended.html.erb" => "parent content", + "render/blank_render/overridden.html.erb" => "parent content", + "render/child_render/overridden.html.erb" => "child content" )] def index @@ -25,13 +25,13 @@ module Render render :action => "access_action_name" end - def overriden_with_own_view_paths_appended + def overridden_with_own_view_paths_appended end - def overriden_with_own_view_paths_prepended + def overridden_with_own_view_paths_prepended end - def overriden + def overridden end private @@ -49,8 +49,8 @@ module Render end class ChildRenderController < BlankRenderController - append_view_path ActionView::FixtureResolver.new("render/child_render/overriden_with_own_view_paths_appended.html.erb" => "child content") - prepend_view_path ActionView::FixtureResolver.new("render/child_render/overriden_with_own_view_paths_prepended.html.erb" => "child content") + append_view_path ActionView::FixtureResolver.new("render/child_render/overridden_with_own_view_paths_appended.html.erb" => "child content") + prepend_view_path ActionView::FixtureResolver.new("render/child_render/overridden_with_own_view_paths_prepended.html.erb" => "child content") end class RenderTest < Rack::TestCase @@ -114,17 +114,17 @@ module Render class TestViewInheritance < Rack::TestCase test "Template from child controller gets picked over parent one" do - get "/render/child_render/overriden" + get "/render/child_render/overridden" assert_body "child content" end test "Template from child controller with custom view_paths prepended gets picked over parent one" do - get "/render/child_render/overriden_with_own_view_paths_prepended" + get "/render/child_render/overridden_with_own_view_paths_prepended" assert_body "child content" end test "Template from child controller with custom view_paths appended gets picked over parent one" do - get "/render/child_render/overriden_with_own_view_paths_appended" + get "/render/child_render/overridden_with_own_view_paths_appended" assert_body "child content" end diff --git a/actionpack/test/controller/new_base/render_text_test.rb b/actionpack/test/controller/new_base/render_text_test.rb index d6c3926a4d..10bad57cd6 100644 --- a/actionpack/test/controller/new_base/render_text_test.rb +++ b/actionpack/test/controller/new_base/render_text_test.rb @@ -1,11 +1,20 @@ require 'abstract_unit' module RenderText + class MinimalController < ActionController::Metal + include AbstractController::Rendering + include ActionController::Rendering + + def index + render text: "Hello World!" + end + end + class SimpleController < ActionController::Base self.view_paths = [ActionView::FixtureResolver.new] def index - render :text => "hello david" + render text: "hello david" end end @@ -17,55 +26,61 @@ module RenderText )] def index - render :text => "hello david" + render text: "hello david" end def custom_code - render :text => "hello world", :status => 404 + render text: "hello world", status: 404 end def with_custom_code_as_string - render :text => "hello world", :status => "404 Not Found" + render text: "hello world", status: "404 Not Found" end def with_nil - render :text => nil + render text: nil end def with_nil_and_status - render :text => nil, :status => 403 + render text: nil, status: 403 end def with_false - render :text => false + render text: false end def with_layout_true - render :text => "hello world", :layout => true + render text: "hello world", layout: true end def with_layout_false - render :text => "hello world", :layout => false + render text: "hello world", layout: false end def with_layout_nil - render :text => "hello world", :layout => nil + render text: "hello world", layout: nil end def with_custom_layout - render :text => "hello world", :layout => "greetings" + render text: "hello world", layout: "greetings" end def with_ivar_in_layout @ivar = "hello world" - render :text => "hello world", :layout => "ivar" + render text: "hello world", layout: "ivar" end end class RenderTextTest < Rack::TestCase + test "rendering text from a minimal controller" do + get "/render_text/minimal/index" + assert_body "Hello World!" + assert_status 200 + end + test "rendering text from an action with default options renders the text with the layout" do with_routing do |set| - set.draw { get ':controller', :action => 'index' } + set.draw { get ':controller', action: 'index' } get "/render_text/simple" assert_body "hello david" @@ -75,7 +90,7 @@ module RenderText test "rendering text from an action with default options renders the text without the layout" do with_routing do |set| - set.draw { get ':controller', :action => 'index' } + set.draw { get ':controller', action: 'index' } get "/render_text/with_layout" @@ -91,17 +106,17 @@ module RenderText assert_status 404 end - test "rendering text with nil returns an empty body padded for Safari" do + test "rendering text with nil returns an empty body" do get "/render_text/with_layout/with_nil" - assert_body " " + assert_body "" assert_status 200 end - test "Rendering text with nil and custom status code returns an empty body padded for Safari and the status" do + test "Rendering text with nil and custom status code returns an empty body and the status" do get "/render_text/with_layout/with_nil_and_status" - assert_body " " + assert_body "" assert_status 403 end @@ -112,28 +127,28 @@ module RenderText assert_status 200 end - test "rendering text with :layout => true" do + test "rendering text with layout: true" do get "/render_text/with_layout/with_layout_true" assert_body "hello world, I'm here!" assert_status 200 end - test "rendering text with :layout => 'greetings'" do + test "rendering text with layout: 'greetings'" do get "/render_text/with_layout/with_custom_layout" assert_body "hello world, I wish thee well." assert_status 200 end - test "rendering text with :layout => false" do + test "rendering text with layout: false" do get "/render_text/with_layout/with_layout_false" assert_body "hello world" assert_status 200 end - test "rendering text with :layout => nil" do + test "rendering text with layout: nil" do get "/render_text/with_layout/with_layout_nil" assert_body "hello world" diff --git a/actionpack/test/controller/output_escaping_test.rb b/actionpack/test/controller/output_escaping_test.rb index f6913a2138..c3c549fbfc 100644 --- a/actionpack/test/controller/output_escaping_test.rb +++ b/actionpack/test/controller/output_escaping_test.rb @@ -3,7 +3,7 @@ require 'abstract_unit' class OutputEscapingTest < ActiveSupport::TestCase test "escape_html shouldn't die when passed nil" do - assert_blank ERB::Util.h(nil) + assert ERB::Util.h(nil).blank? end test "escapeHTML should escape strings" do @@ -11,8 +11,6 @@ class OutputEscapingTest < ActiveSupport::TestCase end test "escapeHTML shouldn't touch explicitly safe strings" do - # TODO this seems easier to compose and reason about, but - # this should be verified assert_equal "<", ERB::Util.h("<".html_safe) end diff --git a/actionpack/test/controller/parameters/accessors_test.rb b/actionpack/test/controller/parameters/accessors_test.rb new file mode 100644 index 0000000000..97875c3cbb --- /dev/null +++ b/actionpack/test/controller/parameters/accessors_test.rb @@ -0,0 +1,125 @@ +require 'abstract_unit' +require 'action_controller/metal/strong_parameters' +require 'active_support/core_ext/hash/transform_values' + +class ParametersAccessorsTest < ActiveSupport::TestCase + setup do + @params = ActionController::Parameters.new( + person: { + age: '32', + name: { + first: 'David', + last: 'Heinemeier Hansson' + }, + addresses: [{city: 'Chicago', state: 'Illinois'}] + } + ) + end + + test "[] retains permitted status" do + @params.permit! + assert @params[:person].permitted? + assert @params[:person][:name].permitted? + end + + test "[] retains unpermitted status" do + assert_not @params[:person].permitted? + assert_not @params[:person][:name].permitted? + end + + test "each carries permitted status" do + @params.permit! + @params.each { |key, value| assert(value.permitted?) if key == "person" } + end + + test "each carries unpermitted status" do + @params.each { |key, value| assert_not(value.permitted?) if key == "person" } + end + + test "each_pair carries permitted status" do + @params.permit! + @params.each_pair { |key, value| assert(value.permitted?) if key == "person" } + end + + test "each_pair carries unpermitted status" do + @params.each_pair { |key, value| assert_not(value.permitted?) if key == "person" } + end + + test "except retains permitted status" do + @params.permit! + assert @params.except(:person).permitted? + assert @params[:person].except(:name).permitted? + end + + test "except retains unpermitted status" do + assert_not @params.except(:person).permitted? + assert_not @params[:person].except(:name).permitted? + end + + test "fetch retains permitted status" do + @params.permit! + assert @params.fetch(:person).permitted? + assert @params[:person].fetch(:name).permitted? + end + + test "fetch retains unpermitted status" do + assert_not @params.fetch(:person).permitted? + assert_not @params[:person].fetch(:name).permitted? + end + + test "reject retains permitted status" do + assert_not @params.reject { |k| k == "person" }.permitted? + end + + test "reject retains unpermitted status" do + @params.permit! + assert @params.reject { |k| k == "person" }.permitted? + end + + test "select retains permitted status" do + @params.permit! + assert @params.select { |k| k == "person" }.permitted? + end + + test "select retains unpermitted status" do + assert_not @params.select { |k| k == "person" }.permitted? + end + + test "slice retains permitted status" do + @params.permit! + assert @params.slice(:person).permitted? + end + + test "slice retains unpermitted status" do + assert_not @params.slice(:person).permitted? + end + + test "transform_keys retains permitted status" do + @params.permit! + assert @params.transform_keys { |k| k }.permitted? + end + + test "transform_keys retains unpermitted status" do + assert_not @params.transform_keys { |k| k }.permitted? + end + + test "transform_values retains permitted status" do + @params.permit! + assert @params.transform_values { |v| v }.permitted? + end + + test "transform_values retains unpermitted status" do + assert_not @params.transform_values { |v| v }.permitted? + end + + test "values_at retains permitted status" do + @params.permit! + assert @params.values_at(:person).first.permitted? + assert @params[:person].values_at(:name).first.permitted? + end + + test "values_at retains unpermitted status" do + assert_not @params.values_at(:person).first.permitted? + assert_not @params[:person].values_at(:name).first.permitted? + end +end diff --git a/actionpack/test/controller/parameters/always_permitted_parameters_test.rb b/actionpack/test/controller/parameters/always_permitted_parameters_test.rb new file mode 100644 index 0000000000..059f310d49 --- /dev/null +++ b/actionpack/test/controller/parameters/always_permitted_parameters_test.rb @@ -0,0 +1,29 @@ +require 'abstract_unit' +require 'action_controller/metal/strong_parameters' + +class AlwaysPermittedParametersTest < ActiveSupport::TestCase + def setup + ActionController::Parameters.action_on_unpermitted_parameters = :raise + ActionController::Parameters.always_permitted_parameters = %w( controller action format ) + end + + def teardown + ActionController::Parameters.action_on_unpermitted_parameters = false + ActionController::Parameters.always_permitted_parameters = %w( controller action ) + end + + test "shows deprecations warning on NEVER_UNPERMITTED_PARAMS" do + assert_deprecated do + ActionController::Parameters::NEVER_UNPERMITTED_PARAMS + end + end + + test "permits parameters that are whitelisted" do + params = ActionController::Parameters.new({ + book: { pages: 65 }, + format: "json" + }) + permitted = params.permit book: [:pages] + assert permitted.permitted? + end +end diff --git a/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb b/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb new file mode 100644 index 0000000000..9ce04b9aeb --- /dev/null +++ b/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb @@ -0,0 +1,72 @@ +require 'abstract_unit' +require 'action_controller/metal/strong_parameters' + +class LogOnUnpermittedParamsTest < ActiveSupport::TestCase + def setup + ActionController::Parameters.action_on_unpermitted_parameters = :log + end + + def teardown + ActionController::Parameters.action_on_unpermitted_parameters = false + end + + test "logs on unexpected param" do + params = ActionController::Parameters.new({ + book: { pages: 65 }, + fishing: "Turnips" + }) + + assert_logged("Unpermitted parameter: fishing") do + params.permit(book: [:pages]) + end + end + + test "logs on unexpected params" do + params = ActionController::Parameters.new({ + book: { pages: 65 }, + fishing: "Turnips", + car: "Mersedes" + }) + + assert_logged("Unpermitted parameters: fishing, car") do + params.permit(book: [:pages]) + end + end + + test "logs on unexpected nested param" do + params = ActionController::Parameters.new({ + book: { pages: 65, title: "Green Cats and where to find then." } + }) + + assert_logged("Unpermitted parameter: title") do + params.permit(book: [:pages]) + end + end + + test "logs on unexpected nested params" do + params = ActionController::Parameters.new({ + book: { pages: 65, title: "Green Cats and where to find then.", author: "G. A. Dog" } + }) + + assert_logged("Unpermitted parameters: title, author") do + params.permit(book: [:pages]) + end + end + + private + + def assert_logged(message) + old_logger = ActionController::Base.logger + log = StringIO.new + ActionController::Base.logger = Logger.new(log) + + begin + yield + + log.rewind + assert_match message, log.read + ensure + ActionController::Base.logger = old_logger + end + end +end diff --git a/actionpack/test/controller/parameters/mutators_test.rb b/actionpack/test/controller/parameters/mutators_test.rb new file mode 100644 index 0000000000..744d8664be --- /dev/null +++ b/actionpack/test/controller/parameters/mutators_test.rb @@ -0,0 +1,99 @@ +require 'abstract_unit' +require 'action_controller/metal/strong_parameters' +require 'active_support/core_ext/hash/transform_values' + +class ParametersMutatorsTest < ActiveSupport::TestCase + setup do + @params = ActionController::Parameters.new( + person: { + age: '32', + name: { + first: 'David', + last: 'Heinemeier Hansson' + }, + addresses: [{city: 'Chicago', state: 'Illinois'}] + } + ) + end + + test "delete retains permitted status" do + @params.permit! + assert @params.delete(:person).permitted? + end + + test "delete retains unpermitted status" do + assert_not @params.delete(:person).permitted? + end + + test "delete_if retains permitted status" do + @params.permit! + assert @params.delete_if { |k| k == "person" }.permitted? + end + + test "delete_if retains unpermitted status" do + assert_not @params.delete_if { |k| k == "person" }.permitted? + end + + test "extract! retains permitted status" do + @params.permit! + assert @params.extract!(:person).permitted? + end + + test "extract! retains unpermitted status" do + assert_not @params.extract!(:person).permitted? + end + + test "keep_if retains permitted status" do + @params.permit! + assert @params.keep_if { |k,v| k == "person" }.permitted? + end + + test "keep_if retains unpermitted status" do + assert_not @params.keep_if { |k,v| k == "person" }.permitted? + end + + test "reject! retains permitted status" do + @params.permit! + assert @params.reject! { |k| k == "person" }.permitted? + end + + test "reject! retains unpermitted status" do + assert_not @params.reject! { |k| k == "person" }.permitted? + end + + test "select! retains permitted status" do + @params.permit! + assert @params.select! { |k| k != "person" }.permitted? + end + + test "select! retains unpermitted status" do + assert_not @params.select! { |k| k != "person" }.permitted? + end + + test "slice! retains permitted status" do + @params.permit! + assert @params.slice!(:person).permitted? + end + + test "slice! retains unpermitted status" do + assert_not @params.slice!(:person).permitted? + end + + test "transform_keys! retains permitted status" do + @params.permit! + assert @params.transform_keys! { |k| k }.permitted? + end + + test "transform_keys! retains unpermitted status" do + assert_not @params.transform_keys! { |k| k }.permitted? + end + + test "transform_values! retains permitted status" do + @params.permit! + assert @params.transform_values! { |v| v }.permitted? + end + + test "transform_values! retains unpermitted status" do + assert_not @params.transform_values! { |v| v }.permitted? + end +end diff --git a/actionpack/test/controller/parameters/nested_parameters_test.rb b/actionpack/test/controller/parameters/nested_parameters_test.rb index 6df849c4e2..3b1257e8d5 100644 --- a/actionpack/test/controller/parameters/nested_parameters_test.rb +++ b/actionpack/test/controller/parameters/nested_parameters_test.rb @@ -2,6 +2,10 @@ require 'abstract_unit' require 'action_controller/metal/strong_parameters' class NestedParametersTest < ActiveSupport::TestCase + def assert_filtered_out(params, key) + assert !params.has_key?(key), "key #{key.inspect} has not been filtered out" + end + test "permitted nested parameters" do params = ActionController::Parameters.new({ book: { @@ -11,6 +15,8 @@ class NestedParametersTest < ActiveSupport::TestCase born: "1564-04-26" }, { name: "Christopher Marlowe" + }, { + name: %w(malicious injected names) }], details: { pages: 200, @@ -30,10 +36,12 @@ class NestedParametersTest < ActiveSupport::TestCase assert_equal "William Shakespeare", permitted[:book][:authors][0][:name] assert_equal "Christopher Marlowe", permitted[:book][:authors][1][:name] assert_equal 200, permitted[:book][:details][:pages] - assert_nil permitted[:book][:id] - assert_nil permitted[:book][:details][:genre] - assert_nil permitted[:book][:authors][0][:born] - assert_nil permitted[:magazine] + + assert_filtered_out permitted, :magazine + assert_filtered_out permitted[:book], :id + assert_filtered_out permitted[:book][:details], :genre + assert_filtered_out permitted[:book][:authors][0], :born + assert_filtered_out permitted[:book][:authors][2], :name end test "permitted nested parameters with a string or a symbol as a key" do @@ -63,25 +71,25 @@ class NestedParametersTest < ActiveSupport::TestCase test "nested arrays with strings" do params = ActionController::Parameters.new({ - :book => { - :genres => ["Tragedy"] + book: { + genres: ["Tragedy"] } }) - permitted = params.permit :book => :genres + permitted = params.permit book: {genres: []} assert_equal ["Tragedy"], permitted[:book][:genres] end test "permit may specify symbols or strings" do params = ActionController::Parameters.new({ - :book => { - :title => "Romeo and Juliet", - :author => "William Shakespeare" + book: { + title: "Romeo and Juliet", + author: "William Shakespeare" }, - :magazine => "Shakespeare Today" + magazine: "Shakespeare Today" }) - permitted = params.permit({:book => ["title", :author]}, "magazine") + permitted = params.permit({book: ["title", :author]}, "magazine") assert_equal "Romeo and Juliet", permitted[:book][:title] assert_equal "William Shakespeare", permitted[:book][:author] assert_equal "Shakespeare Today", permitted[:magazine] @@ -127,16 +135,53 @@ class NestedParametersTest < ActiveSupport::TestCase book: { authors_attributes: { :'0' => { name: 'William Shakespeare', age_of_death: '52' }, - :'-1' => { name: 'Unattributed Assistant' } + :'1' => { name: 'Unattributed Assistant' }, + :'2' => { name: %w(injected names)} } } }) permitted = params.permit book: { authors_attributes: [ :name ] } assert_not_nil permitted[:book][:authors_attributes]['0'] - assert_not_nil permitted[:book][:authors_attributes]['-1'] - assert_nil permitted[:book][:authors_attributes]['0'][:age_of_death] + assert_not_nil permitted[:book][:authors_attributes]['1'] + assert_empty permitted[:book][:authors_attributes]['2'] assert_equal 'William Shakespeare', permitted[:book][:authors_attributes]['0'][:name] - assert_equal 'Unattributed Assistant', permitted[:book][:authors_attributes]['-1'][:name] + assert_equal 'Unattributed Assistant', permitted[:book][:authors_attributes]['1'][:name] + + assert_filtered_out permitted[:book][:authors_attributes]['0'], :age_of_death + end + + test "fields_for-style nested params with negative numbers" do + params = ActionController::Parameters.new({ + book: { + authors_attributes: { + :'-1' => { name: 'William Shakespeare', age_of_death: '52' }, + :'-2' => { name: 'Unattributed Assistant' } + } + } + }) + permitted = params.permit book: { authors_attributes: [:name] } + + assert_not_nil permitted[:book][:authors_attributes]['-1'] + assert_not_nil permitted[:book][:authors_attributes]['-2'] + assert_equal 'William Shakespeare', permitted[:book][:authors_attributes]['-1'][:name] + assert_equal 'Unattributed Assistant', permitted[:book][:authors_attributes]['-2'][:name] + + assert_filtered_out permitted[:book][:authors_attributes]['-1'], :age_of_death + end + + test "nested number as key" do + params = ActionController::Parameters.new({ + product: { + properties: { + '0' => "prop0", + '1' => "prop1" + } + } + }) + params = params.require(:product).permit(:properties => ["0"]) + assert_not_nil params[:properties]["0"] + assert_nil params[:properties]["1"] + assert_equal "prop0", params[:properties]["0"] end end diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb index 7cc71fe6dc..2ed486516d 100644 --- a/actionpack/test/controller/parameters/parameters_permit_test.rb +++ b/actionpack/test/controller/parameters/parameters_permit_test.rb @@ -1,59 +1,197 @@ require 'abstract_unit' +require 'action_dispatch/http/upload' require 'action_controller/metal/strong_parameters' class ParametersPermitTest < ActiveSupport::TestCase + def assert_filtered_out(params, key) + assert !params.has_key?(key), "key #{key.inspect} has not been filtered out" + end + setup do - @params = ActionController::Parameters.new({ person: { - age: "32", name: { first: "David", last: "Heinemeier Hansson" } - }}) + @params = ActionController::Parameters.new( + person: { + age: '32', + name: { + first: 'David', + last: 'Heinemeier Hansson' + }, + addresses: [{city: 'Chicago', state: 'Illinois'}] + } + ) + + @struct_fields = [] + %w(0 1 12).each do |number| + ['', 'i', 'f'].each do |suffix| + @struct_fields << "sf(#{number}#{suffix})" + end + end end - test "fetch raises ParameterMissing exception" do - e = assert_raises(ActionController::ParameterMissing) do - @params.fetch :foo + test 'if nothing is permitted, the hash becomes empty' do + params = ActionController::Parameters.new(id: '1234') + permitted = params.permit + assert permitted.permitted? + assert permitted.empty? + end + + test 'key: permitted scalar values' do + values = ['a', :a, nil] + values += [0, 1.0, 2**128, BigDecimal.new(1)] + values += [true, false] + values += [Date.today, Time.now, DateTime.now] + values += [STDOUT, StringIO.new, ActionDispatch::Http::UploadedFile.new(tempfile: __FILE__), + Rack::Test::UploadedFile.new(__FILE__)] + + values.each do |value| + params = ActionController::Parameters.new(id: value) + permitted = params.permit(:id) + assert_equal value, permitted[:id] + + @struct_fields.each do |sf| + params = ActionController::Parameters.new(sf => value) + permitted = params.permit(:sf) + assert_equal value, permitted[sf] + end end - assert_equal :foo, e.param end - test "fetch doesnt raise ParameterMissing exception if there is a default" do - assert_equal "monkey", @params.fetch(:foo, "monkey") - assert_equal "monkey", @params.fetch(:foo) { "monkey" } + test 'key: unknown keys are filtered out' do + params = ActionController::Parameters.new(id: '1234', injected: 'injected') + permitted = params.permit(:id) + assert_equal '1234', permitted[:id] + assert_filtered_out permitted, :injected end - test "not permitted is sticky on accessors" do - assert !@params.slice(:person).permitted? - assert !@params[:person][:name].permitted? - assert !@params[:person].except(:name).permitted? + test 'key: arrays are filtered out' do + [[], [1], ['1']].each do |array| + params = ActionController::Parameters.new(id: array) + permitted = params.permit(:id) + assert_filtered_out permitted, :id - @params.each { |key, value| assert(!value.permitted?) if key == "person" } + @struct_fields.each do |sf| + params = ActionController::Parameters.new(sf => array) + permitted = params.permit(:sf) + assert_filtered_out permitted, sf + end + end + end - assert !@params.fetch(:person).permitted? + test 'key: hashes are filtered out' do + [{}, {foo: 1}, {foo: 'bar'}].each do |hash| + params = ActionController::Parameters.new(id: hash) + permitted = params.permit(:id) + assert_filtered_out permitted, :id - assert !@params.values_at(:person).first.permitted? + @struct_fields.each do |sf| + params = ActionController::Parameters.new(sf => hash) + permitted = params.permit(:sf) + assert_filtered_out permitted, sf + end + end end - test "permitted is sticky on accessors" do - @params.permit! - assert @params.slice(:person).permitted? - assert @params[:person][:name].permitted? - assert @params[:person].except(:name).permitted? + test 'key: non-permitted scalar values are filtered out' do + params = ActionController::Parameters.new(id: Object.new) + permitted = params.permit(:id) + assert_filtered_out permitted, :id + + @struct_fields.each do |sf| + params = ActionController::Parameters.new(sf => Object.new) + permitted = params.permit(:sf) + assert_filtered_out permitted, sf + end + end + + test 'key: it is not assigned if not present in params' do + params = ActionController::Parameters.new(name: 'Joe') + permitted = params.permit(:id) + assert !permitted.has_key?(:id) + end - @params.each { |key, value| assert(value.permitted?) if key == "person" } + test 'key to empty array: empty arrays pass' do + params = ActionController::Parameters.new(id: []) + permitted = params.permit(id: []) + assert_equal [], permitted[:id] + end - assert @params.fetch(:person).permitted? + test 'do not break params filtering on nil values' do + params = ActionController::Parameters.new(a: 1, b: [1, 2, 3], c: nil) - assert @params.values_at(:person).first.permitted? + permitted = params.permit(:a, c: [], b: []) + assert_equal 1, permitted[:a] + assert_equal [1, 2, 3], permitted[:b] + assert_equal nil, permitted[:c] end - test "not permitted is sticky on mutators" do - assert !@params.delete_if { |k| k == "person" }.permitted? - assert !@params.keep_if { |k,v| k == "person" }.permitted? + test 'key to empty array: arrays of permitted scalars pass' do + [['foo'], [1], ['foo', 'bar'], [1, 2, 3]].each do |array| + params = ActionController::Parameters.new(id: array) + permitted = params.permit(id: []) + assert_equal array, permitted[:id] + end end - test "permitted is sticky on mutators" do - @params.permit! - assert @params.delete_if { |k| k == "person" }.permitted? - assert @params.keep_if { |k,v| k == "person" }.permitted? + test 'key to empty array: permitted scalar values do not pass' do + ['foo', 1].each do |permitted_scalar| + params = ActionController::Parameters.new(id: permitted_scalar) + permitted = params.permit(id: []) + assert_filtered_out permitted, :id + end + end + + test 'key to empty array: arrays of non-permitted scalar do not pass' do + [[Object.new], [[]], [[1]], [{}], [{id: '1'}]].each do |non_permitted_scalar| + params = ActionController::Parameters.new(id: non_permitted_scalar) + permitted = params.permit(id: []) + assert_filtered_out permitted, :id + end + end + + test "fetch raises ParameterMissing exception" do + e = assert_raises(ActionController::ParameterMissing) do + @params.fetch :foo + end + assert_equal :foo, e.param + end + + test "fetch with a default value of a hash does not mutate the object" do + params = ActionController::Parameters.new({}) + params.fetch :foo, {} + assert_equal nil, params[:foo] + end + + test 'hashes in array values get wrapped' do + params = ActionController::Parameters.new(foo: [{}, {}]) + params[:foo].each do |hash| + assert !hash.permitted? + end + end + + # Strong params has an optimization to avoid looping every time you read + # a key whose value is an array and building a new object. We check that + # optimization here. + test 'arrays are converted at most once' do + params = ActionController::Parameters.new(foo: [{}]) + assert_same params[:foo], params[:foo] + end + + # Strong params has an internal cache to avoid duplicated loops in the most + # common usage pattern. See the docs of the method `converted_arrays`. + # + # This test checks that if we push a hash to an array (in-place modification) + # the cache does not get fooled, the hash is still wrapped as strong params, + # and not permitted. + test 'mutated arrays are detected' do + params = ActionController::Parameters.new(users: [{id: 1}]) + + permitted = params.permit(users: [:id]) + permitted[:users] << {injected: 1} + assert_not permitted[:users].last.permitted? + end + + test "fetch doesnt raise ParameterMissing exception if there is a default" do + assert_equal "monkey", @params.fetch(:foo, "monkey") + assert_equal "monkey", @params.fetch(:foo) { "monkey" } end test "not permitted is sticky beyond merges" do @@ -73,10 +211,6 @@ class ParametersPermitTest < ActiveSupport::TestCase assert_equal "Jonas", @params[:person][:family][:brother] end - test "permitting parameters that are not there should not include the keys" do - assert !@params.permit(:person, :funky).has_key?(:funky) - end - test "permit state is kept on a dup" do @params.permit! assert_equal @params.permitted?, @params.dup.permitted? @@ -87,6 +221,7 @@ class ParametersPermitTest < ActiveSupport::TestCase assert @params.permitted? assert @params[:person].permitted? assert @params[:person][:name].permitted? + assert @params[:person][:addresses][0].permitted? end test "permitted takes a default value when Parameters.permit_all_parameters is set" do @@ -106,4 +241,49 @@ class ParametersPermitTest < ActiveSupport::TestCase test "permitting parameters as an array" do assert_equal "32", @params[:person].permit([ :age ])[:age] end + + test "to_h returns empty hash on unpermitted params" do + assert @params.to_h.is_a? Hash + assert_not @params.to_h.is_a? ActionController::Parameters + assert @params.to_h.empty? + end + + test "to_h returns converted hash on permitted params" do + @params.permit! + + assert @params.to_h.is_a? Hash + assert_not @params.to_h.is_a? ActionController::Parameters + assert_equal @params.to_hash, @params.to_h + end + + test "to_h returns converted hash when .permit_all_parameters is set" do + begin + ActionController::Parameters.permit_all_parameters = true + params = ActionController::Parameters.new(crab: "Senjougahara Hitagi") + + assert params.to_h.is_a? Hash + assert_not @params.to_h.is_a? ActionController::Parameters + assert_equal({ "crab" => "Senjougahara Hitagi" }, params.to_h) + ensure + ActionController::Parameters.permit_all_parameters = false + end + end + + test "to_h returns always permitted parameter on unpermitted params" do + params = ActionController::Parameters.new( + controller: "users", + action: "create", + user: { + name: "Sengoku Nadeko" + } + ) + + assert_equal({ "controller" => "users", "action" => "create" }, params.to_h) + end + + test "to_unsafe_h returns unfiltered params" do + assert @params.to_h.is_a? Hash + assert_not @params.to_h.is_a? ActionController::Parameters + assert_equal @params.to_hash, @params.to_unsafe_h + end end diff --git a/actionpack/test/controller/parameters/parameters_require_test.rb b/actionpack/test/controller/parameters/parameters_require_test.rb deleted file mode 100644 index bdaba8d2d8..0000000000 --- a/actionpack/test/controller/parameters/parameters_require_test.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'abstract_unit' -require 'action_controller/metal/strong_parameters' - -class ParametersRequireTest < ActiveSupport::TestCase - test "required parameters must be present not merely not nil" do - assert_raises(ActionController::ParameterMissing) do - ActionController::Parameters.new(person: {}).require(:person) - end - end -end diff --git a/actionpack/test/controller/parameters/raise_on_unpermitted_params_test.rb b/actionpack/test/controller/parameters/raise_on_unpermitted_params_test.rb new file mode 100644 index 0000000000..f9cc9f96f1 --- /dev/null +++ b/actionpack/test/controller/parameters/raise_on_unpermitted_params_test.rb @@ -0,0 +1,33 @@ +require 'abstract_unit' +require 'action_controller/metal/strong_parameters' + +class RaiseOnUnpermittedParamsTest < ActiveSupport::TestCase + def setup + ActionController::Parameters.action_on_unpermitted_parameters = :raise + end + + def teardown + ActionController::Parameters.action_on_unpermitted_parameters = false + end + + test "raises on unexpected params" do + params = ActionController::Parameters.new({ + book: { pages: 65 }, + fishing: "Turnips" + }) + + assert_raises(ActionController::UnpermittedParameters) do + params.permit(book: [:pages]) + end + end + + test "raises on unexpected nested params" do + params = ActionController::Parameters.new({ + book: { pages: 65, title: "Green Cats and where to find then." } + }) + + assert_raises(ActionController::UnpermittedParameters) do + params.permit(book: [:pages]) + end + end +end diff --git a/actionpack/test/controller/params_wrapper_test.rb b/actionpack/test/controller/params_wrapper_test.rb index d87e2b85b0..645ecae220 100644 --- a/actionpack/test/controller/params_wrapper_test.rb +++ b/actionpack/test/controller/params_wrapper_test.rb @@ -188,6 +188,26 @@ class ParamsWrapperTest < ActionController::TestCase assert_parameters({ 'username' => 'sikachu', 'title' => 'Developer', 'user' => { 'username' => 'sikachu', 'title' => 'Developer' }}) end end + + def test_preserves_query_string_params + with_default_wrapper_options do + @request.env['CONTENT_TYPE'] = 'application/json' + get :parse, { 'user' => { 'username' => 'nixon' } } + assert_parameters( + {'user' => { 'username' => 'nixon' } } + ) + end + end + + def test_empty_parameter_set + with_default_wrapper_options do + @request.env['CONTENT_TYPE'] = 'application/json' + post :parse, {} + assert_parameters( + {'user' => { } } + ) + end + end end class NamespacedParamsWrapperTest < ActionController::TestCase @@ -317,14 +337,26 @@ class IrregularInflectionParamsWrapperTest < ActionController::TestCase tests ParamswrappernewsController def test_uses_model_attribute_names_with_irregular_inflection - ActiveSupport::Inflector.inflections do |inflect| - inflect.irregular 'paramswrappernews_item', 'paramswrappernews' - end + with_dup do + ActiveSupport::Inflector.inflections do |inflect| + inflect.irregular 'paramswrappernews_item', 'paramswrappernews' + end - with_default_wrapper_options do - @request.env['CONTENT_TYPE'] = 'application/json' - post :parse, { 'username' => 'sikachu', 'test_attr' => 'test_value' } - assert_parameters({ 'username' => 'sikachu', 'test_attr' => 'test_value', 'paramswrappernews_item' => { 'test_attr' => 'test_value' }}) + with_default_wrapper_options do + @request.env['CONTENT_TYPE'] = 'application/json' + post :parse, { 'username' => 'sikachu', 'test_attr' => 'test_value' } + assert_parameters({ 'username' => 'sikachu', 'test_attr' => 'test_value', 'paramswrappernews_item' => { 'test_attr' => 'test_value' }}) + end end end + + private + + def with_dup + original = ActiveSupport::Inflector::Inflections.instance_variable_get(:@__instance__)[:en] + ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: original.dup) + yield + ensure + ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: original) + end end diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index 4331333b98..103ca9c776 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -90,6 +90,10 @@ class RedirectController < ActionController::Base redirect_to nil end + def redirect_to_params + redirect_to ActionController::Parameters.new(status: 200, protocol: 'javascript', f: '%0Aeval(name)') + end + def redirect_to_with_block redirect_to proc { "http://www.rubyonrails.org/" } end @@ -281,6 +285,12 @@ class RedirectTest < ActionController::TestCase end end + def test_redirect_to_params + assert_raise(ActionController::ActionControllerError) do + get :redirect_to_params + end + end + def test_redirect_to_with_block get :redirect_to_with_block assert_response :redirect diff --git a/actionpack/test/controller/render_js_test.rb b/actionpack/test/controller/render_js_test.rb index f070109b27..d550422a2f 100644 --- a/actionpack/test/controller/render_js_test.rb +++ b/actionpack/test/controller/render_js_test.rb @@ -22,7 +22,7 @@ class RenderJSTest < ActionController::TestCase tests TestController def test_render_vanilla_js - get :render_vanilla_js_hello + xhr :get, :render_vanilla_js_hello assert_equal "alert('hello')", @response.body assert_equal "text/javascript", @response.content_type end diff --git a/actionpack/test/controller/render_json_test.rb b/actionpack/test/controller/render_json_test.rb index 7c0a6bd67e..ada978aa11 100644 --- a/actionpack/test/controller/render_json_test.rb +++ b/actionpack/test/controller/render_json_test.rb @@ -100,13 +100,13 @@ class RenderJsonTest < ActionController::TestCase end def test_render_json_with_callback - get :render_json_hello_world_with_callback - assert_equal 'alert({"hello":"world"})', @response.body + xhr :get, :render_json_hello_world_with_callback + assert_equal '/**/alert({"hello":"world"})', @response.body assert_equal 'text/javascript', @response.content_type end def test_render_json_with_custom_content_type - get :render_json_with_custom_content_type + xhr :get, :render_json_with_custom_content_type assert_equal '{"hello":"world"}', @response.body assert_equal 'text/javascript', @response.content_type end diff --git a/actionpack/test/controller/render_other_test.rb b/actionpack/test/controller/render_other_test.rb index b5e74e373d..af50e11261 100644 --- a/actionpack/test/controller/render_other_test.rb +++ b/actionpack/test/controller/render_other_test.rb @@ -1,9 +1,5 @@ require 'abstract_unit' -ActionController.add_renderer :simon do |says, options| - self.content_type = Mime::TEXT - self.response_body = "Simon says: #{says}" -end class RenderOtherTest < ActionController::TestCase class TestController < ActionController::Base @@ -15,7 +11,14 @@ class RenderOtherTest < ActionController::TestCase tests TestController def test_using_custom_render_option + ActionController.add_renderer :simon do |says, options| + self.content_type = Mime::TEXT + self.response_body = "Simon says: #{says}" + end + get :render_simon_says assert_equal "Simon says: foo", @response.body + ensure + ActionController.remove_renderer :simon end end diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index c7a66f4298..929b161eb6 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -2,26 +2,6 @@ require 'abstract_unit' require 'controller/fake_models' require 'pathname' -module Fun - class GamesController < ActionController::Base - # :ported: - def hello_world - end - - def nested_partial_with_form_builder - render :partial => ActionView::Helpers::FormBuilder.new(:post, nil, view_context, {}) - end - end -end - -module Quiz - class QuestionsController < ActionController::Base - def new - render :partial => Quiz::Question.new("Namespaced Partial") - end - end -end - class TestControllerWithExtraEtags < ActionController::Base etag { nil } etag { 'ab' } @@ -30,7 +10,17 @@ class TestControllerWithExtraEtags < ActionController::Base etag { nil } def fresh - render text: "stale" if stale?(etag: '123') + render text: "stale" if stale?(etag: '123', template: false) + end + + def array + render text: "stale" if stale?(etag: %w(1 2 3), template: false) + end + + def with_template + if stale? template: 'test/hello_world' + render text: 'stale' + end end end @@ -54,10 +44,6 @@ class TestController < ActionController::Base def hello_world end - def hello_world_file - render :file => File.expand_path("../../fixtures/hello", __FILE__), :formats => [:html] - end - def conditional_hello if stale?(:last_modified => Time.now.utc.beginning_of_day, :etag => [:foo, 123]) render :action => 'hello_world' @@ -143,327 +129,10 @@ class TestController < ActionController::Base fresh_when(:last_modified => Time.now.utc.beginning_of_day, :etag => [ :foo, 123 ]) end - # :ported: - def render_hello_world - render :template => "test/hello_world" - end - - def render_hello_world_with_last_modified_set - response.last_modified = Date.new(2008, 10, 10).to_time - render :template => "test/hello_world" - end - - # :ported: compatibility - def render_hello_world_with_forward_slash - render :template => "/test/hello_world" - end - - # :ported: - def render_template_in_top_directory - render :template => 'shared' - end - - # :deprecated: - def render_template_in_top_directory_with_slash - render :template => '/shared' - end - - # :ported: - def render_hello_world_from_variable - @person = "david" - render :text => "hello #{@person}" - end - - # :ported: - def render_action_hello_world - render :action => "hello_world" - end - - def render_action_upcased_hello_world - render :action => "Hello_world" - end - - def render_action_hello_world_as_string - render "hello_world" - end - - def render_action_hello_world_with_symbol - render :action => :hello_world - end - - # :ported: - def render_text_hello_world - render :text => "hello world" - end - - # :ported: - def render_text_hello_world_with_layout - @variable_for_layout = ", I am here!" - render :text => "hello world", :layout => true - end - - def hello_world_with_layout_false - render :layout => false - end - - # :ported: - def render_file_with_instance_variables - @secret = 'in the sauce' - path = File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_ivar') - render :file => path - end - - # :ported: - def render_file_as_string_with_instance_variables - @secret = 'in the sauce' - path = File.expand_path(File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_ivar')) - render path - end - - # :ported: - def render_file_not_using_full_path - @secret = 'in the sauce' - render :file => 'test/render_file_with_ivar' - end - - def render_file_not_using_full_path_with_dot_in_path - @secret = 'in the sauce' - render :file => 'test/dot.directory/render_file_with_ivar' - end - - def render_file_using_pathname - @secret = 'in the sauce' - render :file => Pathname.new(File.dirname(__FILE__)).join('..', 'fixtures', 'test', 'dot.directory', 'render_file_with_ivar') - end - - def render_file_from_template - @secret = 'in the sauce' - @path = File.expand_path(File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_ivar')) - end - - def render_file_with_locals - path = File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_locals') - render :file => path, :locals => {:secret => 'in the sauce'} - end - - def render_file_as_string_with_locals - path = File.expand_path(File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_locals')) - render path, :locals => {:secret => 'in the sauce'} - end - - def accessing_request_in_template - render :inline => "Hello: <%= request.host %>" - end - - def accessing_logger_in_template - render :inline => "<%= logger.class %>" - end - - def accessing_action_name_in_template - render :inline => "<%= action_name %>" - end - - def accessing_controller_name_in_template - render :inline => "<%= controller_name %>" - end - - # :ported: - def render_custom_code - render :text => "hello world", :status => 404 - end - - # :ported: - def render_text_with_nil - render :text => nil - end - - # :ported: - def render_text_with_false - render :text => false - end - - def render_text_with_resource - render :text => Customer.new("David") - end - - # :ported: - def render_nothing_with_appendix - render :text => "appended" - end - - # This test is testing 3 things: - # render :file in AV :ported: - # render :template in AC :ported: - # setting content type - def render_xml_hello - @name = "David" - render :template => "test/hello" - end - - def render_xml_hello_as_string_template - @name = "David" - render "test/hello" - end - - def render_line_offset - render :inline => '<% raise %>', :locals => {:foo => 'bar'} - end - def heading head :ok end - def greeting - # let's just rely on the template - end - - # :ported: - def blank_response - render :text => ' ' - end - - # :ported: - def layout_test - render :action => "hello_world" - end - - # :ported: - def builder_layout_test - @name = nil - render :action => "hello", :layout => "layouts/builder" - end - - # :move: test this in Action View - def builder_partial_test - render :action => "hello_world_container" - end - - # :ported: - def partials_list - @test_unchanged = 'hello' - @customers = [ Customer.new("david"), Customer.new("mary") ] - render :action => "list" - end - - def partial_only - render :partial => true - end - - def hello_in_a_string - @customers = [ Customer.new("david"), Customer.new("mary") ] - render :text => "How's there? " + render_to_string(:template => "test/list") - end - - def accessing_params_in_template - render :inline => "Hello: <%= params[:name] %>" - end - - def accessing_local_assigns_in_inline_template - name = params[:local_name] - render :inline => "<%= 'Goodbye, ' + local_name %>", - :locals => { :local_name => name } - end - - def render_implicit_html_template_from_xhr_request - end - - def render_implicit_js_template_without_layout - end - - def formatted_html_erb - end - - def formatted_xml_erb - end - - def render_to_string_test - @foo = render_to_string :inline => "this is a test" - end - - def default_render - @alternate_default_render ||= nil - if @alternate_default_render - @alternate_default_render.call - else - super - end - end - - def render_action_hello_world_as_symbol - render :action => :hello_world - end - - def layout_test_with_different_layout - render :action => "hello_world", :layout => "standard" - end - - def layout_test_with_different_layout_and_string_action - render "hello_world", :layout => "standard" - end - - def layout_test_with_different_layout_and_symbol_action - render :hello_world, :layout => "standard" - end - - def rendering_without_layout - render :action => "hello_world", :layout => false - end - - def layout_overriding_layout - render :action => "hello_world", :layout => "standard" - end - - def rendering_nothing_on_layout - render :nothing => true - end - - def render_to_string_with_assigns - @before = "i'm before the render" - render_to_string :text => "foo" - @after = "i'm after the render" - render :template => "test/hello_world" - end - - def render_to_string_with_exception - render_to_string :file => "exception that will not be caught - this will certainly not work" - end - - def render_to_string_with_caught_exception - @before = "i'm before the render" - begin - render_to_string :file => "exception that will be caught- hope my future instance vars still work!" - rescue - end - @after = "i'm after the render" - render :template => "test/hello_world" - end - - def accessing_params_in_template_with_layout - render :layout => true, :inline => "Hello: <%= params[:name] %>" - end - - # :ported: - def render_with_explicit_template - render :template => "test/hello_world" - end - - def render_with_explicit_unescaped_template - render :template => "test/h*llo_world" - end - - def render_with_explicit_escaped_template - render :template => "test/hello,world" - end - - def render_with_explicit_string_template - render "test/hello_world" - end - - # :ported: - def render_with_explicit_template_with_locals - render :template => "test/render_file_with_locals", :locals => { :secret => 'area51' } - end - # :ported: def double_render render :text => "hello" @@ -504,25 +173,6 @@ class TestController < ActionController::Base render :template => "test/hello_world_from_rxml", :handlers => [:builder] end - def action_talk_to_layout - # Action template sets variable that's picked up by layout - end - - # :addressed: - def render_text_with_assigns - @hello = "world" - render :text => "foo" - end - - def yield_content_for - render :action => "content_for", :layout => "yield" - end - - def render_content_type_from_body - response.content_type = Mime::RSS - render :text => "hello world!" - end - def head_created head :created end @@ -567,160 +217,13 @@ class TestController < ActionController::Base head :forbidden, :x_custom_header => "something" end - def render_using_layout_around_block - render :action => "using_layout_around_block" - end + def head_with_no_content + # Fill in the headers with dummy data to make + # sure they get removed during the testing + response.headers["Content-Type"] = "dummy" + response.headers["Content-Length"] = 42 - def render_using_layout_around_block_in_main_layout_and_within_content_for_layout - render :action => "using_layout_around_block", :layout => "layouts/block_with_layout" - end - - def partial_formats_html - render :partial => 'partial', :formats => [:html] - end - - def partial - render :partial => 'partial' - end - - def partial_html_erb - render :partial => 'partial_html_erb' - end - - def render_to_string_with_partial - @partial_only = render_to_string :partial => "partial_only" - @partial_with_locals = render_to_string :partial => "customer", :locals => { :customer => Customer.new("david") } - render :template => "test/hello_world" - end - - def render_to_string_with_template_and_html_partial - @text = render_to_string :template => "test/with_partial", :formats => [:text] - @html = render_to_string :template => "test/with_partial", :formats => [:html] - render :template => "test/with_html_partial" - end - - def render_to_string_and_render_with_different_formats - @html = render_to_string :template => "test/with_partial", :formats => [:html] - render :template => "test/with_partial", :formats => [:text] - end - - def render_template_within_a_template_with_other_format - render :template => "test/with_xml_template", - :formats => [:html], - :layout => "with_html_partial" - end - - def partial_with_counter - render :partial => "counter", :locals => { :counter_counter => 5 } - end - - def partial_with_locals - render :partial => "customer", :locals => { :customer => Customer.new("david") } - end - - def partial_with_form_builder - render :partial => ActionView::Helpers::FormBuilder.new(:post, nil, view_context, {}) - end - - def partial_with_form_builder_subclass - render :partial => LabellingFormBuilder.new(:post, nil, view_context, {}) - end - - def partial_collection - render :partial => "customer", :collection => [ Customer.new("david"), Customer.new("mary") ] - end - - def partial_collection_with_as - render :partial => "customer_with_var", :collection => [ Customer.new("david"), Customer.new("mary") ], :as => :customer - end - - def partial_collection_with_counter - render :partial => "customer_counter", :collection => [ Customer.new("david"), Customer.new("mary") ] - end - - def partial_collection_with_as_and_counter - render :partial => "customer_counter_with_as", :collection => [ Customer.new("david"), Customer.new("mary") ], :as => :client - end - - def partial_collection_with_locals - render :partial => "customer_greeting", :collection => [ Customer.new("david"), Customer.new("mary") ], :locals => { :greeting => "Bonjour" } - end - - def partial_collection_with_spacer - render :partial => "customer", :spacer_template => "partial_only", :collection => [ Customer.new("david"), Customer.new("mary") ] - end - - def partial_collection_with_spacer_which_uses_render - render :partial => "customer", :spacer_template => "partial_with_partial", :collection => [ Customer.new("david"), Customer.new("mary") ] - end - - def partial_collection_shorthand_with_locals - render :partial => [ Customer.new("david"), Customer.new("mary") ], :locals => { :greeting => "Bonjour" } - end - - def partial_collection_shorthand_with_different_types_of_records - render :partial => [ - BadCustomer.new("mark"), - GoodCustomer.new("craig"), - BadCustomer.new("john"), - GoodCustomer.new("zach"), - GoodCustomer.new("brandon"), - BadCustomer.new("dan") ], - :locals => { :greeting => "Bonjour" } - end - - def empty_partial_collection - render :partial => "customer", :collection => [] - end - - def partial_collection_shorthand_with_different_types_of_records_with_counter - partial_collection_shorthand_with_different_types_of_records - end - - def missing_partial - render :partial => 'thisFileIsntHere' - end - - def partial_with_hash_object - render :partial => "hash_object", :object => {:first_name => "Sam"} - end - - def partial_with_nested_object - render :partial => "quiz/questions/question", :object => Quiz::Question.new("first") - end - - def partial_with_nested_object_shorthand - render Quiz::Question.new("first") - end - - def partial_hash_collection - render :partial => "hash_object", :collection => [ {:first_name => "Pratik"}, {:first_name => "Amy"} ] - end - - def partial_hash_collection_with_locals - render :partial => "hash_greeting", :collection => [ {:first_name => "Pratik"}, {:first_name => "Amy"} ], :locals => { :greeting => "Hola" } - end - - def partial_with_implicit_local_assignment - @customer = Customer.new("Marcel") - render :partial => "customer" - end - - def render_call_to_partial_with_layout - render :action => "calling_partial_with_layout" - end - - def render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout - render :action => "calling_partial_with_layout", :layout => "layouts/partial_with_layout" - end - - before_action only: :render_with_filters do - request.format = :xml - end - - # Ensure that the before filter is executed *before* self.formats is set. - def render_with_filters - render :action => :formatted_xml_erb + head 204 end private @@ -751,497 +254,246 @@ class TestController < ActionController::Base end class MetalTestController < ActionController::Metal + include AbstractController::Rendering + include ActionView::Rendering include ActionController::Rendering + include ActionController::RackDelegation + def accessing_logger_in_template render :inline => "<%= logger.class %>" end end -class RenderTest < ActionController::TestCase +class ExpiresInRenderTest < ActionController::TestCase tests TestController - def setup - # enable a logger so that (e.g.) the benchmarking stuff runs, so we can get - # a more accurate simulation of what happens in "real life". - super - @controller.logger = ActiveSupport::Logger.new(nil) - ActionView::Base.logger = ActiveSupport::Logger.new(nil) - - @request.host = "www.nextangle.com" - end - - # :ported: - def test_simple_show - get :hello_world - assert_response 200 - assert_response :success - assert_template "test/hello_world" - assert_equal "<html>Hello world!</html>", @response.body - end - - # :ported: - def test_renders_default_template_for_missing_action - get :'hyphen-ated' - assert_template 'test/hyphen-ated' - end - - # :ported: - def test_render - get :render_hello_world - assert_template "test/hello_world" - end - - def test_line_offset - begin - get :render_line_offset - flunk "the action should have raised an exception" - rescue StandardError => exc - line = exc.backtrace.first - assert(line =~ %r{:(\d+):}) - assert_equal "1", $1, - "The line offset is wrong, perhaps the wrong exception has been raised, exception was: #{exc.inspect}" - end - end - - # :ported: compatibility - def test_render_with_forward_slash - get :render_hello_world_with_forward_slash - assert_template "test/hello_world" - end - - # :ported: - def test_render_in_top_directory - get :render_template_in_top_directory - assert_template "shared" - assert_equal "Elastica", @response.body - end - - # :ported: - def test_render_in_top_directory_with_slash - get :render_template_in_top_directory_with_slash - assert_template "shared" - assert_equal "Elastica", @response.body - end - - # :ported: - def test_render_from_variable - get :render_hello_world_from_variable - assert_equal "hello david", @response.body - end - - # :ported: - def test_render_action - get :render_action_hello_world - assert_template "test/hello_world" - end - - def test_render_action_upcased - assert_raise ActionView::MissingTemplate do - get :render_action_upcased_hello_world - end - end - - # :ported: - def test_render_action_hello_world_as_string - get :render_action_hello_world_as_string - assert_equal "Hello world!", @response.body - assert_template "test/hello_world" - end - - # :ported: - def test_render_action_with_symbol - get :render_action_hello_world_with_symbol - assert_template "test/hello_world" - end - - # :ported: - def test_render_text - get :render_text_hello_world - assert_equal "hello world", @response.body - end - - # :ported: - def test_do_with_render_text_and_layout - get :render_text_hello_world_with_layout - assert_equal "<html>hello world, I am here!</html>", @response.body - end - - # :ported: - def test_do_with_render_action_and_layout_false - get :hello_world_with_layout_false - assert_equal 'Hello world!', @response.body - end - - # :ported: - def test_render_file_with_instance_variables - get :render_file_with_instance_variables - assert_equal "The secret is in the sauce\n", @response.body - end - - def test_render_file - get :hello_world_file - assert_equal "Hello world!", @response.body - end - - # :ported: - def test_render_file_as_string_with_instance_variables - get :render_file_as_string_with_instance_variables - assert_equal "The secret is in the sauce\n", @response.body - end - - # :ported: - def test_render_file_not_using_full_path - get :render_file_not_using_full_path - assert_equal "The secret is in the sauce\n", @response.body - end - - # :ported: - def test_render_file_not_using_full_path_with_dot_in_path - get :render_file_not_using_full_path_with_dot_in_path - assert_equal "The secret is in the sauce\n", @response.body - end - - # :ported: - def test_render_file_using_pathname - get :render_file_using_pathname - assert_equal "The secret is in the sauce\n", @response.body - end - - # :ported: - def test_render_file_with_locals - get :render_file_with_locals - assert_equal "The secret is in the sauce\n", @response.body - end - - # :ported: - def test_render_file_as_string_with_locals - get :render_file_as_string_with_locals - assert_equal "The secret is in the sauce\n", @response.body - end - - # :assessed: - def test_render_file_from_template - get :render_file_from_template - assert_equal "The secret is in the sauce\n", @response.body - end - - # :ported: - def test_render_custom_code - get :render_custom_code - assert_response 404 - assert_response :missing - assert_equal 'hello world', @response.body - end - - # :ported: - def test_render_text_with_nil - get :render_text_with_nil - assert_response 200 - assert_equal ' ', @response.body - end - - # :ported: - def test_render_text_with_false - get :render_text_with_false - assert_equal 'false', @response.body - end - - # :ported: - def test_render_nothing_with_appendix - get :render_nothing_with_appendix - assert_response 200 - assert_equal 'appended', @response.body - end - - def test_render_text_with_resource - get :render_text_with_resource - assert_equal 'name: "David"', @response.body - end - - # :ported: - def test_attempt_to_access_object_method - assert_raise(AbstractController::ActionNotFound, "No action responded to [clone]") { get :clone } - end - - # :ported: - def test_private_methods - assert_raise(AbstractController::ActionNotFound, "No action responded to [determine_layout]") { get :determine_layout } - end - - # :ported: - def test_access_to_request_in_view - get :accessing_request_in_template - assert_equal "Hello: www.nextangle.com", @response.body - end - - def test_access_to_logger_in_view - get :accessing_logger_in_template - assert_equal "ActiveSupport::Logger", @response.body - end - - # :ported: - def test_access_to_action_name_in_view - get :accessing_action_name_in_template - assert_equal "accessing_action_name_in_template", @response.body - end - - # :ported: - def test_access_to_controller_name_in_view - get :accessing_controller_name_in_template - assert_equal "test", @response.body # name is explicitly set in the controller. - end - - # :ported: - def test_render_xml - get :render_xml_hello - assert_equal "<html>\n <p>Hello David</p>\n<p>This is grand!</p>\n</html>\n", @response.body - assert_equal "application/xml", @response.content_type - end - - # :ported: - def test_render_xml_as_string_template - get :render_xml_hello_as_string_template - assert_equal "<html>\n <p>Hello David</p>\n<p>This is grand!</p>\n</html>\n", @response.body - assert_equal "application/xml", @response.content_type - end - - # :ported: - def test_render_xml_with_default - get :greeting - assert_equal "<p>This is grand!</p>\n", @response.body - end - - # :move: test in AV - def test_render_xml_with_partial - get :builder_partial_test - assert_equal "<test>\n <hello/>\n</test>\n", @response.body + def test_expires_in_header + get :conditional_hello_with_expires_in + assert_equal "max-age=60, private", @response.headers["Cache-Control"] end - # :ported: - def test_layout_rendering - get :layout_test - assert_equal "<html>Hello world!</html>", @response.body + def test_expires_in_header_with_public + get :conditional_hello_with_expires_in_with_public + assert_equal "max-age=60, public", @response.headers["Cache-Control"] end - def test_render_xml_with_layouts - get :builder_layout_test - assert_equal "<wrapper>\n<html>\n <p>Hello </p>\n<p>This is grand!</p>\n</html>\n</wrapper>\n", @response.body + def test_expires_in_header_with_must_revalidate + get :conditional_hello_with_expires_in_with_must_revalidate + assert_equal "max-age=60, private, must-revalidate", @response.headers["Cache-Control"] end - def test_partials_list - get :partials_list - assert_equal "goodbyeHello: davidHello: marygoodbye\n", @response.body + def test_expires_in_header_with_public_and_must_revalidate + get :conditional_hello_with_expires_in_with_public_and_must_revalidate + assert_equal "max-age=60, public, must-revalidate", @response.headers["Cache-Control"] end - def test_render_to_string - get :hello_in_a_string - assert_equal "How's there? goodbyeHello: davidHello: marygoodbye\n", @response.body + def test_expires_in_header_with_additional_headers + get :conditional_hello_with_expires_in_with_public_with_more_keys + assert_equal "max-age=60, public, s-maxage=18000", @response.headers["Cache-Control"] end - def test_render_to_string_resets_assigns - get :render_to_string_test - assert_equal "The value of foo is: ::this is a test::\n", @response.body + def test_expires_in_old_syntax + get :conditional_hello_with_expires_in_with_public_with_more_keys_old_syntax + assert_equal "max-age=60, public, s-maxage=18000", @response.headers["Cache-Control"] end - def test_render_to_string_inline - get :render_to_string_with_inline_and_render - assert_template "test/hello_world" + def test_expires_now + get :conditional_hello_with_expires_now + assert_equal "no-cache", @response.headers["Cache-Control"] end - # :ported: - def test_nested_rendering - @controller = Fun::GamesController.new - get :hello_world - assert_equal "Living in a nested world", @response.body + def test_expires_now_with_cache_control_headers + get :conditional_hello_with_cache_control_headers + assert_match(/no-cache/, @response.headers["Cache-Control"]) + assert_match(/no-transform/, @response.headers["Cache-Control"]) end - def test_accessing_params_in_template - get :accessing_params_in_template, :name => "David" - assert_equal "Hello: David", @response.body + def test_date_header_when_expires_in + time = Time.mktime(2011,10,30) + Time.stubs(:now).returns(time) + get :conditional_hello_with_expires_in + assert_equal Time.now.httpdate, @response.headers["Date"] end +end - def test_accessing_local_assigns_in_inline_template - get :accessing_local_assigns_in_inline_template, :local_name => "Local David" - assert_equal "Goodbye, Local David", @response.body - assert_equal "text/html", @response.content_type - end +class LastModifiedRenderTest < ActionController::TestCase + tests TestController - def test_should_implicitly_render_html_template_from_xhr_request - xhr :get, :render_implicit_html_template_from_xhr_request - assert_equal "XHR!\nHello HTML!", @response.body + def setup + super + @last_modified = Time.now.utc.beginning_of_day.httpdate end - def test_should_implicitly_render_js_template_without_layout - get :render_implicit_js_template_without_layout, :format => :js - assert_no_match %r{<html>}, @response.body + def test_responds_with_last_modified + get :conditional_hello + assert_equal @last_modified, @response.headers['Last-Modified'] end - def test_should_render_formatted_template - get :formatted_html_erb - assert_equal 'formatted html erb', @response.body + def test_request_not_modified + @request.if_modified_since = @last_modified + get :conditional_hello + assert_equal 304, @response.status.to_i + assert @response.body.blank? + assert_equal @last_modified, @response.headers['Last-Modified'] end - def test_should_render_formatted_html_erb_template - get :formatted_xml_erb - assert_equal '<test>passed formatted html erb</test>', @response.body + def test_request_not_modified_but_etag_differs + @request.if_modified_since = @last_modified + @request.if_none_match = "234" + get :conditional_hello + assert_response :success end - def test_should_render_formatted_html_erb_template_with_faulty_accepts_header - @request.accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, appliction/x-shockwave-flash, */*" - get :formatted_xml_erb - assert_equal '<test>passed formatted html erb</test>', @response.body + def test_request_modified + @request.if_modified_since = 'Thu, 16 Jul 2008 00:00:00 GMT' + get :conditional_hello + assert_equal 200, @response.status.to_i + assert @response.body.present? + assert_equal @last_modified, @response.headers['Last-Modified'] end - def test_layout_test_with_different_layout - get :layout_test_with_different_layout - assert_equal "<html>Hello world!</html>", @response.body - end - def test_layout_test_with_different_layout_and_string_action - get :layout_test_with_different_layout_and_string_action - assert_equal "<html>Hello world!</html>", @response.body + def test_responds_with_last_modified_with_record + get :conditional_hello_with_record + assert_equal @last_modified, @response.headers['Last-Modified'] end - def test_layout_test_with_different_layout_and_symbol_action - get :layout_test_with_different_layout_and_symbol_action - assert_equal "<html>Hello world!</html>", @response.body + def test_request_not_modified_with_record + @request.if_modified_since = @last_modified + get :conditional_hello_with_record + assert_equal 304, @response.status.to_i + assert @response.body.blank? + assert_equal @last_modified, @response.headers['Last-Modified'] end - def test_rendering_without_layout - get :rendering_without_layout - assert_equal "Hello world!", @response.body + def test_request_not_modified_but_etag_differs_with_record + @request.if_modified_since = @last_modified + @request.if_none_match = "234" + get :conditional_hello_with_record + assert_response :success end - def test_layout_overriding_layout - get :layout_overriding_layout - assert_no_match %r{<title>}, @response.body + def test_request_modified_with_record + @request.if_modified_since = 'Thu, 16 Jul 2008 00:00:00 GMT' + get :conditional_hello_with_record + assert_equal 200, @response.status.to_i + assert @response.body.present? + assert_equal @last_modified, @response.headers['Last-Modified'] end - def test_rendering_nothing_on_layout - get :rendering_nothing_on_layout - assert_equal " ", @response.body + def test_request_with_bang_gets_last_modified + get :conditional_hello_with_bangs + assert_equal @last_modified, @response.headers['Last-Modified'] + assert_response :success end - def test_render_to_string_doesnt_break_assigns - get :render_to_string_with_assigns - assert_equal "i'm before the render", assigns(:before) - assert_equal "i'm after the render", assigns(:after) + def test_request_with_bang_obeys_last_modified + @request.if_modified_since = @last_modified + get :conditional_hello_with_bangs + assert_response :not_modified end - def test_bad_render_to_string_still_throws_exception - assert_raise(ActionView::MissingTemplate) { get :render_to_string_with_exception } + def test_last_modified_works_with_less_than_too + @request.if_modified_since = 5.years.ago.httpdate + get :conditional_hello_with_bangs + assert_response :success end +end - def test_render_to_string_that_throws_caught_exception_doesnt_break_assigns - assert_nothing_raised { get :render_to_string_with_caught_exception } - assert_equal "i'm before the render", assigns(:before) - assert_equal "i'm after the render", assigns(:after) - end +class EtagRenderTest < ActionController::TestCase + tests TestControllerWithExtraEtags - def test_accessing_params_in_template_with_layout - get :accessing_params_in_template_with_layout, :name => "David" - assert_equal "<html>Hello: David</html>", @response.body - end + def test_multiple_etags + @request.if_none_match = etag(["123", 'ab', :cde, [:f]]) + get :fresh + assert_response :not_modified - def test_render_with_explicit_template - get :render_with_explicit_template + @request.if_none_match = %("nomatch") + get :fresh assert_response :success end - def test_render_with_explicit_unescaped_template - assert_raise(ActionView::MissingTemplate) { get :render_with_explicit_unescaped_template } - get :render_with_explicit_escaped_template - assert_equal "Hello w*rld!", @response.body - end - - def test_render_with_explicit_string_template - get :render_with_explicit_string_template - assert_equal "<html>Hello world!</html>", @response.body - end - - def test_render_with_filters - get :render_with_filters - assert_equal "<test>passed formatted xml erb</test>", @response.body - end + def test_array + @request.if_none_match = etag([%w(1 2 3), 'ab', :cde, [:f]]) + get :array + assert_response :not_modified - # :ported: - def test_double_render - assert_raise(AbstractController::DoubleRenderError) { get :double_render } + @request.if_none_match = %("nomatch") + get :array + assert_response :success end - def test_double_redirect - assert_raise(AbstractController::DoubleRenderError) { get :double_redirect } - end + def test_etag_reflects_template_digest + get :with_template + assert_response :ok + assert_not_nil etag = @response.etag - def test_render_and_redirect - assert_raise(AbstractController::DoubleRenderError) { get :render_and_redirect } - end + request.if_none_match = etag + get :with_template + assert_response :not_modified - # specify the one exception to double render rule - render_to_string followed by render - def test_render_to_string_and_render - get :render_to_string_and_render - assert_equal("Hi web users! here is some cached stuff", @response.body) - end + # Modify the template digest + path = File.expand_path('../../fixtures/test/hello_world.erb', __FILE__) + old = File.read(path) - def test_rendering_with_conflicting_local_vars - get :rendering_with_conflicting_local_vars - assert_equal("First: David\nSecond: Stephan\nThird: David\nFourth: David\nFifth: ", @response.body) - end + begin + File.write path, 'foo' + ActionView::Digestor.cache.clear - def test_action_talk_to_layout - get :action_talk_to_layout - assert_equal "<title>Talking to the layout</title>\nAction was here!", @response.body + request.if_none_match = etag + get :with_template + assert_response :ok + assert_not_equal etag, @response.etag + ensure + File.write path, old + end end - # :addressed: - def test_render_text_with_assigns - get :render_text_with_assigns - assert_equal "world", assigns["hello"] + def etag(record) + Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(record)).inspect end +end - # :ported: - def test_template_with_locals - get :render_with_explicit_template_with_locals - assert_equal "The secret is area51\n", @response.body - end +class MetalRenderTest < ActionController::TestCase + tests MetalTestController - def test_yield_content_for - get :yield_content_for - assert_equal "<title>Putting stuff in the title!</title>\nGreat stuff!\n", @response.body + def test_access_to_logger_in_view + get :accessing_logger_in_template + assert_equal "NilClass", @response.body end +end - def test_overwritting_rendering_relative_file_with_extension - get :hello_world_from_rxml_using_template - assert_equal "<html>\n <p>Hello</p>\n</html>\n", @response.body +class HeadRenderTest < ActionController::TestCase + tests TestController - get :hello_world_from_rxml_using_action - assert_equal "<html>\n <p>Hello</p>\n</html>\n", @response.body + def setup + @request.host = "www.nextangle.com" end def test_head_created post :head_created - assert_blank @response.body + assert @response.body.blank? assert_response :created end def test_head_created_with_application_json_content_type post :head_created_with_application_json_content_type - assert_blank @response.body + assert @response.body.blank? assert_equal "application/json", @response.header["Content-Type"] assert_response :created end def test_head_ok_with_image_png_content_type post :head_ok_with_image_png_content_type - assert_blank @response.body + assert @response.body.blank? assert_equal "image/png", @response.header["Content-Type"] assert_response :ok end def test_head_with_location_header get :head_with_location_header - assert_blank @response.body + assert @response.body.blank? assert_equal "/foo", @response.headers["Location"] assert_response :ok end @@ -1254,7 +506,7 @@ class RenderTest < ActionController::TestCase end get :head_with_location_object - assert_blank @response.body + assert @response.body.blank? assert_equal "http://www.nextangle.com/customers/1", @response.headers["Location"] assert_response :ok end @@ -1262,14 +514,14 @@ class RenderTest < ActionController::TestCase def test_head_with_custom_header get :head_with_custom_header - assert_blank @response.body + assert @response.body.blank? assert_equal "something", @response.headers["X-Custom-Header"] assert_response :ok end def test_head_with_www_authenticate_header get :head_with_www_authenticate_header - assert_blank @response.body + assert @response.body.blank? assert_equal "something", @response.headers["WWW-Authenticate"] assert_response :ok end @@ -1302,6 +554,14 @@ class RenderTest < ActionController::TestCase end end + def test_head_with_no_content + get :head_with_no_content + + assert_equal 204, @response.status + assert_nil @response.headers["Content-Type"] + assert_nil @response.headers["Content-Length"] + end + def test_head_with_string_status get :head_with_string_status, :status => "404 Eat Dirt" assert_equal 404, @response.response_code @@ -1316,386 +576,4 @@ class RenderTest < ActionController::TestCase assert_equal "something", @response.headers["X-Custom-Header"] assert_response :forbidden end - - def test_using_layout_around_block - get :render_using_layout_around_block - assert_equal "Before (David)\nInside from block\nAfter", @response.body - end - - def test_using_layout_around_block_in_main_layout_and_within_content_for_layout - get :render_using_layout_around_block_in_main_layout_and_within_content_for_layout - assert_equal "Before (Anthony)\nInside from first block in layout\nAfter\nBefore (David)\nInside from block\nAfter\nBefore (Ramm)\nInside from second block in layout\nAfter\n", @response.body - end - - def test_partial_only - get :partial_only - assert_equal "only partial", @response.body - assert_equal "text/html", @response.content_type - end - - def test_should_render_html_formatted_partial - get :partial - assert_equal "partial html", @response.body - assert_equal "text/html", @response.content_type - end - - def test_render_html_formatted_partial_even_with_other_mime_time_in_accept - @request.accept = "text/javascript, text/html" - - get :partial_html_erb - - assert_equal "partial.html.erb", @response.body.strip - assert_equal "text/html", @response.content_type - end - - def test_should_render_html_partial_with_formats - get :partial_formats_html - assert_equal "partial html", @response.body - assert_equal "text/html", @response.content_type - end - - def test_render_to_string_partial - get :render_to_string_with_partial - assert_equal "only partial", assigns(:partial_only) - assert_equal "Hello: david", assigns(:partial_with_locals) - assert_equal "text/html", @response.content_type - end - - def test_render_to_string_with_template_and_html_partial - get :render_to_string_with_template_and_html_partial - assert_equal "**only partial**\n", assigns(:text) - assert_equal "<strong>only partial</strong>\n", assigns(:html) - assert_equal "<strong>only html partial</strong>\n", @response.body - assert_equal "text/html", @response.content_type - end - - def test_render_to_string_and_render_with_different_formats - get :render_to_string_and_render_with_different_formats - assert_equal "<strong>only partial</strong>\n", assigns(:html) - assert_equal "**only partial**\n", @response.body - assert_equal "text/plain", @response.content_type - end - - def test_render_template_within_a_template_with_other_format - get :render_template_within_a_template_with_other_format - expected = "only html partial<p>This is grand!</p>" - assert_equal expected, @response.body.strip - assert_equal "text/html", @response.content_type - end - - def test_partial_with_counter - get :partial_with_counter - assert_equal "5", @response.body - end - - def test_partial_with_locals - get :partial_with_locals - assert_equal "Hello: david", @response.body - end - - def test_partial_with_form_builder - get :partial_with_form_builder - assert_match(/<label/, @response.body) - assert_template('test/_form') - end - - def test_partial_with_form_builder_subclass - get :partial_with_form_builder_subclass - assert_match(/<label/, @response.body) - assert_template('test/_labelling_form') - end - - def test_nested_partial_with_form_builder - @controller = Fun::GamesController.new - get :nested_partial_with_form_builder - assert_match(/<label/, @response.body) - assert_template('fun/games/_form') - end - - def test_namespaced_object_partial - @controller = Quiz::QuestionsController.new - get :new - assert_equal "Namespaced Partial", @response.body - end - - def test_partial_collection - get :partial_collection - assert_equal "Hello: davidHello: mary", @response.body - end - - def test_partial_collection_with_as - get :partial_collection_with_as - assert_equal "david david davidmary mary mary", @response.body - end - - def test_partial_collection_with_counter - get :partial_collection_with_counter - assert_equal "david0mary1", @response.body - end - - def test_partial_collection_with_as_and_counter - get :partial_collection_with_as_and_counter - assert_equal "david0mary1", @response.body - end - - def test_partial_collection_with_locals - get :partial_collection_with_locals - assert_equal "Bonjour: davidBonjour: mary", @response.body - end - - def test_locals_option_to_assert_template_is_not_supported - get :partial_collection_with_locals - - warning_buffer = StringIO.new - $stderr = warning_buffer - - assert_template partial: 'customer_greeting', locals: { greeting: 'Bonjour' } - assert_equal "the :locals option to #assert_template is only supported in a ActionView::TestCase\n", warning_buffer.string - ensure - $stderr = STDERR - end - - def test_partial_collection_with_spacer - get :partial_collection_with_spacer - assert_equal "Hello: davidonly partialHello: mary", @response.body - assert_template :partial => '_customer' - end - - def test_partial_collection_with_spacer_which_uses_render - get :partial_collection_with_spacer_which_uses_render - assert_equal "Hello: davidpartial html\npartial with partial\nHello: mary", @response.body - assert_template :partial => '_customer' - end - - def test_partial_collection_shorthand_with_locals - get :partial_collection_shorthand_with_locals - assert_equal "Bonjour: davidBonjour: mary", @response.body - assert_template :partial => 'customers/_customer', :count => 2 - assert_template :partial => '_completely_fake_and_made_up_template_that_cannot_possibly_be_rendered', :count => 0 - end - - def test_partial_collection_shorthand_with_different_types_of_records - get :partial_collection_shorthand_with_different_types_of_records - assert_equal "Bonjour bad customer: mark0Bonjour good customer: craig1Bonjour bad customer: john2Bonjour good customer: zach3Bonjour good customer: brandon4Bonjour bad customer: dan5", @response.body - assert_template :partial => 'good_customers/_good_customer', :count => 3 - assert_template :partial => 'bad_customers/_bad_customer', :count => 3 - end - - def test_empty_partial_collection - get :empty_partial_collection - assert_equal " ", @response.body - assert_template :partial => false - end - - def test_partial_with_hash_object - get :partial_with_hash_object - assert_equal "Sam\nmaS\n", @response.body - end - - def test_partial_with_nested_object - get :partial_with_nested_object - assert_equal "first", @response.body - end - - def test_partial_with_nested_object_shorthand - get :partial_with_nested_object_shorthand - assert_equal "first", @response.body - end - - def test_hash_partial_collection - get :partial_hash_collection - assert_equal "Pratik\nkitarP\nAmy\nymA\n", @response.body - end - - def test_partial_hash_collection_with_locals - get :partial_hash_collection_with_locals - assert_equal "Hola: PratikHola: Amy", @response.body - end - - def test_render_missing_partial_template - assert_raise(ActionView::MissingTemplate) do - get :missing_partial - end - end - - def test_render_call_to_partial_with_layout - get :render_call_to_partial_with_layout - assert_equal "Before (David)\nInside from partial (David)\nAfter", @response.body - end - - def test_render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout - get :render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout - assert_equal "Before (Anthony)\nInside from partial (Anthony)\nAfter\nBefore (David)\nInside from partial (David)\nAfter\nBefore (Ramm)\nInside from partial (Ramm)\nAfter", @response.body - end -end - -class ExpiresInRenderTest < ActionController::TestCase - tests TestController - - def setup - @request.host = "www.nextangle.com" - end - - def test_expires_in_header - get :conditional_hello_with_expires_in - assert_equal "max-age=60, private", @response.headers["Cache-Control"] - end - - def test_expires_in_header_with_public - get :conditional_hello_with_expires_in_with_public - assert_equal "max-age=60, public", @response.headers["Cache-Control"] - end - - def test_expires_in_header_with_must_revalidate - get :conditional_hello_with_expires_in_with_must_revalidate - assert_equal "max-age=60, private, must-revalidate", @response.headers["Cache-Control"] - end - - def test_expires_in_header_with_public_and_must_revalidate - get :conditional_hello_with_expires_in_with_public_and_must_revalidate - assert_equal "max-age=60, public, must-revalidate", @response.headers["Cache-Control"] - end - - def test_expires_in_header_with_additional_headers - get :conditional_hello_with_expires_in_with_public_with_more_keys - assert_equal "max-age=60, public, s-maxage=18000", @response.headers["Cache-Control"] - end - - def test_expires_in_old_syntax - get :conditional_hello_with_expires_in_with_public_with_more_keys_old_syntax - assert_equal "max-age=60, public, s-maxage=18000", @response.headers["Cache-Control"] - end - - def test_expires_now - get :conditional_hello_with_expires_now - assert_equal "no-cache", @response.headers["Cache-Control"] - end - - def test_expires_now_with_cache_control_headers - get :conditional_hello_with_cache_control_headers - assert_match(/no-cache/, @response.headers["Cache-Control"]) - assert_match(/no-transform/, @response.headers["Cache-Control"]) - end - - def test_date_header_when_expires_in - time = Time.mktime(2011,10,30) - Time.stubs(:now).returns(time) - get :conditional_hello_with_expires_in - assert_equal Time.now.httpdate, @response.headers["Date"] - end -end - -class LastModifiedRenderTest < ActionController::TestCase - tests TestController - - def setup - super - @request.host = "www.nextangle.com" - @last_modified = Time.now.utc.beginning_of_day.httpdate - end - - def test_responds_with_last_modified - get :conditional_hello - assert_equal @last_modified, @response.headers['Last-Modified'] - end - - def test_request_not_modified - @request.if_modified_since = @last_modified - get :conditional_hello - assert_equal 304, @response.status.to_i - assert_blank @response.body - assert_equal @last_modified, @response.headers['Last-Modified'] - end - - def test_request_not_modified_but_etag_differs - @request.if_modified_since = @last_modified - @request.if_none_match = "234" - get :conditional_hello - assert_response :success - end - - def test_request_modified - @request.if_modified_since = 'Thu, 16 Jul 2008 00:00:00 GMT' - get :conditional_hello - assert_equal 200, @response.status.to_i - assert_present @response.body - assert_equal @last_modified, @response.headers['Last-Modified'] - end - - - def test_responds_with_last_modified_with_record - get :conditional_hello_with_record - assert_equal @last_modified, @response.headers['Last-Modified'] - end - - def test_request_not_modified_with_record - @request.if_modified_since = @last_modified - get :conditional_hello_with_record - assert_equal 304, @response.status.to_i - assert_blank @response.body - assert_equal @last_modified, @response.headers['Last-Modified'] - end - - def test_request_not_modified_but_etag_differs_with_record - @request.if_modified_since = @last_modified - @request.if_none_match = "234" - get :conditional_hello_with_record - assert_response :success - end - - def test_request_modified_with_record - @request.if_modified_since = 'Thu, 16 Jul 2008 00:00:00 GMT' - get :conditional_hello_with_record - assert_equal 200, @response.status.to_i - assert_present @response.body - assert_equal @last_modified, @response.headers['Last-Modified'] - end - - - def test_request_with_bang_gets_last_modified - get :conditional_hello_with_bangs - assert_equal @last_modified, @response.headers['Last-Modified'] - assert_response :success - end - - def test_request_with_bang_obeys_last_modified - @request.if_modified_since = @last_modified - get :conditional_hello_with_bangs - assert_response :not_modified - end - - def test_last_modified_works_with_less_than_too - @request.if_modified_since = 5.years.ago.httpdate - get :conditional_hello_with_bangs - assert_response :success - end -end - -class EtagRenderTest < ActionController::TestCase - tests TestControllerWithExtraEtags - - def setup - super - @request.host = "www.nextangle.com" - end - - def test_multiple_etags - @request.if_none_match = %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key([ "123", 'ab', :cde, [:f] ]))}") - get :fresh - assert_response :not_modified - - @request.if_none_match = %("nomatch") - get :fresh - assert_response :success - end -end - - -class MetalRenderTest < ActionController::TestCase - tests MetalTestController - - def test_access_to_logger_in_view - get :accessing_logger_in_template - assert_equal "NilClass", @response.body - end end diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index 1f637eb791..3e0bfe8d14 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -52,20 +52,56 @@ module RequestForgeryProtectionActions render :inline => "<%= form_for(:some_resource, :remote => true, :authenticity_token => 'external_token') {} %>" end + def same_origin_js + render js: 'foo();' + end + + def negotiate_same_origin + respond_to do |format| + format.js { same_origin_js } + end + end + + def cross_origin_js + same_origin_js + end + + def negotiate_cross_origin + negotiate_same_origin + end + def rescue_action(e) raise e end end # sample controllers class RequestForgeryProtectionControllerUsingResetSession < ActionController::Base include RequestForgeryProtectionActions - protect_from_forgery :only => %w(index meta), :with => :reset_session + protect_from_forgery :only => %w(index meta same_origin_js negotiate_same_origin), :with => :reset_session end class RequestForgeryProtectionControllerUsingException < ActionController::Base include RequestForgeryProtectionActions - protect_from_forgery :only => %w(index meta), :with => :exception + protect_from_forgery :only => %w(index meta same_origin_js negotiate_same_origin), :with => :exception end +class RequestForgeryProtectionControllerUsingNullSession < ActionController::Base + protect_from_forgery :with => :null_session + + def signed + cookies.signed[:foo] = 'bar' + render :nothing => true + end + + def encrypted + cookies.encrypted[:foo] = 'bar' + render :nothing => true + end + + def try_to_reset_session + reset_session + render :nothing => true + end +end class FreeCookieController < RequestForgeryProtectionControllerUsingResetSession self.allow_forgery_protection = false @@ -89,27 +125,29 @@ end module RequestForgeryProtectionTests def setup @token = "cf50faa3fe97702ca1ae" - - SecureRandom.stubs(:base64).returns(@token) + @controller.stubs(:form_authenticity_token).returns(@token) + @controller.stubs(:valid_authenticity_token?).with{ |_, t| t == @token }.returns(true) + @controller.stubs(:valid_authenticity_token?).with{ |_, t| t != @token }.returns(false) + @old_request_forgery_protection_token = ActionController::Base.request_forgery_protection_token ActionController::Base.request_forgery_protection_token = :custom_authenticity_token end def teardown - ActionController::Base.request_forgery_protection_token = nil + ActionController::Base.request_forgery_protection_token = @old_request_forgery_protection_token end def test_should_render_form_with_token_tag assert_not_blocked do get :index end - assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', @token + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end def test_should_render_button_to_with_token_tag assert_not_blocked do get :show_button end - assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', @token + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end def test_should_render_form_without_token_tag_if_remote @@ -139,7 +177,7 @@ module RequestForgeryProtectionTests assert_not_blocked do get :form_for_remote_with_external_token end - assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', 'external_token' + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', 'external_token' ensure ActionView::Helpers::FormTagHelper.embed_authenticity_token_in_remote_forms = original end @@ -149,27 +187,31 @@ module RequestForgeryProtectionTests assert_not_blocked do get :form_for_remote_with_external_token end - assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', 'external_token' + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', 'external_token' end def test_should_render_form_with_token_tag_if_remote_and_authenticity_token_requested assert_not_blocked do get :form_for_remote_with_token end - assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', @token + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end def test_should_render_form_with_token_tag_with_authenticity_token_requested assert_not_blocked do get :form_for_with_token end - assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', @token + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end def test_should_allow_get assert_not_blocked { get :index } end + def test_should_allow_head + assert_not_blocked { head :index } + end + def test_should_allow_post_without_token_on_unsafe_action assert_not_blocked { post :unsafe } end @@ -179,7 +221,7 @@ module RequestForgeryProtectionTests end def test_should_not_allow_post_without_token_irrespective_of_format - assert_blocked { post :index, :format=>'xml' } + assert_blocked { post :index, format: 'xml' } end def test_should_not_allow_patch_without_token @@ -249,6 +291,64 @@ module RequestForgeryProtectionTests end end + def test_should_not_warn_if_csrf_logging_disabled + old_logger = ActionController::Base.logger + logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new + ActionController::Base.logger = logger + ActionController::Base.log_warning_on_csrf_failure = false + + begin + assert_blocked { post :index } + + assert_equal 0, logger.logged(:warn).size + ensure + ActionController::Base.logger = old_logger + ActionController::Base.log_warning_on_csrf_failure = true + end + end + + def test_should_only_allow_same_origin_js_get_with_xhr_header + assert_cross_origin_blocked { get :same_origin_js } + assert_cross_origin_blocked { get :same_origin_js, format: 'js' } + assert_cross_origin_blocked do + @request.accept = 'text/javascript' + get :negotiate_same_origin + end + + assert_cross_origin_not_blocked { xhr :get, :same_origin_js } + assert_cross_origin_not_blocked { xhr :get, :same_origin_js, format: 'js' } + assert_cross_origin_not_blocked do + @request.accept = 'text/javascript' + xhr :get, :negotiate_same_origin + end + end + + # Allow non-GET requests since GET is all a remote <script> tag can muster. + def test_should_allow_non_get_js_without_xhr_header + assert_cross_origin_not_blocked { post :same_origin_js, custom_authenticity_token: @token } + assert_cross_origin_not_blocked { post :same_origin_js, format: 'js', custom_authenticity_token: @token } + assert_cross_origin_not_blocked do + @request.accept = 'text/javascript' + post :negotiate_same_origin, custom_authenticity_token: @token + end + end + + def test_should_only_allow_cross_origin_js_get_without_xhr_header_if_protection_disabled + assert_cross_origin_not_blocked { get :cross_origin_js } + assert_cross_origin_not_blocked { get :cross_origin_js, format: 'js' } + assert_cross_origin_not_blocked do + @request.accept = 'text/javascript' + get :negotiate_cross_origin + end + + assert_cross_origin_not_blocked { xhr :get, :cross_origin_js } + assert_cross_origin_not_blocked { xhr :get, :cross_origin_js, format: 'js' } + assert_cross_origin_not_blocked do + @request.accept = 'text/javascript' + xhr :get, :negotiate_cross_origin + end + end + def assert_blocked session[:something_like_user_id] = 1 yield @@ -260,6 +360,16 @@ module RequestForgeryProtectionTests assert_nothing_raised { yield } assert_response :success end + + def assert_cross_origin_blocked + assert_raises(ActionController::InvalidCrossOriginRequest) do + yield + end + end + + def assert_cross_origin_not_blocked + assert_not_blocked { yield } + end end # OK let's get our test on @@ -268,18 +378,47 @@ class RequestForgeryProtectionControllerUsingResetSessionTest < ActionController include RequestForgeryProtectionTests setup do + @old_request_forgery_protection_token = ActionController::Base.request_forgery_protection_token ActionController::Base.request_forgery_protection_token = :custom_authenticity_token end teardown do - ActionController::Base.request_forgery_protection_token = nil + ActionController::Base.request_forgery_protection_token = @old_request_forgery_protection_token end test 'should emit a csrf-param meta tag and a csrf-token meta tag' do - SecureRandom.stubs(:base64).returns(@token + '<=?') + @controller.stubs(:form_authenticity_token).returns(@token + '<=?') get :meta assert_select 'meta[name=?][content=?]', 'csrf-param', 'custom_authenticity_token' - assert_select 'meta[name=?][content=?]', 'csrf-token', 'cf50faa3fe97702ca1ae<=?' + assert_select 'meta[name=?]', 'csrf-token' + assert_match(/cf50faa3fe97702ca1ae<=\?/, @response.body) + end +end + +class RequestForgeryProtectionControllerUsingNullSessionTest < ActionController::TestCase + class NullSessionDummyKeyGenerator + def generate_key(secret) + '03312270731a2ed0d11ed091c2338a06' + end + end + + def setup + @request.env[ActionDispatch::Cookies::GENERATOR_KEY] = NullSessionDummyKeyGenerator.new + end + + test 'should allow to set signed cookies' do + post :signed + assert_response :ok + end + + test 'should allow to set encrypted cookies' do + post :encrypted + assert_response :ok + end + + test 'should allow reset_session' do + post :try_to_reset_session + assert_response :ok end end @@ -320,23 +459,45 @@ class FreeCookieControllerTest < ActionController::TestCase test 'should not emit a csrf-token meta tag' do get :meta - assert_blank @response.body + assert @response.body.blank? end end class CustomAuthenticityParamControllerTest < ActionController::TestCase def setup - ActionController::Base.request_forgery_protection_token = :custom_token_name super + @old_logger = ActionController::Base.logger + @logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new + @token = Base64.strict_encode64(SecureRandom.random_bytes(32)) + @old_request_forgery_protection_token = ActionController::Base.request_forgery_protection_token + ActionController::Base.request_forgery_protection_token = @token end def teardown - ActionController::Base.request_forgery_protection_token = :authenticity_token + ActionController::Base.request_forgery_protection_token = @old_request_forgery_protection_token super end - def test_should_allow_custom_token - post :index, :custom_token_name => 'foobar' - assert_response :ok + def test_should_not_warn_if_form_authenticity_param_matches_form_authenticity_token + ActionController::Base.logger = @logger + @controller.stubs(:valid_authenticity_token?).returns(:true) + + begin + post :index, :custom_token_name => 'foobar' + assert_equal 0, @logger.logged(:warn).size + ensure + ActionController::Base.logger = @old_logger + end + end + + def test_should_warn_if_form_authenticity_param_does_not_match_form_authenticity_token + ActionController::Base.logger = @logger + + begin + post :index, :custom_token_name => 'bazqux' + assert_equal 1, @logger.logged(:warn).size + ensure + ActionController::Base.logger = @old_logger + end end end diff --git a/actionpack/test/controller/required_params_test.rb b/actionpack/test/controller/required_params_test.rb index 661bcb3945..6803dbbb62 100644 --- a/actionpack/test/controller/required_params_test.rb +++ b/actionpack/test/controller/required_params_test.rb @@ -11,11 +11,13 @@ class ActionControllerRequiredParamsTest < ActionController::TestCase tests BooksController test "missing required parameters will raise exception" do - post :create, { magazine: { name: "Mjallo!" } } - assert_response :bad_request + assert_raise ActionController::ParameterMissing do + post :create, { magazine: { name: "Mjallo!" } } + end - post :create, { book: { title: "Mjallo!" } } - assert_response :bad_request + assert_raise ActionController::ParameterMissing do + post :create, { book: { title: "Mjallo!" } } + end end test "required parameters that are present will not raise" do @@ -23,8 +25,27 @@ class ActionControllerRequiredParamsTest < ActionController::TestCase assert_response :ok end - test "missing parameters will be mentioned in the return" do - post :create, { magazine: { name: "Mjallo!" } } - assert_equal "Required parameter missing: book", response.body + test "required parameters with false value will not raise" do + post :create, { book: { name: false } } + assert_response :ok + end +end + +class ParametersRequireTest < ActiveSupport::TestCase + + test "required parameters should accept and return false value" do + assert_equal(false, ActionController::Parameters.new(person: false).require(:person)) + end + + test "required parameters must not be nil" do + assert_raises(ActionController::ParameterMissing) do + ActionController::Parameters.new(person: nil).require(:person) + end + end + + test "required parameters must not be empty" do + assert_raises(ActionController::ParameterMissing) do + ActionController::Parameters.new(person: {}).require(:person) + end end end diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index 305659b219..0e15883f43 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -1,8 +1,10 @@ require 'abstract_unit' require 'active_support/core_ext/object/try' require 'active_support/core_ext/object/with_options' +require 'active_support/core_ext/array/extract_options' class ResourcesTest < ActionController::TestCase + def test_default_restful_routes with_restful_routing :messages do assert_simply_restful_for :messages @@ -41,11 +43,11 @@ class ResourcesTest < ActionController::TestCase :member => member_methods, :path_names => path_names do |options| - collection_methods.keys.each do |action| + collection_methods.each_key do |action| assert_named_route "/messages/#{path_names[action] || action}", "#{action}_messages_path", :action => action end - member_methods.keys.each do |action| + member_methods.each_key do |action| assert_named_route "/messages/1/#{path_names[action] || action}", "#{action}_message_path", :action => action, :id => "1" end @@ -148,7 +150,7 @@ class ResourcesTest < ActionController::TestCase end assert_restful_named_routes_for :messages do |options| - actions.keys.each do |action| + actions.each_key do |action| assert_named_route "/messages/#{action}", "#{action}_messages_path", :action => action end end @@ -178,7 +180,7 @@ class ResourcesTest < ActionController::TestCase end assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options| - actions.keys.each do |action| + actions.each_key do |action| assert_named_route "/threads/1/messages/#{action}", "#{action}_thread_messages_path", :action => action end end @@ -205,7 +207,7 @@ class ResourcesTest < ActionController::TestCase end assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options| - actions.keys.each do |action| + actions.each_key do |action| assert_named_route "/threads/1/messages/#{action}", "#{action}_thread_messages_path", :action => action end end @@ -235,7 +237,7 @@ class ResourcesTest < ActionController::TestCase end assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options| - actions.keys.each do |action| + actions.each_key do |action| assert_named_route "/threads/1/messages/#{action}.xml", "#{action}_thread_messages_path", :action => action, :format => 'xml' end end @@ -1003,7 +1005,7 @@ class ResourcesTest < ActionController::TestCase end end - assert_resource_allowed_routes('images', { :product_id => '1' }, { :id => '2' }, [:index, :new, :create, :show, :edit, :update, :destory], [], 'products/1/images') + assert_resource_allowed_routes('images', { :product_id => '1' }, { :id => '2' }, [:index, :new, :create, :show, :edit, :update, :destroy], [], 'products/1/images') assert_resource_allowed_routes('images', { :product_id => '1', :format => 'xml' }, { :id => '2' }, [:index, :new, :create, :show, :edit, :update, :destroy], [], 'products/1/images') end end @@ -1319,6 +1321,8 @@ class ResourcesTest < ActionController::TestCase assert_recognizes options, path_options elsif Array(not_allowed).include?(action) assert_not_recognizes options, path_options + else + raise Assertion, 'Invalid Action has passed' end end diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index f0430e516f..9caa5cbe57 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -2,6 +2,7 @@ require 'abstract_unit' require 'controller/fake_controllers' require 'active_support/core_ext/object/with_options' +require 'active_support/core_ext/object/json' class MilestonesController < ActionController::Base def index() head :ok end @@ -57,13 +58,13 @@ class UriReservedCharactersRoutingTest < ActiveSupport::TestCase end class MockController - def self.build(helpers) + def self.build(helpers, additional_options = {}) Class.new do - def url_options - options = super + define_method :url_options do + options = super() options[:protocol] ||= "http" options[:host] ||= "test.host" - options + options.merge(additional_options) end include helpers @@ -76,46 +77,47 @@ class LegacyRouteSetTests < ActiveSupport::TestCase include ActionDispatch::RoutingVerbs attr_reader :rs + attr_accessor :controller alias :routes :rs def setup - @rs = ::ActionDispatch::Routing::RouteSet.new + @rs = make_set @response = nil end def test_symbols_with_dashes rs.draw do get '/:artist/:song-omg', :to => lambda { |env| - resp = JSON.dump env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] + resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters [200, {}, [resp]] } end - hash = JSON.load get(URI('http://example.org/journey/faithfully-omg')) + hash = ActiveSupport::JSON.decode get(URI('http://example.org/journey/faithfully-omg')) assert_equal({"artist"=>"journey", "song"=>"faithfully"}, hash) end def test_id_with_dash rs.draw do get '/journey/:id', :to => lambda { |env| - resp = JSON.dump env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] + resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters [200, {}, [resp]] } end - hash = JSON.load get(URI('http://example.org/journey/faithfully-omg')) + hash = ActiveSupport::JSON.decode get(URI('http://example.org/journey/faithfully-omg')) assert_equal({"id"=>"faithfully-omg"}, hash) end def test_dash_with_custom_regexp rs.draw do get '/:artist/:song-omg', :constraints => { :song => /\d+/ }, :to => lambda { |env| - resp = JSON.dump env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] + resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters [200, {}, [resp]] } end - hash = JSON.load get(URI('http://example.org/journey/123-omg')) + hash = ActiveSupport::JSON.decode get(URI('http://example.org/journey/123-omg')) assert_equal({"artist"=>"journey", "song"=>"123"}, hash) assert_equal 'Not Found', get(URI('http://example.org/journey/faithfully-omg')) end @@ -123,24 +125,24 @@ class LegacyRouteSetTests < ActiveSupport::TestCase def test_pre_dash rs.draw do get '/:artist/omg-:song', :to => lambda { |env| - resp = JSON.dump env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] + resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters [200, {}, [resp]] } end - hash = JSON.load get(URI('http://example.org/journey/omg-faithfully')) + hash = ActiveSupport::JSON.decode get(URI('http://example.org/journey/omg-faithfully')) assert_equal({"artist"=>"journey", "song"=>"faithfully"}, hash) end def test_pre_dash_with_custom_regexp rs.draw do get '/:artist/omg-:song', :constraints => { :song => /\d+/ }, :to => lambda { |env| - resp = JSON.dump env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] + resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters [200, {}, [resp]] } end - hash = JSON.load get(URI('http://example.org/journey/omg-123')) + hash = ActiveSupport::JSON.decode get(URI('http://example.org/journey/omg-123')) assert_equal({"artist"=>"journey", "song"=>"123"}, hash) assert_equal 'Not Found', get(URI('http://example.org/journey/omg-faithfully')) end @@ -160,14 +162,14 @@ class LegacyRouteSetTests < ActiveSupport::TestCase def test_star_paths_are_greedy_but_not_too_much rs.draw do get "/*path", :to => lambda { |env| - x = JSON.dump env["action_dispatch.request.path_parameters"] + x = ActiveSupport::JSON.encode env["action_dispatch.request.path_parameters"] [200, {}, [x]] } end expected = { "path" => "foo/bar", "format" => "html" } u = URI('http://example.org/foo/bar.html') - assert_equal expected, JSON.parse(get(u)) + assert_equal expected, ActiveSupport::JSON.decode(get(u)) end def test_optional_star_paths_are_greedy @@ -185,7 +187,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase def test_optional_star_paths_are_greedy_but_not_too_much rs.draw do get "/(*filters)", :to => lambda { |env| - x = JSON.dump env["action_dispatch.request.path_parameters"] + x = ActiveSupport::JSON.encode env["action_dispatch.request.path_parameters"] [200, {}, [x]] } end @@ -193,7 +195,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase expected = { "filters" => "ne_27.065938,-80.6092/sw_25.489856,-82", "format" => "542794" } u = URI('http://example.org/ne_27.065938,-80.6092/sw_25.489856,-82.542794') - assert_equal expected, JSON.parse(get(u)) + assert_equal expected, ActiveSupport::JSON.decode(get(u)) end def test_regexp_precidence @@ -242,6 +244,32 @@ class LegacyRouteSetTests < ActiveSupport::TestCase assert_equal 'clients', get(URI('http://clients.example.org/')) end + def test_scoped_lambda + scope_called = false + rs.draw do + scope '/foo', :constraints => lambda { |req| scope_called = true } do + get '/', :to => lambda { |env| [200, {}, %w{default}] } + end + end + + assert_equal 'default', get(URI('http://www.example.org/foo/')) + assert scope_called, "scope constraint should be called" + end + + def test_scoped_lambda_with_get_lambda + inner_called = false + + rs.draw do + scope '/foo', :constraints => lambda { |req| flunk "should not be called" } do + get '/', :constraints => lambda { |req| inner_called = true }, + :to => lambda { |env| [200, {}, %w{default}] } + end + end + + assert_equal 'default', get(URI('http://www.example.org/foo/')) + assert inner_called, "inner constraint should be called" + end + def test_empty_string_match rs.draw do get '/:username', :constraints => { :username => /[^\/]+/ }, @@ -290,11 +318,16 @@ class LegacyRouteSetTests < ActiveSupport::TestCase assert_equal '/admin/user/show/10', url_for(rs, { :controller => 'admin/user', :action => 'show', :id => 10 }) - assert_equal '/admin/user/show', url_for(rs, { :action => 'show' }, { :controller => 'admin/user', :action => 'list', :id => '10' }) - assert_equal '/admin/user/list/10', url_for(rs, {}, { :controller => 'admin/user', :action => 'list', :id => '10' }) + get URI('http://test.host/admin/user/list/10') + + assert_equal({ :controller => 'admin/user', :action => 'list', :id => '10' }, + controller.request.path_parameters) + + assert_equal '/admin/user/show', controller.url_for({ :action => 'show', :only_path => true }) + assert_equal '/admin/user/list/10', controller.url_for({:only_path => true}) - assert_equal '/admin/stuff', url_for(rs, { :controller => 'stuff' }, { :controller => 'admin/user', :action => 'list', :id => '10' }) - assert_equal '/stuff', url_for(rs, { :controller => '/stuff' }, { :controller => 'admin/user', :action => 'list', :id => '10' }) + assert_equal '/admin/stuff', controller.url_for({ :controller => 'stuff', :only_path => true }) + assert_equal '/stuff', controller.url_for({ :controller => '/stuff', :only_path => true }) end def test_ignores_leading_slash @@ -418,18 +451,11 @@ class LegacyRouteSetTests < ActiveSupport::TestCase get 'page' => 'content#show_page', :as => 'pages', :host => 'foo.com' end routes = setup_for_named_route - routes.expects(:url_for).with({ - :host => 'foo.com', - :only_path => false, - :controller => 'content', - :action => 'show_page', - :use_route => 'pages' - }).once - routes.send(:pages_url) + assert_equal "http://foo.com/page", routes.pages_url end - def setup_for_named_route - MockController.build(rs.url_helpers).new + def setup_for_named_route(options = {}) + MockController.build(rs.url_helpers, options).new end def test_named_route_without_hash @@ -456,6 +482,32 @@ class LegacyRouteSetTests < ActiveSupport::TestCase assert_equal("/", routes.send(:root_path)) end + def test_named_route_root_with_hash + rs.draw do + root "hello#index", as: :index + end + + routes = setup_for_named_route + assert_equal("http://test.host/", routes.send(:index_url)) + assert_equal("/", routes.send(:index_path)) + end + + def test_root_without_path_raises_argument_error + assert_raises ArgumentError do + rs.draw { root nil } + end + end + + def test_named_route_root_with_trailing_slash + rs.draw do + root "hello#index" + end + + routes = setup_for_named_route(trailing_slash: true) + assert_equal("http://test.host/", routes.send(:root_url)) + assert_equal("http://test.host/?foo=bar", routes.send(:root_url, foo: :bar)) + end + def test_named_route_with_regexps rs.draw do get 'page/:year/:month/:day/:title' => 'page#show', :as => 'article', @@ -472,9 +524,10 @@ class LegacyRouteSetTests < ActiveSupport::TestCase def test_changing_controller rs.draw { get ':controller/:action/:id' } + get URI('http://test.host/admin/user/index/10') + assert_equal '/admin/stuff/show/10', - url_for(rs, {:controller => 'stuff', :action => 'show', :id => 10}, - {:controller => 'admin/user', :action => 'index'}) + controller.url_for({:controller => 'stuff', :action => 'show', :id => 10, :only_path => true}) end def test_paths_escaped @@ -533,8 +586,12 @@ class LegacyRouteSetTests < ActiveSupport::TestCase get '*path' => 'content#show_file' end + get URI('http://test.host/pages/boo') + assert_equal({:controller=>"content", :action=>"show_file", :path=>"pages/boo"}, + controller.request.path_parameters) + assert_equal '/pages/boo', - url_for(rs, {}, { :controller => 'content', :action => 'show_file', :path => %w(pages boo) }) + controller.url_for(:only_path => true) end def test_backwards @@ -543,7 +600,8 @@ class LegacyRouteSetTests < ActiveSupport::TestCase get ':controller(/:action(/:id))' end - assert_equal '/page/20', url_for(rs, { :id => 20 }, { :controller => 'pages', :action => 'show' }) + get URI('http://test.host/pages/show') + assert_equal '/page/20', controller.url_for({ :id => 20, :only_path => true }) assert_equal '/page/20', url_for(rs, { :controller => 'pages', :id => 20, :action => 'show' }) assert_equal '/pages/boo', url_for(rs, { :controller => 'pages', :action => 'boo' }) end @@ -584,7 +642,8 @@ class LegacyRouteSetTests < ActiveSupport::TestCase def test_action_expiry rs.draw { get ':controller(/:action(/:id))' } - assert_equal '/content', url_for(rs, { :controller => 'content' }, { :controller => 'content', :action => 'show' }) + get URI('http://test.host/content/show') + assert_equal '/content', controller.url_for(:controller => 'content', :only_path => true) end def test_requirement_should_prevent_optional_id @@ -627,14 +686,18 @@ class LegacyRouteSetTests < ActiveSupport::TestCase assert_equal '/pages/2005/6/12', url_for(rs, { :controller => 'content', :action => 'list_pages', :year => 2005, :month => 6, :day => 12 }) + get URI('http://test.host/pages/2005/6/12') + assert_equal({ :controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12' }, + controller.request.path_parameters) + assert_equal '/pages/2005/6/4', - url_for(rs, { :day => 4 }, { :controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12' }) + controller.url_for({ :day => 4, :only_path => true }) assert_equal '/pages/2005/6', - url_for(rs, { :day => nil }, { :controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12' }) + controller.url_for({ :day => nil, :only_path => true }) assert_equal '/pages/2005', - url_for(rs, { :day => nil, :month => nil }, { :controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12' }) + controller.url_for({ :day => nil, :month => nil, :only_path => true }) end def test_root_url_generation_with_controller_and_action @@ -689,17 +752,13 @@ class LegacyRouteSetTests < ActiveSupport::TestCase def setup_request_method_routes_for(method) rs.draw do - match '/match' => 'books#get', :via => :get - match '/match' => 'books#post', :via => :post - match '/match' => 'books#put', :via => :put - match '/match' => 'books#patch', :via => :patch - match '/match' => 'books#delete', :via => :delete + match '/match' => "books##{method}", :via => method.to_sym end end %w(GET PATCH POST PUT DELETE).each do |request_method| define_method("test_request_method_recognized_with_#{request_method}") do - setup_request_method_routes_for(request_method) + setup_request_method_routes_for(request_method.downcase) params = rs.recognize_path("/match", :method => request_method) assert_equal request_method.downcase, params[:action] end @@ -796,9 +855,15 @@ end class RouteSetTest < ActiveSupport::TestCase include RoutingTestHelpers + include ActionDispatch::RoutingVerbs + + attr_reader :set + alias :routes :set + attr_accessor :controller - def set - @set ||= ROUTING::RouteSet.new + def setup + super + @set = make_set end def request @@ -819,13 +884,13 @@ class RouteSetTest < ActiveSupport::TestCase set.draw { get ':controller/(:action(/:id))' } path, extras = set.generate_extras(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world") assert_equal "/foo/bar/15", path - assert_equal %w(that this), extras.map { |e| e.to_s }.sort + assert_equal %w(that this), extras.map(&:to_s).sort end def test_extra_keys set.draw { get ':controller/:action/:id' } extras = set.extra_keys(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world") - assert_equal %w(that this), extras.map { |e| e.to_s }.sort + assert_equal %w(that this), extras.map(&:to_s).sort end def test_generate_extras_not_first @@ -835,7 +900,7 @@ class RouteSetTest < ActiveSupport::TestCase end path, extras = set.generate_extras(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world") assert_equal "/foo/bar/15", path - assert_equal %w(that this), extras.map { |e| e.to_s }.sort + assert_equal %w(that this), extras.map(&:to_s).sort end def test_generate_not_first @@ -853,7 +918,7 @@ class RouteSetTest < ActiveSupport::TestCase get ':controller/:action/:id' end extras = set.extra_keys(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world") - assert_equal %w(that this), extras.map { |e| e.to_s }.sort + assert_equal %w(that this), extras.map(&:to_s).sort end def test_draw @@ -882,12 +947,13 @@ class RouteSetTest < ActiveSupport::TestCase assert_equal set.routes.first, set.named_routes[:hello] end - def test_earlier_named_routes_take_precedence - set.draw do - get '/hello/world' => 'a#b', :as => 'hello' - get '/hello' => 'a#b', :as => 'hello' + def test_duplicate_named_route_raises_rather_than_pick_precedence + assert_raise ArgumentError do + set.draw do + get '/hello/world' => 'a#b', :as => 'hello' + get '/hello' => 'a#b', :as => 'hello' + end end - assert_equal set.routes.first, set.named_routes[:hello] end def setup_named_route_test @@ -898,7 +964,8 @@ class RouteSetTest < ActiveSupport::TestCase get '/admin/users' => 'admin/users#index', :as => "users" end - MockController.build(set.url_helpers).new + get URI('http://test.host/people') + controller end def test_named_route_url_method @@ -934,6 +1001,9 @@ class RouteSetTest < ActiveSupport::TestCase assert_equal "http://test.host/people?baz=bar#location", controller.send(:index_url, :baz => "bar", :anchor => 'location') + + assert_equal "http://test.host/people", controller.send(:index_url, anchor: nil) + assert_equal "http://test.host/people", controller.send(:index_url, anchor: false) end def test_named_route_url_method_with_port @@ -995,12 +1065,12 @@ class RouteSetTest < ActiveSupport::TestCase get '/:controller(/:action(/:id))' end - assert_equal({:controller => 'pages', :action => 'index'}, set.recognize_path('/pages')) - assert_equal({:controller => 'pages', :action => 'index'}, set.recognize_path('/pages/index')) - assert_equal({:controller => 'pages', :action => 'list'}, set.recognize_path('/pages/list')) + assert_equal({:controller => 'pages', :action => 'index'}, request_path_params('/pages')) + assert_equal({:controller => 'pages', :action => 'index'}, request_path_params('/pages/index')) + assert_equal({:controller => 'pages', :action => 'list'}, request_path_params('/pages/list')) - assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, set.recognize_path('/pages/show/10')) - assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, set.recognize_path('/page/10')) + assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, request_path_params('/pages/show/10')) + assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, request_path_params('/page/10')) end def test_route_constraints_on_request_object_with_anchors_are_valid @@ -1052,9 +1122,7 @@ class RouteSetTest < ActiveSupport::TestCase get "/people" => "missing#index" end - assert_raise(ActionController::RoutingError) { - set.recognize_path("/people", :method => :get) - } + assert_raises(ActionController::RoutingError) { request_path_params '/people' } end def test_recognize_with_encoded_id_and_regex @@ -1062,8 +1130,8 @@ class RouteSetTest < ActiveSupport::TestCase get 'page/:id' => 'pages#show', :id => /[a-zA-Z0-9\+]+/ end - assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, set.recognize_path('/page/10')) - assert_equal({:controller => 'pages', :action => 'show', :id => 'hello+world'}, set.recognize_path('/page/hello+world')) + assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, request_path_params('/page/10')) + assert_equal({:controller => 'pages', :action => 'show', :id => 'hello+world'}, request_path_params('/page/hello+world')) end def test_recognize_with_http_methods @@ -1076,40 +1144,40 @@ class RouteSetTest < ActiveSupport::TestCase delete "/people/:id" => "people#destroy" end - params = set.recognize_path("/people", :method => :get) + params = request_path_params("/people", :method => :get) assert_equal("index", params[:action]) - params = set.recognize_path("/people", :method => :post) + params = request_path_params("/people", :method => :post) assert_equal("create", params[:action]) - params = set.recognize_path("/people/5", :method => :put) + params = request_path_params("/people/5", :method => :put) assert_equal("update", params[:action]) - params = set.recognize_path("/people/5", :method => :patch) + params = request_path_params("/people/5", :method => :patch) assert_equal("update", params[:action]) assert_raise(ActionController::UnknownHttpMethod) { - set.recognize_path("/people", :method => :bacon) + request_path_params("/people", :method => :bacon) } - params = set.recognize_path("/people/5", :method => :get) + params = request_path_params("/people/5", :method => :get) assert_equal("show", params[:action]) assert_equal("5", params[:id]) - params = set.recognize_path("/people/5", :method => :put) + params = request_path_params("/people/5", :method => :put) assert_equal("update", params[:action]) assert_equal("5", params[:id]) - params = set.recognize_path("/people/5", :method => :patch) + params = request_path_params("/people/5", :method => :patch) assert_equal("update", params[:action]) assert_equal("5", params[:id]) - params = set.recognize_path("/people/5", :method => :delete) + params = request_path_params("/people/5", :method => :delete) assert_equal("destroy", params[:action]) assert_equal("5", params[:id]) assert_raise(ActionController::RoutingError) { - set.recognize_path("/people/5", :method => :post) + request_path_params("/people/5", :method => :post) } end @@ -1119,11 +1187,11 @@ class RouteSetTest < ActiveSupport::TestCase root :to => "people#index" end - params = set.recognize_path("/people", :method => :get) + params = request_path_params("/people", :method => :get) assert_equal("people", params[:controller]) assert_equal("index", params[:action]) - params = set.recognize_path("/", :method => :get) + params = request_path_params("/", :method => :get) assert_equal("people", params[:controller]) assert_equal("index", params[:action]) end @@ -1134,7 +1202,7 @@ class RouteSetTest < ActiveSupport::TestCase :year => /\d{4}/, :day => /\d{1,2}/, :month => /\d{1,2}/ end - params = set.recognize_path("/articles/2005/11/05/a-very-interesting-article", :method => :get) + params = request_path_params("/articles/2005/11/05/a-very-interesting-article", :method => :get) assert_equal("permalink", params[:action]) assert_equal("2005", params[:year]) assert_equal("11", params[:month]) @@ -1148,7 +1216,7 @@ class RouteSetTest < ActiveSupport::TestCase get '/profile' => 'profile#index' end - set.recognize_path("/profile") rescue nil + request_path_params("/profile") rescue nil assert !Object.const_defined?("Profiler__"), "Profiler should not be loaded" end @@ -1161,17 +1229,17 @@ class RouteSetTest < ActiveSupport::TestCase get "people/:id(.:format)" => "people#show" end - params = set.recognize_path("/people/5", :method => :get) + params = request_path_params("/people/5", :method => :get) assert_equal("show", params[:action]) assert_equal("5", params[:id]) - params = set.recognize_path("/people/5", :method => :put) + params = request_path_params("/people/5", :method => :put) assert_equal("update", params[:action]) - params = set.recognize_path("/people/5", :method => :patch) + params = request_path_params("/people/5", :method => :patch) assert_equal("update", params[:action]) - params = set.recognize_path("/people/5.png", :method => :get) + params = request_path_params("/people/5.png", :method => :get) assert_equal("show", params[:action]) assert_equal("5", params[:id]) assert_equal("png", params[:format]) @@ -1190,7 +1258,7 @@ class RouteSetTest < ActiveSupport::TestCase def test_root_map set.draw { root :to => 'people#index' } - params = set.recognize_path("", :method => :get) + params = request_path_params("", :method => :get) assert_equal("people", params[:controller]) assert_equal("index", params[:action]) end @@ -1204,7 +1272,7 @@ class RouteSetTest < ActiveSupport::TestCase end - params = set.recognize_path("/api/inventory", :method => :get) + params = request_path_params("/api/inventory", :method => :get) assert_equal("api/products", params[:controller]) assert_equal("inventory", params[:action]) end @@ -1216,7 +1284,7 @@ class RouteSetTest < ActiveSupport::TestCase end end - params = set.recognize_path("/api", :method => :get) + params = request_path_params("/api", :method => :get) assert_equal("api/products", params[:controller]) assert_equal("index", params[:action]) end @@ -1228,7 +1296,7 @@ class RouteSetTest < ActiveSupport::TestCase end end - params = set.recognize_path("/prefix/inventory", :method => :get) + params = request_path_params("/prefix/inventory", :method => :get) assert_equal("api/products", params[:controller]) assert_equal("inventory", params[:action]) end @@ -1240,38 +1308,36 @@ class RouteSetTest < ActiveSupport::TestCase end end - params = set.recognize_path("/inventory", :method => :get) + params = request_path_params("/inventory", :method => :get) assert_equal("api/products", params[:controller]) assert_equal("inventory", params[:action]) end - def test_generate_changes_controller_module - set.draw { get ':controller/:action/:id' } - current = { :controller => "bling/bloop", :action => "bap", :id => 9 } - - assert_equal "/foo/bar/baz/7", - url_for(set, { :controller => "foo/bar", :action => "baz", :id => 7 }, current) - end - def test_id_is_sticky_when_it_ought_to_be + @set = make_set false + set.draw do get ':controller/:id/:action' end - url = url_for(set, { :action => "destroy" }, { :controller => "people", :action => "show", :id => "7" }) - assert_equal "/people/7/destroy", url + get URI('http://test.host/people/7/show') + + assert_equal "/people/7/destroy", controller.url_for(:action => 'destroy', :only_path => true) end def test_use_static_path_when_possible + @set = make_set false + set.draw do get 'about' => "welcome#about" get ':controller/:action/:id' end - url = url_for(set, { :controller => "welcome", :action => "about" }, - { :controller => "welcome", :action => "get", :id => "7" }) + get URI('http://test.host/welcom/get/7') - assert_equal "/about", url + assert_equal "/about", controller.url_for(:controller => 'welcome', + :action => 'about', + :only_path => true) end def test_generate @@ -1306,38 +1372,51 @@ class RouteSetTest < ActiveSupport::TestCase end def test_named_routes_are_never_relative_to_modules + @set = make_set false + set.draw do get "/connection/manage(/:action)" => 'connection/manage#index' get "/connection/connection" => "connection/connection#index" get '/connection' => 'connection#index', :as => 'family_connection' end - url = url_for(set, { :controller => "connection" }, { :controller => 'connection/manage' }) + assert_equal({ :controller => 'connection/manage', + :action => 'index', }, request_path_params('/connection/manage')) + + url = controller.url_for({ :controller => "connection", :only_path => true }) assert_equal "/connection/connection", url - url = url_for(set, { :use_route => :family_connection, :controller => "connection" }, { :controller => 'connection/manage' }) + url = controller.url_for({ :use_route => "family_connection", + :controller => "connection", :only_path => true }) assert_equal "/connection", url end def test_action_left_off_when_id_is_recalled + @set = make_set false + set.draw do get ':controller(/:action(/:id))' end - assert_equal '/books', url_for(set, - {:controller => 'books', :action => 'index'}, - {:controller => 'books', :action => 'show', :id => '10'} - ) + + get URI('http://test.host/books/show/10') + + assert_equal '/books', controller.url_for(:controller => 'books', + :only_path => true, + :action => 'index') end def test_query_params_will_be_shown_when_recalled + @set = make_set false + set.draw do get 'show_weblog/:parameter' => 'weblog#show' get ':controller(/:action(/:id))' end - assert_equal '/weblog/edit?parameter=1', url_for(set, - {:action => 'edit', :parameter => 1}, - {:controller => 'weblog', :action => 'show', :parameter => 1} - ) + + get URI('http://test.host/weblog/show/1') + + assert_equal '/weblog/edit?parameter=1', controller.url_for( + {:action => 'edit', :parameter => 1, :only_path => true}) end def test_format_is_not_inherit @@ -1345,22 +1424,30 @@ class RouteSetTest < ActiveSupport::TestCase get '/posts(.:format)' => 'posts#index' end - assert_equal '/posts', url_for(set, - {:controller => 'posts'}, - {:controller => 'posts', :action => 'index', :format => 'xml'} - ) + get URI('http://test.host/posts.xml') + assert_equal({:controller => 'posts', :action => 'index', :format => 'xml'}, + controller.request.path_parameters) - assert_equal '/posts.xml', url_for(set, - {:controller => 'posts', :format => 'xml'}, - {:controller => 'posts', :action => 'index', :format => 'xml'} - ) + assert_equal '/posts', controller.url_for( + {:controller => 'posts', :only_path => true}) + + assert_equal '/posts.xml', controller.url_for( + {:controller => 'posts', :format => 'xml', :only_path => true}) end def test_expiry_determination_should_consider_values_with_to_param + @set = make_set false + set.draw { get 'projects/:project_id/:controller/:action' } - assert_equal '/projects/1/weblog/show', url_for(set, - { :action => 'show', :project_id => 1 }, - { :controller => 'weblog', :action => 'show', :project_id => '1' }) + + get URI('http://test.host/projects/1/weblog/show') + + assert_equal( + { :controller => 'weblog', :action => 'show', :project_id => '1' }, + controller.request.path_parameters) + + assert_equal '/projects/1/weblog/show', + controller.url_for({ :action => 'show', :project_id => 1, :only_path => true }) end def test_named_route_in_nested_resource @@ -1563,7 +1650,6 @@ class RouteSetTest < ActiveSupport::TestCase end def test_slashes_are_implied - @set = nil set.draw { get("/:controller(/:action(/:id))") } assert_equal '/content', url_for(set, { :controller => 'content', :action => 'index' }) @@ -1661,7 +1747,43 @@ class RouteSetTest < ActiveSupport::TestCase assert_equal '/ibocorp', url_for(set, { :controller => 'ibocorp', :action => "show", :page => 1 }) end + include ActionDispatch::RoutingVerbs + + class TestSet < ROUTING::RouteSet + def initialize(block) + @block = block + super() + end + + class Dispatcher < ROUTING::RouteSet::Dispatcher + def initialize(defaults, set, block) + super(defaults) + @block = block + @set = set + end + + def controller_reference(controller_param) + block = @block + set = @set + Class.new(ActionController::Base) { + include set.url_helpers + define_method(:process) { |name| block.call(self) } + def to_a; [200, {}, []]; end + } + end + end + + def dispatcher defaults + TestSet::Dispatcher.new defaults, self, @block + end + end + + alias :routes :set + def test_generate_with_optional_params_recalls_last_request + controller = nil + @set = TestSet.new ->(c) { controller = c } + set.draw do get "blog/", :controller => "blog", :action => "index" @@ -1676,23 +1798,29 @@ class RouteSetTest < ActiveSupport::TestCase get "*anything", :controller => "blog", :action => "unknown_request" end - assert_equal({:controller => "blog", :action => "index"}, set.recognize_path("/blog")) - assert_equal({:controller => "blog", :action => "show", :id => "123"}, set.recognize_path("/blog/show/123")) - assert_equal({:controller => "blog", :action => "show_date", :year => "2004", :day => nil, :month => nil }, set.recognize_path("/blog/2004")) - assert_equal({:controller => "blog", :action => "show_date", :year => "2004", :month => "12", :day => nil }, set.recognize_path("/blog/2004/12")) - assert_equal({:controller => "blog", :action => "show_date", :year => "2004", :month => "12", :day => "25"}, set.recognize_path("/blog/2004/12/25")) - assert_equal({:controller => "articles", :action => "edit", :id => "123"}, set.recognize_path("/blog/articles/edit/123")) - assert_equal({:controller => "articles", :action => "show_stats"}, set.recognize_path("/blog/articles/show_stats")) - assert_equal({:controller => "blog", :action => "unknown_request", :anything => "blog/wibble"}, set.recognize_path("/blog/wibble")) - assert_equal({:controller => "blog", :action => "unknown_request", :anything => "junk"}, set.recognize_path("/junk")) + recognize_path = ->(path) { + get(URI("http://example.org" + path)) + controller.request.path_parameters + } + + assert_equal({:controller => "blog", :action => "index"}, recognize_path.("/blog")) + assert_equal({:controller => "blog", :action => "show", :id => "123"}, recognize_path.("/blog/show/123")) + assert_equal({:controller => "blog", :action => "show_date", :year => "2004", :day => nil, :month => nil }, recognize_path.("/blog/2004")) + assert_equal({:controller => "blog", :action => "show_date", :year => "2004", :month => "12", :day => nil }, recognize_path.("/blog/2004/12")) + assert_equal({:controller => "blog", :action => "show_date", :year => "2004", :month => "12", :day => "25"}, recognize_path.("/blog/2004/12/25")) + assert_equal({:controller => "articles", :action => "edit", :id => "123"}, recognize_path.("/blog/articles/edit/123")) + assert_equal({:controller => "articles", :action => "show_stats"}, recognize_path.("/blog/articles/show_stats")) + assert_equal({:controller => "blog", :action => "unknown_request", :anything => "blog/wibble"}, recognize_path.("/blog/wibble")) + assert_equal({:controller => "blog", :action => "unknown_request", :anything => "junk"}, recognize_path.("/junk")) + + get URI('http://example.org/blog/2006/07/28') - last_request = set.recognize_path("/blog/2006/07/28").freeze - assert_equal({:controller => "blog", :action => "show_date", :year => "2006", :month => "07", :day => "28"}, last_request) - assert_equal("/blog/2006/07/25", url_for(set, { :day => 25 }, last_request)) - assert_equal("/blog/2005", url_for(set, { :year => 2005 }, last_request)) - assert_equal("/blog/show/123", url_for(set, { :action => "show" , :id => 123 }, last_request)) - assert_equal("/blog/2006", url_for(set, { :year => 2006 }, last_request)) - assert_equal("/blog/2006", url_for(set, { :year => 2006, :month => nil }, last_request)) + assert_equal({:controller => "blog", :action => "show_date", :year => "2006", :month => "07", :day => "28"}, controller.request.path_parameters) + assert_equal("/blog/2006/07/25", controller.url_for({ :day => 25, :only_path => true })) + assert_equal("/blog/2005", controller.url_for({ :year => 2005, :only_path => true })) + assert_equal("/blog/show/123", controller.url_for({ :action => "show" , :id => 123, :only_path => true })) + assert_equal("/blog/2006", controller.url_for({ :year => 2006, :only_path => true })) + assert_equal("/blog/2006", controller.url_for({ :year => 2006, :month => nil, :only_path => true })) end private @@ -1765,6 +1893,9 @@ class RackMountIntegrationTests < ActiveSupport::TestCase root :to => "news#index" } + attr_reader :routes + attr_reader :controller + def setup @routes = ActionDispatch::Routing::RouteSet.new @routes.draw(&Mapping) @@ -1809,11 +1940,11 @@ class RackMountIntegrationTests < ActiveSupport::TestCase assert_equal({:controller => 'foo', :action => 'id_default', :id => 1 }, @routes.recognize_path('/id_default')) assert_equal({:controller => 'foo', :action => 'get_or_post'}, @routes.recognize_path('/get_or_post', :method => :get)) assert_equal({:controller => 'foo', :action => 'get_or_post'}, @routes.recognize_path('/get_or_post', :method => :post)) - assert_raise(ActionController::ActionControllerError) { @routes.recognize_path('/get_or_post', :method => :put) } - assert_raise(ActionController::ActionControllerError) { @routes.recognize_path('/get_or_post', :method => :delete) } + assert_raise(ActionController::RoutingError) { @routes.recognize_path('/get_or_post', :method => :put) } + assert_raise(ActionController::RoutingError) { @routes.recognize_path('/get_or_post', :method => :delete) } assert_equal({:controller => 'posts', :action => 'index', :optional => 'bar'}, @routes.recognize_path('/optional/bar')) - assert_raise(ActionController::ActionControllerError) { @routes.recognize_path('/optional') } + assert_raise(ActionController::RoutingError) { @routes.recognize_path('/optional') } assert_equal({:controller => 'posts', :action => 'show', :id => '1', :ws => true}, @routes.recognize_path('/ws/posts/show/1', :method => :get)) assert_equal({:controller => 'posts', :action => 'list', :ws => true}, @routes.recognize_path('/ws/posts/list', :method => :get)) @@ -1881,6 +2012,10 @@ class RackMountIntegrationTests < ActiveSupport::TestCase assert_equal({:controller => 'news', :action => 'index'}, @routes.recognize_path(URI.parser.escape('こんにちは/世界'), :method => :get)) end + def test_downcased_unicode_path + assert_equal({:controller => 'news', :action => 'index'}, @routes.recognize_path(URI.parser.escape('こんにちは/世界').downcase, :method => :get)) + end + private def sort_extras!(extras) if extras.length == 2 @@ -1888,11 +2023,4 @@ class RackMountIntegrationTests < ActiveSupport::TestCase end extras end - - def assert_raise(e) - result = yield - flunk "Did not raise #{e}, but returned #{result.inspect}" - rescue e - assert true - end end diff --git a/actionpack/test/controller/selector_test.rb b/actionpack/test/controller/selector_test.rb deleted file mode 100644 index 1e80c8601c..0000000000 --- a/actionpack/test/controller/selector_test.rb +++ /dev/null @@ -1,629 +0,0 @@ -#-- -# Copyright (c) 2006 Assaf Arkin (http://labnotes.org) -# Under MIT and/or CC By license. -#++ - -require 'abstract_unit' -require 'controller/fake_controllers' -require 'action_view/vendor/html-scanner' - -class SelectorTest < ActiveSupport::TestCase - # - # Basic selector: element, id, class, attributes. - # - - def test_element - parse(%Q{<div id="1"></div><p></p><div id="2"></div>}) - # Match element by name. - select("div") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "2", @matches[1].attributes["id"] - # Not case sensitive. - select("DIV") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "2", @matches[1].attributes["id"] - # Universal match (all elements). - select("*") - assert_equal 3, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal nil, @matches[1].attributes["id"] - assert_equal "2", @matches[2].attributes["id"] - end - - - def test_identifier - parse(%Q{<div id="1"></div><p></p><div id="2"></div>}) - # Match element by ID. - select("div#1") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - # Match element by ID, substitute value. - select("div#?", 2) - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - # Element name does not match ID. - select("p#?", 2) - assert_equal 0, @matches.size - # Use regular expression. - select("#?", /\d/) - assert_equal 2, @matches.size - end - - - def test_class_name - parse(%Q{<div id="1" class=" foo "></div><p id="2" class=" foo bar "></p><div id="3" class="bar"></div>}) - # Match element with specified class. - select("div.foo") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - # Match any element with specified class. - select("*.foo") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "2", @matches[1].attributes["id"] - # Match elements with other class. - select("*.bar") - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - # Match only element with both class names. - select("*.bar.foo") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - end - - - def test_attribute - parse(%Q{<div id="1"></div><p id="2" title="" bar="foo"></p><div id="3" title="foo"></div>}) - # Match element with attribute. - select("div[title]") - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - # Match any element with attribute. - select("*[title]") - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - # Match element with attribute value. - select("*[title=foo]") - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - # Match element with attribute and attribute value. - select("[bar=foo][title]") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - # Not case sensitive. - select("[BAR=foo][TiTle]") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - end - - - def test_attribute_quoted - parse(%Q{<div id="1" title="foo"></div><div id="2" title="bar"></div><div id="3" title=" bar "></div>}) - # Match without quotes. - select("[title = bar]") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - # Match with single quotes. - select("[title = 'bar' ]") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - # Match with double quotes. - select("[title = \"bar\" ]") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - # Match with spaces. - select("[title = \" bar \" ]") - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - end - - - def test_attribute_equality - parse(%Q{<div id="1" title="foo bar"></div><div id="2" title="barbaz"></div>}) - # Match (fail) complete value. - select("[title=bar]") - assert_equal 0, @matches.size - # Match space-separate word. - select("[title~=foo]") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - select("[title~=bar]") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - # Match beginning of value. - select("[title^=ba]") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - # Match end of value. - select("[title$=ar]") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - # Match text in value. - select("[title*=bar]") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "2", @matches[1].attributes["id"] - # Match first space separated word. - select("[title|=foo]") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - select("[title|=bar]") - assert_equal 0, @matches.size - end - - - # - # Selector composition: groups, sibling, children - # - - - def test_selector_group - parse(%Q{<h1 id="1"></h1><h2 id="2"></h2><h3 id="3"></h3>}) - # Simple group selector. - select("h1,h3") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - select("h1 , h3") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - # Complex group selector. - parse(%Q{<h1 id="1"><a href="foo"></a></h1><h2 id="2"><a href="bar"></a></h2><h3 id="2"><a href="baz"></a></h3>}) - select("h1 a, h3 a") - assert_equal 2, @matches.size - assert_equal "foo", @matches[0].attributes["href"] - assert_equal "baz", @matches[1].attributes["href"] - # And now for the three selector challenge. - parse(%Q{<h1 id="1"><a href="foo"></a></h1><h2 id="2"><a href="bar"></a></h2><h3 id="2"><a href="baz"></a></h3>}) - select("h1 a, h2 a, h3 a") - assert_equal 3, @matches.size - assert_equal "foo", @matches[0].attributes["href"] - assert_equal "bar", @matches[1].attributes["href"] - assert_equal "baz", @matches[2].attributes["href"] - end - - - def test_sibling_selector - parse(%Q{<h1 id="1"></h1><h2 id="2"></h2><h3 id="3"></h3>}) - # Test next sibling. - select("h1+*") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - select("h1+h2") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - select("h1+h3") - assert_equal 0, @matches.size - select("*+h3") - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - # Test any sibling. - select("h1~*") - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - select("h2~*") - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - end - - - def test_children_selector - parse(%Q{<div><p id="1"><span id="2"></span></p></div><div><p id="3"><span id="4" class="foo"></span></p></div>}) - # Test child selector. - select("div>p") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - select("div>span") - assert_equal 0, @matches.size - select("div>p#3") - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - select("div>p>span") - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "4", @matches[1].attributes["id"] - # Test descendant selector. - select("div p") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - select("div span") - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "4", @matches[1].attributes["id"] - select("div *#3") - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - select("div *#4") - assert_equal 1, @matches.size - assert_equal "4", @matches[0].attributes["id"] - # This is here because it failed before when whitespaces - # were not properly stripped. - select("div .foo") - assert_equal 1, @matches.size - assert_equal "4", @matches[0].attributes["id"] - end - - - # - # Pseudo selectors: root, nth-child, empty, content, etc - # - - - def test_root_selector - parse(%Q{<div id="1"><div id="2"></div></div>}) - # Can only find element if it's root. - select(":root") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - select("#1:root") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - select("#2:root") - assert_equal 0, @matches.size - # Opposite for nth-child. - select("#1:nth-child(1)") - assert_equal 0, @matches.size - end - - - def test_nth_child_odd_even - parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # Test odd nth children. - select("tr:nth-child(odd)") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - # Test even nth children. - select("tr:nth-child(even)") - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "4", @matches[1].attributes["id"] - end - - - def test_nth_child_a_is_zero - parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # Test the third child. - select("tr:nth-child(0n+3)") - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - # Same but an can be omitted when zero. - select("tr:nth-child(3)") - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - # Second element (but not every second element). - select("tr:nth-child(0n+2)") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - # Before first and past last returns nothing.: - assert_raise(ArgumentError) { select("tr:nth-child(-1)") } - select("tr:nth-child(0)") - assert_equal 0, @matches.size - select("tr:nth-child(5)") - assert_equal 0, @matches.size - end - - - def test_nth_child_a_is_one - parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # a is group of one, pick every element in group. - select("tr:nth-child(1n+0)") - assert_equal 4, @matches.size - # Same but a can be omitted when one. - select("tr:nth-child(n+0)") - assert_equal 4, @matches.size - # Same but b can be omitted when zero. - select("tr:nth-child(n)") - assert_equal 4, @matches.size - end - - - def test_nth_child_b_is_zero - parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # If b is zero, pick the n-th element (here each one). - select("tr:nth-child(n+0)") - assert_equal 4, @matches.size - # If b is zero, pick the n-th element (here every second). - select("tr:nth-child(2n+0)") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - # If a and b are both zero, no element selected. - select("tr:nth-child(0n+0)") - assert_equal 0, @matches.size - select("tr:nth-child(0)") - assert_equal 0, @matches.size - end - - - def test_nth_child_a_is_negative - parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # Since a is -1, picks the first three elements. - select("tr:nth-child(-n+3)") - assert_equal 3, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "2", @matches[1].attributes["id"] - assert_equal "3", @matches[2].attributes["id"] - # Since a is -2, picks the first in every second of first four elements. - select("tr:nth-child(-2n+3)") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - # Since a is -2, picks the first in every second of first three elements. - select("tr:nth-child(-2n+2)") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - end - - - def test_nth_child_b_is_negative - parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # Select last of four. - select("tr:nth-child(4n-1)") - assert_equal 1, @matches.size - assert_equal "4", @matches[0].attributes["id"] - # Select first of four. - select("tr:nth-child(4n-4)") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - # Select last of every second. - select("tr:nth-child(2n-1)") - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "4", @matches[1].attributes["id"] - # Select nothing since an+b always < 0 - select("tr:nth-child(-1n-1)") - assert_equal 0, @matches.size - end - - - def test_nth_child_substitution_values - parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # Test with ?n?. - select("tr:nth-child(?n?)", 2, 1) - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - select("tr:nth-child(?n?)", 2, 2) - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "4", @matches[1].attributes["id"] - select("tr:nth-child(?n?)", 4, 2) - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - # Test with ? (b only). - select("tr:nth-child(?)", 3) - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - select("tr:nth-child(?)", 5) - assert_equal 0, @matches.size - end - - - def test_nth_last_child - parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # Last two elements. - select("tr:nth-last-child(-n+2)") - assert_equal 2, @matches.size - assert_equal "3", @matches[0].attributes["id"] - assert_equal "4", @matches[1].attributes["id"] - # All old elements counting from last one. - select("tr:nth-last-child(odd)") - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "4", @matches[1].attributes["id"] - end - - - def test_nth_of_type - parse(%Q{<table><thead></thead><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # First two elements. - select("tr:nth-of-type(-n+2)") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "2", @matches[1].attributes["id"] - # All old elements counting from last one. - select("tr:nth-last-of-type(odd)") - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "4", @matches[1].attributes["id"] - end - - - def test_first_and_last - parse(%Q{<table><thead></thead><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # First child. - select("tr:first-child") - assert_equal 0, @matches.size - select(":first-child") - assert_equal 1, @matches.size - assert_equal "thead", @matches[0].name - # First of type. - select("tr:first-of-type") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - select("thead:first-of-type") - assert_equal 1, @matches.size - assert_equal "thead", @matches[0].name - select("div:first-of-type") - assert_equal 0, @matches.size - # Last child. - select("tr:last-child") - assert_equal 1, @matches.size - assert_equal "4", @matches[0].attributes["id"] - # Last of type. - select("tr:last-of-type") - assert_equal 1, @matches.size - assert_equal "4", @matches[0].attributes["id"] - select("thead:last-of-type") - assert_equal 1, @matches.size - assert_equal "thead", @matches[0].name - select("div:last-of-type") - assert_equal 0, @matches.size - end - - - def test_only_child_and_only_type_first_and_last - # Only child. - parse(%Q{<table><tr></tr></table>}) - select("table:only-child") - assert_equal 0, @matches.size - select("tr:only-child") - assert_equal 1, @matches.size - assert_equal "tr", @matches[0].name - parse(%Q{<table><tr></tr><tr></tr></table>}) - select("tr:only-child") - assert_equal 0, @matches.size - # Only of type. - parse(%Q{<table><thead></thead><tr></tr><tr></tr></table>}) - select("thead:only-of-type") - assert_equal 1, @matches.size - assert_equal "thead", @matches[0].name - select("td:only-of-type") - assert_equal 0, @matches.size - end - - - def test_empty - parse(%Q{<table><tr></tr></table>}) - select("table:empty") - assert_equal 0, @matches.size - select("tr:empty") - assert_equal 1, @matches.size - parse(%Q{<div> </div>}) - select("div:empty") - assert_equal 1, @matches.size - end - - - def test_content - parse(%Q{<div> </div>}) - select("div:content()") - assert_equal 1, @matches.size - parse(%Q{<div>something </div>}) - select("div:content()") - assert_equal 0, @matches.size - select("div:content(something)") - assert_equal 1, @matches.size - select("div:content( 'something' )") - assert_equal 1, @matches.size - select("div:content( \"something\" )") - assert_equal 1, @matches.size - select("div:content(?)", "something") - assert_equal 1, @matches.size - select("div:content(?)", /something/) - assert_equal 1, @matches.size - end - - - # - # Test negation. - # - - - def test_element_negation - parse(%Q{<p></p><div></div>}) - select("*") - assert_equal 2, @matches.size - select("*:not(p)") - assert_equal 1, @matches.size - assert_equal "div", @matches[0].name - select("*:not(div)") - assert_equal 1, @matches.size - assert_equal "p", @matches[0].name - select("*:not(span)") - assert_equal 2, @matches.size - end - - - def test_id_negation - parse(%Q{<p id="1"></p><p id="2"></p>}) - select("p") - assert_equal 2, @matches.size - select(":not(#1)") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - select(":not(#2)") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - end - - - def test_class_name_negation - parse(%Q{<p class="foo"></p><p class="bar"></p>}) - select("p") - assert_equal 2, @matches.size - select(":not(.foo)") - assert_equal 1, @matches.size - assert_equal "bar", @matches[0].attributes["class"] - select(":not(.bar)") - assert_equal 1, @matches.size - assert_equal "foo", @matches[0].attributes["class"] - end - - - def test_attribute_negation - parse(%Q{<p title="foo"></p><p title="bar"></p>}) - select("p") - assert_equal 2, @matches.size - select(":not([title=foo])") - assert_equal 1, @matches.size - assert_equal "bar", @matches[0].attributes["title"] - select(":not([title=bar])") - assert_equal 1, @matches.size - assert_equal "foo", @matches[0].attributes["title"] - end - - - def test_pseudo_class_negation - parse(%Q{<div><p id="1"></p><p id="2"></p></div>}) - select("p") - assert_equal 2, @matches.size - select("p:not(:first-child)") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - select("p:not(:nth-child(2))") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - end - - - def test_negation_details - parse(%Q{<p id="1"></p><p id="2"></p><p id="3"></p>}) - assert_raise(ArgumentError) { select(":not(") } - assert_raise(ArgumentError) { select(":not(:not())") } - select("p:not(#1):not(#3)") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - end - - - def test_select_from_element - parse(%Q{<div><p id="1"></p><p id="2"></p></div>}) - select("div") - @matches = @matches[0].select("p") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "2", @matches[1].attributes["id"] - end - - -protected - - def parse(html) - @html = HTML::Document.new(html).root - end - - def select(*selector) - @matches = HTML.selector(*selector).select(@html) - end - -end diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index 8ecc1c7d73..c002cf4d8f 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -9,6 +9,7 @@ end class SendFileController < ActionController::Base include TestFileUtils + include ActionController::Testing layout "layouts/standard" # to make sure layouts don't interfere attr_writer :options @@ -25,12 +26,13 @@ class SendFileController < ActionController::Base end end +class SendFileWithActionControllerLive < SendFileController + include ActionController::Live +end + class SendFileTest < ActionController::TestCase - tests SendFileController include TestFileUtils - Mime::Type.register "image/png", :png unless defined? Mime::PNG - def setup @controller = SendFileController.new @request = ActionController::TestRequest.new @@ -144,7 +146,7 @@ class SendFileTest < ActionController::TestCase } @controller.headers = {} - assert !@controller.send(:send_file_headers!, options) + assert_raise(ArgumentError) { @controller.send(:send_file_headers!, options) } end def test_send_file_headers_guess_type_from_extension @@ -196,4 +198,12 @@ class SendFileTest < ActionController::TestCase assert_equal 200, @response.status end end + + def test_send_file_with_action_controller_live + @controller = SendFileWithActionControllerLive.new + @controller.options = { :content_type => "application/x-ruby" } + + response = process('file') + assert_equal 200, response.status + end end diff --git a/actionpack/test/controller/show_exceptions_test.rb b/actionpack/test/controller/show_exceptions_test.rb index 888791b874..f7eba1ef43 100644 --- a/actionpack/test/controller/show_exceptions_test.rb +++ b/actionpack/test/controller/show_exceptions_test.rb @@ -32,7 +32,7 @@ module ShowExceptions test 'show diagnostics from a local ip if show_detailed_exceptions? is set to request.local?' do @app = ShowExceptionsController.action(:boom) - ['127.0.0.1', '127.0.0.127', '::1', '0:0:0:0:0:0:0:1', '0:0:0:0:0:0:0:1%0'].each do |ip_address| + ['127.0.0.1', '127.0.0.127', '127.12.1.1', '::1', '0:0:0:0:0:0:0:1', '0:0:0:0:0:0:0:1%0'].each do |ip_address| self.remote_addr = ip_address get '/' assert_match(/boom/, body) @@ -47,7 +47,7 @@ module ShowExceptions end end - class ShowExceptionsOverridenController < ShowExceptionsController + class ShowExceptionsOverriddenController < ShowExceptionsController private def show_detailed_exceptions? @@ -55,15 +55,15 @@ module ShowExceptions end end - class ShowExceptionsOverridenTest < ActionDispatch::IntegrationTest + class ShowExceptionsOverriddenTest < ActionDispatch::IntegrationTest test 'show error page' do - @app = ShowExceptionsOverridenController.action(:boom) + @app = ShowExceptionsOverriddenController.action(:boom) get '/', {'detailed' => '0'} assert_equal "500 error fixture\n", body end test 'show diagnostics message' do - @app = ShowExceptionsOverridenController.action(:boom) + @app = ShowExceptionsOverriddenController.action(:boom) get '/', {'detailed' => '1'} assert_match(/boom/, body) end @@ -71,23 +71,23 @@ module ShowExceptions class ShowExceptionsFormatsTest < ActionDispatch::IntegrationTest def test_render_json_exception - @app = ShowExceptionsOverridenController.action(:boom) + @app = ShowExceptionsOverriddenController.action(:boom) get "/", {}, 'HTTP_ACCEPT' => 'application/json' assert_response :internal_server_error assert_equal 'application/json', response.content_type.to_s - assert_equal({ :status => '500', :error => 'boom!' }.to_json, response.body) + assert_equal({ :status => '500', :error => 'Internal Server Error' }.to_json, response.body) end def test_render_xml_exception - @app = ShowExceptionsOverridenController.action(:boom) + @app = ShowExceptionsOverriddenController.action(:boom) get "/", {}, 'HTTP_ACCEPT' => 'application/xml' assert_response :internal_server_error assert_equal 'application/xml', response.content_type.to_s - assert_equal({ :status => '500', :error => 'boom!' }.to_xml, response.body) + assert_equal({ :status => '500', :error => 'Internal Server Error' }.to_xml, response.body) end def test_render_fallback_exception - @app = ShowExceptionsOverridenController.action(:boom) + @app = ShowExceptionsOverriddenController.action(:boom) get "/", {}, 'HTTP_ACCEPT' => 'text/csv' assert_response :internal_server_error assert_equal 'text/html', response.content_type.to_s @@ -96,7 +96,7 @@ module ShowExceptions class ShowFailsafeExceptionsTest < ActionDispatch::IntegrationTest def test_render_failsafe_exception - @app = ShowExceptionsOverridenController.action(:boom) + @app = ShowExceptionsOverriddenController.action(:boom) @exceptions_app = @app.instance_variable_get(:@exceptions_app) @app.instance_variable_set(:@exceptions_app, nil) $stderr = StringIO.new diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb index bdca1d4d77..ba2ff7d12c 100644 --- a/actionpack/test/controller/test_case_test.rb +++ b/actionpack/test/controller/test_case_test.rb @@ -1,5 +1,7 @@ require 'abstract_unit' require 'controller/fake_controllers' +require 'active_support/json/decoding' +require 'rails/engine' class TestCaseTest < ActionController::TestCase class TestController < ActionController::Base @@ -57,6 +59,10 @@ class TestCaseTest < ActionController::TestCase render :text => request.protocol end + def test_headers + render text: request.headers.env.to_json + end + def test_html_output render :text => <<HTML <html> @@ -127,6 +133,14 @@ XML render :nothing => true end + def test_without_body + render html: '<div class="foo"></div>'.html_safe + end + + def test_with_body + render html: '<body class="foo"></body>'.html_safe + end + private def generate_url(opts) @@ -158,6 +172,42 @@ XML end end + class DefaultUrlOptionsCachingController < ActionController::Base + before_action { @dynamic_opt = 'opt' } + + def test_url_options_reset + render text: url_for(params) + end + + def default_url_options + if defined?(@dynamic_opt) + super.merge dynamic_opt: @dynamic_opt + else + super + end + end + end + + def test_assert_select_without_body + get :test_without_body + + assert_select 'body', 0 + assert_select 'div.foo' + end + + def test_assert_select_with_body + get :test_with_body + + assert_select 'body.foo' + end + + def test_url_options_reset + @controller = DefaultUrlOptionsCachingController.new + get :test_url_options_reset + assert_nil @request.params['dynamic_opt'] + assert_match(/dynamic_opt=opt/, @response.body) + end + def test_raw_post_handling params = Hash[:page, {:name => 'page name'}, 'some key', 123] post :render_raw_post, params.dup @@ -193,15 +243,10 @@ XML assert_equal 200, @response.status end - def test_head_params_as_sting + def test_head_params_as_string assert_raise(NoMethodError) { head :test_params, "document body", :id => 10 } end - def test_options - options :test_params - assert_equal 200, @response.status - end - def test_process_without_flash process :set_flash assert_equal '><', flash['test'] @@ -281,13 +326,6 @@ XML assert_equal "/test_case_test/test/test_uri/7", @response.body end - def test_process_with_old_api - assert_deprecated do - process :test_uri, :id => 7 - assert_equal "/test_case_test/test/test_uri/7", @response.body - end - end - def test_process_with_request_uri_with_params_with_explicit_uri @request.env['PATH_INFO'] = "/explicit/uri" process :test_uri, "GET", :id => 7 @@ -337,168 +375,6 @@ XML assert_equal "bar", assigns[:bar] end - def test_assert_tag_tag - process :test_html_output - - # there is a 'form' tag - assert_tag :tag => 'form' - # there is not an 'hr' tag - assert_no_tag :tag => 'hr' - end - - def test_assert_tag_attributes - process :test_html_output - - # there is a tag with an 'id' of 'bar' - assert_tag :attributes => { :id => "bar" } - # there is no tag with a 'name' of 'baz' - assert_no_tag :attributes => { :name => "baz" } - end - - def test_assert_tag_parent - process :test_html_output - - # there is a tag with a parent 'form' tag - assert_tag :parent => { :tag => "form" } - # there is no tag with a parent of 'input' - assert_no_tag :parent => { :tag => "input" } - end - - def test_assert_tag_child - process :test_html_output - - # there is a tag with a child 'input' tag - assert_tag :child => { :tag => "input" } - # there is no tag with a child 'strong' tag - assert_no_tag :child => { :tag => "strong" } - end - - def test_assert_tag_ancestor - process :test_html_output - - # there is a 'li' tag with an ancestor having an id of 'foo' - assert_tag :ancestor => { :attributes => { :id => "foo" } }, :tag => "li" - # there is no tag of any kind with an ancestor having an href matching 'foo' - assert_no_tag :ancestor => { :attributes => { :href => /foo/ } } - end - - def test_assert_tag_descendant - process :test_html_output - - # there is a tag with a descendant 'li' tag - assert_tag :descendant => { :tag => "li" } - # there is no tag with a descendant 'html' tag - assert_no_tag :descendant => { :tag => "html" } - end - - def test_assert_tag_sibling - process :test_html_output - - # there is a tag with a sibling of class 'item' - assert_tag :sibling => { :attributes => { :class => "item" } } - # there is no tag with a sibling 'ul' tag - assert_no_tag :sibling => { :tag => "ul" } - end - - def test_assert_tag_after - process :test_html_output - - # there is a tag following a sibling 'div' tag - assert_tag :after => { :tag => "div" } - # there is no tag following a sibling tag with id 'bar' - assert_no_tag :after => { :attributes => { :id => "bar" } } - end - - def test_assert_tag_before - process :test_html_output - - # there is a tag preceding a tag with id 'bar' - assert_tag :before => { :attributes => { :id => "bar" } } - # there is no tag preceding a 'form' tag - assert_no_tag :before => { :tag => "form" } - end - - def test_assert_tag_children_count - process :test_html_output - - # there is a tag with 2 children - assert_tag :children => { :count => 2 } - # in particular, there is a <ul> tag with two children (a nameless pair of <li>s) - assert_tag :tag => 'ul', :children => { :count => 2 } - # there is no tag with 4 children - assert_no_tag :children => { :count => 4 } - end - - def test_assert_tag_children_less_than - process :test_html_output - - # there is a tag with less than 5 children - assert_tag :children => { :less_than => 5 } - # there is no 'ul' tag with less than 2 children - assert_no_tag :children => { :less_than => 2 }, :tag => "ul" - end - - def test_assert_tag_children_greater_than - process :test_html_output - - # there is a 'body' tag with more than 1 children - assert_tag :children => { :greater_than => 1 }, :tag => "body" - # there is no tag with more than 10 children - assert_no_tag :children => { :greater_than => 10 } - end - - def test_assert_tag_children_only - process :test_html_output - - # there is a tag containing only one child with an id of 'foo' - assert_tag :children => { :count => 1, - :only => { :attributes => { :id => "foo" } } } - # there is no tag containing only one 'li' child - assert_no_tag :children => { :count => 1, :only => { :tag => "li" } } - end - - def test_assert_tag_content - process :test_html_output - - # the output contains the string "Name" - assert_tag :content => /Name/ - # the output does not contain the string "test" - assert_no_tag :content => /test/ - end - - def test_assert_tag_multiple - process :test_html_output - - # there is a 'div', id='bar', with an immediate child whose 'action' - # attribute matches the regexp /somewhere/. - assert_tag :tag => "div", :attributes => { :id => "bar" }, - :child => { :attributes => { :action => /somewhere/ } } - - # there is no 'div', id='foo', with a 'ul' child with more than - # 2 "li" children. - assert_no_tag :tag => "div", :attributes => { :id => "foo" }, - :child => { - :tag => "ul", - :children => { :greater_than => 2, - :only => { :tag => "li" } } } - end - - def test_assert_tag_children_without_content - process :test_html_output - - # there is a form tag with an 'input' child which is a self closing tag - assert_tag :tag => "form", - :children => { :count => 1, - :only => { :tag => "input" } } - - # the body tag has an 'a' child which in turn has an 'img' child - assert_tag :tag => "body", - :children => { :count => 1, - :only => { :tag => "a", - :children => { :count => 1, - :only => { :tag => "img" } } } } - end - def test_should_not_impose_childless_html_tags_in_xml process :test_xml_output @@ -513,23 +389,6 @@ XML assert err.empty? end - def test_assert_tag_attribute_matching - @response.body = '<input type="text" name="my_name">' - assert_tag :tag => 'input', - :attributes => { :name => /my/, :type => 'text' } - assert_no_tag :tag => 'input', - :attributes => { :name => 'my', :type => 'text' } - assert_no_tag :tag => 'input', - :attributes => { :name => /^my$/, :type => 'text' } - end - - def test_assert_tag_content_matching - @response.body = "<p>hello world</p>" - assert_tag :tag => "p", :content => "hello world" - assert_tag :tag => "p", :content => /hello/ - assert_no_tag :tag => "p", :content => "hello" - end - def test_assert_generates assert_generates 'controller/action/5', :controller => 'controller', :action => 'action', :id => '5' assert_generates 'controller/action/7', {:id => "7"}, {:controller => "controller", :action => "action"} @@ -626,9 +485,27 @@ XML assert_equal 2004, page[:year] end + test "set additional HTTP headers" do + @request.headers['Referer'] = "http://nohost.com/home" + @request.headers['Content-Type'] = "application/rss+xml" + get :test_headers + parsed_env = ActiveSupport::JSON.decode(@response.body) + assert_equal "http://nohost.com/home", parsed_env["HTTP_REFERER"] + assert_equal "application/rss+xml", parsed_env["CONTENT_TYPE"] + end + + test "set additional env variables" do + @request.headers['HTTP_REFERER'] = "http://example.com/about" + @request.headers['CONTENT_TYPE'] = "application/json" + get :test_headers + parsed_env = ActiveSupport::JSON.decode(@response.body) + assert_equal "http://example.com/about", parsed_env["HTTP_REFERER"] + assert_equal "application/json", parsed_env["CONTENT_TYPE"] + end + def test_id_converted_to_string get :test_params, :id => 20, :foo => Object.new - assert_kind_of String, @request.path_parameters['id'] + assert_kind_of String, @request.path_parameters[:id] end def test_array_path_parameter_handled_properly @@ -639,17 +516,29 @@ XML end get :test_params, :path => ['hello', 'world'] - assert_equal ['hello', 'world'], @request.path_parameters['path'] - assert_equal 'hello/world', @request.path_parameters['path'].to_param + assert_equal ['hello', 'world'], @request.path_parameters[:path] + assert_equal 'hello/world', @request.path_parameters[:path].to_param + end + end + + def test_use_route + with_routing do |set| + set.draw do + get 'via_unnamed_route', to: 'test_case_test/test#test_uri' + get 'via_named_route', as: :a_named_route, to: 'test_case_test/test#test_uri' + end + + assert_deprecated { get :test_uri, use_route: :a_named_route } + assert_equal '/via_named_route', @response.body end end def test_assert_realistic_path_parameters get :test_params, :id => 20, :foo => Object.new - # All elements of path_parameters should use string keys - @request.path_parameters.keys.each do |key| - assert_kind_of String, key + # All elements of path_parameters should use Symbol keys + @request.path_parameters.each_key do |key| + assert_kind_of Symbol, key end end @@ -679,6 +568,7 @@ XML def test_header_properly_reset_after_remote_http_request xhr :get, :test_params assert_nil @request.env['HTTP_X_REQUESTED_WITH'] + assert_nil @request.env['HTTP_ACCEPT'] end def test_header_properly_reset_after_get_request @@ -687,20 +577,28 @@ XML assert_nil @request.instance_variable_get("@request_method") end - def test_params_reset_after_post_request + def test_params_reset_between_post_requests post :no_op, :foo => "bar" assert_equal "bar", @request.params[:foo] - @request.recycle! + post :no_op - assert_blank @request.params[:foo] + assert @request.params[:foo].blank? end - def test_symbolized_path_params_reset_after_request + def test_filtered_parameters_reset_between_requests + get :no_op, :foo => "bar" + assert_equal "bar", @request.filtered_parameters[:foo] + + get :no_op, :foo => "baz" + assert_equal "baz", @request.filtered_parameters[:foo] + end + + def test_path_params_reset_between_request get :test_params, :id => "foo" - assert_equal "foo", @request.symbolized_path_parameters[:id] - @request.recycle! + assert_equal "foo", @request.path_parameters[:id] + get :test_params - assert_nil @request.symbolized_path_parameters[:id] + assert_nil @request.path_parameters[:id] end def test_request_protocol_is_reset_after_request @@ -856,6 +754,57 @@ XML end end +module EngineControllerTests + class Engine < ::Rails::Engine + isolate_namespace EngineControllerTests + + routes.draw do + get '/' => 'bar#index' + end + end + + class BarController < ActionController::Base + def index + render :text => 'bar' + end + end + + class BarControllerTest < ActionController::TestCase + tests BarController + + def test_engine_controller_route + get :index + assert_equal @response.body, 'bar' + end + end + + class BarControllerTestWithExplicitRouteSet < ActionController::TestCase + tests BarController + + def setup + @routes = Engine.routes + end + + def test_engine_controller_route + get :index + assert_equal @response.body, 'bar' + end + end + + class BarControllerTestWithHostApplicationRouteSet < ActionController::TestCase + tests BarController + + def test_use_route + with_routing do |set| + set.draw { mount Engine => '/foo' } + + assert_deprecated { get :index, use_route: :foo } + assert_equal @response.body, 'bar' + end + end + end +end + class InferringClassNameTest < ActionController::TestCase def test_determine_controller_class assert_equal ContentController, determine_class("ContentControllerTest") @@ -931,3 +880,34 @@ class AnonymousControllerTest < ActionController::TestCase assert_equal 'anonymous', @response.body end end + +class RoutingDefaultsTest < ActionController::TestCase + def setup + @controller = Class.new(ActionController::Base) do + def post + render :text => request.fullpath + end + + def project + render :text => request.fullpath + end + end.new + + @routes = ActionDispatch::Routing::RouteSet.new.tap do |r| + r.draw do + get '/posts/:id', :to => 'anonymous#post', :bucket_type => 'post' + get '/projects/:id', :to => 'anonymous#project', :defaults => { :bucket_type => 'project' } + end + end + end + + def test_route_option_can_be_passed_via_process + get :post, :id => 1, :bucket_type => 'post' + assert_equal '/posts/1', @response.body + end + + def test_route_default_is_not_required_for_building_request_uri + get :project, :id => 2 + assert_equal '/projects/2', @response.body + end +end diff --git a/actionpack/test/controller/url_for_integration_test.rb b/actionpack/test/controller/url_for_integration_test.rb index 6c2311e7a5..24a09222b1 100644 --- a/actionpack/test/controller/url_for_integration_test.rb +++ b/actionpack/test/controller/url_for_integration_test.rb @@ -6,6 +6,7 @@ require 'active_support/core_ext/object/with_options' module ActionPack class URLForIntegrationTest < ActiveSupport::TestCase include RoutingTestHelpers + include ActionDispatch::RoutingVerbs Model = Struct.new(:to_param) @@ -61,8 +62,11 @@ module ActionPack root :to => "news#index" } + attr_reader :routes + attr_accessor :controller + def setup - @routes = ActionDispatch::Routing::RouteSet.new + @routes = make_set false @routes.draw(&Mapping) end @@ -70,9 +74,9 @@ module ActionPack ['/admin/users',[ { :use_route => 'admin_users' }]], ['/admin/users',[ { :controller => 'admin/users' }]], ['/admin/users',[ { :controller => 'admin/users', :action => 'index' }]], - ['/admin/users',[ { :action => 'index' }, { :controller => 'admin/users' }]], - ['/admin/users',[ { :controller => 'users', :action => 'index' }, { :controller => 'admin/accounts' }]], - ['/people',[ { :controller => '/people', :action => 'index' }, { :controller => 'admin/accounts' }]], + ['/admin/users',[ { :action => 'index' }, { :controller => 'admin/users', :action => 'index' }, '/admin/users']], + ['/admin/users',[ { :controller => 'users', :action => 'index' }, { :controller => 'admin/accounts', :action => 'show', :id => '1' }, '/admin/accounts/show/1']], + ['/people',[ { :controller => '/people', :action => 'index' }, {:controller=>"admin/accounts", :action=>"foo", :id=>"bar"}, '/admin/accounts/foo/bar']], ['/admin/posts',[ { :controller => 'admin/posts' }]], ['/admin/posts/new',[ { :controller => 'admin/posts', :action => 'new' }]], @@ -86,11 +90,11 @@ module ActionPack ['/archive?year=january',[ { :controller => 'archive', :action => 'index', :year => 'january' }]], ['/people',[ { :controller => 'people', :action => 'index' }]], - ['/people',[ { :action => 'index' }, { :controller => 'people' }]], - ['/people',[ { :action => 'index' }, { :controller => 'people', :action => 'show', :id => '1' }]], - ['/people',[ { :controller => 'people', :action => 'index' }, { :controller => 'people', :action => 'show', :id => '1' }]], - ['/people',[ {}, { :controller => 'people', :action => 'index' }]], - ['/people/1',[ { :controller => 'people', :action => 'show' }, { :controller => 'people', :action => 'show', :id => '1' }]], + ['/people',[ { :action => 'index' }, { :controller => 'people', :action => 'index' }, '/people']], + ['/people',[ { :action => 'index' }, { :controller => 'people', :action => 'show', :id => '1' }, '/people/show/1']], + ['/people',[ { :controller => 'people', :action => 'index' }, { :controller => 'people', :action => 'show', :id => '1' }, '/people/show/1']], + ['/people',[ {}, { :controller => 'people', :action => 'index' }, '/people']], + ['/people/1',[ { :controller => 'people', :action => 'show' }, { :controller => 'people', :action => 'show', :id => '1' }, '/people/show/1']], ['/people/new',[ { :use_route => 'new_person' }]], ['/people/new',[ { :controller => 'people', :action => 'new' }]], ['/people/1',[ { :use_route => 'person', :id => '1' }]], @@ -98,11 +102,11 @@ module ActionPack ['/people/1.xml',[ { :controller => 'people', :action => 'show', :id => '1', :format => 'xml' }]], ['/people/1',[ { :controller => 'people', :action => 'show', :id => 1 }]], ['/people/1',[ { :controller => 'people', :action => 'show', :id => Model.new('1') }]], - ['/people/1',[ { :action => 'show', :id => '1' }, { :controller => 'people', :action => 'index' }]], - ['/people/1',[ { :action => 'show', :id => 1 }, { :controller => 'people', :action => 'show', :id => '1' }]], - ['/people',[ { :controller => 'people', :action => 'index' }, { :controller => 'people', :action => 'show', :id => '1' }]], - ['/people/1',[ {}, { :controller => 'people', :action => 'show', :id => '1' }]], - ['/people/1',[ { :controller => 'people', :action => 'show' }, { :controller => 'people', :action => 'index', :id => '1' }]], + ['/people/1',[ { :action => 'show', :id => '1' }, { :controller => 'people', :action => 'index' }, '/people']], + ['/people/1',[ { :action => 'show', :id => 1 }, { :controller => 'people', :action => 'show', :id => '1' }, '/people/show/1']], + ['/people',[ { :controller => 'people', :action => 'index' }, { :controller => 'people', :action => 'show', :id => '1' }, '/people/show/1']], + ['/people/1',[ {}, { :controller => 'people', :action => 'show', :id => '1' }, '/people/show/1']], + ['/people/1',[ { :controller => 'people', :action => 'show' }, { :controller => 'people', :action => 'index', :id => '1' }, '/people/index/1']], ['/people/1/edit',[ { :controller => 'people', :action => 'edit', :id => '1' }]], ['/people/1/edit.xml',[ { :controller => 'people', :action => 'edit', :id => '1', :format => 'xml' }]], ['/people/1/edit',[ { :use_route => 'edit_person', :id => '1' }]], @@ -118,16 +122,15 @@ module ActionPack ['/project',[ { :controller => 'project', :action => 'index' }]], ['/projects/1',[ { :controller => 'project', :action => 'index', :project_id => '1' }]], - ['/projects/1',[ { :controller => 'project', :action => 'index'}, {:project_id => '1' }]], + ['/projects/1',[ { :controller => 'project', :action => 'index'}, {:project_id => '1', :controller => 'project', :action => 'index' }, '/projects/1']], ['/projects/1',[ { :use_route => 'project', :controller => 'project', :action => 'index', :project_id => '1' }]], - ['/projects/1',[ { :use_route => 'project', :controller => 'project', :action => 'index' }, { :project_id => '1' }]], + ['/projects/1',[ { :use_route => 'project', :controller => 'project', :action => 'index' }, { :controller => 'project', :action => 'index', :project_id => '1' }, '/projects/1']], ['/clients',[ { :controller => 'projects', :action => 'index' }]], ['/clients?project_id=1',[ { :controller => 'projects', :action => 'index', :project_id => '1' }]], - ['/clients',[ { :controller => 'projects', :action => 'index' }, { :project_id => '1' }]], - ['/clients',[ { :action => 'index' }, { :controller => 'projects', :action => 'index', :project_id => '1' }]], + ['/clients',[ { :controller => 'projects', :action => 'index' }, { :project_id => '1', :controller => 'project', :action => 'index' }, '/projects/1']], - ['/comment/20',[ { :id => 20 }, { :controller => 'comments', :action => 'show' }]], + ['/comment/20',[ { :id => 20 }, { :controller => 'comments', :action => 'show' }, '/comments/show']], ['/comment/20',[ { :controller => 'comments', :id => 20, :action => 'show' }]], ['/comments/boo',[ { :controller => 'comments', :action => 'boo' }]], @@ -144,24 +147,21 @@ module ActionPack ['/notes',[ { :page_id => nil, :controller => 'notes' }]], ['/notes',[ { :controller => 'notes' }]], ['/notes/print',[ { :controller => 'notes', :action => 'print' }]], - ['/notes/print',[ {}, { :controller => 'notes', :action => 'print' }]], - - ['/notes/index/1',[ { :controller => 'notes' }, { :controller => 'notes', :id => '1' }]], - ['/notes/index/1',[ { :controller => 'notes' }, { :controller => 'notes', :id => '1', :foo => 'bar' }]], - ['/notes/index/1',[ { :controller => 'notes' }, { :controller => 'notes', :id => '1' }]], - ['/notes/index/1',[ { :action => 'index' }, { :controller => 'notes', :id => '1' }]], - ['/notes/index/1',[ {}, { :controller => 'notes', :id => '1' }]], - ['/notes/show/1',[ {}, { :controller => 'notes', :action => 'show', :id => '1' }]], - ['/notes/index/1',[ { :controller => 'notes', :id => '1' }, { :foo => 'bar' }]], - ['/posts',[ { :controller => 'posts' }, { :controller => 'notes', :action => 'show', :id => '1' }]], - ['/notes/list',[ { :action => 'list' }, { :controller => 'notes', :action => 'show', :id => '1' }]], + ['/notes/print',[ {}, { :controller => 'notes', :action => 'print' }, '/notes/print']], + + ['/notes/index/1',[ { :controller => 'notes' }, { :controller => 'notes', :action => 'index', :id => '1' }, '/notes/index/1']], + ['/notes/index/1',[ { :controller => 'notes' }, { :controller => 'notes', :id => '1', :action => 'index' }, '/notes/index/1']], + ['/notes/index/1',[ { :action => 'index' }, { :controller => 'notes', :id => '1', :action => 'index' }, '/notes/index/1']], + ['/notes/index/1',[ {}, { :controller => 'notes', :id => '1', :action => 'index' }, '/notes/index/1']], + ['/notes/show/1',[ {}, { :controller => 'notes', :action => 'show', :id => '1' }, '/notes/show/1']], + ['/posts',[ { :controller => 'posts' }, { :controller => 'notes', :action => 'show', :id => '1' }, '/notes/show/1']], + ['/notes/list',[ { :action => 'list' }, { :controller => 'notes', :action => 'show', :id => '1' }, '/notes/show/1']], ['/posts/ping',[ { :controller => 'posts', :action => 'ping' }]], ['/posts/show/1',[ { :controller => 'posts', :action => 'show', :id => '1' }]], ['/posts',[ { :controller => 'posts' }]], ['/posts',[ { :controller => 'posts', :action => 'index' }]], - ['/posts',[ { :controller => 'posts' }, { :controller => 'posts', :action => 'index' }]], - ['/posts/create',[ { :action => 'create' }, { :controller => 'posts' }]], + ['/posts/create',[ { :action => 'create' }, {:day=>nil, :month=>nil, :controller=>"posts", :action=>"show_date"}, '/blog']], ['/posts?foo=bar',[ { :controller => 'posts', :foo => 'bar' }]], ['/posts?foo%5B%5D=bar&foo%5B%5D=baz', [{ :controller => 'posts', :foo => ['bar', 'baz'] }]], ['/posts?page=2', [{ :controller => 'posts', :page => 2 }]], @@ -169,9 +169,20 @@ module ActionPack ['/news.rss', [{ :controller => 'news', :action => 'index', :format => 'rss' }]], ].each_with_index do |(url, params), i| - define_method("test_#{url.gsub(/\W/, '_')}_#{i}") do - assert_equal url, url_for(@routes, *params), params.inspect - end + if params.length > 1 + hash, path_params, route = *params + hash[:only_path] = true + + define_method("test_#{url.gsub(/\W/, '_')}_#{i}") do + get URI('http://test.host' + route.to_s) + assert_equal path_params, controller.request.path_parameters + assert_equal url, controller.url_for(hash), params.inspect + end + else + define_method("test_#{url.gsub(/\W/, '_')}_#{i}") do + assert_equal url, url_for(@routes, params.first), params.inspect + end + end end end end diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb index d3fc7128e9..0ffa2d2a03 100644 --- a/actionpack/test/controller/url_for_test.rb +++ b/actionpack/test/controller/url_for_test.rb @@ -2,7 +2,6 @@ require 'abstract_unit' module AbstractController module Testing - class UrlForTest < ActionController::TestCase class W include ActionDispatch::Routing::RouteSet.new.tap { |r| r.draw { get ':controller(/:action(/:id(.:format)))' } }.url_helpers @@ -12,6 +11,25 @@ module AbstractController W.default_url_options.clear end + def test_nested_optional + klass = Class.new { + include ActionDispatch::Routing::RouteSet.new.tap { |r| + r.draw { + get "/foo/(:bar/(:baz))/:zot", :as => 'fun', + :controller => :articles, + :action => :index + } + }.url_helpers + self.default_url_options[:host] = 'example.com' + } + + path = klass.new.fun_path({:controller => :articles, + :baz => "baz", + :zot => "zot"}) + # :bar key isn't provided + assert_equal '/foo/zot', path + end + def add_host! W.default_url_options[:host] = 'www.basecamphq.com' end @@ -36,6 +54,20 @@ module AbstractController ) end + def test_nil_anchor + assert_equal( + '/c/a', + W.new.url_for(only_path: true, controller: 'c', action: 'a', anchor: nil) + ) + end + + def test_false_anchor + assert_equal( + '/c/a', + W.new.url_for(only_path: true, controller: 'c', action: 'a', anchor: false) + ) + end + def test_anchor_should_call_to_param assert_equal('/c/a#anchor', W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('anchor')) @@ -76,7 +108,7 @@ module AbstractController end def test_subdomain_may_be_object - model = mock(:to_param => 'api') + model = Class.new { def self.to_param; 'api'; end } add_host! assert_equal('http://api.basecamphq.com/c/a/i', W.new.url_for(:subdomain => model, :controller => 'c', :action => 'a', :id => 'i') @@ -90,6 +122,13 @@ module AbstractController ) end + def test_subdomain_may_be_removed_with_blank_string + W.default_url_options[:host] = 'api.basecamphq.com' + assert_equal('http://basecamphq.com/c/a/i', + W.new.url_for(:subdomain => '', :controller => 'c', :action => 'a', :id => 'i') + ) + end + def test_multiple_subdomains_may_be_removed W.default_url_options[:host] = 'mobile.www.api.basecamphq.com' assert_equal('http://basecamphq.com/c/a/i', @@ -163,6 +202,18 @@ module AbstractController ) end + def test_without_protocol_and_with_port + add_host! + add_port! + + assert_equal('//www.basecamphq.com:3000/c/a/i', + W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => '//') + ) + assert_equal('//www.basecamphq.com:3000/c/a/i', + W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => false) + ) + end + def test_trailing_slash add_host! options = {:controller => 'foo', :trailing_slash => true, :action => 'bar', :id => '33'} @@ -193,14 +244,11 @@ module AbstractController def test_trailing_slash_with_params url = W.new.url_for(:trailing_slash => true, :only_path => true, :controller => 'cont', :action => 'act', :p1 => 'cafe', :p2 => 'link') params = extract_params(url) - assert_equal params[0], { :p1 => 'cafe' }.to_query - assert_equal params[1], { :p2 => 'link' }.to_query + assert_equal({p1: 'cafe'}.to_query, params[0]) + assert_equal({p2: 'link'}.to_query, params[1]) end def test_relative_url_root_is_respected - # ROUTES TODO: Tests should not have to pass :relative_url_root directly. This - # should probably come from routes. - add_host! assert_equal('https://www.basecamphq.com/subdir/c/a/i', W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https', :script_name => '/subdir') @@ -242,6 +290,13 @@ module AbstractController end end + def test_using_nil_script_name_properly_concats_with_original_script_name + add_host! + assert_equal('https://www.basecamphq.com/subdir/c/a/i', + W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https', :script_name => nil, :original_script_name => '/subdir') + ) + end + def test_only_path with_routing do |set| set.draw do @@ -252,12 +307,12 @@ module AbstractController # We need to create a new class in order to install the new named route. kls = Class.new { include set.url_helpers } controller = kls.new - assert controller.respond_to?(:home_url) + assert_respond_to controller, :home_url assert_equal '/brave/new/world', - controller.send(:url_for, :controller => 'brave', :action => 'new', :id => 'world', :only_path => true) + controller.url_for(:controller => 'brave', :action => 'new', :id => 'world', :only_path => true) - assert_equal("/home/sweet/home/alabama", controller.send(:home_url, :user => 'alabama', :host => 'unused', :only_path => true)) - assert_equal("/home/sweet/home/alabama", controller.send(:home_path, 'alabama')) + assert_equal("/home/sweet/home/alabama", controller.home_path(:user => 'alabama', :host => 'unused')) + assert_equal("/home/sweet/home/alabama", controller.home_path('alabama')) end end @@ -270,40 +325,40 @@ module AbstractController def test_two_parameters url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :p1 => 'X1', :p2 => 'Y2') params = extract_params(url) - assert_equal params[0], { :p1 => 'X1' }.to_query - assert_equal params[1], { :p2 => 'Y2' }.to_query + assert_equal({p1: 'X1'}.to_query, params[0]) + assert_equal({p2: 'Y2'}.to_query, params[1]) end def test_hash_parameter url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => {:name => 'Bob', :category => 'prof'}) params = extract_params(url) - assert_equal params[0], { 'query[category]' => 'prof' }.to_query - assert_equal params[1], { 'query[name]' => 'Bob' }.to_query + assert_equal({'query[category]' => 'prof'}.to_query, params[0]) + assert_equal({'query[name]' => 'Bob'}.to_query, params[1]) end def test_array_parameter url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => ['Bob', 'prof']) params = extract_params(url) - assert_equal params[0], { 'query[]' => 'Bob' }.to_query - assert_equal params[1], { 'query[]' => 'prof' }.to_query + assert_equal({'query[]' => 'Bob'}.to_query, params[0]) + assert_equal({'query[]' => 'prof'}.to_query, params[1]) end def test_hash_recursive_parameters url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => {:person => {:name => 'Bob', :position => 'prof'}, :hobby => 'piercing'}) params = extract_params(url) - assert_equal params[0], { 'query[hobby]' => 'piercing' }.to_query - assert_equal params[1], { 'query[person][name]' => 'Bob' }.to_query - assert_equal params[2], { 'query[person][position]' => 'prof' }.to_query + assert_equal({'query[hobby]' => 'piercing'}.to_query, params[0]) + assert_equal({'query[person][name]' => 'Bob' }.to_query, params[1]) + assert_equal({'query[person][position]' => 'prof' }.to_query, params[2]) end def test_hash_recursive_and_array_parameters url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :id => 101, :query => {:person => {:name => 'Bob', :position => ['prof', 'art director']}, :hobby => 'piercing'}) assert_match(%r(^/c/a/101), url) params = extract_params(url) - assert_equal params[0], { 'query[hobby]' => 'piercing' }.to_query - assert_equal params[1], { 'query[person][name]' => 'Bob' }.to_query - assert_equal params[2], { 'query[person][position][]' => 'art director' }.to_query - assert_equal params[3], { 'query[person][position][]' => 'prof' }.to_query + assert_equal({'query[hobby]' => 'piercing' }.to_query, params[0]) + assert_equal({'query[person][name]' => 'Bob' }.to_query, params[1]) + assert_equal({'query[person][position][]' => 'art director'}.to_query, params[2]) + assert_equal({'query[person][position][]' => 'prof' }.to_query, params[3]) end def test_path_generation_for_symbol_parameter_keys @@ -350,10 +405,10 @@ module AbstractController def test_with_hash_with_indifferent_access W.default_url_options[:controller] = 'd' W.default_url_options[:only_path] = false - assert_equal("/c", W.new.url_for(HashWithIndifferentAccess.new('controller' => 'c', 'only_path' => true))) + assert_equal("/c", W.new.url_for(ActiveSupport::HashWithIndifferentAccess.new('controller' => 'c', 'only_path' => true))) W.default_url_options[:action] = 'b' - assert_equal("/c/a", W.new.url_for(HashWithIndifferentAccess.new('controller' => 'c', 'action' => 'a', 'only_path' => true))) + assert_equal("/c/a", W.new.url_for(ActiveSupport::HashWithIndifferentAccess.new('controller' => 'c', 'action' => 'a', 'only_path' => true))) end def test_url_params_with_nil_to_param_are_not_in_url @@ -364,6 +419,24 @@ module AbstractController assert_equal("/c/a?show=false", W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :show => false)) end + def test_url_generation_with_array_and_hash + with_routing do |set| + set.draw do + namespace :admin do + resources :posts + end + end + + kls = Class.new { include set.url_helpers } + kls.default_url_options[:host] = 'www.basecamphq.com' + + controller = kls.new + assert_equal("http://www.basecamphq.com/admin/posts/new?param=value", + controller.send(:url_for, [:new, :admin, :post, { param: 'value' }]) + ) + end + end + private def extract_params(url) url.split('?', 2).last.split('&').sort diff --git a/actionpack/test/controller/view_paths_test.rb b/actionpack/test/controller/view_paths_test.rb deleted file mode 100644 index c6e7a523b9..0000000000 --- a/actionpack/test/controller/view_paths_test.rb +++ /dev/null @@ -1,174 +0,0 @@ -require 'abstract_unit' - -class ViewLoadPathsTest < ActionController::TestCase - class TestController < ActionController::Base - def self.controller_path() "test" end - - before_action :add_view_path, only: :hello_world_at_request_time - - def hello_world() end - def hello_world_at_request_time() render(:action => 'hello_world') end - - private - def add_view_path - prepend_view_path "#{FIXTURE_LOAD_PATH}/override" - end - end - - module Test - class SubController < ActionController::Base - layout 'test/sub' - def hello_world; render(:template => 'test/hello_world'); end - end - end - - def setup - @request = ActionController::TestRequest.new - @response = ActionController::TestResponse.new - @controller = TestController.new - @paths = TestController.view_paths - end - - def teardown - TestController.view_paths = @paths - end - - def expand(array) - array.map {|x| File.expand_path(x.to_s)} - end - - def assert_paths(*paths) - controller = paths.first.is_a?(Class) ? paths.shift : @controller - assert_equal expand(paths), controller.view_paths.map { |p| p.to_s } - end - - def test_template_load_path_was_set_correctly - assert_paths FIXTURE_LOAD_PATH - end - - def test_controller_appends_view_path_correctly - @controller.append_view_path 'foo' - assert_paths(FIXTURE_LOAD_PATH, "foo") - - @controller.append_view_path(%w(bar baz)) - assert_paths(FIXTURE_LOAD_PATH, "foo", "bar", "baz") - - @controller.append_view_path(FIXTURE_LOAD_PATH) - assert_paths(FIXTURE_LOAD_PATH, "foo", "bar", "baz", FIXTURE_LOAD_PATH) - end - - def test_controller_prepends_view_path_correctly - @controller.prepend_view_path 'baz' - assert_paths("baz", FIXTURE_LOAD_PATH) - - @controller.prepend_view_path(%w(foo bar)) - assert_paths "foo", "bar", "baz", FIXTURE_LOAD_PATH - - @controller.prepend_view_path(FIXTURE_LOAD_PATH) - assert_paths FIXTURE_LOAD_PATH, "foo", "bar", "baz", FIXTURE_LOAD_PATH - end - - def test_template_appends_view_path_correctly - @controller.instance_variable_set :@template, ActionView::Base.new(TestController.view_paths, {}, @controller) - class_view_paths = TestController.view_paths - - @controller.append_view_path 'foo' - assert_paths FIXTURE_LOAD_PATH, "foo" - - @controller.append_view_path(%w(bar baz)) - assert_paths FIXTURE_LOAD_PATH, "foo", "bar", "baz" - assert_paths TestController, *class_view_paths - end - - def test_template_prepends_view_path_correctly - @controller.instance_variable_set :@template, ActionView::Base.new(TestController.view_paths, {}, @controller) - class_view_paths = TestController.view_paths - - @controller.prepend_view_path 'baz' - assert_paths "baz", FIXTURE_LOAD_PATH - - @controller.prepend_view_path(%w(foo bar)) - assert_paths "foo", "bar", "baz", FIXTURE_LOAD_PATH - assert_paths TestController, *class_view_paths - end - - def test_view_paths - get :hello_world - assert_response :success - assert_equal "Hello world!", @response.body - end - - def test_view_paths_override - TestController.prepend_view_path "#{FIXTURE_LOAD_PATH}/override" - get :hello_world - assert_response :success - assert_equal "Hello overridden world!", @response.body - end - - def test_view_paths_override_for_layouts_in_controllers_with_a_module - @controller = Test::SubController.new - Test::SubController.view_paths = [ "#{FIXTURE_LOAD_PATH}/override", FIXTURE_LOAD_PATH, "#{FIXTURE_LOAD_PATH}/override2" ] - get :hello_world - assert_response :success - assert_equal "layout: Hello overridden world!", @response.body - end - - def test_view_paths_override_at_request_time - get :hello_world_at_request_time - assert_response :success - assert_equal "Hello overridden world!", @response.body - end - - def test_decorate_view_paths_with_custom_resolver - decorator_class = Class.new(ActionView::PathResolver) do - def initialize(path_set) - @path_set = path_set - end - - def find_all(*args) - @path_set.find_all(*args).collect do |template| - ::ActionView::Template.new( - "Decorated body", - template.identifier, - template.handler, - { - :virtual_path => template.virtual_path, - :format => template.formats - } - ) - end - end - end - - decorator = decorator_class.new(TestController.view_paths) - TestController.view_paths = ActionView::PathSet.new.push(decorator) - - get :hello_world - assert_response :success - assert_equal "Decorated body", @response.body - end - - def test_inheritance - original_load_paths = ActionController::Base.view_paths - - self.class.class_eval %{ - class A < ActionController::Base; end - class B < A; end - class C < ActionController::Base; end - } - - A.view_paths = ['a/path'] - - assert_paths A, "a/path" - assert_paths A, *B.view_paths - assert_paths C, *original_load_paths - - C.view_paths = [] - assert_nothing_raised { C.append_view_path 'c/path' } - assert_paths C, "c/path" - end - - def test_lookup_context_accessor - assert_equal ["test"], TestController.new.lookup_context.prefixes - end -end diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb index c0b9833603..2b109ff19e 100644 --- a/actionpack/test/controller/webservice_test.rb +++ b/actionpack/test/controller/webservice_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/json/decoding' class WebServiceTest < ActionDispatch::IntegrationTest class TestController < ActionController::Base @@ -32,211 +33,62 @@ class WebServiceTest < ActionDispatch::IntegrationTest end end - def test_post_xml + def test_post_json with_test_route_set do - post "/", '<entry attributed="true"><summary>content...</summary></entry>', - {'CONTENT_TYPE' => 'application/xml'} + post "/", '{"entry":{"summary":"content..."}}', 'CONTENT_TYPE' => 'application/json' assert_equal 'entry', @controller.response.body assert @controller.params.has_key?(:entry) assert_equal 'content...', @controller.params["entry"]['summary'] - assert_equal 'true', @controller.params["entry"]['attributed'] end end - def test_put_xml + def test_put_json with_test_route_set do - put "/", '<entry attributed="true"><summary>content...</summary></entry>', - {'CONTENT_TYPE' => 'application/xml'} + put "/", '{"entry":{"summary":"content..."}}', 'CONTENT_TYPE' => 'application/json' assert_equal 'entry', @controller.response.body assert @controller.params.has_key?(:entry) assert_equal 'content...', @controller.params["entry"]['summary'] - assert_equal 'true', @controller.params["entry"]['attributed'] end end - def test_put_xml_using_a_type_node + def test_register_and_use_json_simple with_test_route_set do - put "/", '<type attributed="true"><summary>content...</summary></type>', - {'CONTENT_TYPE' => 'application/xml'} - - assert_equal 'type', @controller.response.body - assert @controller.params.has_key?(:type) - assert_equal 'content...', @controller.params["type"]['summary'] - assert_equal 'true', @controller.params["type"]['attributed'] - end - end - - def test_put_xml_using_a_type_node_and_attribute - with_test_route_set do - put "/", '<type attributed="true"><summary type="boolean">false</summary></type>', - {'CONTENT_TYPE' => 'application/xml'} - - assert_equal 'type', @controller.response.body - assert @controller.params.has_key?(:type) - assert_equal false, @controller.params["type"]['summary'] - assert_equal 'true', @controller.params["type"]['attributed'] - end - end - - def test_post_xml_using_a_type_node - with_test_route_set do - post "/", '<font attributed="true"><type>arial</type></font>', - {'CONTENT_TYPE' => 'application/xml'} - - assert_equal 'font', @controller.response.body - assert @controller.params.has_key?(:font) - assert_equal 'arial', @controller.params['font']['type'] - assert_equal 'true', @controller.params["font"]['attributed'] - end - end - - def test_post_xml_using_a_root_node_named_type - with_test_route_set do - post "/", '<type type="integer">33</type>', - {'CONTENT_TYPE' => 'application/xml'} - - assert @controller.params.has_key?(:type) - assert_equal 33, @controller.params['type'] - end - end - - def test_post_xml_using_an_attributted_node_named_type - with_test_route_set do - with_params_parsers Mime::XML => Proc.new { |data| Hash.from_xml(data)['request'].with_indifferent_access } do - post "/", '<request><type type="string">Arial,12</type><z>3</z></request>', - {'CONTENT_TYPE' => 'application/xml'} - - assert_equal 'type, z', @controller.response.body - assert @controller.params.has_key?(:type) - assert_equal 'Arial,12', @controller.params['type'], @controller.params.inspect - assert_equal '3', @controller.params['z'], @controller.params.inspect - end - end - end - - def test_register_and_use_yaml - with_test_route_set do - with_params_parsers Mime::YAML => Proc.new { |d| YAML.load(d) } do - post "/", {"entry" => "loaded from yaml"}.to_yaml, - {'CONTENT_TYPE' => 'application/x-yaml'} - - assert_equal 'entry', @controller.response.body - assert @controller.params.has_key?(:entry) - assert_equal 'loaded from yaml', @controller.params["entry"] - end - end - end - - def test_register_and_use_yaml_as_symbol - with_test_route_set do - with_params_parsers Mime::YAML => :yaml do - post "/", {"entry" => "loaded from yaml"}.to_yaml, - {'CONTENT_TYPE' => 'application/x-yaml'} - - assert_equal 'entry', @controller.response.body - assert @controller.params.has_key?(:entry) - assert_equal 'loaded from yaml', @controller.params["entry"] - end - end - end - - def test_register_and_use_xml_simple - with_test_route_set do - with_params_parsers Mime::XML => Proc.new { |data| Hash.from_xml(data)['request'].with_indifferent_access } do - post "/", '<request><summary>content...</summary><title>SimpleXml</title></request>', - {'CONTENT_TYPE' => 'application/xml'} + with_params_parsers Mime::JSON => Proc.new { |data| ActiveSupport::JSON.decode(data)['request'].with_indifferent_access } do + post "/", '{"request":{"summary":"content...","title":"JSON"}}', + 'CONTENT_TYPE' => 'application/json' assert_equal 'summary, title', @controller.response.body assert @controller.params.has_key?(:summary) assert @controller.params.has_key?(:title) assert_equal 'content...', @controller.params["summary"] - assert_equal 'SimpleXml', @controller.params["title"] + assert_equal 'JSON', @controller.params["title"] end end end - def test_use_xml_ximple_with_empty_request + def test_use_json_with_empty_request with_test_route_set do - assert_nothing_raised { post "/", "", {'CONTENT_TYPE' => 'application/xml'} } + assert_nothing_raised { post "/", "", 'CONTENT_TYPE' => 'application/json' } assert_equal '', @controller.response.body end end - def test_dasherized_keys_as_xml - with_test_route_set do - post "/?full=1", "<first-key>\n<sub-key>...</sub-key>\n</first-key>", - {'CONTENT_TYPE' => 'application/xml'} - assert_equal 'action, controller, first_key(sub_key), full', @controller.response.body - assert_equal "...", @controller.params[:first_key][:sub_key] - end - end - - def test_typecast_as_xml + def test_dasherized_keys_as_json with_test_route_set do - xml = <<-XML - <data> - <a type="integer">15</a> - <b type="boolean">false</b> - <c type="boolean">true</c> - <d type="date">2005-03-17</d> - <e type="datetime">2005-03-17T21:41:07Z</e> - <f>unparsed</f> - <g type="integer">1</g> - <g>hello</g> - <g type="date">1974-07-25</g> - </data> - XML - post "/", xml, {'CONTENT_TYPE' => 'application/xml'} - - params = @controller.params - assert_equal 15, params[:data][:a] - assert_equal false, params[:data][:b] - assert_equal true, params[:data][:c] - assert_equal Date.new(2005,3,17), params[:data][:d] - assert_equal Time.utc(2005,3,17,21,41,7), params[:data][:e] - assert_equal "unparsed", params[:data][:f] - assert_equal [1, "hello", Date.new(1974,7,25)], params[:data][:g] - end - end - - def test_entities_unescaped_as_xml_simple - with_test_route_set do - xml = <<-XML - <data><foo "bar's" & friends></data> - XML - post "/", xml, {'CONTENT_TYPE' => 'application/xml'} - assert_equal %(<foo "bar's" & friends>), @controller.params[:data] + post "/?full=1", '{"first-key":{"sub-key":"..."}}', 'CONTENT_TYPE' => 'application/json' + assert_equal 'action, controller, first-key(sub-key), full', @controller.response.body + assert_equal "...", @controller.params['first-key']['sub-key'] end end - def test_typecast_as_yaml + def test_parsing_json_doesnot_rescue_exception with_test_route_set do - with_params_parsers Mime::YAML => :yaml do - yaml = (<<-YAML).strip - --- - data: - a: 15 - b: false - c: true - d: 2005-03-17 - e: 2005-03-17T21:41:07Z - f: unparsed - g: - - 1 - - hello - - 1974-07-25 - YAML - post "/", yaml, {'CONTENT_TYPE' => 'application/x-yaml'} - params = @controller.params - assert_equal 15, params[:data][:a] - assert_equal false, params[:data][:b] - assert_equal true, params[:data][:c] - assert_equal Date.new(2005,3,17), params[:data][:d] - assert_equal Time.utc(2005,3,17,21,41,7), params[:data][:e] - assert_equal "unparsed", params[:data][:f] - assert_equal [1, "hello", Date.new(1974,7,25)], params[:data][:g] + with_params_parsers Mime::JSON => Proc.new { |data| raise Interrupt } do + assert_raises(Interrupt) do + post "/", '{"title":"JSON"}}', 'CONTENT_TYPE' => 'application/json' + end end end end |