Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ nav_order: 6

## main

* Allow I18n calls in `render?`.
* Split render lifecycle into separate methods, to be able to test component methods that rely on `t(...)` directly.
* Added `setup_render` test helper to allow testing of component methods that rely on `t(...)` without rendering the component.

*23tux*

* Resolve deprecation warning for `ActiveSupport::Configurable`.

*Simon Fish*
Expand Down
17 changes: 17 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,23 @@ render_inline(MyComponent.new)
assert_text("Hello, World!")
```

### `#setup_render(component)` → [ViewComponent::Base]

Triggers the render setup of a component without actually rendering it.
The method is also called internally by `#render_in` when a component is rendered.
Useful for testing methods of components that need access to the view context or the `@virtual_path` variable like I18n's `t(...)` helper.

```ruby
class MyComponent < ViewComponent::Base
def message
t("hello.world")
end
end

component = setup_render(MyComponent.new)
assert_equal(component.message, "Hello, World!")
```

### `#render_preview(name, from: __vc_test_helpers_preview_class, params: {})` → [Nokogiri::HTML5]

Render a preview inline. Internally sets `page` to be a `Capybara::Node::Simple`,
Expand Down
21 changes: 17 additions & 4 deletions lib/view_component/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ def initialize
#
# @return [String]
def render_in(view_context, &block)
setup_render(view_context, &block)
before_render
perform_render
ensure
teardown_render(view_context)
end

def setup_render(view_context, &block)
self.class.__vc_compile(raise_errors: true)

@view_context = view_context
Expand All @@ -123,7 +131,7 @@ def render_in(view_context, &block)

# For caching, such as #cache_if
@current_template = nil unless defined?(@current_template)
old_current_template = @current_template
@old_current_template = @current_template

if block && defined?(@__vc_content_set_by_with_content)
raise DuplicateContentError.new(self.class.name)
Expand All @@ -132,13 +140,15 @@ def render_in(view_context, &block)
@__vc_content_evaluated = false
@__vc_render_in_block = block

before_render
@view_context.instance_variable_set(:@virtual_path, virtual_path)
self
end

def perform_render
if render?
value = nil

@output_buffer.with_buffer do
@view_context.instance_variable_set(:@virtual_path, virtual_path)

rendered_template =
around_render do
Expand All @@ -162,8 +172,11 @@ def render_in(view_context, &block)
else
""
end
ensure
end

def teardown_render(view_context)
view_context.instance_variable_set(:@virtual_path, @old_virtual_path)
old_current_template = remove_instance_variable(:@current_template) if defined?(@current_template)
@current_template = old_current_template
end

Expand Down
4 changes: 4 additions & 0 deletions lib/view_component/test_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ def render_inline(component, **args, &block)
fragment
end

def setup_render(component)
component.setup_render(vc_test_view_context)
end

# Returns the view context used to render components in tests. Note that the view context
# is reset after each call to `render_inline`.
#
Expand Down
4 changes: 4 additions & 0 deletions test/sandbox/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ en:

my_component:
message: "OH NO! (you shouldn't see me)"

rendering_test:
i18n_test_component:
message: "I can be called in render?"
30 changes: 28 additions & 2 deletions test/sandbox/test/rendering_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def test_render_inline_allocations
MyComponent.__vc_ensure_compiled

with_instrumentation_enabled_option(false) do
assert_allocations({"3.5" => 67, "3.4" => 72..74, "3.3" => 72, "3.2" => 75..76}) do
assert_allocations({"3.5" => 67, "3.4" => 72..74, "3.3" => 72..75, "3.2" => 71..76}) do
render_inline(MyComponent.new)
end
end
Expand All @@ -34,7 +34,7 @@ def test_render_collection_inline_allocations
ViewComponent::CompileCache.cache.delete(ProductComponent)
ProductComponent.__vc_ensure_compiled

allocations = {"3.5" => 66, "3.4" => 70..82, "3.3" => 86, "3.2" => 89..90}
allocations = {"3.5" => 66, "3.4" => 70..82, "3.3" => 86, "3.2" => 70..90}

products = [Product.new(name: "Radio clock"), Product.new(name: "Mints")]
notice = "On sale"
Expand Down Expand Up @@ -1322,4 +1322,30 @@ def test_render_partial_with_yield
render_inline(PartialWithYieldComponent.new)
assert_text "hello world", exact: true, normalize_ws: true
end

class I18nTestComponent < ViewComponent::Base
def message
t(".message")
end

def render?
message
end

def call
content_tag :div, t(".message")
end
end

def test_i18n_in_render_hook
vc_test_request.params[:hello] = "world"
render_inline(I18nTestComponent.new)

assert_selector("div", text: I18n.t("rendering_test.i18n_test_component.message"))
end

def test_render_lifecycle_hooks
component = setup_render(I18nTestComponent.new)
assert_equal(component.message, I18n.t("rendering_test.i18n_test_component.message"))
end
end
Loading