From f1b04eec3b696c9a9134b6ed9ef11ac70356e57f Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Wed, 25 Dec 2024 11:14:19 +0000 Subject: [PATCH 2/2] Fixes for rspec-expectations hash syntax --- .../built_in_matchers/include.feature | 4 +- .../built_in_matchers/predicates.feature | 2 +- .../step_definitions/additional_cli_steps.rb | 26 +++++++ .../spec/rspec/matchers/aliases_spec.rb | 6 +- .../rspec/matchers/built_in/captures_spec.rb | 4 +- .../spec/rspec/matchers/built_in/eq_spec.rb | 2 +- .../spec/rspec/matchers/built_in/has_spec.rb | 4 +- .../matchers/built_in/have_attributes_spec.rb | 16 +++- .../rspec/matchers/built_in/include_spec.rb | 74 +++++++++++++------ 9 files changed, 102 insertions(+), 36 deletions(-) diff --git a/rspec-expectations/features/built_in_matchers/include.feature b/rspec-expectations/features/built_in_matchers/include.feature index 295d9b60f..012307135 100644 --- a/rspec-expectations/features/built_in_matchers/include.feature +++ b/rspec-expectations/features/built_in_matchers/include.feature @@ -139,7 +139,7 @@ Feature: `include` matcher end """ When I run `rspec hash_include_matcher_spec.rb` - Then the output should contain all of these: + Then the output should contain all of these, ignoring hash syntax: | 22 examples, 13 failures | | expected {:a => 7, :b => 5} not to include :a | | expected {:a => 7, :b => 5} not to include :b and :a | @@ -176,7 +176,7 @@ Feature: `include` matcher end """ When I run `rspec include_matcher_with_counts_spec.rb` - Then the output should contain all of these: + Then the output should contain all of these, ignoring hash syntax: | 12 examples, 4 failures | | expected [{:c => 7}, {:a => 1}, {:b => 2}, {:c => 1}, {:a => 3}, {:c => 7}] not to include (have key :b) once | | expected [{:c => 7}, {:a => 1}, {:b => 2}, {:c => 1}, {:a => 3}, {:c => 7}] not to include (have key :a) twice | diff --git a/rspec-expectations/features/built_in_matchers/predicates.feature b/rspec-expectations/features/built_in_matchers/predicates.feature index 0c3b33c5e..282c7a853 100644 --- a/rspec-expectations/features/built_in_matchers/predicates.feature +++ b/rspec-expectations/features/built_in_matchers/predicates.feature @@ -88,7 +88,7 @@ Feature: Predicate matchers """ When I run `rspec should_have_key_spec.rb` Then the output should contain "2 examples, 1 failure" - And the output should contain "expected `{:foo=>7}.has_key?(:bar)` to be truthy, got false" + And the output should contain, ignoring hash syntax, "expected `{:foo=>7}.has_key?(:bar)` to be truthy, got false" Scenario: Expecting `subject` to have all decimals (based on custom `has_decimals?` method) Given a file named "should_not_have_all_string_keys_spec.rb" with: diff --git a/rspec-expectations/features/step_definitions/additional_cli_steps.rb b/rspec-expectations/features/step_definitions/additional_cli_steps.rb index 2ab176f60..c016e0ea8 100644 --- a/rspec-expectations/features/step_definitions/additional_cli_steps.rb +++ b/rspec-expectations/features/step_definitions/additional_cli_steps.rb @@ -28,3 +28,29 @@ step 'the output should contain "1 failure"' step 'the exit status should not be 0' end + +Then(/^the output should contain, ignoring hash syntax, "(.*)"$/) do |output| + if RUBY_VERSION.to_f > 3.3 + expect(all_output).to include(output.gsub(/:(\w+)=>/, '\1: ')) + else + expect(all_output).to include(output) + end +end + +RSpec::Matchers.define :match_table do |lines| + match do |all_output| + lines.all? { |line| all_output.include?(line) } + end + + diffable +end + +Then "the output should contain all of these, ignoring hash syntax:" do |table| + lines = table.raw.flatten.reject(&:empty?) + + if RUBY_VERSION.to_f > 3.3 + lines = lines.map { |line| line.gsub(/:(\w+)\s+=>\s+/, '\1: ') } + end + + expect(all_output).to match_table(lines) +end diff --git a/rspec-expectations/spec/rspec/matchers/aliases_spec.rb b/rspec-expectations/spec/rspec/matchers/aliases_spec.rb index 1b7b0d2ac..aeaca6a63 100644 --- a/rspec-expectations/spec/rspec/matchers/aliases_spec.rb +++ b/rspec-expectations/spec/rspec/matchers/aliases_spec.rb @@ -219,7 +219,7 @@ module RSpec an_object_having_attributes(:age => 32) ).to be_aliased_to( have_attributes(:age => 32) - ).with_description("an object having attributes {:age => 32}") + ).with_description("an object having attributes #{hash_inspect({ :age => 32 })}") end specify do @@ -227,7 +227,7 @@ module RSpec having_attributes(:age => 32) ).to be_aliased_to( have_attributes(:age => 32) - ).with_description("having attributes {:age => 32}") + ).with_description("having attributes #{hash_inspect({ :age => 32 })}") end specify do @@ -251,7 +251,7 @@ module RSpec a_hash_including(:a => 5) ).to be_aliased_to( include(:a => 5) - ).with_description('a hash including {:a => 5}') + ).with_description("a hash including #{hash_inspect({ :a => 5 })}") end specify do diff --git a/rspec-expectations/spec/rspec/matchers/built_in/captures_spec.rb b/rspec-expectations/spec/rspec/matchers/built_in/captures_spec.rb index b8656f1cc..609326e48 100644 --- a/rspec-expectations/spec/rspec/matchers/built_in/captures_spec.rb +++ b/rspec-expectations/spec/rspec/matchers/built_in/captures_spec.rb @@ -36,7 +36,7 @@ it "has a sensible failure description with a hash including matcher" do expect { expect("a123a").not_to match(Regexp.new("(?123)(asdf)?")).with_captures(a_hash_including(:num => "123")) - }.to fail_with(/num => "123"/) + }.to fail_with(/#{hash_inspect({ :num => "123" })}/) end it "matches named captures when not passing a hash" do @@ -80,7 +80,7 @@ it "has a sensible failure description with a hash including matcher" do expect { expect(Regexp.new("(?123)(asdf)?")).not_to match("a123a").with_captures(a_hash_including(:num => "123")) - }.to fail_with(/num => "123"/) + }.to fail_with(/#{hash_inspect({ :num => "123" })}/) end it "matches named captures when not passing a hash" do diff --git a/rspec-expectations/spec/rspec/matchers/built_in/eq_spec.rb b/rspec-expectations/spec/rspec/matchers/built_in/eq_spec.rb index ad03c1f1d..b1ab6aa5c 100644 --- a/rspec-expectations/spec/rspec/matchers/built_in/eq_spec.rb +++ b/rspec-expectations/spec/rspec/matchers/built_in/eq_spec.rb @@ -120,7 +120,7 @@ module Matchers ['foo', 'eq "foo"'], [/regex/, 'eq /regex/'], [['foo'], 'eq ["foo"]'], - [{ :foo => :bar }, 'eq {:foo=>:bar}'], + [{ :foo => :bar }, "eq #{{ :foo=>:bar }.inspect}"], [Class, 'eq Class'], [RSpec, 'eq RSpec'], [Time.utc(2014, 1, 1), "eq 2014-01-01 00:00:00.#{expected_seconds} +0000"], diff --git a/rspec-expectations/spec/rspec/matchers/built_in/has_spec.rb b/rspec-expectations/spec/rspec/matchers/built_in/has_spec.rb index 190817828..bf6054dbf 100644 --- a/rspec-expectations/spec/rspec/matchers/built_in/has_spec.rb +++ b/rspec-expectations/spec/rspec/matchers/built_in/has_spec.rb @@ -31,7 +31,7 @@ it "fails if #has_sym?(*args) returns false" do expect { expect({ :b => "B" }).to have_key(:a) - }.to fail_with('expected `{:b=>"B"}.has_key?(:a)` to return true, got false') + }.to fail_with("expected `#{{ :b=>"B" }.inspect}.has_key?(:a)` to return true, got false") end obj_with_block_method = Object.new @@ -180,7 +180,7 @@ def o.has_sym?(sym); sym == :foo; end it "fails if #has_sym?(*args) returns true" do expect { expect({ :a => "A" }).not_to have_key(:a) - }.to fail_with('expected `{:a=>"A"}.has_key?(:a)` to return false, got true') + }.to fail_with("expected `#{{ :a=>"A" }.inspect}.has_key?(:a)` to return false, got true") end it "fails if target does not respond to #has_sym?" do diff --git a/rspec-expectations/spec/rspec/matchers/built_in/have_attributes_spec.rb b/rspec-expectations/spec/rspec/matchers/built_in/have_attributes_spec.rb index 92e803b63..aad08effe 100644 --- a/rspec-expectations/spec/rspec/matchers/built_in/have_attributes_spec.rb +++ b/rspec-expectations/spec/rspec/matchers/built_in/have_attributes_spec.rb @@ -108,13 +108,25 @@ def count it 'provides a description' do description = have_attributes(:age => (a_value > 30)).description - expect(description).to eq("have attributes {:age => (a value > 30)}") + expect(description).to eq( + if RUBY_VERSION.to_f > 3.3 + "have attributes {age: (a value > 30)}" + else + "have attributes {:age => (a value > 30)}" + end + ) end it "fails with a clear message when the matcher does not match" do expect { expect(person).to have_attributes(:age => (a_value < 10)) - }.to fail_including("expected #{object_inspect person} to have attributes {:age => (a value < 10)}") + }.to fail_including( + if RUBY_VERSION.to_f > 3.3 + "expected #{object_inspect person} to have attributes {age: (a value < 10)}" + else + "expected #{object_inspect person} to have attributes {:age => (a value < 10)}" + end + ) end end end diff --git a/rspec-expectations/spec/rspec/matchers/built_in/include_spec.rb b/rspec-expectations/spec/rspec/matchers/built_in/include_spec.rb index e760a3a46..19aba9673 100644 --- a/rspec-expectations/spec/rspec/matchers/built_in/include_spec.rb +++ b/rspec-expectations/spec/rspec/matchers/built_in/include_spec.rb @@ -69,8 +69,12 @@ def use_string_keys_in_failure_message? false end - def convert_key(key) - use_string_keys_in_failure_message? ? "\"#{key}\"" : ":#{key}" + def converted_key_hash_inspect(key, value) + if use_string_keys_in_failure_message? + hash_inspect({ key.to_s => value }) + else + hash_inspect({ key => value }) + end end it 'passes if target has the expected as a key' do @@ -82,7 +86,8 @@ def convert_key(key) end it "fails if target does not include expected" do - failure_string = %(expected {#{convert_key(:key)} => "value"} to include :other) + failure_string = %(expected #{converted_key_hash_inspect(:key, "value")} to include :other) + expect { expect(build_target(:key => 'value')).to include(:other) }.to fail_matching(failure_string) @@ -91,7 +96,7 @@ def convert_key(key) it "fails if target doesn't have a key and we expect nil" do expect { expect(build_target({})).to include(:something => nil) - }.to fail_matching("expected {} to include {:something => nil}") + }.to fail_matching("expected {} to include #{hash_inspect({ :something => nil })}") end it 'works even when an entry in the hash overrides #send' do @@ -480,12 +485,23 @@ class PseudoHash < SimpleDelegator { :number => 0 }, { :number => 3 } ) - }.to fail_including(dedent(<<-END)) - |Diff: - |@@ #{one_line_header} @@ - |-[{:number=>1}, {:number=>0}, {:number=>3}] - |+[{:number=>1}, {:number=>2}, {:number=>3}] - END + }.to fail_including( + if RUBY_VERSION.to_f > 3.3 + dedent(<<-END) + |Diff: + |@@ #{one_line_header} @@ + |-[{number: 1}, {number: 0}, {number: 3}] + |+[{number: 1}, {number: 2}, {number: 3}] + END + else + dedent(<<-END) + |Diff: + |@@ #{one_line_header} @@ + |-[{:number=>1}, {:number=>0}, {:number=>3}] + |+[{:number=>1}, {:number=>2}, {:number=>3}] + END + end + ) end end @@ -632,7 +648,7 @@ class PseudoHash < SimpleDelegator it "fails if target includes expected key" do expect { expect({ :key => 'value' }).not_to include(:key) - }.to fail_matching('expected {:key => "value"} not to include :key') + }.to fail_matching("expected #{hash_inspect({ :key => "value" })} not to include :key") end end @@ -751,13 +767,13 @@ class PseudoHash < SimpleDelegator it "fails if target has a different value for key" do expect { expect({ :key => 'different' }).to include(:key => 'value') - }.to fail_matching('expected {:key => "different"} to include {:key => "value"}') + }.to fail_matching("expected #{hash_inspect({ :key => "different" })} to include #{hash_inspect({ :key => "value" })}") end it "fails if target has a different key" do expect { expect({ :other => 'value' }).to include(:key => 'value') - }.to fail_matching('expected {:other => "value"} to include {:key => "value"}') + }.to fail_matching("expected #{hash_inspect({ :other => "value" })} to include #{hash_inspect({ :key => "value" })}") end end @@ -765,7 +781,7 @@ class PseudoHash < SimpleDelegator it "fails if the target does not contain the given hash" do expect { expect(['a', 'b']).to include(:key => 'value') - }.to fail_matching('expected ["a", "b"] to include {:key => "value"}') + }.to fail_matching("expected [\"a\", \"b\"] to include #{hash_inspect({ :key => "value" })}") end it "passes if the target contains the given hash" do @@ -779,13 +795,13 @@ class PseudoHash < SimpleDelegator it "fails if target includes the key/value pair" do expect { expect({ :key => 'value' }).not_to include(:key => 'value') - }.to fail_matching('expected {:key => "value"} not to include {:key => "value"}') + }.to fail_matching("expected #{hash_inspect({ :key => "value" })} not to include #{hash_inspect({ :key => "value" })}") end it "fails if target includes the key/value pair among others" do expect { expect({ :key => 'value', :other => 'different' }).not_to include(:key => 'value') - }.to fail_with(%r|expected #{hash_inspect :key => "value", :other => "different"} not to include \{:key => "value"\}|) + }.to fail_with(%r|expected #{hash_inspect(:key => "value", :other => "different")} not to include #{hash_inspect({ :key => "value" })}|) end it "passes if target has a different value for key" do @@ -805,7 +821,7 @@ class PseudoHash < SimpleDelegator it "fails if the target contains the given hash" do expect { expect(['a', { :key => 'value' }]).not_to include(:key => 'value') - }.to fail_matching('expected ["a", {:key => "value"}] not to include {:key => "value"}') + }.to fail_matching("expected [\"a\", #{hash_inspect({ :key => "value" })}] not to include #{hash_inspect({ :key => "value" })}") end end end @@ -982,13 +998,25 @@ def matches?(_) it 'provides a description' do description = include(:a => a_value_within(3).of(10)).description - expect(description).to eq("include {:a => (a value within 3 of 10)}") + expect(description).to eq( + if RUBY_VERSION.to_f > 3.3 + "include {a: (a value within 3 of 10)}" + else + "include {:a => (a value within 3 of 10)}" + end + ) end it "fails with a clear message when the matcher does not match" do expect { expect(:a => 15).to include(:a => a_value_within(3).of(10)) - }.to fail_matching("expected {:a => 15} to include {:a => (a value within 3 of 10)}") + }.to fail_matching( + if RUBY_VERSION.to_f > 3.3 + "expected {a: 15} to include {a: (a value within 3 of 10)}" + else + "expected {:a => 15} to include {:a => (a value within 3 of 10)}" + end + ) end end @@ -1005,7 +1033,7 @@ def matches?(_) it 'fails with a clear message when the matcher does not match', :if => (RUBY_VERSION.to_f > 1.8) do expect { expect(:drink => "water", :food => "bread").to include(match(/bar/)) - }.to fail_matching('expected {:drink => "water", :food => "bread"} to include (match /bar/)') + }.to fail_matching("expected #{hash_inspect({ :drink => "water", :food => "bread" })} to include (match /bar/)") end end @@ -1034,19 +1062,19 @@ def matches?(_) it 'fails with a clear message when the value does not match', :if => (RUBY_VERSION.to_f > 1.8) do expect { expect(:drink => "water", :food => "bread").to include(match(/foo/) => "meat") - }.to fail_matching('expected {:drink => "water", :food => "bread"} to include {(match /foo/) => "meat"}') + }.to fail_matching("expected #{hash_inspect({ :drink => "water", :food => "bread" })} to include {(match /foo/) => \"meat\"}") end it 'fails with a clear message when the matcher does not match', :if => (RUBY_VERSION.to_f > 1.8) do expect { expect(:drink => "water", :food => "bread").to include(match(/bar/) => "bread") - }.to fail_matching('expected {:drink => "water", :food => "bread"} to include {(match /bar/) => "bread"}') + }.to fail_matching("expected #{hash_inspect({ :drink => "water", :food => "bread" })} to include {(match /bar/) => \"bread\"}") end it 'fails with a clear message when several matchers do not match', :if => (RUBY_VERSION.to_f > 1.8) do expect { expect(:drink => "water", :food => "bread").to include(match(/bar/) => "bread", match(/baz/) => "water") - }.to fail_matching('expected {:drink => "water", :food => "bread"} to include {(match /bar/) => "bread", (match /baz/) => "water"}') + }.to fail_matching("expected #{hash_inspect({ :drink => "water", :food => "bread" })} to include {(match /bar/) => \"bread\", (match /baz/) => \"water\"}") end end