rspec-expectations icon indicating copy to clipboard operation
rspec-expectations copied to clipboard

Output chained matchers description

Open inkstak opened this issue 1 year ago • 6 comments

Subject of the issue

I've created a custom matcher which allows to chain other matchers against a computed value :

matcher :have_html_body do
  chain :to, :other_matcher
  
  match do |actual|
    @actual = actual.parsed_body

    html_body_valid?(@actual) && other_matcher_matches?(@actual)
  end
  
  def other_matcher_matches?(actual)
    other_matcher.nil? || other_matcher.matches?(actual)
  end
end

I can then chain any matchers to match the parsed body :

expect(response).to have_html_body.to include("Hello World")
expect(response).to have_html_body.to match(/hello/)
expect(response).to have_html_body.to have_selector("#test", text: "Hello World")

It works fine, but descriptions are cumbersome :

is expected to have html body to #<RSpec::Matchers::BuiltIn::Include:0x0000000108887c10 [...long output...]>
is expected to have html body to #<RSpec::Matchers::BuiltIn::Match:0x000000010f476228 [...long output...]>
is expected to have html body to #<Capybara::RSpecMatchers::Matchers::HaveText:0x0000000140b35fd8 [...long output...]>

Expected behavior

It should generate a readable description :

is expected to have html body to include "Hello World"
is expected to have html body to match /hello/
is expected to have html body to have visible css "#test" with text "Hello World"

Your environment

  • Ruby 3.2.2
  • rspec 3.12.0
  • rspec-expectations 3.12.3
  • rspec-support 3.12.1

config.include_chain_clauses_in_custom_matcher_descriptions is already set to true

Investigation

There are 3 methods involved to generate chained description :

I'm not sure which one is the most relevant to fix the issue but my guess is EnglishPhrasing. Something like :

   def self.list(obj)
     return " #{RSpec::Support::ObjectFormatter.format(obj)}" if !obj || Struct === obj || Hash === obj
-    items = Array(obj).map { |w| RSpec::Support::ObjectFormatter.format(w) }
+    items = Array(obj).map { |w| w.is_a?(RSpec::Matchers::Composable) ? w.description : RSpec::Support::ObjectFormatter.format(w) }

inkstak avatar Dec 13 '23 11:12 inkstak