diff options
Diffstat (limited to 'actionview/test')
64 files changed, 1703 insertions, 1484 deletions
diff --git a/actionview/test/abstract_unit.rb b/actionview/test/abstract_unit.rb index 9eae3a4fbd..4aa56f60f7 100644 --- a/actionview/test/abstract_unit.rb +++ b/actionview/test/abstract_unit.rb @@ -16,11 +16,9 @@ silence_warnings do end require 'active_support/testing/autorun' -require 'abstract_controller' require 'action_controller' require 'action_view' require 'action_view/testing/resolvers' -require 'action_dispatch' require 'active_support/dependencies' require 'active_model' require 'active_record' @@ -48,7 +46,7 @@ I18n.enforce_available_locales = false # Register danish language for testing I18n.backend.store_translations 'da', {} I18n.backend.store_translations 'pt-BR', {} -ORIGINAL_LOCALES = I18n.available_locales.map {|locale| locale.to_s }.sort +ORIGINAL_LOCALES = I18n.available_locales.map(&:to_s).sort FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures') FIXTURES = Pathname.new(FIXTURE_LOAD_PATH) @@ -274,7 +272,6 @@ ActionView::RoutingUrlFor.send(:include, ActionDispatch::Routing::UrlFor) module ActionController class Base - include ActionController::Testing # This stub emulates the Railtie including the URL helpers from a Rails application include SharedTestRoutes.url_helpers include SharedTestRoutes.mounted_helpers @@ -339,3 +336,10 @@ end def jruby_skip(message = '') skip message if defined?(JRUBY_VERSION) end + +require 'mocha/setup' # FIXME: stop using mocha + +# FIXME: we have tests that depend on run order, we should fix that and +# remove this method call. +require 'active_support/test_case' +ActiveSupport::TestCase.test_order = :sorted diff --git a/actionview/test/actionpack/abstract/abstract_controller_test.rb b/actionview/test/actionpack/abstract/abstract_controller_test.rb index 40d3b17131..490932fef0 100644 --- a/actionview/test/actionpack/abstract/abstract_controller_test.rb +++ b/actionview/test/actionpack/abstract/abstract_controller_test.rb @@ -150,6 +150,38 @@ module AbstractController end end + class OverridingLocalPrefixes < AbstractController::Base + include AbstractController::Rendering + include ActionView::Rendering + append_view_path File.expand_path(File.join(File.dirname(__FILE__), "views")) + + def index + render + end + + def self.local_prefixes + # this would usually return "abstract_controller/testing/overriding_local_prefixes" + super + ["abstract_controller/testing/me3"] + end + + class Inheriting < self + end + end + + class OverridingLocalPrefixesTest < ActiveSupport::TestCase + test "overriding .local_prefixes adds prefix" do + @controller = OverridingLocalPrefixes.new + @controller.process(:index) + assert_equal "Hello from me3/index.erb", @controller.response_body + end + + test ".local_prefixes is inherited" do + @controller = OverridingLocalPrefixes::Inheriting.new + @controller.process(:index) + assert_equal "Hello from me3/index.erb", @controller.response_body + end + end + # Test rendering with layouts # ==== # self._layout is used when defined diff --git a/actionview/test/actionpack/abstract/render_test.rb b/actionview/test/actionpack/abstract/render_test.rb index f9d8c916d9..d09f91c1e2 100644 --- a/actionview/test/actionpack/abstract/render_test.rb +++ b/actionview/test/actionpack/abstract/render_test.rb @@ -60,42 +60,42 @@ module AbstractController end def test_render_template - @controller.process(:template) + assert_equal "With Template", @controller.process(:template) assert_equal "With Template", @controller.response_body end def test_render_file - @controller.process(:file) + assert_equal "With File", @controller.process(:file) assert_equal "With File", @controller.response_body end def test_render_inline - @controller.process(:inline) + assert_equal "With Inline", @controller.process(:inline) assert_equal "With Inline", @controller.response_body end def test_render_text - @controller.process(:text) + assert_equal "With Text", @controller.process(:text) assert_equal "With Text", @controller.response_body end def test_render_default - @controller.process(:default) + assert_equal "With Default", @controller.process(:default) assert_equal "With Default", @controller.response_body end def test_render_string - @controller.process(:string) + assert_equal "With String", @controller.process(:string) assert_equal "With String", @controller.response_body end def test_render_symbol - @controller.process(:symbol) + assert_equal "With Symbol", @controller.process(:symbol) assert_equal "With Symbol", @controller.response_body end def test_render_string_with_path - @controller.process(:string_with_path) + assert_equal "With String With Path", @controller.process(:string_with_path) assert_equal "With String With Path", @controller.response_body end end diff --git a/actionview/test/actionpack/controller/layout_test.rb b/actionview/test/actionpack/controller/layout_test.rb index b44f57a23e..7b8a83e2fe 100644 --- a/actionview/test/actionpack/controller/layout_test.rb +++ b/actionview/test/actionpack/controller/layout_test.rb @@ -1,14 +1,10 @@ require 'abstract_unit' -require 'rbconfig' require 'active_support/core_ext/array/extract_options' # 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/actionpack/layout_tests/' ] class LayoutTest < ActionController::Base @@ -17,6 +13,15 @@ class LayoutTest < ActionController::Base self.view_paths = ActionController::Base.view_paths.dup end +module TemplateHandlerHelper + def with_template_handler(*extensions, handler) + ActionView::Template.register_template_handler(*extensions, handler) + yield + ensure + ActionView::Template.unregister_template_handler(*extensions) + end +end + # Restore view_paths to previous value ActionController::Base.view_paths = old_load_paths @@ -39,6 +44,8 @@ class MultipleExtensions < LayoutTest end class LayoutAutoDiscoveryTest < ActionController::TestCase + include TemplateHandlerHelper + def setup super @request.host = "www.nextangle.com" @@ -57,10 +64,12 @@ class LayoutAutoDiscoveryTest < ActionController::TestCase 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 + with_template_handler :mab, lambda { |template| template.source.inspect } do + @controller = ThirdPartyTemplateLibraryController.new + get :hello + assert_response :success + assert_equal 'layouts/third_party_template_library.mab', @response.body + end end def test_namespaced_controllers_auto_detect_layouts1 @@ -135,6 +144,7 @@ end class LayoutSetInResponseTest < ActionController::TestCase include ActionView::Template::Handlers + include TemplateHandlerHelper def test_layout_set_when_using_default_layout @controller = DefaultLayoutController.new @@ -191,9 +201,11 @@ class LayoutSetInResponseTest < ActionController::TestCase end def test_layout_set_when_using_render - @controller = SetsLayoutInRenderController.new - get :hello - assert_template :layout => "layouts/third_party_template_library" + with_template_handler :mab, lambda { |template| template.source.inspect } do + @controller = SetsLayoutInRenderController.new + get :hello + assert_template :layout => "layouts/third_party_template_library" + end end def test_layout_is_not_set_when_none_rendered diff --git a/actionview/test/actionpack/controller/render_test.rb b/actionview/test/actionpack/controller/render_test.rb index 45b8049b83..563caee8a2 100644 --- a/actionview/test/actionpack/controller/render_test.rb +++ b/actionview/test/actionpack/controller/render_test.rb @@ -91,17 +91,17 @@ class TestController < ApplicationController # :ported: def render_hello_world - render :template => "test/hello_world" + render "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" + render "test/hello_world" end # :ported: compatibility def render_hello_world_with_forward_slash - render :template => "/test/hello_world" + render "/test/hello_world" end # :ported: @@ -111,7 +111,7 @@ class TestController < ApplicationController # :deprecated: def render_template_in_top_directory_with_slash - render :template => '/shared' + render '/shared' end # :ported: @@ -160,13 +160,6 @@ class TestController < ApplicationController 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' @@ -194,7 +187,7 @@ class TestController < ApplicationController 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'} + render file: path, :locals => {:secret => 'in the sauce'} end def accessing_request_in_template @@ -536,6 +529,14 @@ class TestController < ApplicationController render :partial => "customer_with_var", :collection => [ Customer.new("david"), Customer.new("mary") ], :as => :customer end + def partial_collection_with_iteration + render partial: "customer_iteration", collection: [ Customer.new("david"), Customer.new("mary"), Customer.new('christine') ] + end + + def partial_collection_with_as_and_iteration + render partial: "customer_iteration_with_as", collection: [ Customer.new("david"), Customer.new("mary"), Customer.new('christine') ], as: :client + end + def partial_collection_with_counter render :partial => "customer_counter", :collection => [ Customer.new("david"), Customer.new("mary") ] end @@ -720,6 +721,11 @@ class RenderTest < ActionController::TestCase assert_equal "Elastica", @response.body end + def test_render_process + get :render_action_hello_world_as_string + assert_equal ["Hello world!"], @controller.process(:render_action_hello_world_as_string) + end + # :ported: def test_render_from_variable get :render_hello_world_from_variable @@ -781,12 +787,6 @@ class RenderTest < ActionController::TestCase 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 @@ -834,7 +834,7 @@ class RenderTest < ActionController::TestCase def test_render_text_with_nil get :render_text_with_nil assert_response 200 - assert_equal ' ', @response.body + assert_equal '', @response.body end # :ported: @@ -1022,7 +1022,7 @@ class RenderTest < ActionController::TestCase def test_rendering_nothing_on_layout get :rendering_nothing_on_layout - assert_equal " ", @response.body + assert_equal '', @response.body end def test_render_to_string_doesnt_break_assigns @@ -1232,6 +1232,16 @@ class RenderTest < ActionController::TestCase assert_equal "david david davidmary mary mary", @response.body end + def test_partial_collection_with_iteration + get :partial_collection_with_iteration + assert_equal "3-0: david-first3-1: mary3-2: christine-last", @response.body + end + + def test_partial_collection_with_as_and_iteration + get :partial_collection_with_as_and_iteration + assert_equal "3-0: david-first3-1: mary3-2: christine-last", @response.body + end + def test_partial_collection_with_counter get :partial_collection_with_counter assert_equal "david0mary1", @response.body @@ -1332,4 +1342,3 @@ class RenderTest < ActionController::TestCase 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 - diff --git a/actionview/test/actionpack/controller/view_paths_test.rb b/actionview/test/actionpack/controller/view_paths_test.rb index c6e7a523b9..7fba9ff8ff 100644 --- a/actionview/test/actionpack/controller/view_paths_test.rb +++ b/actionview/test/actionpack/controller/view_paths_test.rb @@ -39,7 +39,7 @@ class ViewLoadPathsTest < ActionController::TestCase 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 } + assert_equal expand(paths), controller.view_paths.map(&:to_s) end def test_template_load_path_was_set_correctly diff --git a/actionview/test/active_record_unit.rb b/actionview/test/active_record_unit.rb index 95fbb112c0..cca55c9af4 100644 --- a/actionview/test/active_record_unit.rb +++ b/actionview/test/active_record_unit.rb @@ -57,7 +57,7 @@ class ActiveRecordTestConnector end end - # Load actionpack sqlite tables + # Load actionpack sqlite3 tables def load_schema File.read(File.dirname(__FILE__) + "/fixtures/db_definitions/sqlite.sql").split(';').each do |sql| ActiveRecord::Base.connection.execute(sql) unless sql.blank? diff --git a/actionview/test/activerecord/controller_runtime_test.rb b/actionview/test/activerecord/controller_runtime_test.rb index 368bec1c70..469adff39a 100644 --- a/actionview/test/activerecord/controller_runtime_test.rb +++ b/actionview/test/activerecord/controller_runtime_test.rb @@ -8,8 +8,6 @@ ActionController::Base.send :include, ActiveRecord::Railties::ControllerRuntime class ControllerRuntimeLogSubscriberTest < ActionController::TestCase class LogSubscriberController < ActionController::Base - respond_to :html - def show render :inline => "<%= Project.all %>" end @@ -20,8 +18,8 @@ class ControllerRuntimeLogSubscriberTest < ActionController::TestCase def create ActiveRecord::LogSubscriber.runtime += 100 - project = Project.last - respond_with(project, location: url_for(action: :show)) + Project.last + redirect_to "/" end def redirect diff --git a/actionview/test/template/debug_helper_test.rb b/actionview/test/activerecord/debug_helper_test.rb index 42d06bd9ff..5609694cd5 100644 --- a/actionview/test/template/debug_helper_test.rb +++ b/actionview/test/activerecord/debug_helper_test.rb @@ -3,6 +3,6 @@ require 'active_record_unit' class DebugHelperTest < ActionView::TestCase def test_debug company = Company.new(name: "firebase") - assert_match " name: firebase", debug(company) + assert_match "name: firebase", debug(company) end end diff --git a/actionview/test/activerecord/form_helper_activerecord_test.rb b/actionview/test/activerecord/form_helper_activerecord_test.rb index 0a9628da8d..0a62f49f35 100644 --- a/actionview/test/activerecord/form_helper_activerecord_test.rb +++ b/actionview/test/activerecord/form_helper_activerecord_test.rb @@ -59,12 +59,13 @@ class FormHelperActiveRecordTest < ActionView::TestCase protected def hidden_fields(method = nil) - txt = %{<div style="display:none">} - txt << %{<input name="utf8" type="hidden" value="✓" />} + txt = %{<input name="utf8" type="hidden" value="✓" />} + if method && !%w(get post).include?(method.to_s) txt << %{<input name="_method" type="hidden" value="#{method}" />} end - txt << %{</div>} + + txt end def form_text(action = "/", id = nil, html_class = nil, remote = nil, multipart = nil, method = nil) @@ -88,4 +89,4 @@ class FormHelperActiveRecordTest < ActionView::TestCase form_text(action, id, html_class, remote, multipart, method) + hidden_fields(method) + contents + "</form>" end -end
\ No newline at end of file +end diff --git a/actionview/test/activerecord/polymorphic_routes_test.rb b/actionview/test/activerecord/polymorphic_routes_test.rb index afb714484b..8e1ed2776d 100644 --- a/actionview/test/activerecord/polymorphic_routes_test.rb +++ b/actionview/test/activerecord/polymorphic_routes_test.rb @@ -25,17 +25,19 @@ class Series < ActiveRecord::Base self.table_name = 'projects' end -class ModelDelegator < ActiveRecord::Base - self.table_name = 'projects' - +class ModelDelegator def to_model ModelDelegate.new end end class ModelDelegate - def self.model_name - ActiveModel::Name.new(self) + def persisted? + true + end + + def model_name + ActiveModel::Name.new(self.class) end def to_param @@ -74,25 +76,77 @@ class PolymorphicRoutesTest < ActionController::TestCase @blog_blog = Blog::Blog.new end + def assert_url(url, args) + host = self.class.default_url_options[:host] + + assert_equal url.sub(/http:\/\/#{host}/, ''), polymorphic_path(args) + assert_equal url, polymorphic_url(args) + assert_equal url, url_for(args) + end + + def test_string + with_test_routes do + # FIXME: why are these different? Symbol case passes through to + # `polymorphic_url`, but the String case doesn't. + assert_equal "http://example.com/projects", polymorphic_url("projects") + assert_equal "projects", url_for("projects") + end + end + + def test_string_with_options + with_test_routes do + assert_equal "http://example.com/projects?id=10", polymorphic_url("projects", :id => 10) + end + end + + def test_symbol + with_test_routes do + assert_url "http://example.com/projects", :projects + end + end + + def test_symbol_with_options + with_test_routes do + assert_equal "http://example.com/projects?id=10", polymorphic_url(:projects, :id => 10) + end + end + def test_passing_routes_proxy with_namespaced_routes(:blog) do proxy = ActionDispatch::Routing::RoutesProxy.new(_routes, self) @blog_post.save - assert_equal "http://example.com/posts/#{@blog_post.id}", polymorphic_url([proxy, @blog_post]) + assert_url "http://example.com/posts/#{@blog_post.id}", [proxy, @blog_post] end end def test_namespaced_model with_namespaced_routes(:blog) do @blog_post.save - assert_equal "http://example.com/posts/#{@blog_post.id}", polymorphic_url(@blog_post) + assert_url "http://example.com/posts/#{@blog_post.id}", @blog_post end end def test_namespaced_model_with_name_the_same_as_namespace with_namespaced_routes(:blog) do @blog_blog.save - assert_equal "http://example.com/blogs/#{@blog_blog.id}", polymorphic_url(@blog_blog) + assert_url "http://example.com/blogs/#{@blog_blog.id}", @blog_blog + end + end + + def test_polymorphic_url_with_2_objects + with_namespaced_routes(:blog) do + @blog_blog.save + @blog_post.save + assert_equal "http://example.com/blogs/#{@blog_blog.id}/posts/#{@blog_post.id}", polymorphic_url([@blog_blog, @blog_post]) + end + end + + def test_polymorphic_url_with_3_objects + with_namespaced_routes(:blog) do + @blog_blog.save + @blog_post.save + @fax.save + assert_equal "http://example.com/blogs/#{@blog_blog.id}/posts/#{@blog_post.id}/faxes/#{@fax.id}", polymorphic_url([@blog_blog, @blog_post, @fax]) end end @@ -100,41 +154,121 @@ class PolymorphicRoutesTest < ActionController::TestCase with_namespaced_routes(:blog) do @blog_post.save @blog_blog.save - assert_equal "http://example.com/blogs/#{@blog_blog.id}/posts/#{@blog_post.id}", polymorphic_url([@blog_blog, @blog_post]) + assert_url "http://example.com/blogs/#{@blog_blog.id}/posts/#{@blog_post.id}", [@blog_blog, @blog_post] end end def test_with_nil with_test_routes do - assert_raise ArgumentError, "Nil location provided. Can't build URI." do + exception = assert_raise ArgumentError do polymorphic_url(nil) end + assert_equal "Nil location provided. Can't build URI.", exception.message + end + end + + def test_with_empty_list + with_test_routes do + exception = assert_raise ArgumentError do + polymorphic_url([]) + end + assert_equal "Nil location provided. Can't build URI.", exception.message + end + end + + def test_with_nil_id + with_test_routes do + exception = assert_raise ArgumentError do + polymorphic_url({ :id => nil }) + end + assert_equal "Nil location provided. Can't build URI.", exception.message + end + end + + def test_with_entirely_nil_list + with_test_routes do + exception = assert_raise ArgumentError do + @series.save + polymorphic_url([nil, nil]) + end + assert_equal "Nil location provided. Can't build URI.", exception.message + end + end + + def test_with_nil_in_list_for_resource_that_could_be_top_level_or_nested + with_top_level_and_nested_routes do + @blog_post.save + assert_equal "http://example.com/posts/#{@blog_post.id}", polymorphic_url([nil, @blog_post]) + end + end + + def test_with_nil_in_list_does_not_generate_invalid_link + with_top_level_and_nested_routes do + exception = assert_raise NoMethodError do + @series.save + polymorphic_url([nil, @series]) + end + assert_match(/undefined method `series_url' for/, exception.message) end end def test_with_record with_test_routes do @project.save - assert_equal "http://example.com/projects/#{@project.id}", polymorphic_url(@project) + assert_url "http://example.com/projects/#{@project.id}", @project end end def test_with_class with_test_routes do - assert_equal "http://example.com/projects", polymorphic_url(@project.class) + assert_url "http://example.com/projects", @project.class + end + end + + def test_with_class_list_of_one + with_test_routes do + assert_url "http://example.com/projects", [@project.class] + end + end + + def test_class_with_options + with_test_routes do + assert_equal "http://example.com/projects?foo=bar", polymorphic_url(@project.class, { :foo => :bar }) + assert_equal "/projects?foo=bar", polymorphic_path(@project.class, { :foo => :bar }) end end def test_with_new_record with_test_routes do - assert_equal "http://example.com/projects", polymorphic_url(@project) + assert_url "http://example.com/projects", @project + end + end + + def test_new_record_arguments + params = nil + + with_test_routes do + extend Module.new { + define_method("projects_url") { |*args| + params = args + super(*args) + } + + define_method("projects_path") { |*args| + params = args + super(*args) + } + } + + assert_url "http://example.com/projects", @project + assert_equal [], params end end def test_with_destroyed_record with_test_routes do @project.destroy - assert_equal "http://example.com/projects", polymorphic_url(@project) + assert_url "http://example.com/projects", @project end end @@ -150,6 +284,15 @@ class PolymorphicRoutesTest < ActionController::TestCase end end + def test_regression_path_helper_prefixed_with_new_and_edit + with_test_routes do + assert_equal "/projects/new", new_polymorphic_path(@project) + + @project.save + assert_equal "/projects/#{@project.id}/edit", edit_polymorphic_path(@project) + end + end + def test_url_helper_prefixed_with_edit with_test_routes do @project.save @@ -196,14 +339,14 @@ class PolymorphicRoutesTest < ActionController::TestCase with_test_routes do @project.save @task.save - assert_equal "http://example.com/projects/#{@project.id}/tasks/#{@task.id}", polymorphic_url([@project, @task]) + assert_url "http://example.com/projects/#{@project.id}/tasks/#{@task.id}", [@project, @task] end end def test_with_nested_unsaved with_test_routes do @project.save - assert_equal "http://example.com/projects/#{@project.id}/tasks", polymorphic_url([@project, @task]) + assert_url "http://example.com/projects/#{@project.id}/tasks", [@project, @task] end end @@ -211,20 +354,20 @@ class PolymorphicRoutesTest < ActionController::TestCase with_test_routes do @project.save @task.destroy - assert_equal "http://example.com/projects/#{@project.id}/tasks", polymorphic_url([@project, @task]) + assert_url "http://example.com/projects/#{@project.id}/tasks", [@project, @task] end end def test_with_nested_class with_test_routes do @project.save - assert_equal "http://example.com/projects/#{@project.id}/tasks", polymorphic_url([@project, @task.class]) + assert_url "http://example.com/projects/#{@project.id}/tasks", [@project, @task.class] end end def test_class_with_array_and_namespace with_admin_test_routes do - assert_equal "http://example.com/admin/projects", polymorphic_url([:admin, @project.class]) + assert_url "http://example.com/admin/projects", [:admin, @project.class] end end @@ -236,14 +379,14 @@ class PolymorphicRoutesTest < ActionController::TestCase def test_unsaved_with_array_and_namespace with_admin_test_routes do - assert_equal "http://example.com/admin/projects", polymorphic_url([:admin, @project]) + assert_url "http://example.com/admin/projects", [:admin, @project] end end def test_nested_unsaved_with_array_and_namespace with_admin_test_routes do @project.save - assert_equal "http://example.com/admin/projects/#{@project.id}/tasks", polymorphic_url([:admin, @project, @task]) + assert_url "http://example.com/admin/projects/#{@project.id}/tasks", [:admin, @project, @task] end end @@ -251,7 +394,7 @@ class PolymorphicRoutesTest < ActionController::TestCase with_admin_test_routes do @project.save @task.save - assert_equal "http://example.com/admin/projects/#{@project.id}/tasks/#{@task.id}", polymorphic_url([:admin, @project, @task]) + assert_url "http://example.com/admin/projects/#{@project.id}/tasks/#{@task.id}", [:admin, @project, @task] end end @@ -260,14 +403,14 @@ class PolymorphicRoutesTest < ActionController::TestCase @project.save @task.save @step.save - assert_equal "http://example.com/admin/projects/#{@project.id}/site/tasks/#{@task.id}/steps/#{@step.id}", polymorphic_url([:admin, @project, :site, @task, @step]) + assert_url "http://example.com/admin/projects/#{@project.id}/site/tasks/#{@task.id}/steps/#{@step.id}", [:admin, @project, :site, @task, @step] end end def test_nesting_with_array_ending_in_singleton_resource with_test_routes do @project.save - assert_equal "http://example.com/projects/#{@project.id}/bid", polymorphic_url([@project, :bid]) + assert_url "http://example.com/projects/#{@project.id}/bid", [@project, :bid] end end @@ -275,7 +418,7 @@ class PolymorphicRoutesTest < ActionController::TestCase with_test_routes do @project.save @task.save - assert_equal "http://example.com/projects/#{@project.id}/bid/tasks/#{@task.id}", polymorphic_url([@project, :bid, @task]) + assert_url "http://example.com/projects/#{@project.id}/bid/tasks/#{@task.id}", [@project, :bid, @task] end end @@ -291,34 +434,40 @@ class PolymorphicRoutesTest < ActionController::TestCase with_admin_test_routes do @project.save @task.save - assert_equal "http://example.com/admin/projects/#{@project.id}/bid/tasks/#{@task.id}", polymorphic_url([:admin, @project, :bid, @task]) + assert_url "http://example.com/admin/projects/#{@project.id}/bid/tasks/#{@task.id}", [:admin, @project, :bid, @task] end end - def test_nesting_with_array_containing_nil + def test_nesting_with_array with_test_routes do @project.save - assert_equal "http://example.com/projects/#{@project.id}/bid", polymorphic_url([@project, nil, :bid]) + assert_url "http://example.com/projects/#{@project.id}/bid", [@project, :bid] end end def test_with_array_containing_single_object with_test_routes do @project.save - assert_equal "http://example.com/projects/#{@project.id}", polymorphic_url([nil, @project]) + assert_url "http://example.com/projects/#{@project.id}", [@project] end end def test_with_array_containing_single_name with_test_routes do @project.save - assert_equal "http://example.com/projects", polymorphic_url([:projects]) + assert_url "http://example.com/projects", [:projects] + end + end + + def test_with_array_containing_single_string_name + with_test_routes do + assert_url "http://example.com/projects", ["projects"] end end def test_with_array_containing_symbols with_test_routes do - assert_equal "http://example.com/series/new", polymorphic_url([:new, :series]) + assert_url "http://example.com/series/new", [:new, :series] end end @@ -353,26 +502,26 @@ class PolymorphicRoutesTest < ActionController::TestCase def test_with_irregular_plural_record with_test_routes do @tax.save - assert_equal "http://example.com/taxes/#{@tax.id}", polymorphic_url(@tax) + assert_url "http://example.com/taxes/#{@tax.id}", @tax end end def test_with_irregular_plural_class with_test_routes do - assert_equal "http://example.com/taxes", polymorphic_url(@tax.class) + assert_url "http://example.com/taxes", @tax.class end end def test_with_irregular_plural_new_record with_test_routes do - assert_equal "http://example.com/taxes", polymorphic_url(@tax) + assert_url "http://example.com/taxes", @tax end end def test_with_irregular_plural_destroyed_record with_test_routes do @tax.destroy - assert_equal "http://example.com/taxes", polymorphic_url(@tax) + assert_url "http://example.com/taxes", @tax end end @@ -406,7 +555,7 @@ class PolymorphicRoutesTest < ActionController::TestCase def test_with_nested_unsaved_irregular_plurals with_test_routes do @tax.save - assert_equal "http://example.com/taxes/#{@tax.id}/faxes", polymorphic_url([@tax, @fax]) + assert_url "http://example.com/taxes/#{@tax.id}/faxes", [@tax, @fax] end end @@ -418,34 +567,34 @@ class PolymorphicRoutesTest < ActionController::TestCase def test_class_with_irregular_plural_array_and_namespace with_admin_test_routes do - assert_equal "http://example.com/admin/taxes", polymorphic_url([:admin, @tax.class]) + assert_url "http://example.com/admin/taxes", [:admin, @tax.class] end end def test_unsaved_with_irregular_plural_array_and_namespace with_admin_test_routes do - assert_equal "http://example.com/admin/taxes", polymorphic_url([:admin, @tax]) + assert_url "http://example.com/admin/taxes", [:admin, @tax] end end def test_nesting_with_irregular_plurals_and_array_ending_in_singleton_resource with_test_routes do @tax.save - assert_equal "http://example.com/taxes/#{@tax.id}/bid", polymorphic_url([@tax, :bid]) + assert_url "http://example.com/taxes/#{@tax.id}/bid", [@tax, :bid] end end def test_with_array_containing_single_irregular_plural_object with_test_routes do @tax.save - assert_equal "http://example.com/taxes/#{@tax.id}", polymorphic_url([nil, @tax]) + assert_url "http://example.com/taxes/#{@tax.id}", [@tax] end end def test_with_array_containing_single_name_irregular_plural with_test_routes do @tax.save - assert_equal "http://example.com/taxes", polymorphic_url([:taxes]) + assert_url "http://example.com/taxes", [:taxes] end end @@ -453,15 +602,20 @@ class PolymorphicRoutesTest < ActionController::TestCase def test_uncountable_resource with_test_routes do @series.save - assert_equal "http://example.com/series/#{@series.id}", polymorphic_url(@series) - assert_equal "http://example.com/series", polymorphic_url(Series.new) + assert_url "http://example.com/series/#{@series.id}", @series + assert_url "http://example.com/series", Series.new + end + end + + def test_routing_to_a_model_delegate + with_test_routes do + assert_url "http://example.com/model_delegates/overridden", @delegator end end - def test_routing_a_to_model_delegate + def test_nested_routing_to_a_model_delegate with_test_routes do - @delegator.save - assert_equal "http://example.com/model_delegates/overridden", polymorphic_url(@delegator) + assert_url "http://example.com/foo/model_delegates/overridden", [:foo, @delegator] end end @@ -470,13 +624,15 @@ class PolymorphicRoutesTest < ActionController::TestCase set.draw do scope(:module => name) do resources :blogs do - resources :posts + resources :posts do + resources :faxes + end end resources :posts end end - self.class.send(:include, @routes.url_helpers) + extend @routes.url_helpers yield end end @@ -496,9 +652,27 @@ class PolymorphicRoutesTest < ActionController::TestCase end resources :series resources :model_delegates + namespace :foo do + resources :model_delegates + end end - self.class.send(:include, @routes.url_helpers) + extend @routes.url_helpers + yield + end + end + + def with_top_level_and_nested_routes(options = {}) + with_routing do |set| + set.draw do + resources :blogs do + resources :posts + resources :series + end + resources :posts + end + + extend @routes.url_helpers yield end end @@ -520,7 +694,7 @@ class PolymorphicRoutesTest < ActionController::TestCase end end - self.class.send(:include, @routes.url_helpers) + extend @routes.url_helpers yield end end @@ -539,8 +713,21 @@ class PolymorphicRoutesTest < ActionController::TestCase end end - self.class.send(:include, @routes.url_helpers) + extend @routes.url_helpers yield end end end + +class PolymorphicPathRoutesTest < PolymorphicRoutesTest + include ActionView::RoutingUrlFor + include ActionView::Context + + attr_accessor :controller + + def assert_url(url, args) + host = self.class.default_url_options[:host] + + assert_equal url.sub(/http:\/\/#{host}/, ''), url_for(args) + end +end diff --git a/actionview/test/fixtures/actionpack/test/_customer_iteration.erb b/actionview/test/fixtures/actionpack/test/_customer_iteration.erb new file mode 100644 index 0000000000..fb530b04a7 --- /dev/null +++ b/actionview/test/fixtures/actionpack/test/_customer_iteration.erb @@ -0,0 +1 @@ +<%= customer_iteration_iteration.size %>-<%= customer_iteration_iteration.index %>: <%= customer_iteration.name %><%= '-first' if customer_iteration_iteration.first? %><%= '-last' if customer_iteration_iteration.last? %>
\ No newline at end of file diff --git a/actionview/test/fixtures/actionpack/test/_customer_iteration_with_as.erb b/actionview/test/fixtures/actionpack/test/_customer_iteration_with_as.erb new file mode 100644 index 0000000000..57297d0564 --- /dev/null +++ b/actionview/test/fixtures/actionpack/test/_customer_iteration_with_as.erb @@ -0,0 +1 @@ +<%= client_iteration.size %>-<%= client_iteration.index %>: <%= client.name %><%= '-first' if client_iteration.first? %><%= '-last' if client_iteration.last? %>
\ No newline at end of file diff --git a/actionview/test/fixtures/blog_public/.gitignore b/actionview/test/fixtures/blog_public/.gitignore deleted file mode 100644 index 312e635ee6..0000000000 --- a/actionview/test/fixtures/blog_public/.gitignore +++ /dev/null @@ -1 +0,0 @@ -absolute/* diff --git a/actionview/test/fixtures/blog_public/blog.html b/actionview/test/fixtures/blog_public/blog.html deleted file mode 100644 index 79ad44c010..0000000000 --- a/actionview/test/fixtures/blog_public/blog.html +++ /dev/null @@ -1 +0,0 @@ -/blog/blog.html
\ No newline at end of file diff --git a/actionview/test/fixtures/blog_public/index.html b/actionview/test/fixtures/blog_public/index.html deleted file mode 100644 index 2de3825481..0000000000 --- a/actionview/test/fixtures/blog_public/index.html +++ /dev/null @@ -1 +0,0 @@ -/blog/index.html
\ No newline at end of file diff --git a/actionview/test/fixtures/blog_public/subdir/index.html b/actionview/test/fixtures/blog_public/subdir/index.html deleted file mode 100644 index 517bded335..0000000000 --- a/actionview/test/fixtures/blog_public/subdir/index.html +++ /dev/null @@ -1 +0,0 @@ -/blog/subdir/index.html
\ No newline at end of file diff --git a/actionview/test/fixtures/customers/_customer.xml.erb b/actionview/test/fixtures/customers/_customer.xml.erb new file mode 100644 index 0000000000..d3f1e0768f --- /dev/null +++ b/actionview/test/fixtures/customers/_customer.xml.erb @@ -0,0 +1 @@ +<greeting><%= greeting %></greeting><name><%= customer.name %></name>
\ No newline at end of file diff --git a/actionview/test/fixtures/digestor/messages/new.html+iphone.erb b/actionview/test/fixtures/digestor/messages/new.html+iphone.erb new file mode 100644 index 0000000000..791e1d36b4 --- /dev/null +++ b/actionview/test/fixtures/digestor/messages/new.html+iphone.erb @@ -0,0 +1,15 @@ +<%# Template Dependency: messages/message %> + +<%= render "header" %> +<%= render "comments/comments" %> + +<%= render "messages/actions/move" %> + +<%= render @message.history.events %> + +<%# render "something_missing" %> +<%# render "something_missing_1" %> + +<% + # Template Dependency: messages/form +%>
\ No newline at end of file diff --git a/actionview/test/fixtures/functional_caching/fragment_cached_without_digest.html.erb b/actionview/test/fixtures/functional_caching/fragment_cached_without_digest.html.erb deleted file mode 100644 index 3125583a28..0000000000 --- a/actionview/test/fixtures/functional_caching/fragment_cached_without_digest.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -<body> -<%= cache 'nodigest', skip_digest: true do %><p>ERB</p><% end %> -</body> diff --git a/actionview/test/fixtures/happy_path/render_action/hello_world.erb b/actionview/test/fixtures/happy_path/render_action/hello_world.erb deleted file mode 100644 index 6769dd60bd..0000000000 --- a/actionview/test/fixtures/happy_path/render_action/hello_world.erb +++ /dev/null @@ -1 +0,0 @@ -Hello world!
\ No newline at end of file diff --git a/actionview/test/fixtures/scope/test/modgreet.erb b/actionview/test/fixtures/scope/test/modgreet.erb deleted file mode 100644 index 8947726e89..0000000000 --- a/actionview/test/fixtures/scope/test/modgreet.erb +++ /dev/null @@ -1 +0,0 @@ -<p>Beautiful modules!</p>
\ No newline at end of file diff --git a/actionview/test/fixtures/test/_label_with_block.erb b/actionview/test/fixtures/test/_label_with_block.erb index 40117e594e..94089ea93d 100644 --- a/actionview/test/fixtures/test/_label_with_block.erb +++ b/actionview/test/fixtures/test/_label_with_block.erb @@ -1,4 +1,4 @@ -<%= label 'post', 'message' do %> +<%= label('post', 'message')do %> Message <%= text_field 'post', 'message' %> <% end %> diff --git a/actionview/test/fixtures/test/_partial_shortcut_with_block_content.html.erb b/actionview/test/fixtures/test/_partial_shortcut_with_block_content.html.erb new file mode 100644 index 0000000000..352128f3ba --- /dev/null +++ b/actionview/test/fixtures/test/_partial_shortcut_with_block_content.html.erb @@ -0,0 +1,3 @@ +<%= render "test/layout_for_block_with_args" do |arg_1, arg_2| %> + Yielded: <%= arg_1 %>/<%= arg_2 %> +<% end %> diff --git a/actionview/test/fixtures/test/hello_world.html+phone.erb b/actionview/test/fixtures/test/hello_world.html+phone.erb new file mode 100644 index 0000000000..b4f236f878 --- /dev/null +++ b/actionview/test/fixtures/test/hello_world.html+phone.erb @@ -0,0 +1 @@ +Hello phone!
\ No newline at end of file diff --git a/actionview/test/fixtures/test/hello_world.text+phone.erb b/actionview/test/fixtures/test/hello_world.text+phone.erb new file mode 100644 index 0000000000..611e2ee442 --- /dev/null +++ b/actionview/test/fixtures/test/hello_world.text+phone.erb @@ -0,0 +1 @@ +Hello texty phone!
\ No newline at end of file diff --git a/actionview/test/fixtures/test/nil_return.erb b/actionview/test/fixtures/test/nil_return.erb new file mode 100644 index 0000000000..90ce3881f6 --- /dev/null +++ b/actionview/test/fixtures/test/nil_return.erb @@ -0,0 +1 @@ +This is nil: <%== nil %> diff --git a/actionview/test/lib/controller/fake_models.rb b/actionview/test/lib/controller/fake_models.rb index a463a08bb6..789b1d198b 100644 --- a/actionview/test/lib/controller/fake_models.rb +++ b/actionview/test/lib/controller/fake_models.rb @@ -111,19 +111,6 @@ class CommentRelevance end end -class Sheep - extend ActiveModel::Naming - include ActiveModel::Conversion - - attr_reader :id - def to_key; id ? [id] : nil end - def save; @id = 1 end - def new_record?; @id.nil? end - def name - @id.nil? ? 'new sheep' : "sheep ##{@id}" - end -end - class TagRelevance extend ActiveModel::Naming include ActiveModel::Conversion @@ -183,3 +170,15 @@ end class Car < Struct.new(:color) end + +class Plane + attr_reader :to_key + + def model_name + OpenStruct.new param_key: 'airplane' + end + + def save + @to_key = [1] + end +end diff --git a/actionview/test/template/asset_tag_helper_test.rb b/actionview/test/template/asset_tag_helper_test.rb index 3ca71d3376..dac1c7024d 100644 --- a/actionview/test/template/asset_tag_helper_test.rb +++ b/actionview/test/template/asset_tag_helper_test.rb @@ -195,9 +195,9 @@ class AssetTagHelperTest < ActionView::TestCase } FaviconLinkToTag = { - %(favicon_link_tag) => %(<link href="/images/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />), - %(favicon_link_tag 'favicon.ico') => %(<link href="/images/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />), - %(favicon_link_tag 'favicon.ico', :rel => 'foo') => %(<link href="/images/favicon.ico" rel="foo" type="image/vnd.microsoft.icon" />), + %(favicon_link_tag) => %(<link href="/images/favicon.ico" rel="shortcut icon" type="image/x-icon" />), + %(favicon_link_tag 'favicon.ico') => %(<link href="/images/favicon.ico" rel="shortcut icon" type="image/x-icon" />), + %(favicon_link_tag 'favicon.ico', :rel => 'foo') => %(<link href="/images/favicon.ico" rel="foo" type="image/x-icon" />), %(favicon_link_tag 'favicon.ico', :rel => 'foo', :type => 'bar') => %(<link href="/images/favicon.ico" rel="foo" type="bar" />), %(favicon_link_tag 'mb-icon.png', :rel => 'apple-touch-icon', :type => 'image/png') => %(<link href="/images/mb-icon.png" rel="apple-touch-icon" type="image/png" />) } @@ -302,13 +302,21 @@ class AssetTagHelperTest < ActionView::TestCase def test_autodiscovery_link_tag_with_unknown_type result = auto_discovery_link_tag(:xml, '/feed.xml', :type => 'application/xml') expected = %(<link href="/feed.xml" rel="alternate" title="XML" type="application/xml" />) - assert_equal expected, result + assert_dom_equal expected, result end def test_asset_path_tag AssetPathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } end + def test_asset_path_tag_to_not_create_duplicate_slashes + @controller.config.asset_host = "host/" + assert_dom_equal('http://host/foo', asset_path("foo")) + + @controller.config.relative_url_root = '/some/root/' + assert_dom_equal('http://host/some/root/foo', asset_path("foo")) + end + def test_compute_asset_public_path assert_equal "/robots.txt", compute_asset_path("robots.txt") assert_equal "/robots.txt", compute_asset_path("/robots.txt") @@ -527,6 +535,17 @@ class AssetTagHelperTest < ActionView::TestCase assert_equal copy, source end + class PlaceholderImage + def blank?; true; end + def to_s; 'no-image-yet.png'; end + end + def test_image_tag_with_blank_placeholder + assert_equal '<img alt="" src="/images/no-image-yet.png" />', image_tag(PlaceholderImage.new, alt: "") + end + def test_image_path_with_blank_placeholder + assert_equal '/images/no-image-yet.png', image_path(PlaceholderImage.new) + end + def test_image_path_with_asset_host_proc_returning_nil @controller.config.asset_host = Proc.new do |source| unless source.end_with?("tiff") @@ -538,6 +557,14 @@ class AssetTagHelperTest < ActionView::TestCase assert_equal "http://cdn.example.com/images/file.png", image_path("file.png") end + def test_image_url_with_asset_host_proc_returning_nil + @controller.config.asset_host = Proc.new { nil } + @controller.request = Struct.new(:base_url, :script_name).new("http://www.example.com", nil) + + assert_equal "/images/rails.png", image_path("rails.png") + assert_equal "http://www.example.com/images/rails.png", image_url("rails.png") + end + def test_caching_image_path_with_caching_and_proc_asset_host_using_request @controller.config.asset_host = Proc.new do |source, request| if request.ssl? @@ -588,6 +615,10 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase assert_equal "gopher://www.example.com", compute_asset_host("foo", :protocol => :request) end + def test_should_return_custom_host_if_passed_in_options + assert_equal "http://custom.example.com", compute_asset_host("foo", :host => "http://custom.example.com") + end + def test_should_ignore_relative_root_path_on_complete_url assert_dom_equal(%(http://www.example.com/images/xml.png), image_path("http://www.example.com/images/xml.png")) end @@ -751,4 +782,15 @@ class AssetUrlHelperEmptyModuleTest < ActionView::TestCase assert @module.config.asset_host assert_equal "http://www.example.com/foo", @module.asset_url("foo") end + + def test_asset_url_with_custom_asset_host + @module.instance_eval do + def config + Struct.new(:asset_host).new("http://www.example.com") + end + end + + assert @module.config.asset_host + assert_equal "http://custom.example.com/foo", @module.asset_url("foo", :host => "http://custom.example.com") + end end diff --git a/actionview/test/template/atom_feed_helper_test.rb b/actionview/test/template/atom_feed_helper_test.rb index 63b5ac0fab..68b44c4f0d 100644 --- a/actionview/test/template/atom_feed_helper_test.rb +++ b/actionview/test/template/atom_feed_helper_test.rb @@ -254,7 +254,7 @@ class AtomFeedTest < ActionController::TestCase def test_self_url_should_default_to_current_request_url with_restful_routing(:scrolls) do get :index, :id => "defaults" - assert_select "link[rel=self][href=http://www.nextangle.com/scrolls?id=defaults]" + assert_select "link[rel=self][href=\"http://www.nextangle.com/scrolls?id=defaults\"]" end end @@ -318,22 +318,22 @@ class AtomFeedTest < ActionController::TestCase with_restful_routing(:scrolls) do get :index, :id => "feed_with_xhtml_content" assert_match %r{xmlns="http://www.w3.org/1999/xhtml"}, @response.body - assert_select "summary div p", :text => "Something Boring" - assert_select "summary div p", :text => "after 2" + assert_select "summary", :text => /Something Boring/ + assert_select "summary", :text => /after 2/ end end def test_feed_entry_type_option_default_to_text_html with_restful_routing(:scrolls) do get :index, :id => 'defaults' - assert_select "entry link[rel=alternate][type=text/html]" + assert_select "entry link[rel=alternate][type=\"text/html\"]" end end def test_feed_entry_type_option_specified with_restful_routing(:scrolls) do get :index, :id => 'entry_type_options' - assert_select "entry link[rel=alternate][type=text/xml]" + assert_select "entry link[rel=alternate][type=\"text/xml\"]" end end diff --git a/actionview/test/template/capture_helper_test.rb b/actionview/test/template/capture_helper_test.rb index 938f1c3e54..f213da5934 100644 --- a/actionview/test/template/capture_helper_test.rb +++ b/actionview/test/template/capture_helper_test.rb @@ -207,29 +207,6 @@ class CaptureHelperTest < ActionView::TestCase assert_equal "", @av.with_output_buffer {} end - def test_flush_output_buffer_concats_output_buffer_to_response - view = view_with_controller - assert_equal [], view.response.body_parts - - view.output_buffer << 'OMG' - view.flush_output_buffer - assert_equal ['OMG'], view.response.body_parts - assert_equal '', view.output_buffer - - view.output_buffer << 'foobar' - view.flush_output_buffer - assert_equal ['OMG', 'foobar'], view.response.body_parts - assert_equal '', view.output_buffer - end - - def test_flush_output_buffer_preserves_the_encoding_of_the_output_buffer - view = view_with_controller - alt_encoding = alt_encoding(view.output_buffer) - view.output_buffer.force_encoding(alt_encoding) - flush_output_buffer - assert_equal alt_encoding, view.output_buffer.encoding - end - def alt_encoding(output_buffer) output_buffer.encoding == Encoding::US_ASCII ? Encoding::UTF_8 : Encoding::US_ASCII end diff --git a/actionview/test/template/compiled_templates_test.rb b/actionview/test/template/compiled_templates_test.rb index 2336321f3e..f6c1283b92 100644 --- a/actionview/test/template/compiled_templates_test.rb +++ b/actionview/test/template/compiled_templates_test.rb @@ -1,15 +1,12 @@ require 'abstract_unit' class CompiledTemplatesTest < ActiveSupport::TestCase - def setup - # Clean up any details key cached to expose failures - # that otherwise would appear just on isolated tests + teardown do ActionView::LookupContext::DetailsKey.clear + end - @compiled_templates = ActionView::CompiledTemplates - @compiled_templates.instance_methods.each do |m| - @compiled_templates.send(:remove_method, m) if m =~ /^_render_template_/ - end + def test_template_with_nil_erb_return + assert_equal "This is nil: \n", render(:template => "test/nil_return") end def test_template_gets_recompiled_when_using_different_keys_in_local_assigns diff --git a/actionview/test/template/date_helper_test.rb b/actionview/test/template/date_helper_test.rb index 5f09aef249..bfb073680e 100644 --- a/actionview/test/template/date_helper_test.rb +++ b/actionview/test/template/date_helper_test.rb @@ -326,6 +326,16 @@ class DateHelperTest < ActionView::TestCase assert_dom_equal expected, select_month(8, :add_month_numbers => true) end + def test_select_month_with_format_string + expected = %(<select id="date_month" name="date[month]">\n) + expected << %(<option value="1">January (01)</option>\n<option value="2">February (02)</option>\n<option value="3">March (03)</option>\n<option value="4">April (04)</option>\n<option value="5">May (05)</option>\n<option value="6">June (06)</option>\n<option value="7">July (07)</option>\n<option value="8" selected="selected">August (08)</option>\n<option value="9">September (09)</option>\n<option value="10">October (10)</option>\n<option value="11">November (11)</option>\n<option value="12">December (12)</option>\n) + expected << "</select>\n" + + format_string = '%{name} (%<number>02d)' + assert_dom_equal expected, select_month(Time.mktime(2003, 8, 16), :month_format_string => format_string) + assert_dom_equal expected, select_month(8, :month_format_string => format_string) + end + def test_select_month_with_numbers_and_names_with_abbv expected = %(<select id="date_month" name="date[month]">\n) expected << %(<option value="1">1 - Jan</option>\n<option value="2">2 - Feb</option>\n<option value="3">3 - Mar</option>\n<option value="4">4 - Apr</option>\n<option value="5">5 - May</option>\n<option value="6">6 - Jun</option>\n<option value="7">7 - Jul</option>\n<option value="8" selected="selected">8 - Aug</option>\n<option value="9">9 - Sep</option>\n<option value="10">10 - Oct</option>\n<option value="11">11 - Nov</option>\n<option value="12">12 - Dec</option>\n) @@ -1030,6 +1040,22 @@ class DateHelperTest < ActionView::TestCase assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), {:start_year => 2003, :end_year => 2005, :prefix => "date[first]", :with_css_classes => true}) end + def test_select_date_with_css_classes_option_and_html_class_option + expected = %(<select id="date_first_year" name="date[first][year]" class="datetime optional year">\n) + expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n) + expected << "</select>\n" + + expected << %(<select id="date_first_month" name="date[first][month]" class="datetime optional month">\n) + expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n) + expected << "</select>\n" + + expected << %(<select id="date_first_day" name="date[first][day]" class="datetime optional day">\n) + expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n) + expected << "</select>\n" + + assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), {:start_year => 2003, :end_year => 2005, :prefix => "date[first]", :with_css_classes => true}, { class: 'datetime optional' }) + end + def test_select_datetime expected = %(<select id="date_first_year" name="date[first][year]">\n) expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n) @@ -1478,7 +1504,7 @@ class DateHelperTest < ActionView::TestCase expected << %(<option value="">Choose seconds</option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n) expected << "</select>\n" - assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), :prompt => true, :include_seconds => true, + assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), :include_seconds => true, :prompt => {:hour => 'Choose hour', :minute => 'Choose minute', :second => 'Choose seconds'}) end @@ -1626,9 +1652,9 @@ class DateHelperTest < ActionView::TestCase concat f.date_select(:written_on) end - expected = "<select id='post_written_on_1i' name='post[written_on(1i)]'>\n<option value='1999'>1999</option>\n<option value='2000'>2000</option>\n<option value='2001'>2001</option>\n<option value='2002'>2002</option>\n<option value='2003'>2003</option>\n<option selected='selected' value='2004'>2004</option>\n<option value='2005'>2005</option>\n<option value='2006'>2006</option>\n<option value='2007'>2007</option>\n<option value='2008'>2008</option>\n<option value='2009'>2009</option>\n</select>\n" - expected << "<select id='post_written_on_2i' name='post[written_on(2i)]'>\n<option value='1'>January</option>\n<option value='2'>February</option>\n<option value='3'>March</option>\n<option value='4'>April</option>\n<option value='5'>May</option>\n<option selected='selected' value='6'>June</option>\n<option value='7'>July</option>\n<option value='8'>August</option>\n<option value='9'>September</option>\n<option value='10'>October</option>\n<option value='11'>November</option>\n<option value='12'>December</option>\n</select>\n" - expected << "<select id='post_written_on_3i' name='post[written_on(3i)]'>\n<option value='1'>1</option>\n<option value='2'>2</option>\n<option value='3'>3</option>\n<option value='4'>4</option>\n<option value='5'>5</option>\n<option value='6'>6</option>\n<option value='7'>7</option>\n<option value='8'>8</option>\n<option value='9'>9</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option selected='selected' value='15'>15</option>\n<option value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n<option value='24'>24</option>\n<option value='25'>25</option>\n<option value='26'>26</option>\n<option value='27'>27</option>\n<option value='28'>28</option>\n<option value='29'>29</option>\n<option value='30'>30</option>\n<option value='31'>31</option>\n</select>\n" + expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n</select>\n} + expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option selected="selected" value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n</select>\n} + expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option selected="selected" value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n</select>\n} assert_dom_equal(expected, output_buffer) end @@ -1642,9 +1668,9 @@ class DateHelperTest < ActionView::TestCase concat f.date_select(:written_on) end - expected = "<select id='post_#{id}_written_on_1i' name='post[#{id}][written_on(1i)]'>\n<option value='1999'>1999</option>\n<option value='2000'>2000</option>\n<option value='2001'>2001</option>\n<option value='2002'>2002</option>\n<option value='2003'>2003</option>\n<option selected='selected' value='2004'>2004</option>\n<option value='2005'>2005</option>\n<option value='2006'>2006</option>\n<option value='2007'>2007</option>\n<option value='2008'>2008</option>\n<option value='2009'>2009</option>\n</select>\n" - expected << "<select id='post_#{id}_written_on_2i' name='post[#{id}][written_on(2i)]'>\n<option value='1'>January</option>\n<option value='2'>February</option>\n<option value='3'>March</option>\n<option value='4'>April</option>\n<option value='5'>May</option>\n<option selected='selected' value='6'>June</option>\n<option value='7'>July</option>\n<option value='8'>August</option>\n<option value='9'>September</option>\n<option value='10'>October</option>\n<option value='11'>November</option>\n<option value='12'>December</option>\n</select>\n" - expected << "<select id='post_#{id}_written_on_3i' name='post[#{id}][written_on(3i)]'>\n<option value='1'>1</option>\n<option value='2'>2</option>\n<option value='3'>3</option>\n<option value='4'>4</option>\n<option value='5'>5</option>\n<option value='6'>6</option>\n<option value='7'>7</option>\n<option value='8'>8</option>\n<option value='9'>9</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option selected='selected' value='15'>15</option>\n<option value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n<option value='24'>24</option>\n<option value='25'>25</option>\n<option value='26'>26</option>\n<option value='27'>27</option>\n<option value='28'>28</option>\n<option value='29'>29</option>\n<option value='30'>30</option>\n<option value='31'>31</option>\n</select>\n" + expected = %{<select id="post_#{id}_written_on_1i" name="post[#{id}][written_on(1i)]">\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n</select>\n} + expected << %{<select id="post_#{id}_written_on_2i" name="post[#{id}][written_on(2i)]">\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option selected="selected" value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n</select>\n} + expected << %{<select id="post_#{id}_written_on_3i" name="post[#{id}][written_on(3i)]">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option selected="selected" value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n</select>\n} assert_dom_equal(expected, output_buffer) end @@ -1658,9 +1684,10 @@ class DateHelperTest < ActionView::TestCase concat f.date_select(:written_on) end - expected = "<select id='post_#{id}_written_on_1i' name='post[#{id}][written_on(1i)]'>\n<option value='1999'>1999</option>\n<option value='2000'>2000</option>\n<option value='2001'>2001</option>\n<option value='2002'>2002</option>\n<option value='2003'>2003</option>\n<option selected='selected' value='2004'>2004</option>\n<option value='2005'>2005</option>\n<option value='2006'>2006</option>\n<option value='2007'>2007</option>\n<option value='2008'>2008</option>\n<option value='2009'>2009</option>\n</select>\n" - expected << "<select id='post_#{id}_written_on_2i' name='post[#{id}][written_on(2i)]'>\n<option value='1'>January</option>\n<option value='2'>February</option>\n<option value='3'>March</option>\n<option value='4'>April</option>\n<option value='5'>May</option>\n<option selected='selected' value='6'>June</option>\n<option value='7'>July</option>\n<option value='8'>August</option>\n<option value='9'>September</option>\n<option value='10'>October</option>\n<option value='11'>November</option>\n<option value='12'>December</option>\n</select>\n" - expected << "<select id='post_#{id}_written_on_3i' name='post[#{id}][written_on(3i)]'>\n<option value='1'>1</option>\n<option value='2'>2</option>\n<option value='3'>3</option>\n<option value='4'>4</option>\n<option value='5'>5</option>\n<option value='6'>6</option>\n<option value='7'>7</option>\n<option value='8'>8</option>\n<option value='9'>9</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option selected='selected' value='15'>15</option>\n<option value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n<option value='24'>24</option>\n<option value='25'>25</option>\n<option value='26'>26</option>\n<option value='27'>27</option>\n<option value='28'>28</option>\n<option value='29'>29</option>\n<option value='30'>30</option>\n<option value='31'>31</option>\n</select>\n" + + expected = %{<select id="post_#{id}_written_on_1i" name="post[#{id}][written_on(1i)]">\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n</select>\n} + expected << %{<select id="post_#{id}_written_on_2i" name="post[#{id}][written_on(2i)]">\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option selected="selected" value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n</select>\n} + expected << %{<select id="post_#{id}_written_on_3i" name="post[#{id}][written_on(3i)]">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option selected="selected" value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n</select>\n} assert_dom_equal(expected, output_buffer) end @@ -2303,7 +2330,7 @@ class DateHelperTest < ActionView::TestCase # The love zone is UTC+0 mytz = Class.new(ActiveSupport::TimeZone) { attr_accessor :now - }.create('tenderlove', 0) + }.create('tenderlove', 0, ActiveSupport::TimeZone.find_tzinfo('UTC')) now = Time.mktime(2004, 6, 15, 16, 35, 0) mytz.now = now @@ -2348,11 +2375,11 @@ class DateHelperTest < ActionView::TestCase concat f.datetime_select(:updated_at, {}, :class => 'selector') end - expected = "<select id='post_updated_at_1i' name='post[updated_at(1i)]' class='selector'>\n<option value='1999'>1999</option>\n<option value='2000'>2000</option>\n<option value='2001'>2001</option>\n<option value='2002'>2002</option>\n<option value='2003'>2003</option>\n<option selected='selected' value='2004'>2004</option>\n<option value='2005'>2005</option>\n<option value='2006'>2006</option>\n<option value='2007'>2007</option>\n<option value='2008'>2008</option>\n<option value='2009'>2009</option>\n</select>\n" - expected << "<select id='post_updated_at_2i' name='post[updated_at(2i)]' class='selector'>\n<option value='1'>January</option>\n<option value='2'>February</option>\n<option value='3'>March</option>\n<option value='4'>April</option>\n<option value='5'>May</option>\n<option selected='selected' value='6'>June</option>\n<option value='7'>July</option>\n<option value='8'>August</option>\n<option value='9'>September</option>\n<option value='10'>October</option>\n<option value='11'>November</option>\n<option value='12'>December</option>\n</select>\n" - expected << "<select id='post_updated_at_3i' name='post[updated_at(3i)]' class='selector'>\n<option value='1'>1</option>\n<option value='2'>2</option>\n<option value='3'>3</option>\n<option value='4'>4</option>\n<option value='5'>5</option>\n<option value='6'>6</option>\n<option value='7'>7</option>\n<option value='8'>8</option>\n<option value='9'>9</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option selected='selected' value='15'>15</option>\n<option value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n<option value='24'>24</option>\n<option value='25'>25</option>\n<option value='26'>26</option>\n<option value='27'>27</option>\n<option value='28'>28</option>\n<option value='29'>29</option>\n<option value='30'>30</option>\n<option value='31'>31</option>\n</select>\n" - expected << " — <select id='post_updated_at_4i' name='post[updated_at(4i)]' class='selector'>\n<option value='00'>00</option>\n<option value='01'>01</option>\n<option value='02'>02</option>\n<option value='03'>03</option>\n<option value='04'>04</option>\n<option value='05'>05</option>\n<option value='06'>06</option>\n<option value='07'>07</option>\n<option value='08'>08</option>\n<option value='09'>09</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option value='15'>15</option>\n<option selected='selected' value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n</select>\n" - expected << " : <select id='post_updated_at_5i' name='post[updated_at(5i)]' class='selector'>\n<option value='00'>00</option>\n<option value='01'>01</option>\n<option value='02'>02</option>\n<option value='03'>03</option>\n<option value='04'>04</option>\n<option value='05'>05</option>\n<option value='06'>06</option>\n<option value='07'>07</option>\n<option value='08'>08</option>\n<option value='09'>09</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option value='15'>15</option>\n<option value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n<option value='24'>24</option>\n<option value='25'>25</option>\n<option value='26'>26</option>\n<option value='27'>27</option>\n<option value='28'>28</option>\n<option value='29'>29</option>\n<option value='30'>30</option>\n<option value='31'>31</option>\n<option value='32'>32</option>\n<option value='33'>33</option>\n<option value='34'>34</option>\n<option selected='selected' value='35'>35</option>\n<option value='36'>36</option>\n<option value='37'>37</option>\n<option value='38'>38</option>\n<option value='39'>39</option>\n<option value='40'>40</option>\n<option value='41'>41</option>\n<option value='42'>42</option>\n<option value='43'>43</option>\n<option value='44'>44</option>\n<option value='45'>45</option>\n<option value='46'>46</option>\n<option value='47'>47</option>\n<option value='48'>48</option>\n<option value='49'>49</option>\n<option value='50'>50</option>\n<option value='51'>51</option>\n<option value='52'>52</option>\n<option value='53'>53</option>\n<option value='54'>54</option>\n<option value='55'>55</option>\n<option value='56'>56</option>\n<option value='57'>57</option>\n<option value='58'>58</option>\n<option value='59'>59</option>\n</select>\n" + expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]" class="selector">\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n</select>\n} + expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]" class="selector">\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option selected="selected" value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n</select>\n} + expected << %{<select id="post_updated_at_3i" name="post[updated_at(3i)]" class="selector">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option selected="selected" value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n</select>\n} + expected << %{ — <select id="post_updated_at_4i" name="post[updated_at(4i)]" class="selector">\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option selected="selected" value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n</select>\n} + expected << %{ : <select id="post_updated_at_5i" name="post[updated_at(5i)]" class="selector">\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option selected="selected" value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n</select>\n} assert_dom_equal expected, output_buffer end diff --git a/actionview/test/template/dependency_tracker_test.rb b/actionview/test/template/dependency_tracker_test.rb index 7a9b4b26ac..bb375076c6 100644 --- a/actionview/test/template/dependency_tracker_test.rb +++ b/actionview/test/template/dependency_tracker_test.rb @@ -1,3 +1,5 @@ +# encoding: utf-8 + require 'abstract_unit' require 'action_view/dependency_tracker' @@ -29,6 +31,7 @@ class DependencyTrackerTest < ActionView::TestCase end def teardown + ActionView::Template.unregister_template_handler :neckbeard tracker.remove_tracker(:neckbeard) end @@ -52,23 +55,142 @@ class ERBTrackerTest < Minitest::Test def test_dependency_of_erb_template_with_number_in_filename template = FakeTemplate.new("<%# render 'messages/message123' %>", :erb) - tracker = make_tracker('messages/_message123', template) + tracker = make_tracker("messages/_message123", template) assert_equal ["messages/message123"], tracker.dependencies end + def test_dependency_of_template_partial_with_layout + skip # FIXME: Needs to be fixed properly, right now we can only match one dependency per line. Need multiple! + template = FakeTemplate.new("<%# render partial: 'messages/show', layout: 'messages/layout' %>", :erb) + tracker = make_tracker("multiple/_dependencies", template) + + assert_equal ["messages/layout", "messages/show"], tracker.dependencies + end + + def test_dependency_of_template_layout_standalone + template = FakeTemplate.new("<%# render layout: 'messages/layout' do %>", :erb) + tracker = make_tracker("messages/layout", template) + + assert_equal ["messages/layout"], tracker.dependencies + end + def test_finds_dependency_in_correct_directory template = FakeTemplate.new("<%# render(message.topic) %>", :erb) - tracker = make_tracker('messages/_message', template) + tracker = make_tracker("messages/_message", template) assert_equal ["topics/topic"], tracker.dependencies end def test_finds_dependency_in_correct_directory_with_underscore template = FakeTemplate.new("<%# render(message_type.messages) %>", :erb) - tracker = make_tracker('message_types/_message_type', template) + tracker = make_tracker("message_types/_message_type", template) assert_equal ["messages/message"], tracker.dependencies end -end + def test_dependency_of_erb_template_with_no_spaces_after_render + template = FakeTemplate.new("<%# render'messages/message' %>", :erb) + tracker = make_tracker("messages/_message", template) + + assert_equal ["messages/message"], tracker.dependencies + end + + def test_finds_no_dependency_when_render_begins_the_name_of_an_identifier + template = FakeTemplate.new("<%# rendering 'it useless' %>", :erb) + tracker = make_tracker("resources/_resource", template) + + assert_equal [], tracker.dependencies + end + + def test_finds_no_dependency_when_render_ends_the_name_of_another_method + template = FakeTemplate.new("<%# surrender 'to reason' %>", :erb) + tracker = make_tracker("resources/_resource", template) + + assert_equal [], tracker.dependencies + end + + def test_finds_dependency_on_multiline_render_calls + template = FakeTemplate.new("<%# + render :object => @all_posts, + :partial => 'posts' %>", :erb) + + tracker = make_tracker("some/_little_posts", template) + + assert_equal ["some/posts"], tracker.dependencies + end + + def test_finds_multiple_unrelated_odd_dependencies + template = FakeTemplate.new(" + <%# render('shared/header', title: 'Title') %> + <h2>Section title</h2> + <%# render@section %> + ", :erb) + + tracker = make_tracker("multiple/_dependencies", template) + + assert_equal ["shared/header", "sections/section"], tracker.dependencies + end + + def test_finds_dependencies_for_all_kinds_of_identifiers + template = FakeTemplate.new(" + <%# render $globals %> + <%# render @instance_variables %> + <%# render @@class_variables %> + ", :erb) + + tracker = make_tracker("identifiers/_all", template) + + assert_equal [ + "globals/global", + "instance_variables/instance_variable", + "class_variables/class_variable" + ], tracker.dependencies + end + + def test_finds_dependencies_on_method_chains + template = FakeTemplate.new("<%# render @parent.child.grandchildren %>", :erb) + tracker = make_tracker("method/_chains", template) + + assert_equal ["grandchildren/grandchild"], tracker.dependencies + end + + def test_finds_dependencies_with_special_characters + template = FakeTemplate.new("<%# render @pokémon, partial: 'ピカチュウ' %>", :erb) + tracker = make_tracker("special/_characters", template) + + assert_equal ["special/ピカチュウ"], tracker.dependencies + end + + def test_finds_dependencies_with_quotes_within + template = FakeTemplate.new(%{ + <%# render "single/quote's" %> + <%# render 'double/quote"s' %> + }, :erb) + + tracker = make_tracker("quotes/_single_and_double", template) + + assert_equal ["single/quote's", 'double/quote"s'], tracker.dependencies + end + + def test_finds_dependencies_with_extra_spaces + template = FakeTemplate.new(%{ + <%= render "header" %> + <%= render partial: "form" %> + <%= render @message %> + <%= render ( @message.events ) %> + <%= render :collection => @message.comments, + :partial => "comments/comment" %> + }, :erb) + + tracker = make_tracker("spaces/_extra", template) + + assert_equal [ + "spaces/header", + "spaces/form", + "messages/message", + "events/event", + "comments/comment" + ], tracker.dependencies + end +end diff --git a/actionview/test/template/digestor_test.rb b/actionview/test/template/digestor_test.rb index 779a7fb53c..c2b8439df3 100644 --- a/actionview/test/template/digestor_test.rb +++ b/actionview/test/template/digestor_test.rb @@ -15,18 +15,30 @@ end class FixtureFinder FIXTURES_DIR = "#{File.dirname(__FILE__)}/../fixtures/digestor" - attr_reader :details + attr_reader :details + attr_accessor :formats + attr_accessor :variants def initialize - @details = {} + @details = {} + @formats = [] + @variants = [] end def details_key details.hash end - def find(logical_name, keys, partial, options) - FixtureTemplate.new("digestor/#{partial ? logical_name.gsub(%r|/([^/]+)$|, '/_\1') : logical_name}.#{options[:formats].first}.erb") + def find(name, prefixes = [], partial = false, keys = [], options = {}) + partial_name = partial ? name.gsub(%r|/([^/]+)$|, '/_\1') : name + format = @formats.first.to_s + format += "+#{@variants.first}" if @variants.any? + + FixtureTemplate.new("digestor/#{partial_name}.#{format}.erb") + end + + def disable_cache(&block) + yield end end @@ -88,13 +100,13 @@ class TemplateDigestorTest < ActionView::TestCase end def test_logging_of_missing_template - assert_logged "Couldn't find template for digesting: messages/something_missing.html" do + assert_logged "Couldn't find template for digesting: messages/something_missing" do digest("messages/show") end end def test_logging_of_missing_template_ending_with_number - assert_logged "Couldn't find template for digesting: messages/something_missing_1.html" do + assert_logged "Couldn't find template for digesting: messages/something_missing_1" do digest("messages/show") end end @@ -194,6 +206,13 @@ class TemplateDigestorTest < ActionView::TestCase end end + def test_variants + assert_digest_difference("messages/new", false, variants: [:iphone]) do + change_template("messages/new", :iphone) + change_template("messages/_header", :iphone) + end + end + def test_dependencies_via_options_results_in_different_digest digest_plain = digest("comments/_comment") digest_fridge = digest("comments/_comment", dependencies: ["fridge"]) @@ -214,6 +233,7 @@ class TemplateDigestorTest < ActionView::TestCase assert_digest_difference("messages/edit", true) do change_template("comments/_comment") end + ensure ActionView::Resolver.caching = resolver_before end @@ -242,6 +262,7 @@ class TemplateDigestorTest < ActionView::TestCase ActionView::Resolver.caching = resolver_before end + private def assert_logged(message) old_logger = ActionView::Base.logger @@ -258,26 +279,33 @@ class TemplateDigestorTest < ActionView::TestCase end end - def assert_digest_difference(template_name, persistent = false) - previous_digest = digest(template_name) + def assert_digest_difference(template_name, persistent = false, options = {}) + previous_digest = digest(template_name, options) ActionView::Digestor.cache.clear unless persistent yield - assert previous_digest != digest(template_name), "digest didn't change" + assert previous_digest != digest(template_name, options), "digest didn't change" ActionView::Digestor.cache.clear end - def digest(template_name, options={}) - ActionView::Digestor.digest(template_name, :html, finder, options) + def digest(template_name, options = {}) + options = options.dup + + finder.formats = [:html] + finder.variants = options.delete(:variants) || [] + + ActionView::Digestor.digest({ name: template_name, finder: finder }.merge(options)) end def finder @finder ||= FixtureFinder.new end - def change_template(template_name) - File.open("digestor/#{template_name}.html.erb", "w") do |f| + def change_template(template_name, variant = nil) + variant = "+#{variant}" if variant.present? + + File.open("digestor/#{template_name}.html#{variant}.erb", "w") do |f| f.write "\nTHIS WAS CHANGED!" end end diff --git a/actionview/test/template/erb_util_test.rb b/actionview/test/template/erb_util_test.rb index 9bacbba908..3e72be31de 100644 --- a/actionview/test/template/erb_util_test.rb +++ b/actionview/test/template/erb_util_test.rb @@ -84,7 +84,7 @@ class ErbUtilTest < ActiveSupport::TestCase end def test_rest_in_ascii - (0..127).to_a.map {|int| int.chr }.each do |chr| + (0..127).to_a.map(&:chr).each do |chr| next if %('"&<>).include?(chr) assert_equal chr, html_escape(chr) end @@ -92,6 +92,7 @@ class ErbUtilTest < ActiveSupport::TestCase def test_html_escape_once assert_equal '1 <>&"' 2 & 3', html_escape_once('1 <>&"\' 2 & 3') + assert_equal " ' ' λ λ " ' < > ", html_escape_once(" ' ' λ λ \" ' < > ") end def test_html_escape_once_returns_unsafe_strings_when_passed_unsafe_strings diff --git a/actionview/test/template/form_collections_helper_test.rb b/actionview/test/template/form_collections_helper_test.rb index 18632465db..b193d387c3 100644 --- a/actionview/test/template/form_collections_helper_test.rb +++ b/actionview/test/template/form_collections_helper_test.rb @@ -68,6 +68,23 @@ class FormCollectionsHelperTest < ActionView::TestCase assert_no_select 'input[type=radio][value=false][disabled=disabled]' end + test 'collection radio accepts multiple readonly items' do + collection = [[1, true], [0, false], [2, 'other']] + with_collection_radio_buttons :user, :active, collection, :last, :first, :readonly => [true, false] + + assert_select 'input[type=radio][value=true][readonly=readonly]' + assert_select 'input[type=radio][value=false][readonly=readonly]' + assert_no_select 'input[type=radio][value=other][readonly=readonly]' + end + + test 'collection radio accepts single readonly item' do + collection = [[1, true], [0, false]] + with_collection_radio_buttons :user, :active, collection, :last, :first, :readonly => true + + assert_select 'input[type=radio][value=true][readonly=readonly]' + assert_no_select 'input[type=radio][value=false][readonly=readonly]' + end + test 'collection radio accepts html options as input' do collection = [[1, true], [0, false]] with_collection_radio_buttons :user, :active, collection, :last, :first, {}, :class => 'special-radio' @@ -168,8 +185,8 @@ class FormCollectionsHelperTest < ActionView::TestCase p.collection_radio_buttons :category_id, collection, :id, :name end - assert_select 'input#post_category_id_1[type=radio][value=1]' - assert_select 'input#post_category_id_2[type=radio][value=2]' + assert_select 'input#post_category_id_1[type=radio][value="1"]' + assert_select 'input#post_category_id_2[type=radio][value="2"]' assert_select 'label[for=post_category_id_1]', 'Category 1' assert_select 'label[for=post_category_id_2]', 'Category 2' @@ -186,22 +203,36 @@ class FormCollectionsHelperTest < ActionView::TestCase collection = [Category.new(1, 'Category 1'), Category.new(2, 'Category 2')] with_collection_check_boxes :user, :category_ids, collection, :id, :name - assert_select 'input#user_category_ids_1[type=checkbox][value=1]' - assert_select 'input#user_category_ids_2[type=checkbox][value=2]' + assert_select 'input#user_category_ids_1[type=checkbox][value="1"]' + assert_select 'input#user_category_ids_2[type=checkbox][value="2"]' end test 'collection check boxes generates only one hidden field for the entire collection, to ensure something will be sent back to the server when posting an empty collection' do collection = [Category.new(1, 'Category 1'), Category.new(2, 'Category 2')] with_collection_check_boxes :user, :category_ids, collection, :id, :name - assert_select "input[type=hidden][name='user[category_ids][]'][value=]", :count => 1 + assert_select "input[type=hidden][name='user[category_ids][]'][value='']", :count => 1 end test 'collection check boxes generates a hidden field using the given :name in :html_options' do collection = [Category.new(1, 'Category 1'), Category.new(2, 'Category 2')] with_collection_check_boxes :user, :category_ids, collection, :id, :name, {}, {name: "user[other_category_ids][]"} - assert_select "input[type=hidden][name='user[other_category_ids][]'][value=]", :count => 1 + assert_select "input[type=hidden][name='user[other_category_ids][]'][value='']", :count => 1 + end + + test 'collection check boxes generates a hidden field with index if it was provided' do + collection = [Category.new(1, 'Category 1'), Category.new(2, 'Category 2')] + with_collection_check_boxes :user, :category_ids, collection, :id, :name, { index: 322 } + + assert_select "input[type=hidden][name='user[322][category_ids][]'][value='']", count: 1 + end + + test 'collection check boxes does not generate a hidden field if include_hidden option is false' do + collection = [Category.new(1, 'Category 1'), Category.new(2, 'Category 2')] + with_collection_check_boxes :user, :category_ids, collection, :id, :name, include_hidden: false + + assert_select "input[type=hidden][name='user[category_ids][]'][value='']", :count => 0 end test 'collection check boxes accepts a collection and generate a series of checkboxes with labels for label method' do @@ -229,8 +260,8 @@ class FormCollectionsHelperTest < ActionView::TestCase collection = [[1, 'Category 1', {class: 'foo'}], [2, 'Category 2', {class: 'bar'}]] with_collection_check_boxes :user, :active, collection, :first, :second - assert_select 'input[type=checkbox][value=1].foo' - assert_select 'input[type=checkbox][value=2].bar' + assert_select 'input[type=checkbox][value="1"].foo' + assert_select 'input[type=checkbox][value="2"].bar' end test 'collection check boxes sets the label class defined inside the block' do @@ -255,27 +286,27 @@ class FormCollectionsHelperTest < ActionView::TestCase collection = (1..3).map{|i| [i, "Category #{i}"] } with_collection_check_boxes :user, :category_ids, collection, :first, :last, :checked => [1, 3] - assert_select 'input[type=checkbox][value=1][checked=checked]' - assert_select 'input[type=checkbox][value=3][checked=checked]' - assert_no_select 'input[type=checkbox][value=2][checked=checked]' + assert_select 'input[type=checkbox][value="1"][checked=checked]' + assert_select 'input[type=checkbox][value="3"][checked=checked]' + assert_no_select 'input[type=checkbox][value="2"][checked=checked]' end test 'collection check boxes accepts selected string values as :checked option' do collection = (1..3).map{|i| [i, "Category #{i}"] } with_collection_check_boxes :user, :category_ids, collection, :first, :last, :checked => ['1', '3'] - assert_select 'input[type=checkbox][value=1][checked=checked]' - assert_select 'input[type=checkbox][value=3][checked=checked]' - assert_no_select 'input[type=checkbox][value=2][checked=checked]' + assert_select 'input[type=checkbox][value="1"][checked=checked]' + assert_select 'input[type=checkbox][value="3"][checked=checked]' + assert_no_select 'input[type=checkbox][value="2"][checked=checked]' end test 'collection check boxes accepts a single checked value' do collection = (1..3).map{|i| [i, "Category #{i}"] } with_collection_check_boxes :user, :category_ids, collection, :first, :last, :checked => 3 - assert_select 'input[type=checkbox][value=3][checked=checked]' - assert_no_select 'input[type=checkbox][value=1][checked=checked]' - assert_no_select 'input[type=checkbox][value=2][checked=checked]' + assert_select 'input[type=checkbox][value="3"][checked=checked]' + assert_no_select 'input[type=checkbox][value="1"][checked=checked]' + assert_no_select 'input[type=checkbox][value="2"][checked=checked]' end test 'collection check boxes accepts selected values as :checked option and override the model values' do @@ -286,44 +317,71 @@ class FormCollectionsHelperTest < ActionView::TestCase p.collection_check_boxes :category_ids, collection, :first, :last, :checked => [1, 3] end - assert_select 'input[type=checkbox][value=1][checked=checked]' - assert_select 'input[type=checkbox][value=3][checked=checked]' - assert_no_select 'input[type=checkbox][value=2][checked=checked]' + assert_select 'input[type=checkbox][value="1"][checked=checked]' + assert_select 'input[type=checkbox][value="3"][checked=checked]' + assert_no_select 'input[type=checkbox][value="2"][checked=checked]' end test 'collection check boxes accepts multiple disabled items' do collection = (1..3).map{|i| [i, "Category #{i}"] } with_collection_check_boxes :user, :category_ids, collection, :first, :last, :disabled => [1, 3] - assert_select 'input[type=checkbox][value=1][disabled=disabled]' - assert_select 'input[type=checkbox][value=3][disabled=disabled]' - assert_no_select 'input[type=checkbox][value=2][disabled=disabled]' + assert_select 'input[type=checkbox][value="1"][disabled=disabled]' + assert_select 'input[type=checkbox][value="3"][disabled=disabled]' + assert_no_select 'input[type=checkbox][value="2"][disabled=disabled]' end test 'collection check boxes accepts single disabled item' do collection = (1..3).map{|i| [i, "Category #{i}"] } with_collection_check_boxes :user, :category_ids, collection, :first, :last, :disabled => 1 - assert_select 'input[type=checkbox][value=1][disabled=disabled]' - assert_no_select 'input[type=checkbox][value=3][disabled=disabled]' - assert_no_select 'input[type=checkbox][value=2][disabled=disabled]' + assert_select 'input[type=checkbox][value="1"][disabled=disabled]' + assert_no_select 'input[type=checkbox][value="3"][disabled=disabled]' + assert_no_select 'input[type=checkbox][value="2"][disabled=disabled]' end test 'collection check boxes accepts a proc to disabled items' do collection = (1..3).map{|i| [i, "Category #{i}"] } with_collection_check_boxes :user, :category_ids, collection, :first, :last, :disabled => proc { |i| i.first == 1 } - assert_select 'input[type=checkbox][value=1][disabled=disabled]' - assert_no_select 'input[type=checkbox][value=3][disabled=disabled]' - assert_no_select 'input[type=checkbox][value=2][disabled=disabled]' + assert_select 'input[type=checkbox][value="1"][disabled=disabled]' + assert_no_select 'input[type=checkbox][value="3"][disabled=disabled]' + assert_no_select 'input[type=checkbox][value="2"][disabled=disabled]' + end + + test 'collection check boxes accepts multiple readonly items' do + collection = (1..3).map{|i| [i, "Category #{i}"] } + with_collection_check_boxes :user, :category_ids, collection, :first, :last, :readonly => [1, 3] + + assert_select 'input[type=checkbox][value="1"][readonly=readonly]' + assert_select 'input[type=checkbox][value="3"][readonly=readonly]' + assert_no_select 'input[type=checkbox][value="2"][readonly=readonly]' + end + + test 'collection check boxes accepts single readonly item' do + collection = (1..3).map{|i| [i, "Category #{i}"] } + with_collection_check_boxes :user, :category_ids, collection, :first, :last, :readonly => 1 + + assert_select 'input[type=checkbox][value="1"][readonly=readonly]' + assert_no_select 'input[type=checkbox][value="3"][readonly=readonly]' + assert_no_select 'input[type=checkbox][value="2"][readonly=readonly]' + end + + test 'collection check boxes accepts a proc to readonly items' do + collection = (1..3).map{|i| [i, "Category #{i}"] } + with_collection_check_boxes :user, :category_ids, collection, :first, :last, :readonly => proc { |i| i.first == 1 } + + assert_select 'input[type=checkbox][value="1"][readonly=readonly]' + assert_no_select 'input[type=checkbox][value="3"][readonly=readonly]' + assert_no_select 'input[type=checkbox][value="2"][readonly=readonly]' end test 'collection check boxes accepts html options' do collection = [[1, 'Category 1'], [2, 'Category 2']] with_collection_check_boxes :user, :category_ids, collection, :first, :last, {}, :class => 'check' - assert_select 'input.check[type=checkbox][value=1]' - assert_select 'input.check[type=checkbox][value=2]' + assert_select 'input.check[type=checkbox][value="1"]' + assert_select 'input.check[type=checkbox][value="2"]' end test 'collection check boxes with fields for' do @@ -332,8 +390,8 @@ class FormCollectionsHelperTest < ActionView::TestCase p.collection_check_boxes :category_ids, collection, :id, :name end - assert_select 'input#post_category_ids_1[type=checkbox][value=1]' - assert_select 'input#post_category_ids_2[type=checkbox][value=2]' + assert_select 'input#post_category_ids_1[type=checkbox][value="1"]' + assert_select 'input#post_category_ids_2[type=checkbox][value="2"]' assert_select 'label[for=post_category_ids_1]', 'Category 1' assert_select 'label[for=post_category_ids_2]', 'Category 2' diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb index fe82349265..fff1e1e572 100644 --- a/actionview/test/template/form_helper_test.rb +++ b/actionview/test/template/form_helper_test.rb @@ -10,15 +10,20 @@ class FormHelperTest < ActionView::TestCase @output_buffer = super end - def setup - super + teardown do + I18n.backend.reload! + end + setup do # Create "label" locale for testing I18n label helpers I18n.backend.store_translations 'label', { activemodel: { attributes: { post: { cost: "Total cost" + }, + :"post/language" => { + spanish: "Espanol" } } }, @@ -54,6 +59,35 @@ class FormHelperTest < ActionView::TestCase } } + I18n.backend.store_translations 'placeholder', { + activemodel: { + attributes: { + post: { + cost: "Total cost" + }, + :"post/cost" => { + uk: "Pounds" + } + } + }, + helpers: { + placeholder: { + post: { + title: "What is this about?", + written_on: { + spanish: "Escrito en" + }, + comments: { + body: "Write body here" + } + }, + tag: { + value: "Tag" + } + } + } + } + @post = Post.new @comment = Comment.new def @post.errors() @@ -143,75 +177,74 @@ class FormHelperTest < ActionView::TestCase end def test_label_with_locales_strings - old_locale, I18n.locale = I18n.locale, :label - assert_dom_equal('<label for="post_body">Write entire text here</label>', label("post", "body")) - ensure - I18n.locale = old_locale + with_locale :label do + assert_dom_equal('<label for="post_body">Write entire text here</label>', label("post", "body")) + end end def test_label_with_human_attribute_name - old_locale, I18n.locale = I18n.locale, :label - assert_dom_equal('<label for="post_cost">Total cost</label>', label(:post, :cost)) - ensure - I18n.locale = old_locale + with_locale :label do + assert_dom_equal('<label for="post_cost">Total cost</label>', label(:post, :cost)) + end + end + + def test_label_with_human_attribute_name_and_options + with_locale :label do + assert_dom_equal('<label for="post_language_spanish">Espanol</label>', label(:post, :language, value: "spanish")) + end end def test_label_with_locales_symbols - old_locale, I18n.locale = I18n.locale, :label - assert_dom_equal('<label for="post_body">Write entire text here</label>', label(:post, :body)) - ensure - I18n.locale = old_locale + with_locale :label do + assert_dom_equal('<label for="post_body">Write entire text here</label>', label(:post, :body)) + end end def test_label_with_locales_and_options - old_locale, I18n.locale = I18n.locale, :label - assert_dom_equal( - '<label for="post_body" class="post_body">Write entire text here</label>', - label(:post, :body, class: "post_body") - ) - ensure - I18n.locale = old_locale + with_locale :label do + assert_dom_equal( + '<label for="post_body" class="post_body">Write entire text here</label>', + label(:post, :body, class: "post_body") + ) + end end def test_label_with_locales_and_value - old_locale, I18n.locale = I18n.locale, :label - assert_dom_equal('<label for="post_color_red">Rojo</label>', label(:post, :color, value: "red")) - ensure - I18n.locale = old_locale + with_locale :label do + assert_dom_equal('<label for="post_color_red">Rojo</label>', label(:post, :color, value: "red")) + end end def test_label_with_locales_and_nested_attributes - old_locale, I18n.locale = I18n.locale, :label - form_for(@post, html: { id: 'create-post' }) do |f| - f.fields_for(:comments) do |cf| - concat cf.label(:body) + with_locale :label do + form_for(@post, html: { id: 'create-post' }) do |f| + f.fields_for(:comments) do |cf| + concat cf.label(:body) + end end - end - expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do - '<label for="post_comments_attributes_0_body">Write body here</label>' - end + expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do + '<label for="post_comments_attributes_0_body">Write body here</label>' + end - assert_dom_equal expected, output_buffer - ensure - I18n.locale = old_locale + assert_dom_equal expected, output_buffer + end end def test_label_with_locales_fallback_and_nested_attributes - old_locale, I18n.locale = I18n.locale, :label - form_for(@post, html: { id: 'create-post' }) do |f| - f.fields_for(:tags) do |cf| - concat cf.label(:value) + with_locale :label do + form_for(@post, html: { id: 'create-post' }) do |f| + f.fields_for(:tags) do |cf| + concat cf.label(:value) + end end - end - expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do - '<label for="post_tags_attributes_0_value">Tag</label>' - end + expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do + '<label for="post_tags_attributes_0_value">Tag</label>' + end - assert_dom_equal expected, output_buffer - ensure - I18n.locale = old_locale + assert_dom_equal expected, output_buffer + end end def test_label_with_for_attribute_as_symbol @@ -272,6 +305,13 @@ class FormHelperTest < ActionView::TestCase ) end + def test_label_with_block_and_html + assert_dom_equal( + '<label for="post_terms">Accept <a href="/terms">Terms</a>.</label>', + label(:post, :terms) { 'Accept <a href="/terms">Terms</a>.'.html_safe } + ) + end + def test_label_with_block_and_options assert_dom_equal( '<label for="my_for">The title, please:</label>', @@ -279,13 +319,90 @@ class FormHelperTest < ActionView::TestCase ) end + def test_label_with_block_and_builder + with_locale :label do + assert_dom_equal( + '<label for="post_body"><b>Write entire text here</b></label>', + label(:post, :body) { |b| "<b>#{b.translation}</b>".html_safe } + ) + end + end + def test_label_with_block_in_erb - assert_equal( + assert_dom_equal( %{<label for="post_message">\n Message\n <input id="post_message" name="post[message]" type="text" />\n</label>}, view.render("test/label_with_block") ) end + def test_text_field_placeholder_without_locales + with_locale :placeholder do + assert_dom_equal('<input id="post_body" name="post[body]" placeholder="Body" type="text" value="Back to the hill and over it again!" />', text_field(:post, :body, placeholder: true)) + end + end + + def test_text_field_placeholder_with_locales + with_locale :placeholder do + assert_dom_equal('<input id="post_title" name="post[title]" placeholder="What is this about?" type="text" value="Hello World" />', text_field(:post, :title, placeholder: true)) + end + end + + def test_text_field_placeholder_with_human_attribute_name + with_locale :placeholder do + assert_dom_equal('<input id="post_cost" name="post[cost]" placeholder="Total cost" type="text" />', text_field(:post, :cost, placeholder: true)) + end + end + + def test_text_field_placeholder_with_string_value + with_locale :placeholder do + assert_dom_equal('<input id="post_cost" name="post[cost]" placeholder="HOW MUCH?" type="text" />', text_field(:post, :cost, placeholder: "HOW MUCH?")) + end + end + + def test_text_field_placeholder_with_human_attribute_name_and_value + with_locale :placeholder do + assert_dom_equal('<input id="post_cost" name="post[cost]" placeholder="Pounds" type="text" />', text_field(:post, :cost, placeholder: :uk)) + end + end + + def test_text_field_placeholder_with_locales_and_value + with_locale :placeholder do + assert_dom_equal('<input id="post_written_on" name="post[written_on]" placeholder="Escrito en" type="text" value="2004-06-15" />', text_field(:post, :written_on, placeholder: :spanish)) + end + end + + def test_text_field_placeholder_with_locales_and_nested_attributes + with_locale :placeholder do + form_for(@post, html: { id: 'create-post' }) do |f| + f.fields_for(:comments) do |cf| + concat cf.text_field(:body, placeholder: true) + end + end + + expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do + '<input id="post_comments_attributes_0_body" name="post[comments_attributes][0][body]" placeholder="Write body here" type="text" />' + end + + assert_dom_equal expected, output_buffer + end + end + + def test_text_field_placeholder_with_locales_fallback_and_nested_attributes + with_locale :placeholder do + form_for(@post, html: { id: 'create-post' }) do |f| + f.fields_for(:tags) do |cf| + concat cf.text_field(:value, placeholder: true) + end + end + + expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do + '<input id="post_tags_attributes_0_value" name="post[tags_attributes][0][value]" placeholder="Tag" type="text" value="new tag" />' + end + + assert_dom_equal expected, output_buffer + end + end + def test_text_field assert_dom_equal( '<input id="post_title" name="post[title]" type="text" value="Hello World" />', @@ -352,22 +469,36 @@ class FormHelperTest < ActionView::TestCase def test_text_field_doesnt_change_param_values object_name = 'post[]' expected = '<input id="post_123_title" name="post[123][title]" type="text" value="Hello World" />' - assert_equal expected, text_field(object_name, "title") - assert_equal object_name, "post[]" + assert_dom_equal expected, text_field(object_name, "title") end - def test_file_field_has_no_size + def test_file_field_does_generate_a_hidden_field + expected = '<input name="user[avatar]" type="hidden" value="" /><input id="user_avatar" name="user[avatar]" type="file" />' + assert_dom_equal expected, file_field("user", "avatar") + end + + def test_file_field_does_not_generate_a_hidden_field_if_included_hidden_option_is_false + expected = '<input id="user_avatar" name="user[avatar]" type="file" />' + assert_dom_equal expected, file_field("user", "avatar", include_hidden: false) + end + + def test_file_field_does_not_generate_a_hidden_field_if_included_hidden_option_is_false_with_key_as_string expected = '<input id="user_avatar" name="user[avatar]" type="file" />' + assert_dom_equal expected, file_field("user", "avatar", "include_hidden" => false) + end + + def test_file_field_has_no_size + expected = '<input name="user[avatar]" type="hidden" value="" /><input id="user_avatar" name="user[avatar]" type="file" />' assert_dom_equal expected, file_field("user", "avatar") end def test_file_field_with_multiple_behavior - expected = '<input id="import_file" multiple="multiple" name="import[file][]" type="file" />' + expected = '<input name="import[file][]" type="hidden" value="" /><input id="import_file" multiple="multiple" name="import[file][]" type="file" />' assert_dom_equal expected, file_field("import", "file", :multiple => true) end def test_file_field_with_multiple_behavior_and_explicit_name - expected = '<input id="import_file" multiple="multiple" name="custom" type="file" />' + expected = '<input name="custom" type="hidden" value="" /><input id="import_file" multiple="multiple" name="custom" type="file" />' assert_dom_equal expected, file_field("import", "file", :multiple => true, :name => "custom") end @@ -654,6 +785,92 @@ class FormHelperTest < ActionView::TestCase ) end + def test_text_area_placeholder_without_locales + with_locale :placeholder do + assert_dom_equal( + %{<textarea id="post_body" name="post[body]" placeholder="Body">\nBack to the hill and over it again!</textarea>}, + text_area(:post, :body, placeholder: true) + ) + end + end + + def test_text_area_placeholder_with_locales + with_locale :placeholder do + assert_dom_equal( + %{<textarea id="post_title" name="post[title]" placeholder="What is this about?">\nHello World</textarea>}, + text_area(:post, :title, placeholder: true) + ) + end + end + + def test_text_area_placeholder_with_human_attribute_name + with_locale :placeholder do + assert_dom_equal( + %{<textarea id="post_cost" name="post[cost]" placeholder="Total cost">\n</textarea>}, + text_area(:post, :cost, placeholder: true) + ) + end + end + + def test_text_area_placeholder_with_string_value + with_locale :placeholder do + assert_dom_equal( + %{<textarea id="post_cost" name="post[cost]" placeholder="HOW MUCH?">\n</textarea>}, + text_area(:post, :cost, placeholder: "HOW MUCH?") + ) + end + end + + def test_text_area_placeholder_with_human_attribute_name_and_value + with_locale :placeholder do + assert_dom_equal( + %{<textarea id="post_cost" name="post[cost]" placeholder="Pounds">\n</textarea>}, + text_area(:post, :cost, placeholder: :uk) + ) + end + end + + def test_text_area_placeholder_with_locales_and_value + with_locale :placeholder do + assert_dom_equal( + %{<textarea id="post_written_on" name="post[written_on]" placeholder="Escrito en">\n2004-06-15</textarea>}, + text_area(:post, :written_on, placeholder: :spanish) + ) + end + end + + def test_text_area_placeholder_with_locales_and_nested_attributes + with_locale :placeholder do + form_for(@post, html: { id: 'create-post' }) do |f| + f.fields_for(:comments) do |cf| + concat cf.text_area(:body, placeholder: true) + end + end + + expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do + %{<textarea id="post_comments_attributes_0_body" name="post[comments_attributes][0][body]" placeholder="Write body here">\n</textarea>} + end + + assert_dom_equal expected, output_buffer + end + end + + def test_text_area_placeholder_with_locales_fallback_and_nested_attributes + with_locale :placeholder do + form_for(@post, html: { id: 'create-post' }) do |f| + f.fields_for(:tags) do |cf| + concat cf.text_area(:value, placeholder: true) + end + end + + expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do + %{<textarea id="post_tags_attributes_0_value" name="post[tags_attributes][0][value]" placeholder="Tag">\nnew tag</textarea>} + end + + assert_dom_equal expected, output_buffer + end + end + def test_text_area assert_dom_equal( %{<textarea id="post_body" name="post[body]">\nBack to the hill and over it again!</textarea>}, @@ -683,6 +900,13 @@ class FormHelperTest < ActionView::TestCase ) end + def test_text_area_with_value_before_type_cast + assert_dom_equal( + %{<textarea id="post_id" name="post[id]">\n123</textarea>}, + text_area("post", "id") + ) + end + def test_text_area_with_html_entities @post.body = "The HTML Entity for & is &" assert_dom_equal( @@ -719,6 +943,11 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal(expected, search_field("contact", "notes_query")) end + def test_search_field_with_onsearch_value + expected = %{<input onsearch="true" type="search" name="contact[notes_query]" id="contact_notes_query" incremental="true" />} + assert_dom_equal(expected, search_field("contact", "notes_query", onsearch: true)) + end + def test_telephone_field expected = %{<input id="user_cell" name="user[cell]" type="tel" />} assert_dom_equal(expected, telephone_field("user", "cell")) @@ -765,6 +994,22 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal(expected, date_field("post", "written_on")) end + def test_date_field_with_string_values_for_min_and_max + expected = %{<input id="post_written_on" max="2010-08-15" min="2000-06-15" name="post[written_on]" type="date" value="2004-06-15" />} + @post.written_on = DateTime.new(2004, 6, 15) + min_value = "2000-06-15" + max_value = "2010-08-15" + assert_dom_equal(expected, date_field("post", "written_on", min: min_value, max: max_value)) + end + + def test_date_field_with_invalid_string_values_for_min_and_max + expected = %{<input id="post_written_on" name="post[written_on]" type="date" value="2004-06-15" />} + @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3) + min_value = "foo" + max_value = "bar" + assert_dom_equal(expected, date_field("post", "written_on", min: min_value, max: max_value)) + end + def test_time_field expected = %{<input id="post_written_on" name="post[written_on]" type="time" value="00:00:00.000" />} assert_dom_equal(expected, time_field("post", "written_on")) @@ -800,6 +1045,22 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal(expected, time_field("post", "written_on")) end + def test_time_field_with_string_values_for_min_and_max + expected = %{<input id="post_written_on" max="10:25:00.000" min="20:45:30.000" name="post[written_on]" type="time" value="01:02:03.000" />} + @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3) + min_value = "20:45:30.000" + max_value = "10:25:00.000" + assert_dom_equal(expected, time_field("post", "written_on", min: min_value, max: max_value)) + end + + def test_time_field_with_invalid_string_values_for_min_and_max + expected = %{<input id="post_written_on" name="post[written_on]" type="time" value="01:02:03.000" />} + @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3) + min_value = "foo" + max_value = "bar" + assert_dom_equal(expected, time_field("post", "written_on", min: min_value, max: max_value)) + end + def test_datetime_field expected = %{<input id="post_written_on" name="post[written_on]" type="datetime" value="2004-06-15T00:00:00.000+0000" />} assert_dom_equal(expected, datetime_field("post", "written_on")) @@ -841,6 +1102,22 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal(expected, datetime_field("post", "written_on")) end + def test_datetime_field_with_string_values_for_min_and_max + expected = %{<input id="post_written_on" max="2010-08-15T10:25:00.000+0000" min="2000-06-15T20:45:30.000+0000" name="post[written_on]" type="datetime" value="2004-06-15T01:02:03.000+0000" />} + @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3) + min_value = "2000-06-15T20:45:30.000+0000" + max_value = "2010-08-15T10:25:00.000+0000" + assert_dom_equal(expected, datetime_field("post", "written_on", min: min_value, max: max_value)) + end + + def test_datetime_field_with_invalid_string_values_for_min_and_max + expected = %{<input id="post_written_on" name="post[written_on]" type="datetime" value="2004-06-15T01:02:03.000+0000" />} + @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3) + min_value = "foo" + max_value = "bar" + assert_dom_equal(expected, datetime_field("post", "written_on", min: min_value, max: max_value)) + end + def test_datetime_local_field expected = %{<input id="post_written_on" name="post[written_on]" type="datetime-local" value="2004-06-15T00:00:00" />} assert_dom_equal(expected, datetime_local_field("post", "written_on")) @@ -876,6 +1153,22 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal(expected, datetime_local_field("post", "written_on")) end + def test_datetime_local_field_with_string_values_for_min_and_max + expected = %{<input id="post_written_on" max="2010-08-15T10:25:00" min="2000-06-15T20:45:30" name="post[written_on]" type="datetime-local" value="2004-06-15T01:02:03" />} + @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3) + min_value = "2000-06-15T20:45:30" + max_value = "2010-08-15T10:25:00" + assert_dom_equal(expected, datetime_local_field("post", "written_on", min: min_value, max: max_value)) + end + + def test_datetime_local_field_with_invalid_string_values_for_min_and_max + expected = %{<input id="post_written_on" name="post[written_on]" type="datetime-local" value="2004-06-15T01:02:03" />} + @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3) + min_value = "foo" + max_value = "bar" + assert_dom_equal(expected, datetime_local_field("post", "written_on", min: min_value, max: max_value)) + end + def test_month_field expected = %{<input id="post_written_on" name="post[written_on]" type="month" value="2004-06" />} assert_dom_equal(expected, month_field("post", "written_on")) @@ -1427,7 +1720,7 @@ class FormHelperTest < ActionView::TestCase expected = whole_form("/posts", "new_post", "new_post") do "<input checked='checked' id='post_1_tag_ids_1' name='post[1][tag_ids][]' type='checkbox' value='1' />" + "<label for='post_1_tag_ids_1'>Tag 1</label>" + - "<input name='post[tag_ids][]' type='hidden' value='' />" + "<input name='post[1][tag_ids][]' type='hidden' value='' />" end assert_dom_equal expected, output_buffer @@ -1441,7 +1734,7 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch", multipart: true) do - "<input name='post[file]' type='file' id='post_file' />" + "<input name='post[file]' type='hidden' value='' /><input name='post[file]' type='file' id='post_file' />" end assert_dom_equal expected, output_buffer @@ -1457,7 +1750,7 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form("/posts/123", "edit_post_123", "edit_post", method: "patch", multipart: true) do - "<input name='post[comment][file]' type='file' id='post_comment_file' />" + "<input name='post[comment][file]' type='hidden' value='' /><input name='post[comment][file]' type='file' id='post_comment_file' />" end assert_dom_equal expected, output_buffer @@ -1512,6 +1805,20 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_form_tags_do_not_call_private_properties_on_form_object + obj = Class.new do + private + + def private_property + raise "This method should not be called." + end + end.new + + form_for(obj, as: "other_name", url: '/', html: { id: "edit-other-name" }) do |f| + assert_raise(NoMethodError) { f.hidden_field(:private_property) } + end + end + def test_form_for_with_method_as_part_of_html_options form_for(@post, url: '/', html: { id: 'create-post', method: :delete }) do |f| concat f.text_field(:title) @@ -1577,6 +1884,30 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_form_for_enforce_utf8_true + form_for(:post, enforce_utf8: true) do |f| + concat f.text_field(:title) + end + + expected = whole_form("/", nil, nil, enforce_utf8: true) do + "<input name='post[title]' type='text' id='post_title' value='Hello World' />" + end + + assert_dom_equal expected, output_buffer + end + + def test_form_for_enforce_utf8_false + form_for(:post, enforce_utf8: false) do |f| + concat f.text_field(:title) + end + + expected = whole_form("/", nil, nil, enforce_utf8: false) do + "<input name='post[title]' type='text' id='post_title' value='Hello World' />" + end + + assert_dom_equal expected, output_buffer + end + def test_form_for_with_remote_in_html form_for(@post, url: '/', html: { remote: true, id: 'create-post', method: :patch }) do |f| concat f.text_field(:title) @@ -1811,69 +2142,61 @@ class FormHelperTest < ActionView::TestCase end def test_submit_with_object_as_new_record_and_locale_strings - old_locale, I18n.locale = I18n.locale, :submit + with_locale :submit do + @post.persisted = false + @post.stubs(:to_key).returns(nil) + form_for(@post) do |f| + concat f.submit + end - @post.persisted = false - @post.stubs(:to_key).returns(nil) - form_for(@post) do |f| - concat f.submit - end + expected = whole_form('/posts', 'new_post', 'new_post') do + "<input name='commit' type='submit' value='Create Post' />" + end - expected = whole_form('/posts', 'new_post', 'new_post') do - "<input name='commit' type='submit' value='Create Post' />" + assert_dom_equal expected, output_buffer end - - assert_dom_equal expected, output_buffer - ensure - I18n.locale = old_locale end def test_submit_with_object_as_existing_record_and_locale_strings - old_locale, I18n.locale = I18n.locale, :submit + with_locale :submit do + form_for(@post) do |f| + concat f.submit + end - form_for(@post) do |f| - concat f.submit - end + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do + "<input name='commit' type='submit' value='Confirm Post changes' />" + end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do - "<input name='commit' type='submit' value='Confirm Post changes' />" + assert_dom_equal expected, output_buffer end - - assert_dom_equal expected, output_buffer - ensure - I18n.locale = old_locale end def test_submit_without_object_and_locale_strings - old_locale, I18n.locale = I18n.locale, :submit + with_locale :submit do + form_for(:post) do |f| + concat f.submit class: "extra" + end - form_for(:post) do |f| - concat f.submit class: "extra" - end + expected = whole_form do + "<input name='commit' class='extra' type='submit' value='Save changes' />" + end - expected = whole_form do - "<input name='commit' class='extra' type='submit' value='Save changes' />" + assert_dom_equal expected, output_buffer end - - assert_dom_equal expected, output_buffer - ensure - I18n.locale = old_locale end def test_submit_with_object_and_nested_lookup - old_locale, I18n.locale = I18n.locale, :submit + with_locale :submit do + form_for(@post, as: :another_post) do |f| + concat f.submit + end - form_for(@post, as: :another_post) do |f| - concat f.submit - end + expected = whole_form('/posts/123', 'edit_another_post', 'edit_another_post', method: 'patch') do + "<input name='commit' type='submit' value='Update your Post' />" + end - expected = whole_form('/posts/123', 'edit_another_post', 'edit_another_post', method: 'patch') do - "<input name='commit' type='submit' value='Update your Post' />" + assert_dom_equal expected, output_buffer end - - assert_dom_equal expected, output_buffer - ensure - I18n.locale = old_locale end def test_nested_fields_for @@ -2405,6 +2728,18 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_nested_fields_label_translation_with_more_than_10_records + @post.comments = Array.new(11) { |id| Comment.new(id + 1) } + + I18n.expects(:t).with('post.comments.body', default: [:"comment.body", ''], scope: "helpers.label").times(11).returns "Write body here" + + form_for(@post) do |f| + f.fields_for(:comments) do |cf| + concat cf.label(:body) + end + end + end + def test_nested_fields_for_with_existing_records_on_a_supplied_nested_attributes_collection_different_from_record_one comments = Array.new(2) { |id| Comment.new(id + 1) } @post.comments = [] @@ -2918,7 +3253,7 @@ class FormHelperTest < ActionView::TestCase def test_form_for_with_string_url_option form_for(@post, url: 'http://www.otherdomain.com') do |f| end - assert_equal whole_form("http://www.otherdomain.com", "edit_post_123", "edit_post", method: "patch"), output_buffer + assert_dom_equal whole_form("http://www.otherdomain.com", "edit_post_123", "edit_post", method: "patch"), output_buffer end def test_form_for_with_hash_url_option @@ -2932,14 +3267,14 @@ class FormHelperTest < ActionView::TestCase form_for(@post, url: @post) do |f| end expected = whole_form("/posts/123", "edit_post_123", "edit_post", method: "patch") - assert_equal expected, output_buffer + assert_dom_equal expected, output_buffer end def test_form_for_with_existing_object form_for(@post) do |f| end expected = whole_form("/posts/123", "edit_post_123", "edit_post", method: "patch") - assert_equal expected, output_buffer + assert_dom_equal expected, output_buffer end def test_form_for_with_new_object @@ -2950,7 +3285,7 @@ class FormHelperTest < ActionView::TestCase form_for(post) do |f| end expected = whole_form("/posts", "new_post", "new_post") - assert_equal expected, output_buffer + assert_dom_equal expected, output_buffer end def test_form_for_with_existing_object_in_list @@ -2987,7 +3322,7 @@ class FormHelperTest < ActionView::TestCase form_for(@post, url: "/super_posts") do |f| end expected = whole_form("/super_posts", "edit_post_123", "edit_post", method: "patch") - assert_equal expected, output_buffer + assert_dom_equal expected, output_buffer end def test_form_for_with_default_method_as_patch @@ -3022,13 +3357,20 @@ class FormHelperTest < ActionView::TestCase protected - def hidden_fields(method = nil) - txt = %{<div style="display:none">} - txt << %{<input name="utf8" type="hidden" value="✓" />} + def hidden_fields(options = {}) + method = options[:method] + + if options.fetch(:enforce_utf8, true) + txt = %{<input name="utf8" type="hidden" value="✓" />} + else + txt = '' + end + if method && !%w(get post).include?(method.to_s) txt << %{<input name="_method" type="hidden" value="#{method}" />} end - txt << %{</div>} + + txt end def form_text(action = "/", id = nil, html_class = nil, remote = nil, multipart = nil, method = nil) @@ -3046,10 +3388,17 @@ class FormHelperTest < ActionView::TestCase method, remote, multipart = options.values_at(:method, :remote, :multipart) - form_text(action, id, html_class, remote, multipart, method) + hidden_fields(method) + contents + "</form>" + form_text(action, id, html_class, remote, multipart, method) + hidden_fields(options.slice :method, :enforce_utf8) + contents + "</form>" end def protect_against_forgery? false end + + def with_locale(testing_locale = :label) + old_locale, I18n.locale = I18n.locale, testing_locale + yield + ensure + I18n.locale = old_locale + end end diff --git a/actionview/test/template/form_options_helper_test.rb b/actionview/test/template/form_options_helper_test.rb index 50e9d132a7..d25fa3706f 100644 --- a/actionview/test/template/form_options_helper_test.rb +++ b/actionview/test/template/form_options_helper_test.rb @@ -119,6 +119,26 @@ class FormOptionsHelperTest < ActionView::TestCase ) end + def test_array_options_for_select_with_custom_defined_selected + assert_dom_equal( + "<option selected=\"selected\" type=\"Coach\" value=\"1\">Richard Bandler</option>\n<option type=\"Coachee\" value=\"1\">Richard Bandler</option>", + options_for_select([ + ['Richard Bandler', 1, { type: 'Coach', selected: 'selected' }], + ['Richard Bandler', 1, { type: 'Coachee' }] + ]) + ) + end + + def test_array_options_for_select_with_custom_defined_disabled + assert_dom_equal( + "<option disabled=\"disabled\" type=\"Coach\" value=\"1\">Richard Bandler</option>\n<option type=\"Coachee\" value=\"1\">Richard Bandler</option>", + options_for_select([ + ['Richard Bandler', 1, { type: 'Coach', disabled: 'disabled' }], + ['Richard Bandler', 1, { type: 'Coachee' }] + ]) + ) + end + def test_array_options_for_select_with_selection assert_dom_equal( "<option value=\"Denmark\">Denmark</option>\n<option value=\"<USA>\" selected=\"selected\"><USA></option>\n<option value=\"Sweden\">Sweden</option>", @@ -571,6 +591,19 @@ class FormOptionsHelperTest < ActionView::TestCase ) end + def test_select_under_fields_for_with_block_without_options + @post = Post.new + + output_buffer = fields_for :post, @post do |f| + concat(f.select(:category) {}) + end + + assert_dom_equal( + "<select id=\"post_category\" name=\"post[category]\"></select>", + output_buffer + ) + end + def test_select_with_multiple_to_add_hidden_input output_buffer = select(:post, :category, "", {}, :multiple => true) assert_dom_equal( @@ -813,7 +846,7 @@ class FormOptionsHelperTest < ActionView::TestCase select("post", "category", %w( one two ), :selected => 'two', :prompt => true) ) end - + def test_select_with_disabled_array @post = Post.new @post.category = "<mus>" diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb index 0d5831dc6f..84a581b107 100644 --- a/actionview/test/template/form_tag_helper_test.rb +++ b/actionview/test/template/form_tag_helper_test.rb @@ -14,12 +14,15 @@ class FormTagHelperTest < ActionView::TestCase method = options[:method] enforce_utf8 = options.fetch(:enforce_utf8, true) - txt = %{<div style="display:none">} - txt << %{<input name="utf8" type="hidden" value="✓" />} if enforce_utf8 - if method && !%w(get post).include?(method.to_s) - txt << %{<input name="_method" type="hidden" value="#{method}" />} + ''.tap do |txt| + if enforce_utf8 + txt << %{<input name="utf8" type="hidden" value="✓" />} + end + + if method && !%w(get post).include?(method.to_s) + txt << %{<input name="_method" type="hidden" value="#{method}" />} + end end - txt << %{</div>} end def form_text(action = "http://www.example.com", options = {}) @@ -167,6 +170,13 @@ class FormTagHelperTest < ActionView::TestCase assert_dom_equal expected, actual end + def test_multiple_field_tags_with_same_options + options = {class: 'important'} + assert_dom_equal %(<input name="title" type="file" id="title" class="important"/>), file_field_tag("title", options) + assert_dom_equal %(<input type="password" name="title" id="title" value="Hello!" class="important" />), password_field_tag("title", "Hello!", options) + assert_dom_equal %(<input type="text" name="title" id="title" value="Hello!" class="important" />), text_field_tag("title", "Hello!", options) + end + def test_radio_button_tag actual = radio_button_tag "people", "david" expected = %(<input id="people_david" name="people" type="radio" value="david" />) @@ -222,6 +232,18 @@ class FormTagHelperTest < ActionView::TestCase assert_dom_equal expected, actual end + def test_select_tag_with_include_blank_false + actual = select_tag "places", "<option>Home</option><option>Work</option><option>Pub</option>".html_safe, include_blank: false + expected = %(<select id="places" name="places"><option>Home</option><option>Work</option><option>Pub</option></select>) + assert_dom_equal expected, actual + end + + def test_select_tag_with_include_blank_string + actual = select_tag "places", "<option>Home</option><option>Work</option><option>Pub</option>".html_safe, include_blank: 'Choose' + expected = %(<select id="places" name="places"><option value="">Choose</option><option>Home</option><option>Work</option><option>Pub</option></select>) + assert_dom_equal expected, actual + end + def test_select_tag_with_prompt actual = select_tag "places", "<option>Home</option><option>Work</option><option>Pub</option>".html_safe, :prompt => "string" expected = %(<select id="places" name="places"><option value="">string</option><option>Home</option><option>Work</option><option>Pub</option></select>) @@ -476,6 +498,11 @@ class FormTagHelperTest < ActionView::TestCase assert_dom_equal('<button name="temptation" type="button"><strong>Do not press me</strong></button>', output) end + def test_button_tag_defaults_with_block_and_options + output = button_tag(:name => 'temptation', :value => 'within') { content_tag(:strong, 'Do not press me') } + assert_dom_equal('<button name="temptation" value="within" type="submit" ><strong>Do not press me</strong></button>', output) + end + def test_button_tag_with_confirmation assert_dom_equal( %(<button name="button" type="submit" data-confirm="Are you sure?">Save</button>), @@ -624,6 +651,6 @@ class FormTagHelperTest < ActionView::TestCase private def root_elem(rendered_content) - HTML::Document.new(rendered_content).root.children[0] + Nokogiri::HTML::DocumentFragment.parse(rendered_content).children.first # extract from nodeset end end diff --git a/actionview/test/template/html-scanner/cdata_node_test.rb b/actionview/test/template/html-scanner/cdata_node_test.rb deleted file mode 100644 index 9b58174641..0000000000 --- a/actionview/test/template/html-scanner/cdata_node_test.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'abstract_unit' - -class CDATANodeTest < ActiveSupport::TestCase - def setup - @node = HTML::CDATA.new(nil, 0, 0, "<p>howdy</p>") - end - - def test_to_s - assert_equal "<![CDATA[<p>howdy</p>]]>", @node.to_s - end - - def test_content - assert_equal "<p>howdy</p>", @node.content - end -end diff --git a/actionview/test/template/html-scanner/document_test.rb b/actionview/test/template/html-scanner/document_test.rb deleted file mode 100644 index 17f045d549..0000000000 --- a/actionview/test/template/html-scanner/document_test.rb +++ /dev/null @@ -1,148 +0,0 @@ -require 'abstract_unit' - -class DocumentTest < ActiveSupport::TestCase - def test_handle_doctype - doc = nil - assert_nothing_raised do - doc = HTML::Document.new <<-HTML.strip - <!DOCTYPE "blah" "blah" "blah"> - <html> - </html> - HTML - end - assert_equal 3, doc.root.children.length - assert_equal %{<!DOCTYPE "blah" "blah" "blah">}, doc.root.children[0].content - assert_match %r{\s+}m, doc.root.children[1].content - assert_equal "html", doc.root.children[2].name - end - - def test_find_img - doc = HTML::Document.new <<-HTML.strip - <html> - <body> - <p><img src="hello.gif"></p> - </body> - </html> - HTML - assert doc.find(:tag=>"img", :attributes=>{"src"=>"hello.gif"}) - end - - def test_find_all - doc = HTML::Document.new <<-HTML.strip - <html> - <body> - <p class="test"><img src="hello.gif"></p> - <div class="foo"> - <p class="test">something</p> - <p>here is <em class="test">more</em></p> - </div> - </body> - </html> - HTML - all = doc.find_all :attributes => { :class => "test" } - assert_equal 3, all.length - assert_equal [ "p", "p", "em" ], all.map { |n| n.name } - end - - def test_find_with_text - doc = HTML::Document.new <<-HTML.strip - <html> - <body> - <p>Some text</p> - </body> - </html> - HTML - assert doc.find(:content => "Some text") - assert doc.find(:tag => "p", :child => { :content => "Some text" }) - assert doc.find(:tag => "p", :child => "Some text") - assert doc.find(:tag => "p", :content => "Some text") - end - - def test_parse_xml - assert_nothing_raised { HTML::Document.new("<tags><tag/></tags>", true, true) } - assert_nothing_raised { HTML::Document.new("<outer><link>something</link></outer>", true, true) } - end - - def test_parse_document - doc = HTML::Document.new(<<-HTML) - <div> - <h2>blah</h2> - <table> - </table> - </div> - HTML - assert_not_nil doc.find(:tag => "div", :children => { :count => 1, :only => { :tag => "table" } }) - end - - def test_tag_nesting_nothing_to_s - doc = HTML::Document.new("<tag></tag>") - assert_equal "<tag></tag>", doc.root.to_s - end - - def test_tag_nesting_space_to_s - doc = HTML::Document.new("<tag> </tag>") - assert_equal "<tag> </tag>", doc.root.to_s - end - - def test_tag_nesting_text_to_s - doc = HTML::Document.new("<tag>text</tag>") - assert_equal "<tag>text</tag>", doc.root.to_s - end - - def test_tag_nesting_tag_to_s - doc = HTML::Document.new("<tag><nested /></tag>") - assert_equal "<tag><nested /></tag>", doc.root.to_s - end - - def test_parse_cdata - doc = HTML::Document.new(<<-HTML) -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> - <head> - <title><![CDATA[<br>]]></title> - </head> - <body> - <p>this document has <br> for a title</p> - </body> -</html> -HTML - - assert_nil doc.find(:tag => "title", :descendant => { :tag => "br" }) - assert doc.find(:tag => "title", :child => "<br>") - end - - def test_find_empty_tag - doc = HTML::Document.new("<div id='map'></div>") - assert_nil doc.find(:tag => "div", :attributes => { :id => "map" }, :content => /./) - assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => /\A\Z/) - assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => /^$/) - assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => "") - assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => nil) - end - - def test_parse_invalid_document - assert_nothing_raised do - HTML::Document.new("<html> - <table> - <tr> - <td style=\"color: #FFFFFF; height: 17px; onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" style=\"cursor:pointer; height: 17px;\"; nowrap onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" onmouseout=\"this.bgColor='#0066cc'; this.style.color='#FFFFFF'\" onmouseover=\"this.bgColor='#ffffff'; this.style.color='#0033cc'\">About Us</td> - </tr> - </table> - </html>") - end - end - - def test_invalid_document_raises_exception_when_strict - assert_raise RuntimeError do - HTML::Document.new("<html> - <table> - <tr> - <td style=\"color: #FFFFFF; height: 17px; onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" style=\"cursor:pointer; height: 17px;\"; nowrap onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" onmouseout=\"this.bgColor='#0066cc'; this.style.color='#FFFFFF'\" onmouseover=\"this.bgColor='#ffffff'; this.style.color='#0033cc'\">About Us</td> - </tr> - </table> - </html>", true) - end - end - -end diff --git a/actionview/test/template/html-scanner/node_test.rb b/actionview/test/template/html-scanner/node_test.rb deleted file mode 100644 index 5b5d092036..0000000000 --- a/actionview/test/template/html-scanner/node_test.rb +++ /dev/null @@ -1,89 +0,0 @@ -require 'abstract_unit' - -class NodeTest < ActiveSupport::TestCase - - class MockNode - def initialize(matched, value) - @matched = matched - @value = value - end - - def find(conditions) - @matched && self - end - - def to_s - @value.to_s - end - end - - def setup - @node = HTML::Node.new("parent") - @node.children.concat [MockNode.new(false,1), MockNode.new(true,"two"), MockNode.new(false,:three)] - end - - def test_match - assert !@node.match("foo") - end - - def test_tag - assert !@node.tag? - end - - def test_to_s - assert_equal "1twothree", @node.to_s - end - - def test_find - assert_equal "two", @node.find('blah').to_s - end - - def test_parse_strict - s = "<b foo='hello'' bar='baz'>" - assert_raise(RuntimeError) { HTML::Node.parse(nil,0,0,s) } - end - - def test_parse_relaxed - s = "<b foo='hello'' bar='baz'>" - node = nil - assert_nothing_raised { node = HTML::Node.parse(nil,0,0,s,false) } - assert node.attributes.has_key?("foo") - assert !node.attributes.has_key?("bar") - end - - def test_to_s_with_boolean_attrs - s = "<b foo bar>" - node = HTML::Node.parse(nil,0,0,s) - assert node.attributes.has_key?("foo") - assert node.attributes.has_key?("bar") - assert "<b foo bar>", node.to_s - end - - def test_parse_with_unclosed_tag - s = "<span onmouseover='bang'" - node = nil - assert_nothing_raised { node = HTML::Node.parse(nil,0,0,s,false) } - assert node.attributes.has_key?("onmouseover") - end - - def test_parse_with_valid_cdata_section - s = "<![CDATA[<span>contents</span>]]>" - node = nil - assert_nothing_raised { node = HTML::Node.parse(nil,0,0,s,false) } - assert_kind_of HTML::CDATA, node - assert_equal '<span>contents</span>', node.content - end - - def test_parse_strict_with_unterminated_cdata_section - s = "<![CDATA[neverending..." - assert_raise(RuntimeError) { HTML::Node.parse(nil,0,0,s) } - end - - def test_parse_relaxed_with_unterminated_cdata_section - s = "<![CDATA[neverending..." - node = nil - assert_nothing_raised { node = HTML::Node.parse(nil,0,0,s,false) } - assert_kind_of HTML::CDATA, node - assert_equal 'neverending...', node.content - end -end diff --git a/actionview/test/template/html-scanner/sanitizer_test.rb b/actionview/test/template/html-scanner/sanitizer_test.rb deleted file mode 100644 index b1c1b83807..0000000000 --- a/actionview/test/template/html-scanner/sanitizer_test.rb +++ /dev/null @@ -1,330 +0,0 @@ -require 'abstract_unit' - -class SanitizerTest < ActionController::TestCase - def setup - @sanitizer = nil # used by assert_sanitizer - end - - def test_strip_tags_with_quote - sanitizer = HTML::FullSanitizer.new - string = '<" <img src="trollface.gif" onload="alert(1)"> hi' - - assert_equal ' hi', sanitizer.sanitize(string) - end - - def test_strip_tags - sanitizer = HTML::FullSanitizer.new - assert_equal("<<<bad html", sanitizer.sanitize("<<<bad html")) - assert_equal("<<", sanitizer.sanitize("<<<bad html>")) - assert_equal("Dont touch me", sanitizer.sanitize("Dont touch me")) - assert_equal("This is a test.", sanitizer.sanitize("<p>This <u>is<u> a <a href='test.html'><strong>test</strong></a>.</p>")) - assert_equal("Weirdos", sanitizer.sanitize("Wei<<a>a onclick='alert(document.cookie);'</a>/>rdos")) - assert_equal("This is a test.", sanitizer.sanitize("This is a test.")) - assert_equal( - %{This is a test.\n\n\nIt no longer contains any HTML.\n}, sanitizer.sanitize( - %{<title>This is <b>a <a href="" target="_blank">test</a></b>.</title>\n\n<!-- it has a comment -->\n\n<p>It no <b>longer <strong>contains <em>any <strike>HTML</strike></em>.</strong></b></p>\n})) - assert_equal "This has a here.", sanitizer.sanitize("This has a <!-- comment --> here.") - assert_equal "This has a here.", sanitizer.sanitize("This has a <![CDATA[<section>]]> here.") - assert_equal "This has an unclosed ", sanitizer.sanitize("This has an unclosed <![CDATA[<section>]] here...") - [nil, '', ' '].each { |blank| assert_equal blank, sanitizer.sanitize(blank) } - assert_nothing_raised { sanitizer.sanitize("This is a frozen string with no tags".freeze) } - end - - def test_strip_links - sanitizer = HTML::LinkSanitizer.new - assert_equal "Dont touch me", sanitizer.sanitize("Dont touch me") - assert_equal "on my mind\nall day long", sanitizer.sanitize("<a href='almost'>on my mind</a>\n<A href='almost'>all day long</A>") - assert_equal "0wn3d", sanitizer.sanitize("<a href='http://www.rubyonrails.com/'><a href='http://www.rubyonrails.com/' onlclick='steal()'>0wn3d</a></a>") - assert_equal "Magic", sanitizer.sanitize("<a href='http://www.rubyonrails.com/'>Mag<a href='http://www.ruby-lang.org/'>ic") - assert_equal "FrrFox", sanitizer.sanitize("<href onlclick='steal()'>FrrFox</a></href>") - assert_equal "My mind\nall <b>day</b> long", sanitizer.sanitize("<a href='almost'>My mind</a>\n<A href='almost'>all <b>day</b> long</A>") - assert_equal "all <b>day</b> long", sanitizer.sanitize("<<a>a href='hello'>all <b>day</b> long<</A>/a>") - - assert_equal "<a<a", sanitizer.sanitize("<a<a") - end - - def test_sanitize_form - assert_sanitized "<form action=\"/foo/bar\" method=\"post\"><input></form>", '' - end - - def test_sanitize_plaintext - raw = "<plaintext><span>foo</span></plaintext>" - assert_sanitized raw, "<span>foo</span>" - end - - def test_sanitize_script - assert_sanitized "a b c<script language=\"Javascript\">blah blah blah</script>d e f", "a b cd e f" - end - - def test_sanitize_js_handlers - raw = %{onthis="do that" <a href="#" onclick="hello" name="foo" onbogus="remove me">hello</a>} - assert_sanitized raw, %{onthis="do that" <a name="foo" href="#">hello</a>} - end - - def test_sanitize_javascript_href - raw = %{href="javascript:bang" <a href="javascript:bang" name="hello">foo</a>, <span href="javascript:bang">bar</span>} - assert_sanitized raw, %{href="javascript:bang" <a name="hello">foo</a>, <span>bar</span>} - end - - def test_sanitize_image_src - raw = %{src="javascript:bang" <img src="javascript:bang" width="5">foo</img>, <span src="javascript:bang">bar</span>} - assert_sanitized raw, %{src="javascript:bang" <img width="5">foo</img>, <span>bar</span>} - end - - HTML::WhiteListSanitizer.allowed_tags.each do |tag_name| - define_method "test_should_allow_#{tag_name}_tag" do - assert_sanitized "start <#{tag_name} title=\"1\" onclick=\"foo\">foo <bad>bar</bad> baz</#{tag_name}> end", %(start <#{tag_name} title="1">foo bar baz</#{tag_name}> end) - end - end - - def test_should_allow_anchors - assert_sanitized %(<a href="foo" onclick="bar"><script>baz</script></a>), %(<a href="foo"></a>) - end - - # RFC 3986, sec 4.2 - def test_allow_colons_in_path_component - assert_sanitized("<a href=\"./this:that\">foo</a>") - end - - %w(src width height alt).each do |img_attr| - define_method "test_should_allow_image_#{img_attr}_attribute" do - assert_sanitized %(<img #{img_attr}="foo" onclick="bar" />), %(<img #{img_attr}="foo" />) - end - end - - def test_should_handle_non_html - assert_sanitized 'abc' - end - - def test_should_handle_blank_text - assert_sanitized nil - assert_sanitized '' - end - - def test_should_allow_custom_tags - text = "<u>foo</u>" - sanitizer = HTML::WhiteListSanitizer.new - assert_equal(text, sanitizer.sanitize(text, :tags => %w(u))) - end - - def test_should_allow_only_custom_tags - text = "<u>foo</u> with <i>bar</i>" - sanitizer = HTML::WhiteListSanitizer.new - assert_equal("<u>foo</u> with bar", sanitizer.sanitize(text, :tags => %w(u))) - end - - def test_should_allow_custom_tags_with_attributes - text = %(<blockquote cite="http://example.com/">foo</blockquote>) - sanitizer = HTML::WhiteListSanitizer.new - assert_equal(text, sanitizer.sanitize(text)) - end - - def test_should_allow_custom_tags_with_custom_attributes - text = %(<blockquote foo="bar">Lorem ipsum</blockquote>) - sanitizer = HTML::WhiteListSanitizer.new - assert_equal(text, sanitizer.sanitize(text, :attributes => ['foo'])) - end - - def test_should_raise_argument_error_if_tags_is_not_enumerable - sanitizer = HTML::WhiteListSanitizer.new - e = assert_raise(ArgumentError) do - sanitizer.sanitize('', :tags => 'foo') - end - - assert_equal "You should pass :tags as an Enumerable", e.message - end - - def test_should_raise_argument_error_if_attributes_is_not_enumerable - sanitizer = HTML::WhiteListSanitizer.new - e = assert_raise(ArgumentError) do - sanitizer.sanitize('', :attributes => 'foo') - end - - assert_equal "You should pass :attributes as an Enumerable", e.message - end - - [%w(img src), %w(a href)].each do |(tag, attr)| - define_method "test_should_strip_#{attr}_attribute_in_#{tag}_with_bad_protocols" do - assert_sanitized %(<#{tag} #{attr}="javascript:bang" title="1">boo</#{tag}>), %(<#{tag} title="1">boo</#{tag}>) - end - end - - def test_should_flag_bad_protocols - sanitizer = HTML::WhiteListSanitizer.new - %w(about chrome data disk hcp help javascript livescript lynxcgi lynxexec ms-help ms-its mhtml mocha opera res resource shell vbscript view-source vnd.ms.radio wysiwyg).each do |proto| - assert sanitizer.send(:contains_bad_protocols?, 'src', "#{proto}://bad") - end - end - - def test_should_accept_good_protocols_ignoring_case - sanitizer = HTML::WhiteListSanitizer.new - HTML::WhiteListSanitizer.allowed_protocols.each do |proto| - assert !sanitizer.send(:contains_bad_protocols?, 'src', "#{proto.capitalize}://good") - end - end - - def test_should_accept_good_protocols_ignoring_space - sanitizer = HTML::WhiteListSanitizer.new - HTML::WhiteListSanitizer.allowed_protocols.each do |proto| - assert !sanitizer.send(:contains_bad_protocols?, 'src', " #{proto}://good") - end - end - - def test_should_accept_good_protocols - sanitizer = HTML::WhiteListSanitizer.new - HTML::WhiteListSanitizer.allowed_protocols.each do |proto| - assert !sanitizer.send(:contains_bad_protocols?, 'src', "#{proto}://good") - end - end - - def test_should_reject_hex_codes_in_protocol - assert_sanitized %(<a href="%6A%61%76%61%73%63%72%69%70%74%3A%61%6C%65%72%74%28%22%58%53%53%22%29">1</a>), "<a>1</a>" - assert @sanitizer.send(:contains_bad_protocols?, 'src', "%6A%61%76%61%73%63%72%69%70%74%3A%61%6C%65%72%74%28%22%58%53%53%22%29") - end - - def test_should_block_script_tag - assert_sanitized %(<SCRIPT\nSRC=http://ha.ckers.org/xss.js></SCRIPT>), "" - end - - [%(<IMG SRC="javascript:alert('XSS');">), - %(<IMG SRC=javascript:alert('XSS')>), - %(<IMG SRC=JaVaScRiPt:alert('XSS')>), - %(<IMG """><SCRIPT>alert("XSS")</SCRIPT>">), - %(<IMG SRC=javascript:alert("XSS")>), - %(<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>), - %(<IMG SRC=javascript:alert('XSS')>), - %(<IMG SRC=javascript:alert('XSS')>), - %(<IMG SRC=javascript:alert('XSS')>), - %(<IMG SRC="jav\tascript:alert('XSS');">), - %(<IMG SRC="jav	ascript:alert('XSS');">), - %(<IMG SRC="jav
ascript:alert('XSS');">), - %(<IMG SRC="jav
ascript:alert('XSS');">), - %(<IMG SRC="  javascript:alert('XSS');">), - %(<IMG SRC="javascript:alert('XSS');">), - %(<IMG SRC=`javascript:alert("RSnake says, 'XSS'")`>)].each_with_index do |img_hack, i| - define_method "test_should_not_fall_for_xss_image_hack_#{i+1}" do - assert_sanitized img_hack, "<img>" - end - end - - def test_should_sanitize_tag_broken_up_by_null - assert_sanitized %(<SCR\0IPT>alert(\"XSS\")</SCR\0IPT>), "alert(\"XSS\")" - end - - def test_should_sanitize_invalid_script_tag - assert_sanitized %(<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT>), "" - end - - def test_should_sanitize_script_tag_with_multiple_open_brackets - assert_sanitized %(<<SCRIPT>alert("XSS");//<</SCRIPT>), "<" - assert_sanitized %(<iframe src=http://ha.ckers.org/scriptlet.html\n<a), %(<a) - end - - def test_should_sanitize_unclosed_script - assert_sanitized %(<SCRIPT SRC=http://ha.ckers.org/xss.js?<B>), "<b>" - end - - def test_should_sanitize_half_open_scripts - assert_sanitized %(<IMG SRC="javascript:alert('XSS')"), "<img>" - end - - def test_should_not_fall_for_ridiculous_hack - img_hack = %(<IMG\nSRC\n=\n"\nj\na\nv\na\ns\nc\nr\ni\np\nt\n:\na\nl\ne\nr\nt\n(\n'\nX\nS\nS\n'\n)\n"\n>) - assert_sanitized img_hack, "<img>" - end - - def test_should_sanitize_attributes - assert_sanitized %(<SPAN title="'><script>alert()</script>">blah</SPAN>), %(<span title="#{CGI.escapeHTML "'><script>alert()</script>"}">blah</span>) - end - - def test_should_sanitize_illegal_style_properties - raw = %(display:block; position:absolute; left:0; top:0; width:100%; height:100%; z-index:1; background-color:black; background-image:url(http://www.ragingplatypus.com/i/cam-full.jpg); background-x:center; background-y:center; background-repeat:repeat;) - expected = %(display: block; width: 100%; height: 100%; background-color: black; background-image: ; background-x: center; background-y: center;) - assert_equal expected, sanitize_css(raw) - end - - def test_should_sanitize_with_trailing_space - raw = "display:block; " - expected = "display: block;" - assert_equal expected, sanitize_css(raw) - end - - def test_should_sanitize_xul_style_attributes - raw = %(-moz-binding:url('http://ha.ckers.org/xssmoz.xml#xss')) - assert_equal '', sanitize_css(raw) - end - - def test_should_sanitize_invalid_tag_names - assert_sanitized(%(a b c<script/XSS src="http://ha.ckers.org/xss.js"></script>d e f), "a b cd e f") - end - - def test_should_sanitize_non_alpha_and_non_digit_characters_in_tags - assert_sanitized('<a onclick!#$%&()*~+-_.,:;?@[/|\]^`=alert("XSS")>foo</a>', "<a>foo</a>") - end - - def test_should_sanitize_invalid_tag_names_in_single_tags - assert_sanitized('<img/src="http://ha.ckers.org/xss.js"/>', "<img />") - end - - def test_should_sanitize_img_dynsrc_lowsrc - assert_sanitized(%(<img lowsrc="javascript:alert('XSS')" />), "<img />") - end - - def test_should_sanitize_div_background_image_unicode_encoded - raw = %(background-image:\0075\0072\006C\0028'\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028.1027\0058.1053\0053\0027\0029'\0029) - assert_equal '', sanitize_css(raw) - end - - def test_should_sanitize_div_style_expression - raw = %(width: expression(alert('XSS'));) - assert_equal '', sanitize_css(raw) - end - - def test_should_sanitize_across_newlines - raw = %(\nwidth:\nexpression(alert('XSS'));\n) - assert_equal '', sanitize_css(raw) - end - - def test_should_sanitize_img_vbscript - assert_sanitized %(<img src='vbscript:msgbox("XSS")' />), '<img />' - end - - def test_should_sanitize_cdata_section - assert_sanitized "<![CDATA[<span>section</span>]]>", "<![CDATA[<span>section</span>]]>" - end - - def test_should_sanitize_unterminated_cdata_section - assert_sanitized "<![CDATA[<span>neverending...", "<![CDATA[<span>neverending...]]>" - end - - def test_should_not_mangle_urls_with_ampersand - assert_sanitized %{<a href=\"http://www.domain.com?var1=1&var2=2\">my link</a>} - end - - def test_should_sanitize_neverending_attribute - assert_sanitized "<span class=\"\\", "<span class=\"\\\">" - end - - def test_x03a - assert_sanitized %(<a href="javascript:alert('XSS');">), "<a>" - assert_sanitized %(<a href="javascript:alert('XSS');">), "<a>" - assert_sanitized %(<a href="http://legit">), %(<a href="http://legit">) - assert_sanitized %(<a href="javascript:alert('XSS');">), "<a>" - assert_sanitized %(<a href="javascript:alert('XSS');">), "<a>" - assert_sanitized %(<a href="http://legit">), %(<a href="http://legit">) - end - -protected - def assert_sanitized(input, expected = nil) - @sanitizer ||= HTML::WhiteListSanitizer.new - if input - assert_dom_equal expected || input, @sanitizer.sanitize(input) - else - assert_nil @sanitizer.sanitize(input) - end - end - - def sanitize_css(input) - (@sanitizer ||= HTML::WhiteListSanitizer.new).sanitize_css(input) - end -end diff --git a/actionview/test/template/html-scanner/tag_node_test.rb b/actionview/test/template/html-scanner/tag_node_test.rb deleted file mode 100644 index a29d2d43d7..0000000000 --- a/actionview/test/template/html-scanner/tag_node_test.rb +++ /dev/null @@ -1,243 +0,0 @@ -require 'abstract_unit' - -class TagNodeTest < ActiveSupport::TestCase - def test_open_without_attributes - node = tag("<tag>") - assert_equal "tag", node.name - assert_equal Hash.new, node.attributes - assert_nil node.closing - end - - def test_open_with_attributes - node = tag("<TAG1 foo=hey_ho x:bar=\"blah blah\" BAZ='blah blah blah' >") - assert_equal "tag1", node.name - assert_equal "hey_ho", node["foo"] - assert_equal "blah blah", node["x:bar"] - assert_equal "blah blah blah", node["baz"] - end - - def test_self_closing_without_attributes - node = tag("<tag/>") - assert_equal "tag", node.name - assert_equal Hash.new, node.attributes - assert_equal :self, node.closing - end - - def test_self_closing_with_attributes - node = tag("<tag a=b/>") - assert_equal "tag", node.name - assert_equal( { "a" => "b" }, node.attributes ) - assert_equal :self, node.closing - end - - def test_closing_without_attributes - node = tag("</tag>") - assert_equal "tag", node.name - assert_nil node.attributes - assert_equal :close, node.closing - end - - def test_bracket_op_when_no_attributes - node = tag("</tag>") - assert_nil node["foo"] - end - - def test_bracket_op_when_attributes - node = tag("<tag a=b/>") - assert_equal "b", node["a"] - end - - def test_attributes_with_escaped_quotes - node = tag("<tag a='b\\'c' b=\"bob \\\"float\\\"\">") - assert_equal "b\\'c", node["a"] - assert_equal "bob \\\"float\\\"", node["b"] - end - - def test_to_s - node = tag("<a b=c d='f' g=\"h 'i'\" />") - node = node.to_s - assert node.include?('a') - assert node.include?('b="c"') - assert node.include?('d="f"') - assert node.include?('g="h') - assert node.include?('i') - end - - def test_tag - assert tag("<tag>").tag? - end - - def test_match_tag_as_string - assert tag("<tag>").match(:tag => "tag") - assert !tag("<tag>").match(:tag => "b") - end - - def test_match_tag_as_regexp - assert tag("<tag>").match(:tag => /t.g/) - assert !tag("<tag>").match(:tag => /t[bqs]g/) - end - - def test_match_attributes_as_string - t = tag("<tag a=something b=else />") - assert t.match(:attributes => {"a" => "something"}) - assert t.match(:attributes => {"b" => "else"}) - end - - def test_match_attributes_as_regexp - t = tag("<tag a=something b=else />") - assert t.match(:attributes => {"a" => /^something$/}) - assert t.match(:attributes => {"b" => /e.*e/}) - assert t.match(:attributes => {"a" => /me..i/, "b" => /.ls.$/}) - end - - def test_match_attributes_as_number - t = tag("<tag a=15 b=3.1415 />") - assert t.match(:attributes => {"a" => 15}) - assert t.match(:attributes => {"b" => 3.1415}) - assert t.match(:attributes => {"a" => 15, "b" => 3.1415}) - end - - def test_match_attributes_exist - t = tag("<tag a=15 b=3.1415 />") - assert t.match(:attributes => {"a" => true}) - assert t.match(:attributes => {"b" => true}) - assert t.match(:attributes => {"a" => true, "b" => true}) - end - - def test_match_attributes_not_exist - t = tag("<tag a=15 b=3.1415 />") - assert t.match(:attributes => {"c" => false}) - assert t.match(:attributes => {"c" => nil}) - assert t.match(:attributes => {"a" => true, "c" => false}) - end - - def test_match_parent_success - t = tag("<tag a=15 b='hello'>", tag("<foo k='value'>")) - assert t.match(:parent => {:tag => "foo", :attributes => {"k" => /v.l/, "j" => false}}) - end - - def test_match_parent_fail - t = tag("<tag a=15 b='hello'>", tag("<foo k='value'>")) - assert !t.match(:parent => {:tag => /kafka/}) - end - - def test_match_child_success - t = tag("<tag x:k='something'>") - tag("<child v=john a=kelly>", t) - tag("<sib m=vaughn v=james>", t) - assert t.match(:child => { :tag => "sib", :attributes => {"v" => /j/}}) - assert t.match(:child => { :attributes => {"a" => "kelly"}}) - end - - def test_match_child_fail - t = tag("<tag x:k='something'>") - tag("<child v=john a=kelly>", t) - tag("<sib m=vaughn v=james>", t) - assert !t.match(:child => { :tag => "sib", :attributes => {"v" => /r/}}) - assert !t.match(:child => { :attributes => {"v" => false}}) - end - - def test_match_ancestor_success - t = tag("<tag x:k='something'>", tag("<parent v=john a=kelly>", tag("<grandparent m=vaughn v=james>"))) - assert t.match(:ancestor => {:tag => "parent", :attributes => {"a" => /ll/}}) - assert t.match(:ancestor => {:attributes => {"m" => "vaughn"}}) - end - - def test_match_ancestor_fail - t = tag("<tag x:k='something'>", tag("<parent v=john a=kelly>", tag("<grandparent m=vaughn v=james>"))) - assert !t.match(:ancestor => {:tag => /^parent/, :attributes => {"v" => /m/}}) - assert !t.match(:ancestor => {:attributes => {"v" => false}}) - end - - def test_match_descendant_success - tag("<grandchild m=vaughn v=james>", tag("<child v=john a=kelly>", t = tag("<tag x:k='something'>"))) - assert t.match(:descendant => {:tag => "child", :attributes => {"a" => /ll/}}) - assert t.match(:descendant => {:attributes => {"m" => "vaughn"}}) - end - - def test_match_descendant_fail - tag("<grandchild m=vaughn v=james>", tag("<child v=john a=kelly>", t = tag("<tag x:k='something'>"))) - assert !t.match(:descendant => {:tag => /^child/, :attributes => {"v" => /m/}}) - assert !t.match(:descendant => {:attributes => {"v" => false}}) - end - - def test_match_child_count - t = tag("<tag x:k='something'>") - tag("hello", t) - tag("<child v=john a=kelly>", t) - tag("<sib m=vaughn v=james>", t) - assert t.match(:children => { :count => 2 }) - assert t.match(:children => { :count => 2..4 }) - assert t.match(:children => { :less_than => 4 }) - assert t.match(:children => { :greater_than => 1 }) - assert !t.match(:children => { :count => 3 }) - end - - def test_conditions_as_strings - t = tag("<tag x:k='something'>") - assert t.match("tag" => "tag") - assert t.match("attributes" => { "x:k" => "something" }) - assert !t.match("tag" => "gat") - assert !t.match("attributes" => { "x:j" => "something" }) - end - - def test_attributes_as_symbols - t = tag("<child v=john a=kelly>") - assert t.match(:attributes => { :v => /oh/ }) - assert t.match(:attributes => { :a => /ll/ }) - end - - def test_match_sibling - t = tag("<tag x:k='something'>") - tag("hello", t) - tag("<span a=b>", t) - tag("world", t) - m = tag("<span k=r>", t) - tag("<span m=l>", t) - - assert m.match(:sibling => {:tag => "span", :attributes => {:a => true}}) - assert m.match(:sibling => {:tag => "span", :attributes => {:m => true}}) - assert !m.match(:sibling => {:tag => "span", :attributes => {:k => true}}) - end - - def test_match_sibling_before - t = tag("<tag x:k='something'>") - tag("hello", t) - tag("<span a=b>", t) - tag("world", t) - m = tag("<span k=r>", t) - tag("<span m=l>", t) - - assert m.match(:before => {:tag => "span", :attributes => {:m => true}}) - assert !m.match(:before => {:tag => "span", :attributes => {:a => true}}) - assert !m.match(:before => {:tag => "span", :attributes => {:k => true}}) - end - - def test_match_sibling_after - t = tag("<tag x:k='something'>") - tag("hello", t) - tag("<span a=b>", t) - tag("world", t) - m = tag("<span k=r>", t) - tag("<span m=l>", t) - - assert m.match(:after => {:tag => "span", :attributes => {:a => true}}) - assert !m.match(:after => {:tag => "span", :attributes => {:m => true}}) - assert !m.match(:after => {:tag => "span", :attributes => {:k => true}}) - end - - def test_tag_to_s - t = tag("<b x='foo'>") - tag("hello", t) - tag("<hr />", t) - assert_equal %(<b x="foo">hello<hr /></b>), t.to_s - end - - private - - def tag(content, parent=nil) - node = HTML::Node.parse(parent,0,0,content) - parent.children << node if parent - node - end -end diff --git a/actionview/test/template/html-scanner/text_node_test.rb b/actionview/test/template/html-scanner/text_node_test.rb deleted file mode 100644 index cbcb9e78f0..0000000000 --- a/actionview/test/template/html-scanner/text_node_test.rb +++ /dev/null @@ -1,50 +0,0 @@ -require 'abstract_unit' - -class TextNodeTest < ActiveSupport::TestCase - def setup - @node = HTML::Text.new(nil, 0, 0, "hello, howdy, aloha, annyeong") - end - - def test_to_s - assert_equal "hello, howdy, aloha, annyeong", @node.to_s - end - - def test_find_string - assert_equal @node, @node.find("hello, howdy, aloha, annyeong") - assert_equal false, @node.find("bogus") - end - - def test_find_regexp - assert_equal @node, @node.find(/an+y/) - assert_nil @node.find(/b/) - end - - def test_find_hash - assert_equal @node, @node.find(:content => /howdy/) - assert_nil @node.find(:content => /^howdy$/) - assert_equal false, @node.find(:content => "howdy") - end - - def test_find_other - assert_nil @node.find(:hello) - end - - def test_match_string - assert @node.match("hello, howdy, aloha, annyeong") - assert_equal false, @node.match("bogus") - end - - def test_match_regexp - assert_not_nil @node, @node.match(/an+y/) - assert_nil @node.match(/b/) - end - - def test_match_hash - assert_not_nil @node, @node.match(:content => "howdy") - assert_nil @node.match(:content => /^howdy$/) - end - - def test_match_other - assert_nil @node.match(:hello) - end -end diff --git a/actionview/test/template/html-scanner/tokenizer_test.rb b/actionview/test/template/html-scanner/tokenizer_test.rb deleted file mode 100644 index 1d59de23b6..0000000000 --- a/actionview/test/template/html-scanner/tokenizer_test.rb +++ /dev/null @@ -1,131 +0,0 @@ -require 'abstract_unit' - -class TokenizerTest < ActiveSupport::TestCase - - def test_blank - tokenize "" - assert_end - end - - def test_space - tokenize " " - assert_next " " - assert_end - end - - def test_tag_simple_open - tokenize "<tag>" - assert_next "<tag>" - assert_end - end - - def test_tag_simple_self_closing - tokenize "<tag />" - assert_next "<tag />" - assert_end - end - - def test_tag_simple_closing - tokenize "</tag>" - assert_next "</tag>" - end - - def test_tag_with_single_quoted_attribute - tokenize %{<tag a='hello'>x} - assert_next %{<tag a='hello'>} - end - - def test_tag_with_single_quoted_attribute_with_escape - tokenize %{<tag a='hello\\''>x} - assert_next %{<tag a='hello\\''>} - end - - def test_tag_with_double_quoted_attribute - tokenize %{<tag a="hello">x} - assert_next %{<tag a="hello">} - end - - def test_tag_with_double_quoted_attribute_with_escape - tokenize %{<tag a="hello\\"">x} - assert_next %{<tag a="hello\\"">} - end - - def test_tag_with_unquoted_attribute - tokenize %{<tag a=hello>x} - assert_next %{<tag a=hello>} - end - - def test_tag_with_lt_char_in_attribute - tokenize %{<tag a="x < y">x} - assert_next %{<tag a="x < y">} - end - - def test_tag_with_gt_char_in_attribute - tokenize %{<tag a="x > y">x} - assert_next %{<tag a="x > y">} - end - - def test_doctype_tag - tokenize %{<!DOCTYPE "blah" "blah" "blah">\n <html>} - assert_next %{<!DOCTYPE "blah" "blah" "blah">} - assert_next %{\n } - assert_next %{<html>} - end - - def test_cdata_tag - tokenize %{<![CDATA[<br>]]>} - assert_next %{<![CDATA[<br>]]>} - assert_end - end - - def test_unterminated_cdata_tag - tokenize %{<content:encoded><![CDATA[ neverending...} - assert_next %{<content:encoded>} - assert_next %{<![CDATA[ neverending...} - assert_end - end - - def test_less_than_with_space - tokenize %{original < hello > world} - assert_next %{original } - assert_next %{< hello > world} - end - - def test_less_than_without_matching_greater_than - tokenize %{hello <span onmouseover="gotcha"\n<b>foo</b>\nbar</span>} - assert_next %{hello } - assert_next %{<span onmouseover="gotcha"\n} - assert_next %{<b>} - assert_next %{foo} - assert_next %{</b>} - assert_next %{\nbar} - assert_next %{</span>} - assert_end - end - - def test_unterminated_comment - tokenize %{hello <!-- neverending...} - assert_next %{hello } - assert_next %{<!-- neverending...} - assert_end - end - - private - - def tokenize(text) - @tokenizer = HTML::Tokenizer.new(text) - end - - def assert_next(expected, message=nil) - token = @tokenizer.next - assert_equal expected, token, message - end - - def assert_sequence(*expected) - assert_next expected.shift until expected.empty? - end - - def assert_end(message=nil) - assert_nil @tokenizer.next, message - end -end diff --git a/actionview/test/template/html_test.rb b/actionview/test/template/html_test.rb new file mode 100644 index 0000000000..549c12c88c --- /dev/null +++ b/actionview/test/template/html_test.rb @@ -0,0 +1,17 @@ +require 'abstract_unit' + +class HTMLTest < ActiveSupport::TestCase + test 'formats returns symbol for recognized MIME type' do + assert_equal [:html], ActionView::Template::HTML.new('', :html).formats + end + + test 'formats returns string for recognized MIME type when MIME does not have symbol' do + foo = Mime::Type.lookup("foo") + assert_nil foo.to_sym + assert_equal ['foo'], ActionView::Template::HTML.new('', foo).formats + end + + test 'formats returns string for unknown MIME type' do + assert_equal ['foo'], ActionView::Template::HTML.new('', 'foo').formats + end +end diff --git a/actionview/test/template/javascript_helper_test.rb b/actionview/test/template/javascript_helper_test.rb index 4703111741..9ba7f64ad1 100644 --- a/actionview/test/template/javascript_helper_test.rb +++ b/actionview/test/template/javascript_helper_test.rb @@ -12,14 +12,14 @@ class JavaScriptHelperTest < ActionView::TestCase yield if block_given? end - def setup - super + setup do + @old_escape_html_entities_in_json = ActiveSupport.escape_html_entities_in_json ActiveSupport.escape_html_entities_in_json = true @template = self end def teardown - ActiveSupport.escape_html_entities_in_json = false + ActiveSupport.escape_html_entities_in_json = @old_escape_html_entities_in_json end def test_escape_javascript diff --git a/actionview/test/template/lookup_context_test.rb b/actionview/test/template/lookup_context_test.rb index ce9485e146..4f7823045e 100644 --- a/actionview/test/template/lookup_context_test.rb +++ b/actionview/test/template/lookup_context_test.rb @@ -93,6 +93,20 @@ class LookupContextTest < ActiveSupport::TestCase assert_equal "Hey verden", template.source end + test "find templates with given variants" do + @lookup_context.formats = [:html] + @lookup_context.variants = [:phone] + + template = @lookup_context.find("hello_world", %w(test)) + assert_equal "Hello phone!", template.source + + @lookup_context.variants = [:phone] + @lookup_context.formats = [:text] + + template = @lookup_context.find("hello_world", %w(test)) + assert_equal "Hello texty phone!", template.source + end + test "found templates respects given formats if one cannot be found from template or handler" do ActionView::Template::Handlers::Builder.expects(:default_format).returns(nil) @lookup_context.formats = [:text] diff --git a/actionview/test/template/number_helper_test.rb b/actionview/test/template/number_helper_test.rb index be336ea3fb..b59883b760 100644 --- a/actionview/test/template/number_helper_test.rb +++ b/actionview/test/template/number_helper_test.rb @@ -8,6 +8,8 @@ class NumberHelperTest < ActionView::TestCase assert_equal "555-1234", number_to_phone(5551234) assert_equal "(800) 555-1212 x 123", number_to_phone(8005551212, area_code: true, extension: 123) assert_equal "+18005551212", number_to_phone(8005551212, country_code: 1, delimiter: "") + assert_equal "+<script></script>8005551212", number_to_phone(8005551212, country_code: "<script></script>", delimiter: "") + assert_equal "8005551212 x <script></script>", number_to_phone(8005551212, extension: "<script></script>", delimiter: "") end def test_number_to_currency @@ -16,14 +18,23 @@ class NumberHelperTest < ActionView::TestCase assert_equal "$1,234,567,892", number_to_currency(1234567891.50, precision: 0) assert_equal "1,234,567,890.50 - Kč", number_to_currency("-1234567890.50", unit: raw("Kč"), format: "%n %u", negative_format: "%n - %u") assert_equal "&pound;1,234,567,890.50", number_to_currency("1234567890.50", unit: "£") + assert_equal "<b>1,234,567,890.50</b> $", number_to_currency("1234567890.50", format: "<b>%n</b> %u") + assert_equal "<b>1,234,567,890.50</b> $", number_to_currency("-1234567890.50", negative_format: "<b>%n</b> %u") + assert_equal "<b>1,234,567,890.50</b> $", number_to_currency("-1234567890.50", 'negative_format' => "<b>%n</b> %u") end def test_number_to_percentage assert_equal nil, number_to_percentage(nil) assert_equal "100.000%", number_to_percentage(100) + assert_equal "100.000 %", number_to_percentage(100, format: '%n %') + assert_equal "<b>100.000</b> %", number_to_percentage(100, format: '<b>%n</b> %') + assert_equal "<b>100.000</b> %", number_to_percentage(100, format: raw('<b>%n</b> %')) assert_equal "100%", number_to_percentage(100, precision: 0) assert_equal "123.4%", number_to_percentage(123.400, precision: 3, strip_insignificant_zeros: true) assert_equal "1.000,000%", number_to_percentage(1000, delimiter: ".", separator: ",") + assert_equal "98a%", number_to_percentage("98a") + assert_equal "NaN%", number_to_percentage(Float::NAN) + assert_equal "Inf%", number_to_percentage(Float::INFINITY) end def test_number_with_delimiter @@ -37,6 +48,7 @@ class NumberHelperTest < ActionView::TestCase assert_equal "-111.235", number_with_precision(-111.2346) assert_equal "111.00", number_with_precision(111, precision: 2) assert_equal "0.00100", number_with_precision(0.001, precision: 5) + assert_equal "3.33", number_with_precision(Rational(10, 3), precision: 2) end def test_number_to_human_size @@ -52,6 +64,31 @@ class NumberHelperTest < ActionView::TestCase assert_equal "489.0 Thousand", number_to_human(489000, precision: 4, strip_insignificant_zeros: false) end + def test_number_to_human_escape_units + volume = { unit: "<b>ml</b>", thousand: "<b>lt</b>", million: "<b>m3</b>", trillion: "<b>km3</b>", quadrillion: "<b>Pl</b>" } + assert_equal '123 <b>lt</b>', number_to_human(123456, :units => volume) + assert_equal '12 <b>ml</b>', number_to_human(12, :units => volume) + assert_equal '1.23 <b>m3</b>', number_to_human(1234567, :units => volume) + assert_equal '1.23 <b>km3</b>', number_to_human(1_234_567_000_000, :units => volume) + assert_equal '1.23 <b>Pl</b>', number_to_human(1_234_567_000_000_000, :units => volume) + + #Including fractionals + distance = { mili: "<b>mm</b>", centi: "<b>cm</b>", deci: "<b>dm</b>", unit: "<b>m</b>", + ten: "<b>dam</b>", hundred: "<b>hm</b>", thousand: "<b>km</b>", + micro: "<b>um</b>", nano: "<b>nm</b>", pico: "<b>pm</b>", femto: "<b>fm</b>"} + assert_equal '1.23 <b>mm</b>', number_to_human(0.00123, :units => distance) + assert_equal '1.23 <b>cm</b>', number_to_human(0.0123, :units => distance) + assert_equal '1.23 <b>dm</b>', number_to_human(0.123, :units => distance) + assert_equal '1.23 <b>m</b>', number_to_human(1.23, :units => distance) + assert_equal '1.23 <b>dam</b>', number_to_human(12.3, :units => distance) + assert_equal '1.23 <b>hm</b>', number_to_human(123, :units => distance) + assert_equal '1.23 <b>km</b>', number_to_human(1230, :units => distance) + assert_equal '1.23 <b>um</b>', number_to_human(0.00000123, :units => distance) + assert_equal '1.23 <b>nm</b>', number_to_human(0.00000000123, :units => distance) + assert_equal '1.23 <b>pm</b>', number_to_human(0.00000000000123, :units => distance) + assert_equal '1.23 <b>fm</b>', number_to_human(0.00000000000000123, :units => distance) + end + def test_number_helpers_escape_delimiter_and_separator assert_equal "111<script></script>111<script></script>1111", number_to_phone(1111111111, delimiter: "<script></script>") @@ -73,6 +110,14 @@ class NumberHelperTest < ActionView::TestCase assert_equal "100<script></script>000 Quadrillion", number_to_human(10**20, delimiter: "<script></script>") end + def test_number_to_human_with_custom_translation_scope + I18n.backend.store_translations 'ts', + :custom_units_for_number_to_human => {:mili => "mm", :centi => "cm", :deci => "dm", :unit => "m", :ten => "dam", :hundred => "hm", :thousand => "km"} + assert_equal "1.01 cm", number_to_human(0.0101, :locale => 'ts', :units => :custom_units_for_number_to_human) + ensure + I18n.reload! + end + def test_number_helpers_outputs_are_html_safe assert number_to_human(1).html_safe? assert !number_to_human("<script></script>").html_safe? diff --git a/actionview/test/template/output_buffer_test.rb b/actionview/test/template/output_buffer_test.rb deleted file mode 100644 index eb0df3d1ab..0000000000 --- a/actionview/test/template/output_buffer_test.rb +++ /dev/null @@ -1,59 +0,0 @@ -require 'abstract_unit' - -class OutputBufferTest < ActionController::TestCase - class TestController < ActionController::Base - def index - render :text => 'foo' - end - end - - tests TestController - - def setup - @vc = @controller.view_context - get :index - assert_equal ['foo'], body_parts - end - - test 'output buffer is nil after rendering' do - assert_nil output_buffer - end - - test 'flushing ignores nil output buffer' do - @controller.view_context.flush_output_buffer - assert_nil output_buffer - assert_equal ['foo'], body_parts - end - - test 'flushing ignores empty output buffer' do - @vc.output_buffer = '' - @vc.flush_output_buffer - assert_equal '', output_buffer - assert_equal ['foo'], body_parts - end - - test 'flushing appends the output buffer to the body parts' do - @vc.output_buffer = 'bar' - @vc.flush_output_buffer - assert_equal '', output_buffer - assert_equal ['foo', 'bar'], body_parts - end - - test 'flushing preserves output buffer encoding' do - original_buffer = ' '.force_encoding(Encoding::EUC_JP) - @vc.output_buffer = original_buffer - @vc.flush_output_buffer - assert_equal ['foo', original_buffer], body_parts - assert_not_equal original_buffer, output_buffer - assert_equal Encoding::EUC_JP, output_buffer.encoding - end - - protected - def output_buffer - @vc.output_buffer - end - - def body_parts - @controller.response.body_parts - end -end diff --git a/actionview/test/template/output_safety_helper_test.rb b/actionview/test/template/output_safety_helper_test.rb index 76c71c9e6d..a1bf0e1a5f 100644 --- a/actionview/test/template/output_safety_helper_test.rb +++ b/actionview/test/template/output_safety_helper_test.rb @@ -25,4 +25,11 @@ class OutputSafetyHelperTest < ActionView::TestCase assert_equal "<p>foo</p><br /><p>bar</p>", joined end -end
\ No newline at end of file + test "safe_join should work recursively similarly to Array.join" do + joined = safe_join(['a',['b','c']], ':') + assert_equal 'a:b:c', joined + + joined = safe_join(['"a"',['<b>','<c>']], ' <br/> ') + assert_equal '"a" <br/> <b> <br/> <c>', joined + end +end diff --git a/actionview/test/template/partial_iteration_test.rb b/actionview/test/template/partial_iteration_test.rb new file mode 100644 index 0000000000..695f9f1bef --- /dev/null +++ b/actionview/test/template/partial_iteration_test.rb @@ -0,0 +1,33 @@ +require 'abstract_unit' +require 'action_view/renderer/partial_renderer' + +class PartialIterationTest < ActiveSupport::TestCase + def test_has_size_and_index + iteration = ActionView::PartialIteration.new 3 + assert_equal 0, iteration.index, "should be at the first index" + assert_equal 3, iteration.size, "should have the size" + end + + def test_first_is_true_when_current_is_at_the_first_index + iteration = ActionView::PartialIteration.new 3 + assert iteration.first?, "first when current is 0" + end + + def test_first_is_false_unless_current_is_at_the_first_index + iteration = ActionView::PartialIteration.new 3 + iteration.iterate! + assert !iteration.first?, "not first when current is 1" + end + + def test_last_is_true_when_current_is_at_the_last_index + iteration = ActionView::PartialIteration.new 3 + iteration.iterate! + iteration.iterate! + assert iteration.last?, "last when current is 2" + end + + def test_last_is_false_unless_current_is_at_the_last_index + iteration = ActionView::PartialIteration.new 3 + assert !iteration.last?, "not last when current is 0" + end +end diff --git a/actionview/test/template/record_identifier_test.rb b/actionview/test/template/record_identifier_test.rb index 22038110a5..04898c0b0e 100644 --- a/actionview/test/template/record_identifier_test.rb +++ b/actionview/test/template/record_identifier_test.rb @@ -9,7 +9,6 @@ class RecordIdentifierTest < ActiveSupport::TestCase @record = @klass.new @singular = 'comment' @plural = 'comments' - @uncountable = Sheep end def test_dom_id_with_new_record @@ -47,3 +46,46 @@ class RecordIdentifierTest < ActiveSupport::TestCase assert_equal @singular, ActionView::RecordIdentifier.dom_class(@record) end end + +class RecordIdentifierWithoutActiveModelTest < ActiveSupport::TestCase + include ActionView::RecordIdentifier + + def setup + @record = Plane.new + end + + def test_dom_id_with_new_record + assert_equal "new_airplane", dom_id(@record) + end + + def test_dom_id_with_new_record_and_prefix + assert_equal "custom_prefix_airplane", dom_id(@record, :custom_prefix) + end + + def test_dom_id_with_saved_record + @record.save + assert_equal "airplane_1", dom_id(@record) + end + + def test_dom_id_with_prefix + @record.save + assert_equal "edit_airplane_1", dom_id(@record, :edit) + end + + def test_dom_class + assert_equal 'airplane', dom_class(@record) + end + + def test_dom_class_with_prefix + assert_equal "custom_prefix_airplane", dom_class(@record, :custom_prefix) + end + + def test_dom_id_as_singleton_method + @record.save + assert_equal "airplane_1", ActionView::RecordIdentifier.dom_id(@record) + end + + def test_dom_class_as_singleton_method + assert_equal 'airplane', ActionView::RecordIdentifier.dom_class(@record) + end +end diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb index 055a273cc3..dc4abca048 100644 --- a/actionview/test/template/render_test.rb +++ b/actionview/test/template/render_test.rb @@ -12,17 +12,16 @@ module RenderTestCases @controller_view = TestController.new.view_context # Reload and register danish language for testing - I18n.reload! I18n.backend.store_translations 'da', {} I18n.backend.store_translations 'pt-BR', {} # Ensure original are still the same since we are reindexing view paths - assert_equal ORIGINAL_LOCALES, I18n.available_locales.map {|l| l.to_s }.sort + assert_equal ORIGINAL_LOCALES, I18n.available_locales.map(&:to_s).sort end def test_render_without_options e = assert_raises(ArgumentError) { @view.render() } - assert_match "You invoked render but did not give any of :partial, :template, :inline, :file or :text option.", e.message + assert_match(/You invoked render but did not give any of (.+) option./, e.message) end def test_render_file @@ -176,14 +175,14 @@ module RenderTestCases def test_render_partial_with_invalid_name e = assert_raises(ArgumentError) { @view.render(:partial => "test/200") } assert_equal "The partial name (test/200) is not a valid Ruby identifier; " + - "make sure your partial name starts with a lowercase letter or underscore, " + + "make sure your partial name starts with underscore, " + "and is followed by any combination of letters, numbers and underscores.", e.message end def test_render_partial_with_missing_filename e = assert_raises(ArgumentError) { @view.render(:partial => "test/") } assert_equal "The partial name (test/) is not a valid Ruby identifier; " + - "make sure your partial name starts with a lowercase letter or underscore, " + + "make sure your partial name starts with underscore, " + "and is followed by any combination of letters, numbers and underscores.", e.message end @@ -195,7 +194,21 @@ module RenderTestCases def test_render_partial_with_hyphen e = assert_raises(ArgumentError) { @view.render(:partial => "test/a-in") } assert_equal "The partial name (test/a-in) is not a valid Ruby identifier; " + - "make sure your partial name starts with a lowercase letter or underscore, " + + "make sure your partial name starts with underscore, " + + "and is followed by any combination of letters, numbers and underscores.", e.message + end + + def test_render_partial_with_invalid_option_as + e = assert_raises(ArgumentError) { @view.render(:partial => "test/partial_only", :as => 'a-in') } + assert_equal "The value (a-in) of the option `as` is not a valid Ruby identifier; " + + "make sure it starts with lowercase letter, " + + "and is followed by any combination of letters, numbers and underscores.", e.message + end + + def test_render_partial_with_hyphen_and_invalid_option_as + e = assert_raises(ArgumentError) { @view.render(:partial => "test/a-in", :as => 'a-in') } + assert_equal "The value (a-in) of the option `as` is not a valid Ruby identifier; " + + "make sure it starts with lowercase letter, " + "and is followed by any combination of letters, numbers and underscores.", e.message end @@ -257,7 +270,7 @@ module RenderTestCases end def test_render_partial_collection_without_as - assert_equal "local_inspector,local_inspector_counter", + assert_equal "local_inspector,local_inspector_counter,local_inspector_iteration", @view.render(:partial => "test/local_inspector", :collection => [ Customer.new("mary") ]) end @@ -304,6 +317,16 @@ module RenderTestCases assert_equal "Hola: david", @controller_view.render('customer_greeting', :greeting => 'Hola', :customer_greeting => Customer.new("david")) end + def test_render_partial_with_object_uses_render_partial_path + assert_equal "Hello: lifo", + @controller_view.render(:partial => Customer.new("lifo"), :locals => {:greeting => "Hello"}) + end + + def test_render_partial_with_object_and_format_uses_render_partial_path + assert_equal "<greeting>Hello</greeting><name>lifo</name>", + @controller_view.render(:partial => Customer.new("lifo"), :formats => :xml, :locals => {:greeting => "Hello"}) + end + def test_render_partial_using_object assert_equal "Hello: lifo", @controller_view.render(Customer.new("lifo"), :greeting => "Hello") @@ -315,11 +338,16 @@ module RenderTestCases @controller_view.render(customers, :greeting => "Hello") end + def test_render_partial_using_collection_without_path + assert_equal "hi good customer: david0", @controller_view.render([ GoodCustomer.new("david") ], greeting: "hi") + end + def test_render_partial_without_object_or_collection_does_not_generate_partial_name_local_variable exception = assert_raises ActionView::Template::Error do @controller_view.render("partial_name_local_variable") end - assert_match "undefined local variable or method `partial_name_local_variable'", exception.message + assert_instance_of NameError, exception.original_exception + assert_equal :partial_name_local_variable, exception.original_exception.name end # TODO: The reason for this test is unclear, improve documentation @@ -359,23 +387,48 @@ module RenderTestCases def test_render_inline_with_render_from_to_proc ActionView::Template.register_template_handler :ruby_handler, :source.to_proc - assert_equal '3', @view.render(:inline => "(1 + 2).to_s", :type => :ruby_handler) + assert_equal '3', @view.render(inline: "(1 + 2).to_s", type: :ruby_handler) + ensure + ActionView::Template.unregister_template_handler :ruby_handler end def test_render_inline_with_compilable_custom_type ActionView::Template.register_template_handler :foo, CustomHandler - assert_equal 'source: "Hello, World!"', @view.render(:inline => "Hello, World!", :type => :foo) + assert_equal 'source: "Hello, World!"', @view.render(inline: "Hello, World!", type: :foo) + ensure + ActionView::Template.unregister_template_handler :foo end def test_render_inline_with_locals_and_compilable_custom_type ActionView::Template.register_template_handler :foo, CustomHandler - assert_equal 'source: "Hello, <%= name %>!"', @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }, :type => :foo) + assert_equal 'source: "Hello, <%= name %>!"', @view.render(inline: "Hello, <%= name %>!", locals: { name: "Josh" }, type: :foo) + ensure + ActionView::Template.unregister_template_handler :foo + end + + def test_render_body + assert_equal 'some body', @view.render(body: 'some body') + end + + def test_render_plain + assert_equal 'some plaintext', @view.render(plain: 'some plaintext') end def test_render_knows_about_types_registered_when_extensions_are_checked_earlier_in_initialization ActionView::Template::Handlers.extensions ActionView::Template.register_template_handler :foo, CustomHandler assert ActionView::Template::Handlers.extensions.include?(:foo) + ensure + ActionView::Template.unregister_template_handler :foo + end + + def test_render_does_not_use_unregistered_extension_and_template_handler + ActionView::Template.register_template_handler :foo, CustomHandler + ActionView::Template.unregister_template_handler :foo + assert_not ActionView::Template::Handlers.extensions.include?(:foo) + assert_equal "Hello, World!", @view.render(inline: "Hello, World!", type: :foo) + ensure + ActionView::Template::Handlers.class_variable_get(:@@template_handlers).delete(:foo) end def test_render_ignores_templates_with_malformed_template_handlers @@ -427,6 +480,11 @@ module RenderTestCases @view.render(:partial => 'test/partial_with_partial', :layout => 'test/layout_for_partial', :locals => { :name => 'Foo!'}) end + def test_render_partial_shortcut_with_block_content + assert_equal %(Before (shortcut test)\nBefore\n\n Yielded: arg1/arg2\n\nAfter\nAfter), + @view.render(partial: "test/partial_shortcut_with_block_content", layout: "test/layout_for_partial", locals: { name: "shortcut test" }) + end + def test_render_layout_with_a_nested_render_layout_call assert_equal %(Before (Foo!)\nBefore (Bar!)\npartial html\nAfter\npartial with layout\n\nAfter), @view.render(:partial => 'test/partial_with_layout', :layout => 'test/layout_for_partial', :locals => { :name => 'Foo!'}) @@ -464,7 +522,9 @@ module RenderTestCases def test_render_with_passing_couple_extensions_to_one_register_template_handler_function_call ActionView::Template.register_template_handler :foo1, :foo2, CustomHandler - assert_equal @view.render(:inline => "Hello, World!", :type => :foo1), @view.render(:inline => "Hello, World!", :type => :foo2) + assert_equal @view.render(inline: "Hello, World!", type: :foo1), @view.render(inline: "Hello, World!", type: :foo2) + ensure + ActionView::Template.unregister_template_handler :foo1, :foo2 end def test_render_throws_exception_when_no_extensions_passed_to_register_template_handler_function_call @@ -484,6 +544,7 @@ class CachedViewRenderTest < ActiveSupport::TestCase def teardown GC.start + I18n.reload! end end @@ -501,6 +562,7 @@ class LazyViewRenderTest < ActiveSupport::TestCase def teardown GC.start + I18n.reload! end def test_render_utf8_template_with_magic_comment diff --git a/actionview/test/template/sanitize_helper_test.rb b/actionview/test/template/sanitize_helper_test.rb index 12d5260a9d..e4be21be2c 100644 --- a/actionview/test/template/sanitize_helper_test.rb +++ b/actionview/test/template/sanitize_helper_test.rb @@ -1,19 +1,15 @@ require 'abstract_unit' # The exhaustive tests are in test/controller/html/sanitizer_test.rb. -# This tests the that the helpers hook up correctly to the sanitizer classes. +# This tests that the helpers hook up correctly to the sanitizer classes. class SanitizeHelperTest < ActionView::TestCase tests ActionView::Helpers::SanitizeHelper def test_strip_links assert_equal "Dont touch me", strip_links("Dont touch me") - assert_equal "<a<a", strip_links("<a<a") assert_equal "on my mind\nall day long", strip_links("<a href='almost'>on my mind</a>\n<A href='almost'>all day long</A>") - assert_equal "0wn3d", strip_links("<a href='http://www.rubyonrails.com/'><a href='http://www.rubyonrails.com/' onlclick='steal()'>0wn3d</a></a>") assert_equal "Magic", strip_links("<a href='http://www.rubyonrails.com/'>Mag<a href='http://www.ruby-lang.org/'>ic") - assert_equal "FrrFox", strip_links("<href onlclick='steal()'>FrrFox</a></href>") assert_equal "My mind\nall <b>day</b> long", strip_links("<a href='almost'>My mind</a>\n<A href='almost'>all <b>day</b> long</A>") - assert_equal "all <b>day</b> long", strip_links("<<a>a href='hello'>all <b>day</b> long<</A>/a>") end def test_sanitize_form @@ -22,27 +18,15 @@ class SanitizeHelperTest < ActionView::TestCase def test_should_sanitize_illegal_style_properties raw = %(display:block; position:absolute; left:0; top:0; width:100%; height:100%; z-index:1; background-color:black; background-image:url(http://www.ragingplatypus.com/i/cam-full.jpg); background-x:center; background-y:center; background-repeat:repeat;) - expected = %(display: block; width: 100%; height: 100%; background-color: black; background-image: ; background-x: center; background-y: center;) + expected = %(display: block; width: 100%; height: 100%; background-color: black; background-x: center; background-y: center;) assert_equal expected, sanitize_css(raw) end def test_strip_tags - assert_equal("<<<bad html", strip_tags("<<<bad html")) - assert_equal("<<", strip_tags("<<<bad html>")) assert_equal("Dont touch me", strip_tags("Dont touch me")) assert_equal("This is a test.", strip_tags("<p>This <u>is<u> a <a href='test.html'><strong>test</strong></a>.</p>")) - assert_equal("Weirdos", strip_tags("Wei<<a>a onclick='alert(document.cookie);'</a>/>rdos")) - assert_equal("This is a test.", strip_tags("This is a test.")) - assert_equal( - %{This is a test.\n\n\nIt no longer contains any HTML.\n}, strip_tags( - %{<title>This is <b>a <a href="" target="_blank">test</a></b>.</title>\n\n<!-- it has a comment -->\n\n<p>It no <b>longer <strong>contains <em>any <strike>HTML</strike></em>.</strong></b></p>\n})) assert_equal "This has a here.", strip_tags("This has a <!-- comment --> here.") - [nil, '', ' '].each do |blank| - stripped = strip_tags(blank) - assert_equal blank, stripped - end assert_equal "", strip_tags("<script>") - assert_equal "something <img onerror=alert(1337)", ERB::Util.html_escape(strip_tags("something <img onerror=alert(1337)")) end def test_sanitize_is_marked_safe diff --git a/actionview/test/template/tag_helper_test.rb b/actionview/test/template/tag_helper_test.rb index fb016a52de..d037447567 100644 --- a/actionview/test/template/tag_helper_test.rb +++ b/actionview/test/template/tag_helper_test.rb @@ -50,6 +50,11 @@ class TagHelperTest < ActionView::TestCase assert_dom_equal "<div>Hello world!</div>", buffer end + def test_content_tag_with_block_in_erb_containing_non_displayed_erb + buffer = render_erb("<%= content_tag(:p) do %><% 1 %><% end %>") + assert_dom_equal "<p></p>", buffer + end + def test_content_tag_with_block_and_options_in_erb buffer = render_erb("<%= content_tag(:div, :class => 'green') do %>Hello world!<% end %>") assert_dom_equal %(<div class="green">Hello world!</div>), buffer @@ -64,6 +69,11 @@ class TagHelperTest < ActionView::TestCase content_tag("a", "href" => "create") { "Create" } end + def test_content_tag_with_block_and_non_string_outside_out_of_erb + assert_equal content_tag("p"), + content_tag("p") { 3.times { "do_something" } } + end + def test_content_tag_nested_in_content_tag_out_of_erb assert_equal content_tag("p", content_tag("b", "Hello")), content_tag("p") { content_tag("b", "Hello") }, @@ -80,11 +90,27 @@ class TagHelperTest < ActionView::TestCase str = content_tag('p', "limelight", :class => ["song", "play"]) assert_equal "<p class=\"song play\">limelight</p>", str + + str = content_tag('p', "limelight", :class => ["song", ["play"]]) + assert_equal "<p class=\"song play\">limelight</p>", str end def test_content_tag_with_unescaped_array_class str = content_tag('p', "limelight", {:class => ["song", "play>"]}, false) assert_equal "<p class=\"song play>\">limelight</p>", str + + str = content_tag('p', "limelight", {:class => ["song", ["play>"]]}, false) + assert_equal "<p class=\"song play>\">limelight</p>", str + end + + def test_content_tag_with_empty_array_class + str = content_tag('p', 'limelight', {:class => []}) + assert_equal '<p class="">limelight</p>', str + end + + def test_content_tag_with_unescaped_empty_array_class + str = content_tag('p', 'limelight', {:class => []}, false) + assert_equal '<p class="">limelight</p>', str end def test_content_tag_with_data_attributes @@ -107,6 +133,7 @@ class TagHelperTest < ActionView::TestCase def test_escape_once assert_equal '1 < 2 & 3', escape_once('1 < 2 & 3') + assert_equal " ' ' λ λ " ' < > ", escape_once(" ' ' λ λ \" ' < > ") end def test_tag_honors_html_safe_for_param_values @@ -115,6 +142,14 @@ class TagHelperTest < ActionView::TestCase end end + def test_tag_honors_html_safe_with_escaped_array_class + str = tag('p', :class => ['song>', 'play>'.html_safe]) + assert_equal '<p class="song> play>" />', str + + str = tag('p', :class => ['song>'.html_safe, 'play>']) + assert_equal '<p class="song> play>" />', str + end + def test_skip_invalid_escaped_attributes ['&1;', 'dfa3;', '& #123;'].each do |escaped| assert_equal %(<a href="#{escaped.gsub(/&/, '&')}" />), tag('a', :href => escaped) @@ -131,4 +166,11 @@ class TagHelperTest < ActionView::TestCase tag('a', { data => { a_float: 3.14, a_big_decimal: BigDecimal.new("-123.456"), a_number: 1, string: 'hello', symbol: :foo, array: [1, 2, 3], hash: { key: 'value'}, string_with_quotes: 'double"quote"party"' } }) } end + + def test_aria_attributes + ['aria', :aria].each { |aria| + assert_dom_equal '<a aria-a-float="3.14" aria-a-big-decimal="-123.456" aria-a-number="1" aria-array="[1,2,3]" aria-hash="{"key":"value"}" aria-string-with-quotes="double"quote"party"" aria-string="hello" aria-symbol="foo" />', + tag('a', { aria => { a_float: 3.14, a_big_decimal: BigDecimal.new("-123.456"), a_number: 1, string: 'hello', symbol: :foo, array: [1, 2, 3], hash: { key: 'value'}, string_with_quotes: 'double"quote"party"' } }) + } + end end diff --git a/actionview/test/template/test_case_test.rb b/actionview/test/template/test_case_test.rb index 4ee0930341..5ad1938b61 100644 --- a/actionview/test/template/test_case_test.rb +++ b/actionview/test/template/test_case_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'rails/engine' module ActionView @@ -154,7 +155,7 @@ module ActionView test "view_assigns excludes internal ivars" do INTERNAL_IVARS.each do |ivar| assert defined?(ivar), "expected #{ivar} to be defined" - assert !view_assigns.keys.include?(ivar.to_s.sub('@', '').to_sym), "expected #{ivar} to be excluded from view_assigns" + assert !view_assigns.keys.include?(ivar.to_s.tr('@', '').to_sym), "expected #{ivar} to be excluded from view_assigns" end end end @@ -223,7 +224,7 @@ module ActionView test "is able to use mounted routes" do with_routing do |set| - app = Class.new do + app = Class.new(Rails::Engine) do def self.routes @routes ||= ActionDispatch::Routing::RouteSet.new end @@ -292,6 +293,17 @@ module ActionView assert_select 'li', :text => 'foo' end end + + test "do not memoize the document_root_element in view tests" do + concat form_tag('/foo') + + assert_select 'form' + + concat content_tag(:b, 'Strong', class: 'foo') + + assert_select 'form' + assert_select 'b.foo' + end end class RenderTemplateTest < ActionView::TestCase diff --git a/actionview/test/template/test_test.rb b/actionview/test/template/test_test.rb index 108a674d95..88bac85039 100644 --- a/actionview/test/template/test_test.rb +++ b/actionview/test/template/test_test.rb @@ -37,10 +37,22 @@ class PeopleHelperTest < ActionView::TestCase def test_link_to_person with_test_route_set do - person = mock(:name => "David") - person.class.extend ActiveModel::Naming - expects(:mocha_mock_path).with(person).returns("/people/1") + person = Struct.new(:name) { + extend ActiveModel::Naming + def to_model; self; end + def persisted?; true; end + def self.name; 'Mocha::Mock'; end + }.new "David" + + the_model = nil + extend Module.new { + define_method(:mocha_mock_path) { |model, *args| + the_model = model + "/people/1" + } + } assert_equal '<a href="/people/1">David</a>', link_to_person(person) + assert_equal person, the_model end end diff --git a/actionview/test/template/text_helper_test.rb b/actionview/test/template/text_helper_test.rb index a514bba83d..f05b845e46 100644 --- a/actionview/test/template/text_helper_test.rb +++ b/actionview/test/template/text_helper_test.rb @@ -187,10 +187,16 @@ class TextHelperTest < ActionView::TestCase "This text is not changed because we supplied an empty phrase", highlight("This text is not changed because we supplied an empty phrase", nil) ) + end + def test_highlight_pending assert_equal ' ', highlight(' ', 'blank text is returned verbatim') end + def test_highlight_should_return_blank_string_for_nil + assert_equal '', highlight(nil, 'blank string is returned for nil') + end + def test_highlight_should_sanitize_input assert_equal( "This is a <mark>beautiful</mark> morning", @@ -222,6 +228,11 @@ class TextHelperTest < ActionView::TestCase ) end + def test_highlight_accepts_regexp + assert_equal("This day was challenging for judge <mark>Allen</mark> and his colleagues.", + highlight("This day was challenging for judge Allen and his colleagues.", /\ballen\b/i)) + end + def test_highlight_with_multiple_phrases_in_one_pass assert_equal %(<em>wow</em> <em>em</em>), highlight('wow em', %w(wow em), :highlighter => '<em>\1</em>') end @@ -260,6 +271,13 @@ class TextHelperTest < ActionView::TestCase assert_equal options, passed_options end + def test_highlight_with_block + assert_equal( + "<b>one</b> <b>two</b> <b>three</b>", + highlight("one two three", ["one", "two", "three"]) { |word| "<b>#{word}</b>" } + ) + end + def test_excerpt assert_equal("...is a beautiful morn...", excerpt("This is a beautiful morning", "beautiful", :radius => 5)) assert_equal("This is a...", excerpt("This is a beautiful morning", "this", :radius => 5)) @@ -267,6 +285,16 @@ class TextHelperTest < ActionView::TestCase assert_nil excerpt("This is a beautiful morning", "day") end + def test_excerpt_with_regex + assert_equal('...is a beautiful! mor...', excerpt('This is a beautiful! morning', 'beautiful', :radius => 5)) + assert_equal('...is a beautiful? mor...', excerpt('This is a beautiful? morning', 'beautiful', :radius => 5)) + assert_equal('...is a beautiful? mor...', excerpt('This is a beautiful? morning', /\bbeau\w*\b/i, :radius => 5)) + assert_equal('...is a beautiful? mor...', excerpt('This is a beautiful? morning', /\b(beau\w*)\b/i, :radius => 5)) + assert_equal("...udge Allen and...", excerpt("This day was challenging for judge Allen and his colleagues.", /\ballen\b/i, :radius => 5)) + assert_equal("...judge Allen and...", excerpt("This day was challenging for judge Allen and his colleagues.", /\ballen\b/i, :radius => 1, :separator => ' ')) + assert_equal("...was challenging for...", excerpt("This day was challenging for judge Allen and his colleagues.", /\b(\w*allen\w*)\b/i, :radius => 5)) + end + def test_excerpt_should_not_be_html_safe assert !excerpt('This is a beautiful! morning', 'beautiful', :radius => 5).html_safe? end @@ -288,11 +316,6 @@ class TextHelperTest < ActionView::TestCase assert_equal("...abc...", excerpt("z abc d", "b", :radius => 1)) end - def test_excerpt_with_regex - assert_equal('...is a beautiful! mor...', excerpt('This is a beautiful! morning', 'beautiful', :radius => 5)) - assert_equal('...is a beautiful? mor...', excerpt('This is a beautiful? morning', 'beautiful', :radius => 5)) - end - def test_excerpt_with_omission assert_equal("[...]is a beautiful morn[...]", excerpt("This is a beautiful morning", "beautiful", :omission => "[...]",:radius => 5)) assert_equal( diff --git a/actionview/test/template/text_test.rb b/actionview/test/template/text_test.rb new file mode 100644 index 0000000000..d899d54589 --- /dev/null +++ b/actionview/test/template/text_test.rb @@ -0,0 +1,17 @@ +require 'abstract_unit' + +class TextTest < ActiveSupport::TestCase + test 'formats returns symbol for recognized MIME type' do + assert_equal [:text], ActionView::Template::Text.new('', :text).formats + end + + test 'formats returns string for recognized MIME type when MIME does not have symbol' do + foo = Mime::Type.lookup("foo") + assert_nil foo.to_sym + assert_equal ['foo'], ActionView::Template::Text.new('', foo).formats + end + + test 'formats returns string for unknown MIME type' do + assert_equal ['foo'], ActionView::Template::Text.new('', 'foo').formats + end +end diff --git a/actionview/test/template/translation_helper_test.rb b/actionview/test/template/translation_helper_test.rb index 269714fad0..8fde478ac9 100644 --- a/actionview/test/template/translation_helper_test.rb +++ b/actionview/test/template/translation_helper_test.rb @@ -1,12 +1,19 @@ require 'abstract_unit' +module I18n + class CustomExceptionHandler + def self.call(exception, locale, key, options) + 'from CustomExceptionHandler' + end + end +end + class TranslationHelperTest < ActiveSupport::TestCase - include ActionView::Helpers::TagHelper include ActionView::Helpers::TranslationHelper attr_reader :request, :view - def setup + setup do I18n.backend.store_translations(:en, :translations => { :templates => { @@ -30,6 +37,10 @@ class TranslationHelperTest < ActiveSupport::TestCase @view = ::ActionView::Base.new(ActionController::Base.view_paths, {}) end + teardown do + I18n.backend.reload! + end + def test_delegates_to_i18n_setting_the_rescue_format_option_to_html I18n.expects(:translate).with(:foo, :locale => 'en', :raise=>true).returns("") translate :foo, :locale => 'en' @@ -53,12 +64,38 @@ class TranslationHelperTest < ActiveSupport::TestCase assert_equal false, translate(:"translations.missing", :rescue_format => nil).html_safe? end + def test_raises_missing_translation_message_with_raise_config_option + ActionView::Base.raise_on_missing_translations = true + + assert_raise(I18n::MissingTranslationData) do + translate("translations.missing") + end + ensure + ActionView::Base.raise_on_missing_translations = false + end + def test_raises_missing_translation_message_with_raise_option assert_raise(I18n::MissingTranslationData) do translate(:"translations.missing", :raise => true) end end + def test_uses_custom_exception_handler_when_specified + old_exception_handler = I18n.exception_handler + I18n.exception_handler = I18n::CustomExceptionHandler + assert_equal 'from CustomExceptionHandler', translate(:"translations.missing", raise: false) + ensure + I18n.exception_handler = old_exception_handler + end + + def test_uses_custom_exception_handler_when_specified_for_html + old_exception_handler = I18n.exception_handler + I18n.exception_handler = I18n::CustomExceptionHandler + assert_equal 'from CustomExceptionHandler', translate(:"translations.missing_html", raise: false) + ensure + I18n.exception_handler = old_exception_handler + end + def test_i18n_translate_defaults_to_nil_rescue_format expected = 'translation missing: en.translations.missing' assert_equal expected, I18n.translate(:"translations.missing") @@ -132,6 +169,12 @@ class TranslationHelperTest < ActiveSupport::TestCase assert_equal true, translation.html_safe? end + def test_translate_with_last_default_not_named_html + translation = translate(:'translations.missing', :default => [:'translations.missing_html', :'translations.foo']) + assert_equal 'Foo', translation + assert_equal false, translation.html_safe? + end + def test_translate_with_string_default translation = translate(:'translations.missing', default: 'A Generic String') assert_equal 'A Generic String', translation @@ -141,4 +184,10 @@ class TranslationHelperTest < ActiveSupport::TestCase translation = translate(:'translations.missing', default: ['A Generic String', 'Second generic string']) assert_equal 'A Generic String', translation end + + def test_translate_does_not_change_options + options = {} + translate(:'translations.missing', options) + assert_equal({}, options) + end end diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb index deba33510a..0d6f31af9b 100644 --- a/actionview/test/template/url_helper_test.rb +++ b/actionview/test/template/url_helper_test.rb @@ -25,7 +25,7 @@ class UrlHelperTest < ActiveSupport::TestCase include routes.url_helpers include ActionView::Helpers::JavaScriptHelper - include ActionDispatch::Assertions::DomAssertions + include Rails::Dom::Testing::Assertions::DomAssertions include ActionView::Context include RenderERBUtils @@ -53,14 +53,21 @@ class UrlHelperTest < ActiveSupport::TestCase end def test_button_to_with_straight_url - assert_dom_equal %{<form method="post" action="http://www.example.com" class="button_to"><div><input type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com") + assert_dom_equal %{<form method="post" action="http://www.example.com" class="button_to"><input type="submit" value="Hello" /></form>}, button_to("Hello", "http://www.example.com") + end + + def test_button_to_with_path + assert_dom_equal( + %{<form method="post" action="/article/Hello" class="button_to"><input type="submit" value="Hello" /></form>}, + button_to("Hello", article_path("Hello".html_safe)) + ) end def test_button_to_with_straight_url_and_request_forgery self.request_forgery = true assert_dom_equal( - %{<form method="post" action="http://www.example.com" class="button_to"><div><input type="submit" value="Hello" /><input name="form_token" type="hidden" value="secret" /></div></form>}, + %{<form method="post" action="http://www.example.com" class="button_to"><input type="submit" value="Hello" /><input name="form_token" type="hidden" value="secret" /></form>}, button_to("Hello", "http://www.example.com") ) ensure @@ -68,102 +75,102 @@ class UrlHelperTest < ActiveSupport::TestCase end def test_button_to_with_form_class - assert_dom_equal %{<form method="post" action="http://www.example.com" class="custom-class"><div><input type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com", form_class: 'custom-class') + assert_dom_equal %{<form method="post" action="http://www.example.com" class="custom-class"><input type="submit" value="Hello" /></form>}, button_to("Hello", "http://www.example.com", form_class: 'custom-class') end def test_button_to_with_form_class_escapes - assert_dom_equal %{<form method="post" action="http://www.example.com" class="<script>evil_js</script>"><div><input type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com", form_class: '<script>evil_js</script>') + assert_dom_equal %{<form method="post" action="http://www.example.com" class="<script>evil_js</script>"><input type="submit" value="Hello" /></form>}, button_to("Hello", "http://www.example.com", form_class: '<script>evil_js</script>') end def test_button_to_with_query - assert_dom_equal %{<form method="post" action="http://www.example.com/q1=v1&q2=v2" class="button_to"><div><input type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com/q1=v1&q2=v2") + assert_dom_equal %{<form method="post" action="http://www.example.com/q1=v1&q2=v2" class="button_to"><input type="submit" value="Hello" /></form>}, button_to("Hello", "http://www.example.com/q1=v1&q2=v2") end def test_button_to_with_html_safe_URL - assert_dom_equal %{<form method="post" action="http://www.example.com/q1=v1&q2=v2" class="button_to"><div><input type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com/q1=v1&q2=v2".html_safe) + assert_dom_equal %{<form method="post" action="http://www.example.com/q1=v1&q2=v2" class="button_to"><input type="submit" value="Hello" /></form>}, button_to("Hello", "http://www.example.com/q1=v1&q2=v2".html_safe) end def test_button_to_with_query_and_no_name - assert_dom_equal %{<form method="post" action="http://www.example.com?q1=v1&q2=v2" class="button_to"><div><input type="submit" value="http://www.example.com?q1=v1&q2=v2" /></div></form>}, button_to(nil, "http://www.example.com?q1=v1&q2=v2") + assert_dom_equal %{<form method="post" action="http://www.example.com?q1=v1&q2=v2" class="button_to"><input type="submit" value="http://www.example.com?q1=v1&q2=v2" /></form>}, button_to(nil, "http://www.example.com?q1=v1&q2=v2") end def test_button_to_with_javascript_confirm assert_dom_equal( - %{<form method="post" action="http://www.example.com" class="button_to"><div><input data-confirm="Are you sure?" type="submit" value="Hello" /></div></form>}, + %{<form method="post" action="http://www.example.com" class="button_to"><input data-confirm="Are you sure?" type="submit" value="Hello" /></form>}, button_to("Hello", "http://www.example.com", data: { confirm: "Are you sure?" }) ) end def test_button_to_with_javascript_disable_with assert_dom_equal( - %{<form method="post" action="http://www.example.com" class="button_to"><div><input data-disable-with="Greeting..." type="submit" value="Hello" /></div></form>}, + %{<form method="post" action="http://www.example.com" class="button_to"><input data-disable-with="Greeting..." type="submit" value="Hello" /></form>}, button_to("Hello", "http://www.example.com", data: { disable_with: "Greeting..." }) ) end def test_button_to_with_remote_and_form_options assert_dom_equal( - %{<form method="post" action="http://www.example.com" class="custom-class" data-remote="true" data-type="json"><div><input type="submit" value="Hello" /></div></form>}, + %{<form method="post" action="http://www.example.com" class="custom-class" data-remote="true" data-type="json"><input type="submit" value="Hello" /></form>}, button_to("Hello", "http://www.example.com", remote: true, form: { class: "custom-class", "data-type" => "json" }) ) end def test_button_to_with_remote_and_javascript_confirm assert_dom_equal( - %{<form method="post" action="http://www.example.com" class="button_to" data-remote="true"><div><input data-confirm="Are you sure?" type="submit" value="Hello" /></div></form>}, + %{<form method="post" action="http://www.example.com" class="button_to" data-remote="true"><input data-confirm="Are you sure?" type="submit" value="Hello" /></form>}, button_to("Hello", "http://www.example.com", remote: true, data: { confirm: "Are you sure?" }) ) end def test_button_to_with_remote_and_javascript_disable_with assert_dom_equal( - %{<form method="post" action="http://www.example.com" class="button_to" data-remote="true"><div><input data-disable-with="Greeting..." type="submit" value="Hello" /></div></form>}, + %{<form method="post" action="http://www.example.com" class="button_to" data-remote="true"><input data-disable-with="Greeting..." type="submit" value="Hello" /></form>}, button_to("Hello", "http://www.example.com", remote: true, data: { disable_with: "Greeting..." }) ) end def test_button_to_with_remote_false assert_dom_equal( - %{<form method="post" action="http://www.example.com" class="button_to"><div><input type="submit" value="Hello" /></div></form>}, + %{<form method="post" action="http://www.example.com" class="button_to"><input type="submit" value="Hello" /></form>}, button_to("Hello", "http://www.example.com", remote: false) ) end def test_button_to_enabled_disabled assert_dom_equal( - %{<form method="post" action="http://www.example.com" class="button_to"><div><input type="submit" value="Hello" /></div></form>}, + %{<form method="post" action="http://www.example.com" class="button_to"><input type="submit" value="Hello" /></form>}, button_to("Hello", "http://www.example.com", disabled: false) ) assert_dom_equal( - %{<form method="post" action="http://www.example.com" class="button_to"><div><input disabled="disabled" type="submit" value="Hello" /></div></form>}, + %{<form method="post" action="http://www.example.com" class="button_to"><input disabled="disabled" type="submit" value="Hello" /></form>}, button_to("Hello", "http://www.example.com", disabled: true) ) end def test_button_to_with_method_delete assert_dom_equal( - %{<form method="post" action="http://www.example.com" class="button_to"><div><input type="hidden" name="_method" value="delete" /><input type="submit" value="Hello" /></div></form>}, + %{<form method="post" action="http://www.example.com" class="button_to"><input type="hidden" name="_method" value="delete" /><input type="submit" value="Hello" /></form>}, button_to("Hello", "http://www.example.com", method: :delete) ) end def test_button_to_with_method_get assert_dom_equal( - %{<form method="get" action="http://www.example.com" class="button_to"><div><input type="submit" value="Hello" /></div></form>}, + %{<form method="get" action="http://www.example.com" class="button_to"><input type="submit" value="Hello" /></form>}, button_to("Hello", "http://www.example.com", method: :get) ) end def test_button_to_with_block assert_dom_equal( - %{<form method="post" action="http://www.example.com" class="button_to"><div><button type="submit"><span>Hello</span></button></div></form>}, + %{<form method="post" action="http://www.example.com" class="button_to"><button type="submit"><span>Hello</span></button></form>}, button_to("http://www.example.com") { content_tag(:span, 'Hello') } ) end def test_button_to_with_params assert_dom_equal( - %{<form action="http://www.example.com" class="button_to" method="post"><div><input type="submit" value="Hello" /><input type="hidden" name="foo" value="bar" /><input type="hidden" name="baz" value="quux" /></div></form>}, + %{<form action="http://www.example.com" class="button_to" method="post"><input type="submit" value="Hello" /><input type="hidden" name="foo" value="bar" /><input type="hidden" name="baz" value="quux" /></form>}, button_to("Hello", "http://www.example.com", params: {foo: :bar, baz: "quux"}) ) end @@ -486,8 +493,8 @@ class UrlHelperTest < ActiveSupport::TestCase def test_mail_with_options assert_dom_equal( - %{<a href="mailto:me@example.com?cc=ccaddress%40example.com&bcc=bccaddress%40example.com&body=This%20is%20the%20body%20of%20the%20message.&subject=This%20is%20an%20example%20email">My email</a>}, - mail_to("me@example.com", "My email", cc: "ccaddress@example.com", bcc: "bccaddress@example.com", subject: "This is an example email", body: "This is the body of the message.") + %{<a href="mailto:me@example.com?cc=ccaddress%40example.com&bcc=bccaddress%40example.com&body=This%20is%20the%20body%20of%20the%20message.&subject=This%20is%20an%20example%20email&reply-to=foo%40bar.com">My email</a>}, + mail_to("me@example.com", "My email", cc: "ccaddress@example.com", bcc: "bccaddress@example.com", subject: "This is an example email", body: "This is the body of the message.", reply_to: "foo@bar.com") ) end |