diff options
Diffstat (limited to 'actionview')
-rw-r--r-- | actionview/CHANGELOG.md | 3 | ||||
-rw-r--r-- | actionview/lib/action_view/helpers/atom_feed_helper.rb | 5 | ||||
-rw-r--r-- | actionview/lib/action_view/helpers/date_helper.rb | 2 | ||||
-rw-r--r-- | actionview/lib/action_view/helpers/form_tag_helper.rb | 4 | ||||
-rw-r--r-- | actionview/lib/action_view/helpers/sanitize_helper.rb | 127 | ||||
-rw-r--r-- | actionview/lib/action_view/model_naming.rb | 2 | ||||
-rw-r--r-- | actionview/lib/action_view/record_identifier.rb | 2 | ||||
-rw-r--r-- | actionview/lib/action_view/rendering.rb | 7 | ||||
-rw-r--r-- | actionview/test/actionpack/controller/render_test.rb | 15 | ||||
-rw-r--r-- | actionview/test/template/atom_feed_helper_test.rb | 56 | ||||
-rw-r--r-- | actionview/test/template/form_helper_test.rb | 6 | ||||
-rw-r--r-- | actionview/test/template/url_helper_test.rb | 18 |
12 files changed, 134 insertions, 113 deletions
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index a63eced6ab..743c01e393 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -28,5 +28,8 @@ *Angelo Capilleri* +* Allow entries without a link tag in AtomFeedHelper. + + *Daniel Gomez de Souza* Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/actionview/CHANGELOG.md) for previous changes. diff --git a/actionview/lib/action_view/helpers/atom_feed_helper.rb b/actionview/lib/action_view/helpers/atom_feed_helper.rb index 227ad4cdfa..d8be4e5678 100644 --- a/actionview/lib/action_view/helpers/atom_feed_helper.rb +++ b/actionview/lib/action_view/helpers/atom_feed_helper.rb @@ -174,7 +174,7 @@ module ActionView # # * <tt>:published</tt>: Time first published. Defaults to the created_at attribute on the record if one such exists. # * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists. - # * <tt>:url</tt>: The URL for this entry. Defaults to the polymorphic_url for the record. + # * <tt>:url</tt>: The URL for this entry or false or nil for not having a link tag. Defaults to the polymorphic_url for the record. # * <tt>:id</tt>: The ID for this entry. Defaults to "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}" # * <tt>:type</tt>: The TYPE for this entry. Defaults to "text/html". def entry(record, options = {}) @@ -191,7 +191,8 @@ module ActionView type = options.fetch(:type, 'text/html') - @xml.link(:rel => 'alternate', :type => type, :href => options[:url] || @view.polymorphic_url(record)) + url = options.fetch(:url) { @view.polymorphic_url(record) } + @xml.link(:rel => 'alternate', :type => type, :href => url) if url yield AtomBuilder.new(@xml) end diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb index 4b4f0ae577..46ce7cf0be 100644 --- a/actionview/lib/action_view/helpers/date_helper.rb +++ b/actionview/lib/action_view/helpers/date_helper.rb @@ -177,6 +177,8 @@ module ActionView # and +:name+ (string). A format string would be something like "%{name} (%<number>02d)" for example. # See <tt>Kernel.sprintf</tt> for documentation on format sequences. # * <tt>:date_separator</tt> - Specifies a string to separate the date fields. Default is "" (i.e. nothing). + # * <tt>:time_separator</tt> - Specifies a string to separate the time fields. Default is "" (i.e. nothing). + # * <tt>:datetime_separator</tt>- Specifies a string to separate the date and time fields. Default is "" (i.e. nothing). # * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Date.today.year - 5</tt> if # you are creating new record. While editing existing record, <tt>:start_year</tt> defaults to # the current selected year minus 5. diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb index 93c04fbec6..8bb243b8b7 100644 --- a/actionview/lib/action_view/helpers/form_tag_helper.rb +++ b/actionview/lib/action_view/helpers/form_tag_helper.rb @@ -777,10 +777,10 @@ module ActionView # # => <input id="quantity" name="quantity" min="1" max="9" type="number" /> # # number_field_tag 'quantity', nil, min: 1, max: 10 - # # => <input id="quantity" name="quantity" min="1" max="9" type="number" /> + # # => <input id="quantity" name="quantity" min="1" max="10" type="number" /> # # number_field_tag 'quantity', nil, min: 1, max: 10, step: 2 - # # => <input id="quantity" name="quantity" min="1" max="9" step="2" type="number" /> + # # => <input id="quantity" name="quantity" min="1" max="10" step="2" type="number" /> # # number_field_tag 'quantity', '1', class: 'special_input', disabled: true # # => <input disabled="disabled" class="special_input" id="quantity" name="quantity" type="number" value="1" /> diff --git a/actionview/lib/action_view/helpers/sanitize_helper.rb b/actionview/lib/action_view/helpers/sanitize_helper.rb index e72e85ee5f..463a4e9f60 100644 --- a/actionview/lib/action_view/helpers/sanitize_helper.rb +++ b/actionview/lib/action_view/helpers/sanitize_helper.rb @@ -8,76 +8,77 @@ module ActionView # These helper methods extend Action View making them callable within your template files. module SanitizeHelper extend ActiveSupport::Concern - # This +sanitize+ helper will HTML encode all tags and strip all attributes that - # aren't specifically allowed. + # Sanitizes HTML input, stripping all tags and attributes that aren't whitelisted. # - # It also strips href/src tags with invalid protocols, like javascript: especially. - # It does its best to counter any tricks that hackers may use, like throwing in - # unicode/ascii/hex values to get past the javascript: filters. Check out - # the extensive test suite. + # It also strips href/src attributes with unsafe protocols like + # <tt>javascript:</tt>, while also protecting against attempts to use Unicode, + # ASCII, and hex character references to work around these protocol filters. # - # <%= sanitize @article.body %> + # The default sanitizer is Rails::Html::WhiteListSanitizer. See {Rails HTML + # Sanitizers}[https://github.com/rails/rails-html-sanitizer] for more information. # - # You can add or remove tags/attributes if you want to customize it a bit. - # See ActionView::Base for full docs on the available options. You can add - # tags/attributes for single uses of +sanitize+ by passing either the - # <tt>:attributes</tt> or <tt>:tags</tt> options: + # Custom sanitization rules can also be provided. # - # Normal Use - # - # <%= sanitize @article.body %> + # Please note that sanitizing user-provided text does not guarantee that the + # resulting markup is valid or even well-formed. For example, the output may still + # contain unescaped characters like <tt><</tt>, <tt>></tt>, or <tt>&</tt>. # - # Custom Use - Custom Scrubber - # (supply a Loofah::Scrubber that does the sanitization) + # ==== Options # - # scrubber can either wrap a block: - # scrubber = Loofah::Scrubber.new do |node| - # node.text = "dawn of cats" - # end + # * <tt>:tags</tt> - An array of allowed tags. + # * <tt>:attributes</tt> - An array of allowed attributes. + # * <tt>:scrubber</tt> - A {Rails::Html scrubber}[https://github.com/rails/rails-html-sanitizer] + # or {Loofah::Scrubber}[https://github.com/flavorjones/loofah] object that + # defines custom sanitization rules. A custom scrubber takes precedence over + # custom tags and attributes. # - # or be a subclass of Loofah::Scrubber which responds to scrub: - # class KittyApocalypse < Loofah::Scrubber - # def scrub(node) - # node.text = "dawn of cats" - # end - # end - # scrubber = KittyApocalypse.new + # ==== Examples # - # <%= sanitize @article.body, scrubber: scrubber %> + # Normal use: # - # A custom scrubber takes precedence over custom tags and attributes - # Learn more about scrubbers here: https://github.com/flavorjones/loofah + # <%= sanitize @comment.body %> # - # Custom Use - tags and attributes - # (only the mentioned tags and attributes are allowed, nothing else) + # Providing custom whitelisted tags and attributes: # - # <%= sanitize @article.body, tags: %w(table tr td), attributes: %w(id class style) %> + # <%= sanitize @comment.body, tags: %w(strong em a), attributes: %w(href) %> # - # Add table tags to the default allowed tags + # Providing a custom Rails::Html scrubber: # - # class Application < Rails::Application - # config.action_view.sanitized_allowed_tags = ['table', 'tr', 'td'] - # end + # class CommentScrubber < Rails::Html::PermitScrubber + # def allowed_node?(node) + # !%w(form script comment blockquote).include?(node.name) + # end # - # Remove tags to the default allowed tags + # def skip_node?(node) + # node.text? + # end # - # class Application < Rails::Application - # config.after_initialize do - # ActionView::Base.sanitized_allowed_tags.delete 'div' + # def scrub_attribute?(name) + # name == 'style' # end # end # - # Change allowed default attributes + # <%= sanitize @comment.body, scrubber: CommentScrubber.new %> + # + # See {Rails HTML Sanitizer}[https://github.com/rails/rails-html-sanitizer] for + # documentation about Rails::Html scrubbers. # - # class Application < Rails::Application - # config.action_view.sanitized_allowed_attributes = ['id', 'class', 'style'] + # Providing a custom Loofah::Scrubber: + # + # scrubber = Loofah::Scrubber.new do |node| + # node.remove if node.name == 'script' # end # - # Please note that sanitizing user-provided text does not guarantee that the - # resulting markup is valid (conforming to a document type) or even well-formed. - # The output may still contain e.g. unescaped '<', '>', '&' characters and - # confuse browsers. + # <%= sanitize @comment.body, scrubber: scrubber %> + # + # See {Loofah's documentation}[https://github.com/flavorjones/loofah] for more + # information about defining custom Loofah::Scrubber objects. # + # To set the default allowed tags or attributes across your application: + # + # # In config/application.rb + # config.action_view.sanitized_allowed_tags = ['strong', 'em', 'a'] + # config.action_view.sanitized_allowed_attributes = ['href', 'title'] def sanitize(html, options = {}) self.class.white_list_sanitizer.sanitize(html, options).try(:html_safe) end @@ -87,9 +88,7 @@ module ActionView self.class.white_list_sanitizer.sanitize_css(style) end - # Strips all HTML tags from the +html+, including comments. This uses - # Nokogiri for tokenization (via Loofah) and so its HTML parsing ability - # is limited by that of Nokogiri. + # Strips all HTML tags from +html+, including comments. # # strip_tags("Strip <i>these</i> tags!") # # => Strip these tags! @@ -103,7 +102,7 @@ module ActionView self.class.full_sanitizer.sanitize(html) end - # Strips all link tags from +text+ leaving just the link text. + # Strips all link tags from +html+ leaving just the link text. # # strip_links('<a href="http://www.rubyonrails.org">Ruby on Rails</a>') # # => Ruby on Rails @@ -166,30 +165,6 @@ module ActionView def white_list_sanitizer @white_list_sanitizer ||= sanitizer_vendor.white_list_sanitizer.new end - - ## - # :method: sanitized_allowed_tags= - # - # :call-seq: sanitized_allowed_tags=(tags) - # - # Replaces the allowed tags for the +sanitize+ helper. - # - # class Application < Rails::Application - # config.action_view.sanitized_allowed_tags = ['table', 'tr', 'td'] - # end - # - - ## - # :method: sanitized_allowed_attributes= - # - # :call-seq: sanitized_allowed_attributes=(attributes) - # - # Replaces the allowed HTML attributes for the +sanitize+ helper. - # - # class Application < Rails::Application - # config.action_view.sanitized_allowed_attributes = ['onclick', 'longdesc'] - # end - # end end end diff --git a/actionview/lib/action_view/model_naming.rb b/actionview/lib/action_view/model_naming.rb index d42e436b17..b6ed13424e 100644 --- a/actionview/lib/action_view/model_naming.rb +++ b/actionview/lib/action_view/model_naming.rb @@ -1,5 +1,5 @@ module ActionView - module ModelNaming + module ModelNaming #:nodoc: # Converts the given object to an ActiveModel compliant one. def convert_to_model(object) object.respond_to?(:to_model) ? object.to_model : object diff --git a/actionview/lib/action_view/record_identifier.rb b/actionview/lib/action_view/record_identifier.rb index c8484bed34..6c6e69101b 100644 --- a/actionview/lib/action_view/record_identifier.rb +++ b/actionview/lib/action_view/record_identifier.rb @@ -103,7 +103,7 @@ module ActionView # make sure yourself that your dom ids are valid, in case you overwrite this method. def record_key_for_dom_id(record) key = convert_to_model(record).to_key - key ? key.join('_') : key + key ? key.join(JOIN) : key end end end diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb index abd3b77c67..1e8e7415d1 100644 --- a/actionview/lib/action_view/rendering.rb +++ b/actionview/lib/action_view/rendering.rb @@ -92,12 +92,15 @@ module ActionView # Find and render a template based on the options given. # :api: private def _render_template(options) #:nodoc: - variant = options[:variant] + variant = options.delete(:variant) + assigns = options.delete(:assigns) + context = view_context + context.assign assigns if assigns lookup_context.rendered_format = nil if options[:formats] lookup_context.variants = variant if variant - view_renderer.render(view_context, options) + view_renderer.render(context, options) end # Assign the rendered format to lookup context. diff --git a/actionview/test/actionpack/controller/render_test.rb b/actionview/test/actionpack/controller/render_test.rb index 563caee8a2..0a8842b527 100644 --- a/actionview/test/actionpack/controller/render_test.rb +++ b/actionview/test/actionpack/controller/render_test.rb @@ -453,6 +453,10 @@ class TestController < ApplicationController render :text => "foo" end + def render_with_assigns_option + render inline: '<%= @hello %>', assigns: { hello: "world" } + end + def yield_content_for render :action => "content_for", :layout => "yield" end @@ -953,12 +957,12 @@ class RenderTest < ActionController::TestCase end def test_accessing_params_in_template - get :accessing_params_in_template, :name => "David" + get :accessing_params_in_template, params: { name: "David" } assert_equal "Hello: David", @response.body end def test_accessing_local_assigns_in_inline_template - get :accessing_local_assigns_in_inline_template, :local_name => "Local David" + get :accessing_local_assigns_in_inline_template, params: { local_name: "Local David" } assert_equal "Goodbye, Local David", @response.body assert_equal "text/html", @response.content_type end @@ -1042,7 +1046,7 @@ class RenderTest < ActionController::TestCase end def test_accessing_params_in_template_with_layout - get :accessing_params_in_template_with_layout, :name => "David" + get :accessing_params_in_template_with_layout, params: { name: "David" } assert_equal "<html>Hello: David</html>", @response.body end @@ -1102,6 +1106,11 @@ class RenderTest < ActionController::TestCase assert_equal "world", assigns["hello"] end + def test_render_text_with_assigns_option + get :render_with_assigns_option + assert_equal 'world', response.body + end + # :ported: def test_template_with_locals get :render_with_explicit_template_with_locals diff --git a/actionview/test/template/atom_feed_helper_test.rb b/actionview/test/template/atom_feed_helper_test.rb index 68b44c4f0d..525d99750d 100644 --- a/actionview/test/template/atom_feed_helper_test.rb +++ b/actionview/test/template/atom_feed_helper_test.rb @@ -62,6 +62,23 @@ class ScrollsController < ActionController::Base end end EOT + FEEDS["entry_url_false_option"] = <<-EOT + atom_feed do |feed| + feed.title("My great blog!") + feed.updated((@scrolls.first.created_at)) + + @scrolls.each do |scroll| + feed.entry(scroll, :url => false) do |entry| + entry.title(scroll.title) + entry.content(scroll.body, :type => 'html') + + entry.author do |author| + author.name("DHH") + end + end + end + end + EOT FEEDS["xml_block"] = <<-EOT atom_feed do |feed| feed.title("My great blog!") @@ -214,28 +231,28 @@ class AtomFeedTest < ActionController::TestCase def test_feed_should_use_default_language_if_none_is_given with_restful_routing(:scrolls) do - get :index, :id => "defaults" + get :index, params: { id: "defaults" } assert_match(%r{xml:lang="en-US"}, @response.body) end end def test_feed_should_include_two_entries with_restful_routing(:scrolls) do - get :index, :id => "defaults" + get :index, params: { id: "defaults" } assert_select "entry", 2 end end def test_entry_should_only_use_published_if_created_at_is_present with_restful_routing(:scrolls) do - get :index, :id => "defaults" + get :index, params: { id: "defaults" } assert_select "published", 1 end end def test_providing_builder_to_atom_feed with_restful_routing(:scrolls) do - get :index, :id=>"provide_builder" + get :index, params: { id: "provide_builder" } # because we pass in the non-default builder, the content generated by the # helper should go 'nowhere'. Leaving the response body blank. assert @response.body.blank? @@ -244,7 +261,7 @@ class AtomFeedTest < ActionController::TestCase def test_entry_with_prefilled_options_should_use_those_instead_of_querying_the_record with_restful_routing(:scrolls) do - get :index, :id => "entry_options" + get :index, params: { id: "entry_options" } assert_select "updated", Time.utc(2007, 1, 1).xmlschema assert_select "updated", Time.utc(2007, 1, 2).xmlschema @@ -253,21 +270,21 @@ class AtomFeedTest < ActionController::TestCase def test_self_url_should_default_to_current_request_url with_restful_routing(:scrolls) do - get :index, :id => "defaults" + get :index, params: { id: "defaults" } assert_select "link[rel=self][href=\"http://www.nextangle.com/scrolls?id=defaults\"]" end end def test_feed_id_should_be_a_valid_tag with_restful_routing(:scrolls) do - get :index, :id => "defaults" + get :index, params: { id: "defaults" } assert_select "id", :text => "tag:www.nextangle.com,2008:/scrolls?id=defaults" end end def test_entry_id_should_be_a_valid_tag with_restful_routing(:scrolls) do - get :index, :id => "defaults" + get :index, params: { id: "defaults" } assert_select "entry id", :text => "tag:www.nextangle.com,2008:Scroll/1" assert_select "entry id", :text => "tag:www.nextangle.com,2008:Scroll/2" end @@ -275,14 +292,14 @@ class AtomFeedTest < ActionController::TestCase def test_feed_should_allow_nested_xml_blocks with_restful_routing(:scrolls) do - get :index, :id => "xml_block" + get :index, params: { id: "xml_block" } assert_select "author name", :text => "DHH" end end def test_feed_should_include_atomPub_namespace with_restful_routing(:scrolls) do - get :index, :id => "feed_with_atomPub_namespace" + get :index, params: { id: "feed_with_atomPub_namespace" } assert_match %r{xml:lang="en-US"}, @response.body assert_match %r{xmlns="http://www.w3.org/2005/Atom"}, @response.body assert_match %r{xmlns:app="http://www.w3.org/2007/app"}, @response.body @@ -291,7 +308,7 @@ class AtomFeedTest < ActionController::TestCase def test_feed_should_allow_overriding_ids with_restful_routing(:scrolls) do - get :index, :id => "feed_with_overridden_ids" + get :index, params: { id: "feed_with_overridden_ids" } assert_select "id", :text => "tag:test.rubyonrails.org,2008:test/" assert_select "entry id", :text => "tag:test.rubyonrails.org,2008:1" assert_select "entry id", :text => "tag:test.rubyonrails.org,2008:2" @@ -300,7 +317,7 @@ class AtomFeedTest < ActionController::TestCase def test_feed_xml_processing_instructions with_restful_routing(:scrolls) do - get :index, :id => 'feed_with_xml_processing_instructions' + get :index, params: { id: 'feed_with_xml_processing_instructions' } assert_match %r{<\?xml-stylesheet [^\?]*type="text/css"}, @response.body assert_match %r{<\?xml-stylesheet [^\?]*href="t.css"}, @response.body end @@ -308,7 +325,7 @@ class AtomFeedTest < ActionController::TestCase def test_feed_xml_processing_instructions_duplicate_targets with_restful_routing(:scrolls) do - get :index, :id => 'feed_with_xml_processing_instructions_duplicate_targets' + get :index, params: { id: 'feed_with_xml_processing_instructions_duplicate_targets' } assert_match %r{<\?target1 (a="1" b="2"|b="2" a="1")\?>}, @response.body assert_match %r{<\?target1 (c="3" d="4"|d="4" c="3")\?>}, @response.body end @@ -316,7 +333,7 @@ class AtomFeedTest < ActionController::TestCase def test_feed_xhtml with_restful_routing(:scrolls) do - get :index, :id => "feed_with_xhtml_content" + get :index, params: { id: "feed_with_xhtml_content" } assert_match %r{xmlns="http://www.w3.org/1999/xhtml"}, @response.body assert_select "summary", :text => /Something Boring/ assert_select "summary", :text => /after 2/ @@ -325,18 +342,25 @@ class AtomFeedTest < ActionController::TestCase def test_feed_entry_type_option_default_to_text_html with_restful_routing(:scrolls) do - get :index, :id => 'defaults' + get :index, params: { id: 'defaults' } 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' + get :index, params: { id: 'entry_type_options' } assert_select "entry link[rel=alternate][type=\"text/xml\"]" end end + def test_feed_entry_url_false_option_adds_no_link + with_restful_routing(:scrolls) do + get :index, params: { id: 'entry_url_false_option' } + assert_select "entry link", false + end + end + private def with_restful_routing(resources) with_routing do |set| diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb index e4fd797924..dccf6a2090 100644 --- a/actionview/test/template/form_helper_test.rb +++ b/actionview/test/template/form_helper_test.rb @@ -910,7 +910,11 @@ class FormHelperTest < ActionView::TestCase end def test_inputs_dont_use_before_type_cast_when_value_did_not_come_from_user - def @post.id_came_from_user?; false; end + class << @post + undef id_came_from_user? + def id_came_from_user?; false; end + end + assert_dom_equal( %{<textarea id="post_id" name="post[id]">\n0</textarea>}, text_area("post", "id") diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb index 0d6f31af9b..0ce9d38fa3 100644 --- a/actionview/test/template/url_helper_test.rb +++ b/actionview/test/template/url_helper_test.rb @@ -624,13 +624,13 @@ class UrlHelperControllerTest < ActionController::TestCase end def test_named_route_url_shows_host_and_path - get :show_named_route, kind: 'url' + get :show_named_route, params: { kind: 'url' } assert_equal 'http://test.host/url_helper_controller_test/url_helper/show_named_route', @response.body end def test_named_route_path_shows_only_path - get :show_named_route, kind: 'path' + get :show_named_route, params: { kind: 'path' } assert_equal '/url_helper_controller_test/url_helper/show_named_route', @response.body end @@ -646,7 +646,7 @@ class UrlHelperControllerTest < ActionController::TestCase end end - get :show_named_route, kind: 'url' + get :show_named_route, params: { kind: 'url' } assert_equal 'http://testtwo.host/url_helper_controller_test/url_helper/show_named_route', @response.body end @@ -661,11 +661,11 @@ class UrlHelperControllerTest < ActionController::TestCase end def test_recall_params_should_normalize_id - get :show, id: '123' + get :show, params: { id: '123' } assert_equal 302, @response.status assert_equal 'http://test.host/url_helper_controller_test/url_helper/profile/123', @response.location - get :show, name: '123' + get :show, params: { name: '123' } assert_equal 'ok', @response.body end @@ -704,7 +704,7 @@ class LinkToUnlessCurrentWithControllerTest < ActionController::TestCase end def test_link_to_unless_current_shows_link - get :show, id: 1 + get :show, params: { id: 1 } assert_equal %{<a href="/tasks">tasks</a>\n} + %{<a href="#{@request.protocol}#{@request.host_with_port}/tasks">tasks</a>}, @response.body @@ -778,21 +778,21 @@ class PolymorphicControllerTest < ActionController::TestCase def test_existing_resource @controller = WorkshopsController.new - get :show, id: 1 + get :show, params: { id: 1 } assert_equal %{/workshops/1\n<a href="/workshops/1">Workshop</a>}, @response.body end def test_new_nested_resource @controller = SessionsController.new - get :index, workshop_id: 1 + get :index, params: { workshop_id: 1 } assert_equal %{/workshops/1/sessions\n<a href="/workshops/1/sessions">Session</a>}, @response.body end def test_existing_nested_resource @controller = SessionsController.new - get :show, workshop_id: 1, id: 1 + get :show, params: { workshop_id: 1, id: 1 } assert_equal %{/workshops/1/sessions/1\n<a href="/workshops/1/sessions/1">Session</a>}, @response.body end end |