Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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