Ensure consistent indentation with Rubocop (#997)
* Ensure consistent indentation with Rubocop * add more rubocop spacing rules
This commit is contained in:
Родитель
92195559fb
Коммит
0afe05da0c
|
@ -11,3 +11,9 @@ AllCops:
|
|||
|
||||
Gemspec/OrderedDependencies:
|
||||
Enabled: true
|
||||
|
||||
Layout:
|
||||
Enabled: true
|
||||
|
||||
Layout/DotPosition:
|
||||
EnforcedStyle: trailing
|
||||
|
|
19
Rakefile
19
Rakefile
|
@ -57,8 +57,8 @@ namespace :docs do
|
|||
meths.
|
||||
select do |method|
|
||||
!method.tag(:private) &&
|
||||
method.path.include?("ViewComponent::Base") &&
|
||||
method.visibility == :public
|
||||
method.path.include?("ViewComponent::Base") &&
|
||||
method.visibility == :public
|
||||
end.sort_by { |method| method[:name] }
|
||||
|
||||
instance_methods_to_document = meths.select { |method| method.scope != :class }
|
||||
|
@ -83,9 +83,10 @@ namespace :docs do
|
|||
" (Deprecated)"
|
||||
end
|
||||
|
||||
types = if method.tag(:return)&.types
|
||||
" → [#{method.tag(:return).types.join(',')}]"
|
||||
end
|
||||
types =
|
||||
if method.tag(:return)&.types
|
||||
" → [#{method.tag(:return).types.join(',')}]"
|
||||
end
|
||||
|
||||
f.puts
|
||||
f.puts("### #{method.sep}#{method.signature.gsub('def ', '')}#{types}#{suffix}")
|
||||
|
@ -107,9 +108,10 @@ namespace :docs do
|
|||
" (Deprecated)"
|
||||
end
|
||||
|
||||
types = if method.tag(:return)&.types
|
||||
" → [#{method.tag(:return).types.join(',')}]"
|
||||
end
|
||||
types =
|
||||
if method.tag(:return)&.types
|
||||
" → [#{method.tag(:return).types.join(',')}]"
|
||||
end
|
||||
|
||||
f.puts
|
||||
f.puts("### #{method.sep}#{method.signature.gsub('def ', '')}#{types}#{suffix}")
|
||||
|
@ -131,7 +133,6 @@ namespace :docs do
|
|||
select { |method| method[:mattr_accessor] }.
|
||||
sort_by { |method| method[:name] }.
|
||||
each do |method|
|
||||
|
||||
suffix =
|
||||
if method.tag(:deprecated)
|
||||
" (Deprecated)"
|
||||
|
|
|
@ -7,6 +7,10 @@ title: Changelog
|
|||
|
||||
## main
|
||||
|
||||
* Ensure consistent indentation with Rubocop.
|
||||
|
||||
*Joel Hawksley
|
||||
|
||||
* Bump `activesupport` upper bound from `< 7.0` to `< 8.0`.
|
||||
|
||||
*Richard Macklin*
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "action_view"
|
||||
require "active_support/dependencies/autoload"
|
||||
|
||||
|
|
|
@ -155,6 +155,7 @@ module ViewComponent
|
|||
# @return [ActionController::Base]
|
||||
def controller
|
||||
raise ViewContextCalledBeforeRenderError, "`controller` can only be called at render time." if view_context.nil?
|
||||
|
||||
@__vc_controller ||= view_context.controller
|
||||
end
|
||||
|
||||
|
@ -163,6 +164,7 @@ module ViewComponent
|
|||
# @return [ActionView::Base]
|
||||
def helpers
|
||||
raise ViewContextCalledBeforeRenderError, "`helpers` can only be called at render time." if view_context.nil?
|
||||
|
||||
@__vc_helpers ||= controller.view_context
|
||||
end
|
||||
|
||||
|
@ -214,11 +216,12 @@ module ViewComponent
|
|||
@__vc_content_evaluated = true
|
||||
return @__vc_content if defined?(@__vc_content)
|
||||
|
||||
@__vc_content = if @view_context && @__vc_render_in_block
|
||||
view_context.capture(self, &@__vc_render_in_block)
|
||||
elsif defined?(@__vc_content_set_by_with_content)
|
||||
@__vc_content_set_by_with_content
|
||||
end
|
||||
@__vc_content =
|
||||
if @view_context && @__vc_render_in_block
|
||||
view_context.capture(self, &@__vc_render_in_block)
|
||||
elsif defined?(@__vc_content_set_by_with_content)
|
||||
@__vc_content_set_by_with_content
|
||||
end
|
||||
end
|
||||
|
||||
def content_evaluated?
|
||||
|
@ -288,11 +291,12 @@ module ViewComponent
|
|||
# end
|
||||
#
|
||||
# Without this, `MyOtherComponent` will not look for `my_component/my_other_component.html.erb`
|
||||
nested_component_files = if name.include?("::") && component_name != filename
|
||||
Dir["#{directory}/#{filename}/#{component_name}.*{#{extensions}}"]
|
||||
else
|
||||
[]
|
||||
end
|
||||
nested_component_files =
|
||||
if name.include?("::") && component_name != filename
|
||||
Dir["#{directory}/#{filename}/#{component_name}.*{#{extensions}}"]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
# view files in the same directory as the component
|
||||
sidecar_files = Dir["#{directory}/#{component_name}.*{#{extensions}}"]
|
||||
|
|
|
@ -5,6 +5,7 @@ require "action_view/renderer/collection_renderer" if Rails.version.to_f >= 6.1
|
|||
module ViewComponent
|
||||
class Collection
|
||||
attr_reader :component
|
||||
|
||||
delegate :format, to: :component
|
||||
|
||||
def render_in(view_context, &block)
|
||||
|
|
|
@ -7,6 +7,7 @@ module ViewComponent
|
|||
mattr_accessor :cache, instance_reader: false, instance_accessor: false do
|
||||
Set.new
|
||||
end
|
||||
|
||||
module_function
|
||||
|
||||
def register(klass)
|
||||
|
|
|
@ -21,6 +21,7 @@ module ViewComponent
|
|||
|
||||
if template_errors.present?
|
||||
raise ViewComponent::TemplateError.new(template_errors) if raise_errors
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -77,72 +78,75 @@ module ViewComponent
|
|||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
end
|
||||
|
||||
def template_errors
|
||||
@__vc_template_errors ||= begin
|
||||
errors = []
|
||||
@__vc_template_errors ||=
|
||||
begin
|
||||
errors = []
|
||||
|
||||
if (templates + inline_calls).empty?
|
||||
errors << "Could not find a template file or inline render method for #{component_class}."
|
||||
if (templates + inline_calls).empty?
|
||||
errors << "Could not find a template file or inline render method for #{component_class}."
|
||||
end
|
||||
|
||||
if templates.count { |template| template[:variant].nil? } > 1
|
||||
errors << "More than one template found for #{component_class}. There can only be one default template file per component."
|
||||
end
|
||||
|
||||
invalid_variants =
|
||||
templates.
|
||||
group_by { |template| template[:variant] }.
|
||||
map { |variant, grouped| variant if grouped.length > 1 }.
|
||||
compact.
|
||||
sort
|
||||
|
||||
unless invalid_variants.empty?
|
||||
errors << "More than one template found for #{'variant'.pluralize(invalid_variants.count)} #{invalid_variants.map { |v| "'#{v}'" }.to_sentence} in #{component_class}. There can only be one template file per variant."
|
||||
end
|
||||
|
||||
if templates.find { |template| template[:variant].nil? } && inline_calls_defined_on_self.include?(:call)
|
||||
errors << "Template file and inline render method found for #{component_class}. There can only be a template file or inline render method per component."
|
||||
end
|
||||
|
||||
duplicate_template_file_and_inline_variant_calls =
|
||||
templates.pluck(:variant) & variants_from_inline_calls(inline_calls_defined_on_self)
|
||||
|
||||
unless duplicate_template_file_and_inline_variant_calls.empty?
|
||||
count = duplicate_template_file_and_inline_variant_calls.count
|
||||
|
||||
errors << "Template #{'file'.pluralize(count)} and inline render #{'method'.pluralize(count)} found for #{'variant'.pluralize(count)} #{duplicate_template_file_and_inline_variant_calls.map { |v| "'#{v}'" }.to_sentence} in #{component_class}. There can only be a template file or inline render method per variant."
|
||||
end
|
||||
|
||||
errors
|
||||
end
|
||||
|
||||
if templates.count { |template| template[:variant].nil? } > 1
|
||||
errors << "More than one template found for #{component_class}. There can only be one default template file per component."
|
||||
end
|
||||
|
||||
invalid_variants = templates
|
||||
.group_by { |template| template[:variant] }
|
||||
.map { |variant, grouped| variant if grouped.length > 1 }
|
||||
.compact
|
||||
.sort
|
||||
|
||||
unless invalid_variants.empty?
|
||||
errors << "More than one template found for #{'variant'.pluralize(invalid_variants.count)} #{invalid_variants.map { |v| "'#{v}'" }.to_sentence} in #{component_class}. There can only be one template file per variant."
|
||||
end
|
||||
|
||||
if templates.find { |template| template[:variant].nil? } && inline_calls_defined_on_self.include?(:call)
|
||||
errors << "Template file and inline render method found for #{component_class}. There can only be a template file or inline render method per component."
|
||||
end
|
||||
|
||||
duplicate_template_file_and_inline_variant_calls =
|
||||
templates.pluck(:variant) & variants_from_inline_calls(inline_calls_defined_on_self)
|
||||
|
||||
unless duplicate_template_file_and_inline_variant_calls.empty?
|
||||
count = duplicate_template_file_and_inline_variant_calls.count
|
||||
|
||||
errors << "Template #{'file'.pluralize(count)} and inline render #{'method'.pluralize(count)} found for #{'variant'.pluralize(count)} #{duplicate_template_file_and_inline_variant_calls.map { |v| "'#{v}'" }.to_sentence} in #{component_class}. There can only be a template file or inline render method per variant."
|
||||
end
|
||||
|
||||
errors
|
||||
end
|
||||
end
|
||||
|
||||
def templates
|
||||
@templates ||= begin
|
||||
extensions = ActionView::Template.template_handler_extensions
|
||||
@templates ||=
|
||||
begin
|
||||
extensions = ActionView::Template.template_handler_extensions
|
||||
|
||||
component_class._sidecar_files(extensions).each_with_object([]) do |path, memo|
|
||||
pieces = File.basename(path).split(".")
|
||||
memo << {
|
||||
path: path,
|
||||
variant: pieces.second.split("+").second&.to_sym,
|
||||
handler: pieces.last
|
||||
}
|
||||
component_class._sidecar_files(extensions).each_with_object([]) do |path, memo|
|
||||
pieces = File.basename(path).split(".")
|
||||
memo << {
|
||||
path: path,
|
||||
variant: pieces.second.split("+").second&.to_sym,
|
||||
handler: pieces.last
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def inline_calls
|
||||
@inline_calls ||= begin
|
||||
# Fetch only ViewComponent ancestor classes to limit the scope of
|
||||
# finding inline calls
|
||||
view_component_ancestors =
|
||||
component_class.ancestors.take_while { |ancestor| ancestor != ViewComponent::Base } - component_class.included_modules
|
||||
@inline_calls ||=
|
||||
begin
|
||||
# Fetch only ViewComponent ancestor classes to limit the scope of
|
||||
# finding inline calls
|
||||
view_component_ancestors =
|
||||
component_class.ancestors.take_while { |ancestor| ancestor != ViewComponent::Base } - component_class.included_modules
|
||||
|
||||
view_component_ancestors.flat_map { |ancestor| ancestor.instance_methods(false).grep(/^call/) }.uniq
|
||||
end
|
||||
view_component_ancestors.flat_map { |ancestor| ancestor.instance_methods(false).grep(/^call/) }.uniq
|
||||
end
|
||||
end
|
||||
|
||||
def inline_calls_defined_on_self
|
||||
|
|
|
@ -71,19 +71,20 @@ module ViewComponent # :nodoc:
|
|||
|
||||
# Returns the relative path (from preview_path) to the preview example template if the template exists
|
||||
def preview_example_template_path(example)
|
||||
preview_path = Array(preview_paths).detect do |preview_path|
|
||||
Dir["#{preview_path}/#{preview_name}_preview/#{example}.html.*"].first
|
||||
end
|
||||
preview_path =
|
||||
Array(preview_paths).detect do |preview_path|
|
||||
Dir["#{preview_path}/#{preview_name}_preview/#{example}.html.*"].first
|
||||
end
|
||||
|
||||
if preview_path.nil?
|
||||
raise PreviewTemplateError, "preview template for example #{example} does not exist"
|
||||
end
|
||||
|
||||
path = Dir["#{preview_path}/#{preview_name}_preview/#{example}.html.*"].first
|
||||
Pathname.new(path)
|
||||
.relative_path_from(Pathname.new(preview_path))
|
||||
.to_s
|
||||
.sub(/\..*$/, "")
|
||||
Pathname.new(path).
|
||||
relative_path_from(Pathname.new(preview_path)).
|
||||
to_s.
|
||||
sub(/\..*$/, "")
|
||||
end
|
||||
|
||||
# Returns the method body for the example from the preview file.
|
||||
|
|
|
@ -32,30 +32,31 @@ module ViewComponent
|
|||
|
||||
raise ArgumentError.new("Block provided after calling `with_content`. Use one or the other.") if defined?(@__vc_content_block) && defined?(@__vc_content_set_by_with_content)
|
||||
|
||||
@content = if defined?(@__vc_component_instance)
|
||||
if defined?(@__vc_content_set_by_with_content)
|
||||
@__vc_component_instance.with_content(@__vc_content_set_by_with_content)
|
||||
@content =
|
||||
if defined?(@__vc_component_instance)
|
||||
if defined?(@__vc_content_set_by_with_content)
|
||||
@__vc_component_instance.with_content(@__vc_content_set_by_with_content)
|
||||
|
||||
view_context.capture do
|
||||
@__vc_component_instance.render_in(view_context)
|
||||
view_context.capture do
|
||||
@__vc_component_instance.render_in(view_context)
|
||||
end
|
||||
elsif defined?(@__vc_content_block)
|
||||
view_context.capture do
|
||||
# render_in is faster than `parent.render`
|
||||
@__vc_component_instance.render_in(view_context, &@__vc_content_block)
|
||||
end
|
||||
else
|
||||
view_context.capture do
|
||||
@__vc_component_instance.render_in(view_context)
|
||||
end
|
||||
end
|
||||
elsif defined?(@__vc_content)
|
||||
@__vc_content
|
||||
elsif defined?(@__vc_content_block)
|
||||
view_context.capture do
|
||||
# render_in is faster than `parent.render`
|
||||
@__vc_component_instance.render_in(view_context, &@__vc_content_block)
|
||||
end
|
||||
else
|
||||
view_context.capture do
|
||||
@__vc_component_instance.render_in(view_context)
|
||||
end
|
||||
end
|
||||
elsif defined?(@__vc_content)
|
||||
@__vc_content
|
||||
elsif defined?(@__vc_content_block)
|
||||
view_context.capture(&@__vc_content_block)
|
||||
elsif defined?(@__vc_content_set_by_with_content)
|
||||
@__vc_content_set_by_with_content
|
||||
end
|
||||
view_context.capture(&@__vc_content_block)
|
||||
elsif defined?(@__vc_content_set_by_with_content)
|
||||
@__vc_content_set_by_with_content
|
||||
end
|
||||
|
||||
@content
|
||||
end
|
||||
|
|
|
@ -136,7 +136,7 @@ module ViewComponent
|
|||
# Append Slot instance to collection accessor Array
|
||||
instance_variable_get(slot[:instance_variable_name]) << slot_instance
|
||||
else
|
||||
# Assign the Slot instance to the slot accessor
|
||||
# Assign the Slot instance to the slot accessor
|
||||
instance_variable_set(slot[:instance_variable_name], slot_instance)
|
||||
end
|
||||
|
||||
|
|
|
@ -230,13 +230,14 @@ module ViewComponent
|
|||
# Use `bind(self)` to ensure lambda is executed in the context of the
|
||||
# current component. This is necessary to allow the lambda to access helper
|
||||
# methods like `content_tag` as well as parent component state.
|
||||
renderable_value = if block_given?
|
||||
slot_definition[:renderable_function].bind(self).call(*args, **kwargs) do |*args, **kwargs|
|
||||
view_context.capture(*args, **kwargs, &block)
|
||||
renderable_value =
|
||||
if block_given?
|
||||
slot_definition[:renderable_function].bind(self).call(*args, **kwargs) do |*args, **kwargs|
|
||||
view_context.capture(*args, **kwargs, &block)
|
||||
end
|
||||
else
|
||||
slot_definition[:renderable_function].bind(self).call(*args, **kwargs)
|
||||
end
|
||||
else
|
||||
slot_definition[:renderable_function].bind(self).call(*args, **kwargs)
|
||||
end
|
||||
|
||||
# Function calls can return components, so if it's a component handle it specially
|
||||
if renderable_value.respond_to?(:render_in)
|
||||
|
|
|
@ -52,7 +52,7 @@ module ViewComponent
|
|||
|
||||
def scope_data(data)
|
||||
@i18n_scope.reverse_each do |part|
|
||||
data = { part => data}
|
||||
data = { part => data }
|
||||
end
|
||||
data
|
||||
end
|
||||
|
@ -70,9 +70,10 @@ module ViewComponent
|
|||
key = key&.to_s unless key.is_a?(String)
|
||||
key = "#{i18n_scope}#{key}" if key.start_with?(".")
|
||||
|
||||
translated = catch(:exception) do
|
||||
i18n_backend.translate(locale, key, options)
|
||||
end
|
||||
translated =
|
||||
catch(:exception) do
|
||||
i18n_backend.translate(locale, key, options)
|
||||
end
|
||||
|
||||
# Fallback to the global translations
|
||||
if translated.is_a? ::I18n::MissingTranslation
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SlotsV2Component < ViewComponent::Base
|
||||
renders_one :header, -> (**kwargs) { HeaderComponent.new(**kwargs) }
|
||||
renders_many :items, -> (**kwargs) { ItemComponent.new(**kwargs) }
|
||||
renders_one :header, ->(**kwargs) { HeaderComponent.new(**kwargs) }
|
||||
renders_many :items, ->(**kwargs) { ItemComponent.new(**kwargs) }
|
||||
|
||||
class HeaderComponent < ViewComponent::Base
|
||||
attr_reader :classes
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module NestedSharedState
|
||||
class TableComponent < ViewComponent::Base
|
||||
renders_one :header, -> (arg = nil, **system_arguments, &block) do
|
||||
renders_one :header, ->(arg = nil, **system_arguments, &block) do
|
||||
header_system_arguments = system_arguments
|
||||
header_system_arguments[:selectable] = @selectable
|
||||
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
# This code intentionally has a bug where there is an extra comma after the
|
||||
# :notice reader. `Kernel.silence_warnings` is in place to avoid Ruby emitting
|
||||
# warnings in test output.
|
||||
|
||||
# rubocop:disable all
|
||||
Kernel.silence_warnings do
|
||||
class ProductReaderOopsComponent < ViewComponent::Base
|
||||
attr_reader :product,
|
||||
:notice,
|
||||
attr_reader :product, :notice,
|
||||
|
||||
def initialize(product_reader_oops:)
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable all
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
class SlotsV2Component < ViewComponent::Base
|
||||
renders_one :title
|
||||
renders_one :subtitle
|
||||
renders_one :footer, -> (classes: "", &block) do
|
||||
renders_one :footer, ->(classes: "", &block) do
|
||||
content_tag :footer, class: "footer #{classes}" do
|
||||
block.call if block
|
||||
end
|
||||
|
@ -11,7 +11,7 @@ class SlotsV2Component < ViewComponent::Base
|
|||
|
||||
renders_many :tabs
|
||||
|
||||
renders_many :items, -> (highlighted: false) do
|
||||
renders_many :items, ->(highlighted: false) do
|
||||
MyHighlightComponent.new(highlighted: highlighted)
|
||||
end
|
||||
renders_one :extra, "ExtraComponent"
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
|
||||
# This file is used by Rack-based servers to start the application.
|
||||
|
||||
require ::File.expand_path("../config/environment", __FILE__)
|
||||
require ::File.expand_path("../config/environment", __FILE__)
|
||||
run Sandbox::Application
|
||||
|
|
|
@ -17,7 +17,7 @@ Sandbox::Application.configure do
|
|||
config.action_dispatch.show_exceptions = false
|
||||
|
||||
# Disable request forgery protection in test environment
|
||||
config.action_controller.allow_forgery_protection = false
|
||||
config.action_controller.allow_forgery_protection = false
|
||||
|
||||
config.view_component.show_previews = true
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Rails.application.config.assets.precompile += %w( admin.css )
|
||||
Rails.application.config.assets.precompile += %w(admin.css)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "simplecov"
|
||||
require "simplecov-console"
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@ class ViewComponent::Base::UnitTest < Minitest::Test
|
|||
"/tilda~/component/test_component.html.haml"
|
||||
]
|
||||
expected = [
|
||||
{variant: :phone, handler: "erb"},
|
||||
{variant: :desktop, handler: "slim"},
|
||||
{variant: nil, handler: "haml"}
|
||||
{ variant: :phone, handler: "erb" },
|
||||
{ variant: :desktop, handler: "slim" },
|
||||
{ variant: nil, handler: "haml" }
|
||||
]
|
||||
|
||||
compiler = ViewComponent::Compiler.new(ViewComponent::Base)
|
||||
|
@ -30,17 +30,19 @@ class ViewComponent::Base::UnitTest < Minitest::Test
|
|||
|
||||
def test_calling_helpers_outside_render_raises
|
||||
component = ViewComponent::Base.new
|
||||
err = assert_raises ViewComponent::Base::ViewContextCalledBeforeRenderError do
|
||||
component.helpers
|
||||
end
|
||||
err =
|
||||
assert_raises ViewComponent::Base::ViewContextCalledBeforeRenderError do
|
||||
component.helpers
|
||||
end
|
||||
assert_equal "`helpers` can only be called at render time.", err.message
|
||||
end
|
||||
|
||||
def test_calling_controller_outside_render_raises
|
||||
component = ViewComponent::Base.new
|
||||
err = assert_raises ViewComponent::Base::ViewContextCalledBeforeRenderError do
|
||||
component.controller
|
||||
end
|
||||
err =
|
||||
assert_raises ViewComponent::Base::ViewContextCalledBeforeRenderError do
|
||||
component.controller
|
||||
end
|
||||
assert_equal "`controller` can only be called at render time.", err.message
|
||||
end
|
||||
|
||||
|
|
|
@ -312,10 +312,10 @@ class IntegrationTest < ActionDispatch::IntegrationTest
|
|||
end
|
||||
|
||||
def test_renders_preview_source
|
||||
get "/rails/view_components/preview_component/default"
|
||||
get "/rails/view_components/preview_component/default"
|
||||
|
||||
assert_select ".view-component-source-example h2", "Source:"
|
||||
assert_select ".view-component-source-example pre.source code"
|
||||
assert_select ".view-component-source-example h2", "Source:"
|
||||
assert_select ".view-component-source-example pre.source code"
|
||||
end
|
||||
|
||||
def test_renders_preview_source_with_template_from_layout
|
||||
|
@ -400,9 +400,10 @@ class IntegrationTest < ActionDispatch::IntegrationTest
|
|||
|
||||
if Rails.version.to_f >= 6.1
|
||||
def test_rendering_component_using_the_render_component_helper_raises_an_error
|
||||
error = assert_raises ActionView::Template::Error do
|
||||
get "/render_component"
|
||||
end
|
||||
error =
|
||||
assert_raises ActionView::Template::Error do
|
||||
get "/render_component"
|
||||
end
|
||||
assert_match(/undefined method `render_component'/, error.message)
|
||||
end
|
||||
end
|
||||
|
@ -496,9 +497,10 @@ class IntegrationTest < ActionDispatch::IntegrationTest
|
|||
end
|
||||
|
||||
def test_raises_an_error_if_the_template_is_not_present_and_the_render_with_template_method_is_used_in_the_example
|
||||
error = assert_raises ViewComponent::PreviewTemplateError do
|
||||
get "/rails/view_components/inline_component/without_template"
|
||||
end
|
||||
error =
|
||||
assert_raises ViewComponent::PreviewTemplateError do
|
||||
get "/rails/view_components/inline_component/without_template"
|
||||
end
|
||||
assert_match(/preview template for example without_template does not exist/, error.message)
|
||||
end
|
||||
|
||||
|
|
|
@ -34,7 +34,6 @@ class SlotableTest < ViewComponent::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
assert_selector(".card.mt-4")
|
||||
|
||||
assert_selector(".title", text: "This is my title!")
|
||||
|
@ -82,7 +81,6 @@ class SlotableTest < ViewComponent::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
assert_selector(".card.mt-4")
|
||||
|
||||
assert_selector(".title", text: "This is my title!")
|
||||
|
@ -100,11 +98,12 @@ class SlotableTest < ViewComponent::TestCase
|
|||
end
|
||||
|
||||
def test_invalid_slot_class_raises_error
|
||||
exception = assert_raises ArgumentError do
|
||||
render_inline(BadSlotComponent.new) do |component|
|
||||
component.slot(:title)
|
||||
exception =
|
||||
assert_raises ArgumentError do
|
||||
render_inline(BadSlotComponent.new) do |component|
|
||||
component.slot(:title)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert_includes exception.message, "Title must inherit from ViewComponent::Slot"
|
||||
end
|
||||
|
@ -129,27 +128,30 @@ class SlotableTest < ViewComponent::TestCase
|
|||
end
|
||||
|
||||
def test_renders_slots_template_raise_with_unknown_content_areas
|
||||
exception = assert_raises ArgumentError do
|
||||
render_inline(SlotsComponent.new) do |component|
|
||||
component.slot(:foo) { "Hello!" }
|
||||
exception =
|
||||
assert_raises ArgumentError do
|
||||
render_inline(SlotsComponent.new) do |component|
|
||||
component.slot(:foo) { "Hello!" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert_includes exception.message, "Unknown slot 'foo' - expected one of '[:title, :subtitle, :footer, :tab, :item]'"
|
||||
end
|
||||
|
||||
def test_with_slot_raise_with_duplicate_slot_name
|
||||
exception = assert_raises ArgumentError do
|
||||
SlotsComponent.with_slot :title
|
||||
end
|
||||
exception =
|
||||
assert_raises ArgumentError do
|
||||
SlotsComponent.with_slot :title
|
||||
end
|
||||
|
||||
assert_includes exception.message, "title slot declared multiple times"
|
||||
end
|
||||
|
||||
def test_with_slot_raise_with_content_keyword
|
||||
exception = assert_raises ArgumentError do
|
||||
SlotsComponent.with_slot :content
|
||||
end
|
||||
exception =
|
||||
assert_raises ArgumentError do
|
||||
SlotsComponent.with_slot :content
|
||||
end
|
||||
|
||||
assert_includes exception.message, ":content is a reserved slot name"
|
||||
end
|
||||
|
|
|
@ -125,9 +125,10 @@ class SlotsV2sTest < ViewComponent::TestCase
|
|||
end
|
||||
|
||||
def test_sub_component_raise_with_duplicate_slot_name
|
||||
exception = assert_raises ArgumentError do
|
||||
SlotsV2Component.renders_one :title
|
||||
end
|
||||
exception =
|
||||
assert_raises ArgumentError do
|
||||
SlotsV2Component.renders_one :title
|
||||
end
|
||||
|
||||
assert_includes exception.message, "title slot declared multiple times"
|
||||
end
|
||||
|
@ -264,7 +265,7 @@ class SlotsV2sTest < ViewComponent::TestCase
|
|||
|
||||
def test_slot_with_nested_blocks_content_selectable_true
|
||||
render_inline(NestedSharedState::TableComponent.new(selectable: true)) do |table_card|
|
||||
table_card.header("regular_argument", class_names: "table__header extracted_kwarg", data: { splatted_kwarg: "splatted_keyword_argument"}) do |header|
|
||||
table_card.header("regular_argument", class_names: "table__header extracted_kwarg", data: { splatted_kwarg: "splatted_keyword_argument" }) do |header|
|
||||
header.cell { "Cell1" }
|
||||
header.cell(class_names: "-has-sort") { "Cell2" }
|
||||
end
|
||||
|
@ -302,11 +303,12 @@ class SlotsV2sTest < ViewComponent::TestCase
|
|||
end
|
||||
|
||||
def test_component_raises_when_given_invalid_slot_name
|
||||
exception = assert_raises ArgumentError do
|
||||
Class.new(ViewComponent::Base) do
|
||||
renders_one :content
|
||||
exception =
|
||||
assert_raises ArgumentError do
|
||||
Class.new(ViewComponent::Base) do
|
||||
renders_one :content
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert_includes exception.message, "content is not a valid slot name"
|
||||
end
|
||||
|
@ -339,14 +341,15 @@ class SlotsV2sTest < ViewComponent::TestCase
|
|||
end
|
||||
|
||||
def test_raises_if_using_both_block_content_and_with_content
|
||||
error = assert_raises ArgumentError do
|
||||
component = SlotsV2Component.new
|
||||
slot = component.title("some_argument")
|
||||
slot.with_content("This is my title!")
|
||||
slot.__vc_content_block = "some block"
|
||||
error =
|
||||
assert_raises ArgumentError do
|
||||
component = SlotsV2Component.new
|
||||
slot = component.title("some_argument")
|
||||
slot.with_content("This is my title!")
|
||||
slot.__vc_content_block = "some block"
|
||||
|
||||
render_inline(component)
|
||||
end
|
||||
render_inline(component)
|
||||
end
|
||||
|
||||
assert_equal "Block provided after calling `with_content`. Use one or the other.", error.message
|
||||
end
|
||||
|
|
|
@ -18,15 +18,20 @@ class TranslatableTest < ViewComponent::TestCase
|
|||
end
|
||||
|
||||
def test_multi_key_support
|
||||
assert_equal [
|
||||
"Hello from sidecar translations!",
|
||||
"This is coming from the sidecar",
|
||||
"This is coming from Rails",
|
||||
], translate([
|
||||
".hello",
|
||||
".from.sidecar",
|
||||
"from.rails",
|
||||
])
|
||||
assert_equal(
|
||||
[
|
||||
"Hello from sidecar translations!",
|
||||
"This is coming from the sidecar",
|
||||
"This is coming from Rails",
|
||||
],
|
||||
translate(
|
||||
[
|
||||
".hello",
|
||||
".from.sidecar",
|
||||
"from.rails",
|
||||
]
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def test_relative_keys_missing_from_component_translations
|
||||
|
|
|
@ -34,19 +34,21 @@ class ViewComponentTest < ViewComponent::TestCase
|
|||
end
|
||||
|
||||
def test_raise_error_when_content_already_set
|
||||
error = assert_raises ArgumentError do
|
||||
render_inline(WrapperComponent.new.with_content("setter content")) do
|
||||
"block content"
|
||||
error =
|
||||
assert_raises ArgumentError do
|
||||
render_inline(WrapperComponent.new.with_content("setter content")) do
|
||||
"block content"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert_includes error.message, "Block provided after calling `with_content`"
|
||||
end
|
||||
|
||||
def test_raise_error_when_component_implements_with_content
|
||||
exception = assert_raises ViewComponent::ComponentError do
|
||||
render_inline(InvalidWithRenderComponent.new)
|
||||
end
|
||||
exception =
|
||||
assert_raises ViewComponent::ComponentError do
|
||||
render_inline(InvalidWithRenderComponent.new)
|
||||
end
|
||||
|
||||
assert_includes exception.message, "InvalidWithRenderComponent implements a reserved method, `with_content`."
|
||||
end
|
||||
|
@ -58,9 +60,10 @@ class ViewComponentTest < ViewComponent::TestCase
|
|||
end
|
||||
|
||||
def test_raises_error_when_with_content_is_called_withot_any_values
|
||||
exception = assert_raises ArgumentError do
|
||||
WrapperComponent.new.with_content(nil)
|
||||
end
|
||||
exception =
|
||||
assert_raises ArgumentError do
|
||||
WrapperComponent.new.with_content(nil)
|
||||
end
|
||||
|
||||
assert_equal "No content provided.", exception.message
|
||||
end
|
||||
|
@ -283,19 +286,21 @@ class ViewComponentTest < ViewComponent::TestCase
|
|||
end
|
||||
|
||||
def test_renders_content_areas_template_raise_with_unknown_content_areas
|
||||
exception = assert_raises ArgumentError do
|
||||
render_inline(ContentAreasComponent.new(footer: "Bye!")) do |component|
|
||||
component.with(:foo) { "Hello!" }
|
||||
exception =
|
||||
assert_raises ArgumentError do
|
||||
render_inline(ContentAreasComponent.new(footer: "Bye!")) do |component|
|
||||
component.with(:foo) { "Hello!" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert_includes exception.message, "Unknown content_area 'foo' - expected one of '[:title, :body, :footer]'"
|
||||
end
|
||||
|
||||
def test_with_content_areas_raise_with_content_keyword
|
||||
exception = assert_raises ArgumentError do
|
||||
ContentAreasComponent.with_content_areas :content
|
||||
end
|
||||
exception =
|
||||
assert_raises ArgumentError do
|
||||
ContentAreasComponent.with_content_areas :content
|
||||
end
|
||||
|
||||
assert_includes exception.message, ":content is a reserved content area name"
|
||||
end
|
||||
|
@ -469,18 +474,20 @@ class ViewComponentTest < ViewComponent::TestCase
|
|||
end
|
||||
|
||||
def test_validations_component
|
||||
exception = assert_raises ActiveModel::ValidationError do
|
||||
render_inline(ValidationsComponent.new)
|
||||
end
|
||||
exception =
|
||||
assert_raises ActiveModel::ValidationError do
|
||||
render_inline(ValidationsComponent.new)
|
||||
end
|
||||
|
||||
assert_equal "Validation failed: Content can't be blank", exception.message
|
||||
end
|
||||
|
||||
# TODO: Remove in v3.0.0
|
||||
def test_before_render_check
|
||||
exception = assert_raises ActiveModel::ValidationError do
|
||||
render_inline(OldValidationsComponent.new)
|
||||
end
|
||||
exception =
|
||||
assert_raises ActiveModel::ValidationError do
|
||||
render_inline(OldValidationsComponent.new)
|
||||
end
|
||||
|
||||
assert_equal "Validation failed: Content can't be blank", exception.message
|
||||
end
|
||||
|
@ -500,51 +507,57 @@ class ViewComponentTest < ViewComponent::TestCase
|
|||
end
|
||||
|
||||
def test_raises_error_when_sidecar_template_is_missing
|
||||
exception = assert_raises ViewComponent::TemplateError do
|
||||
render_inline(MissingTemplateComponent.new)
|
||||
end
|
||||
exception =
|
||||
assert_raises ViewComponent::TemplateError do
|
||||
render_inline(MissingTemplateComponent.new)
|
||||
end
|
||||
|
||||
assert_includes exception.message, "Could not find a template file or inline render method for MissingTemplateComponent"
|
||||
end
|
||||
|
||||
def test_raises_error_when_more_than_one_sidecar_template_is_present
|
||||
error = assert_raises ViewComponent::TemplateError do
|
||||
render_inline(TooManySidecarFilesComponent.new)
|
||||
end
|
||||
error =
|
||||
assert_raises ViewComponent::TemplateError do
|
||||
render_inline(TooManySidecarFilesComponent.new)
|
||||
end
|
||||
|
||||
assert_includes error.message, "More than one template found for TooManySidecarFilesComponent."
|
||||
end
|
||||
|
||||
def test_raises_error_when_more_than_one_sidecar_template_for_a_variant_is_present
|
||||
error = assert_raises ViewComponent::TemplateError do
|
||||
render_inline(TooManySidecarFilesForVariantComponent.new)
|
||||
end
|
||||
error =
|
||||
assert_raises ViewComponent::TemplateError do
|
||||
render_inline(TooManySidecarFilesForVariantComponent.new)
|
||||
end
|
||||
|
||||
assert_includes error.message, "More than one template found for variants 'test' and 'testing' in TooManySidecarFilesForVariantComponent"
|
||||
end
|
||||
|
||||
def test_raise_error_when_default_template_file_and_inline_default_call_exist
|
||||
error = assert_raises ViewComponent::TemplateError do
|
||||
render_inline(DefaultTemplateAndInlineDefaultTemplateComponent.new)
|
||||
end
|
||||
error =
|
||||
assert_raises ViewComponent::TemplateError do
|
||||
render_inline(DefaultTemplateAndInlineDefaultTemplateComponent.new)
|
||||
end
|
||||
|
||||
assert_includes error.message, "Template file and inline render method found for DefaultTemplateAndInlineDefaultTemplateComponent."
|
||||
end
|
||||
|
||||
def test_raise_error_when_variant_template_file_and_inline_variant_call_exist
|
||||
error = assert_raises ViewComponent::TemplateError do
|
||||
with_variant :phone do
|
||||
render_inline(VariantTemplateAndInlineVariantTemplateComponent.new)
|
||||
error =
|
||||
assert_raises ViewComponent::TemplateError do
|
||||
with_variant :phone do
|
||||
render_inline(VariantTemplateAndInlineVariantTemplateComponent.new)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert_includes error.message, "Template file and inline render method found for variant 'phone' in VariantTemplateAndInlineVariantTemplateComponent."
|
||||
end
|
||||
|
||||
def test_raise_error_when_template_file_and_sidecar_directory_template_exist
|
||||
error = assert_raises ViewComponent::TemplateError do
|
||||
render_inline(TemplateAndSidecarDirectoryTemplateComponent.new)
|
||||
end
|
||||
error =
|
||||
assert_raises ViewComponent::TemplateError do
|
||||
render_inline(TemplateAndSidecarDirectoryTemplateComponent.new)
|
||||
end
|
||||
|
||||
assert_includes error.message, "More than one template found for TemplateAndSidecarDirectoryTemplateComponent."
|
||||
end
|
||||
|
@ -558,9 +571,10 @@ class ViewComponentTest < ViewComponent::TestCase
|
|||
end
|
||||
|
||||
def test_backtrace_returns_correct_file_and_line_number
|
||||
error = assert_raises NameError do
|
||||
render_inline(ExceptionInTemplateComponent.new)
|
||||
end
|
||||
error =
|
||||
assert_raises NameError do
|
||||
render_inline(ExceptionInTemplateComponent.new)
|
||||
end
|
||||
|
||||
assert_match %r[app/components/exception_in_template_component\.html\.erb:2], error.backtrace[0]
|
||||
end
|
||||
|
@ -622,18 +636,20 @@ class ViewComponentTest < ViewComponent::TestCase
|
|||
end
|
||||
|
||||
def test_render_collection_missing_collection_object
|
||||
exception = assert_raises ArgumentError do
|
||||
render_inline(ProductComponent.with_collection("foo"))
|
||||
end
|
||||
exception =
|
||||
assert_raises ArgumentError do
|
||||
render_inline(ProductComponent.with_collection("foo"))
|
||||
end
|
||||
|
||||
assert_equal exception.message, "The value of the argument isn't a valid collection. Make sure it responds to to_ary: \"foo\""
|
||||
end
|
||||
|
||||
def test_render_collection_missing_arg
|
||||
products = [OpenStruct.new(name: "Radio clock"), OpenStruct.new(name: "Mints")]
|
||||
exception = assert_raises ArgumentError do
|
||||
render_inline(ProductComponent.with_collection(products))
|
||||
end
|
||||
exception =
|
||||
assert_raises ArgumentError do
|
||||
render_inline(ProductComponent.with_collection(products))
|
||||
end
|
||||
|
||||
assert_match(/missing keyword/, exception.message)
|
||||
assert_match(/notice/, exception.message)
|
||||
|
@ -649,19 +665,21 @@ class ViewComponentTest < ViewComponent::TestCase
|
|||
end
|
||||
|
||||
def test_collection_component_missing_parameter_name
|
||||
exception = assert_raises ArgumentError do
|
||||
render_inline(MissingCollectionParameterNameComponent.with_collection([]))
|
||||
end
|
||||
exception =
|
||||
assert_raises ArgumentError do
|
||||
render_inline(MissingCollectionParameterNameComponent.with_collection([]))
|
||||
end
|
||||
|
||||
assert_match(/MissingCollectionParameterNameComponent initializer must accept `foo` collection parameter/, exception.message)
|
||||
end
|
||||
|
||||
def test_collection_component_missing_default_parameter_name
|
||||
exception = assert_raises ArgumentError do
|
||||
render_inline(
|
||||
MissingDefaultCollectionParameterComponent.with_collection([OpenStruct.new(name: "Mints")])
|
||||
)
|
||||
end
|
||||
exception =
|
||||
assert_raises ArgumentError do
|
||||
render_inline(
|
||||
MissingDefaultCollectionParameterComponent.with_collection([OpenStruct.new(name: "Mints")])
|
||||
)
|
||||
end
|
||||
|
||||
assert_match(/MissingDefaultCollectionParameterComponent initializer must accept `missing_default_collection_parameter` collection parameter/, exception.message)
|
||||
end
|
||||
|
@ -671,9 +689,10 @@ class ViewComponentTest < ViewComponent::TestCase
|
|||
old_cache = ViewComponent::CompileCache.cache
|
||||
ViewComponent::CompileCache.cache = Set.new
|
||||
|
||||
exception = assert_raises ViewComponent::ComponentError do
|
||||
InvalidParametersComponent.compile(raise_errors: true)
|
||||
end
|
||||
exception =
|
||||
assert_raises ViewComponent::ComponentError do
|
||||
InvalidParametersComponent.compile(raise_errors: true)
|
||||
end
|
||||
|
||||
assert_match(/InvalidParametersComponent initializer cannot contain `content` since it will override a public ViewComponent method/, exception.message)
|
||||
ensure
|
||||
|
@ -686,9 +705,10 @@ class ViewComponentTest < ViewComponent::TestCase
|
|||
old_cache = ViewComponent::CompileCache.cache
|
||||
ViewComponent::CompileCache.cache = Set.new
|
||||
|
||||
exception = assert_raises ViewComponent::ComponentError do
|
||||
InvalidNamedParametersComponent.compile(raise_errors: true)
|
||||
end
|
||||
exception =
|
||||
assert_raises ViewComponent::ComponentError do
|
||||
InvalidNamedParametersComponent.compile(raise_errors: true)
|
||||
end
|
||||
|
||||
assert_match(/InvalidNamedParametersComponent initializer cannot contain `content` since it will override a public ViewComponent method/, exception.message)
|
||||
ensure
|
||||
|
@ -697,11 +717,12 @@ class ViewComponentTest < ViewComponent::TestCase
|
|||
end
|
||||
|
||||
def test_collection_component_with_trailing_comma_attr_reader
|
||||
exception = assert_raises ArgumentError do
|
||||
render_inline(
|
||||
ProductReaderOopsComponent.with_collection(["foo"])
|
||||
)
|
||||
end
|
||||
exception =
|
||||
assert_raises ArgumentError do
|
||||
render_inline(
|
||||
ProductReaderOopsComponent.with_collection(["foo"])
|
||||
)
|
||||
end
|
||||
|
||||
assert_match(/ProductReaderOopsComponent initializer is empty or invalid/, exception.message)
|
||||
end
|
||||
|
@ -752,17 +773,18 @@ class ViewComponentTest < ViewComponent::TestCase
|
|||
end
|
||||
|
||||
def test_collection_parameter_does_not_require_compile
|
||||
dynamic_component = Class.new(ViewComponent::Base) do
|
||||
with_collection_parameter :greeting
|
||||
dynamic_component =
|
||||
Class.new(ViewComponent::Base) do
|
||||
with_collection_parameter :greeting
|
||||
|
||||
def initialize(greeting = "hello world")
|
||||
@greeting = greeting
|
||||
end
|
||||
def initialize(greeting = "hello world")
|
||||
@greeting = greeting
|
||||
end
|
||||
|
||||
def call
|
||||
content_tag :h1, @greeting
|
||||
def call
|
||||
content_tag :h1, @greeting
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Necessary because anonymous classes don't have a `name` property
|
||||
Object.const_set("MY_COMPONENT", dynamic_component)
|
||||
|
|
Загрузка…
Ссылка в новой задаче