Skip to content

Instantly share code, notes, and snippets.

@matsales28
Created November 16, 2022 15:19
Show Gist options
  • Save matsales28/f823baaa9f32f24a269ee486a0f63336 to your computer and use it in GitHub Desktop.
Save matsales28/f823baaa9f32f24a269ee486a0f63336 to your computer and use it in GitHub Desktop.
Create matcher for asserting components of ViewComponents have been rendered with arguments or not
# This gist was used to create have_rendered_page_component matcher, because our intention was to
# assure that a specific page_component was being rendered.
# This is overriding the instrumentation of ViewComponent
# to include the parameters that a component might receive when rendering.
# frozen_string_literal: true
require "active_support/notifications"
require "active_support/concern"
module ViewComponent
module Instrumentation
def render_in(view_context, &)
arguments = public_methods(false) & instance_variables.map { |method| method.to_s.delete("@").to_sym }
parameters = arguments.index_with { |arg| send(arg) }
ActiveSupport::Notifications.instrument(
"!render.view_component",
{
name: self.class.name,
identifier: self.class.identifier,
parameters: parameters
}
) do
super(view_context, &)
end
end
end
end
# Overriding RenderTemplateMatcher to be able to receive arguments and also in matchers? method to call assert_page_component
module RSpec
module Rails
module Matchers
module RenderTemplate
class RenderTemplateMatcher
def with(**arguments)
@arguments = arguments
self
end
def matches?(*)
match_check = match_unless_raises ActiveSupport::TestCase::Assertion do
@scope.assert_page_component expected, @arguments, @message
end
check_redirect unless match_check
match_check
end
end
end
end
end
end
# Creating the PageComponentMatcher module
module PageComponentMatcher
extend ActiveSupport::Concern
included do
setup :setup_subscriptions
teardown :teardown_subscriptions
end
def setup_subscriptions
@_pages = Hash.new(0)
@_subscribers = []
@_subscribers << ActiveSupport::Notifications.subscribe("!render.view_component") do |event|
payload = event.payload
@_pages[payload[:name]] = payload[:parameters] if payload[:identifier].match?(%r{/pages/(.*)/}) # If you want to test for just components, change this to %r{/components/(.*)/}
end
end
def teardown_subscriptions
return unless defined?(@_subscribers)
@_subscribers.each { |subscriber| ActiveSupport::Notifications.unsubscribe(subscriber) }
end
def reset_template_assertion
@_pages.clear
end
def assert_page_component(component, arguments, message = nil)
response.body
component_message = message || format(
"expecting %<expected>s but rendered %<actual>s", expected: component.inspect, actual: @_pages.keys.first
)
params_message = format("expecting %<args>s but received %<params>s", args: arguments, params: @_pages.values.first)
matches_component = @_pages.any? { |component_name, _| component_name == component }
matches_arguments = arguments.present? ? @_pages.each_value.all?(arguments) : true
assert matches_component, component_message
assert matches_arguments, params_message
end
def have_rendered_page_component(component, message = nil)
RSpec::Rails::Matchers::RenderTemplate::RenderTemplateMatcher.new(self, component, message)
end
end
RSpec.configure do |config|
config.include PageComponentMatcher, type: :request
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment