Rails viewcomponents test render? in RSpec

1.2k Views Asked by At

Rails Viewcomponents allow you to test if a component has rendered in minitest using refute_component_rendered but how do you do the same in RSpec?

class SometimesNotRenderedComponent < ViewComponent::Base
  def initialize(my_param)
    @my_param = my_param
  end

  def render?
    # test this
  end
end


it "renders nothing when..." do
  render_inline(described_class.new(my_param))

  # expect(page).to  ... have no content
end
1

There are 1 best solutions below

1
On

Let's dig a little, otherwise, the answer would be quite short.

The code for refute_component_rendered is pretty simple:
https://github.com/ViewComponent/view_component/blob/v2.78.0/lib/view_component/test_helpers.rb#L14

def refute_component_rendered
  assert_no_selector("body")
end

assert_no_selector is a Capybara matcher. Negative matchers for rspec are defined dynamically with a prefix have_no_ and are not documented.

have_selector delegates to assert_selector.
https://www.rubydoc.info/gems/capybara/Capybara/RSpecMatchers#have_selector-instance_method

Which means have_no_selector delegates to assert_no_selector.

You can use either one, it's just a matter of preference:

it "does not render" do
  render_inline(described_class.new(nil))
  expect(page).to have_no_selector("body")
  expect(page).not_to have_selector("body")
end

it "renders, but why" do
  # why match on `body`? it is just how `render_inline` method works,
  #   https://github.com/ViewComponent/view_component/blob/v2.78.0/lib/view_component/test_helpers.rb#L56
  # it assignes whatever the result of `render` to @rendered_content, like this:
  @rendered_content = "i'm rendering"

  # when you call `page`
  #   https://github.com/ViewComponent/view_component/blob/v2.78.0/lib/view_component/test_helpers.rb#L10
  # it wraps @rendered_content in `Capybara::Node::Simple`
  #   https://www.rubydoc.info/gems/capybara/Capybara/Node/Simple
  # if content is empty, there is no body
  #   Capybara::Node::Simple.new("").native.to_html
  #   # => "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n\n"

  puts page.native.to_xhtml # to see what you're matching on

  expect(page).to_not have_no_selector("body")
end

I've double checked:

$ bin/rspec spec/components/badge_component_spec.rb

BadgeComponent
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
  does not render
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
  <body>
    <p>i'm rendering</p>
  </body>
</html>
  renders, but why

Finished in 0.06006 seconds (files took 2.11 seconds to load)
2 examples, 0 failures