super_diff 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/lib/super_diff/active_record.rb +2 -0
  4. data/lib/super_diff/active_record/monkey_patches.rb +9 -0
  5. data/lib/super_diff/csi.rb +4 -0
  6. data/lib/super_diff/equality_matchers/default.rb +1 -1
  7. data/lib/super_diff/operation_sequences/base.rb +14 -0
  8. data/lib/super_diff/rspec.rb +9 -9
  9. data/lib/super_diff/rspec/differ.rb +6 -6
  10. data/lib/super_diff/rspec/differs.rb +9 -3
  11. data/lib/super_diff/rspec/differs/collection_containing_exactly.rb +1 -1
  12. data/lib/super_diff/rspec/differs/{partial_hash.rb → collection_including.rb} +4 -3
  13. data/lib/super_diff/rspec/differs/{partial_array.rb → hash_including.rb} +4 -3
  14. data/lib/super_diff/rspec/differs/{partial_object.rb → object_having_attributes.rb} +3 -3
  15. data/lib/super_diff/rspec/matcher_text_builders.rb +4 -0
  16. data/lib/super_diff/rspec/matcher_text_builders/be_predicate.rb +26 -7
  17. data/lib/super_diff/rspec/matcher_text_builders/have_predicate.rb +61 -0
  18. data/lib/super_diff/rspec/matcher_text_builders/raise_error.rb +13 -1
  19. data/lib/super_diff/rspec/monkey_patches.rb +218 -111
  20. data/lib/super_diff/rspec/object_inspection/inspectors.rb +6 -6
  21. data/lib/super_diff/rspec/object_inspection/inspectors/{partial_array.rb → collection_including.rb} +2 -2
  22. data/lib/super_diff/rspec/object_inspection/inspectors/{partial_hash.rb → hash_including.rb} +1 -1
  23. data/lib/super_diff/rspec/object_inspection/inspectors/object_having_attributes.rb +22 -0
  24. data/lib/super_diff/rspec/object_inspection/map_extension.rb +7 -7
  25. data/lib/super_diff/rspec/operational_sequencers.rb +6 -6
  26. data/lib/super_diff/rspec/operational_sequencers/collection_containing_exactly.rb +1 -1
  27. data/lib/super_diff/rspec/operational_sequencers/{partial_array.rb → collection_including.rb} +3 -2
  28. data/lib/super_diff/rspec/operational_sequencers/{partial_hash.rb → hash_including.rb} +3 -2
  29. data/lib/super_diff/rspec/operational_sequencers/{partial_object.rb → object_having_attributes.rb} +2 -4
  30. data/lib/super_diff/version.rb +1 -1
  31. data/spec/integration/rails/active_record_spec.rb +1 -1
  32. data/spec/integration/rails/hash_with_indifferent_access_spec.rb +1 -1
  33. data/spec/integration/rspec/be_predicate_matcher_spec.rb +111 -59
  34. data/spec/integration/rspec/eq_matcher_spec.rb +1 -1
  35. data/spec/integration/rspec/have_predicate_matcher_spec.rb +484 -0
  36. data/spec/integration/rspec/match_array_matcher_spec.rb +372 -0
  37. data/spec/integration/rspec/match_matcher_spec.rb +8 -8
  38. data/spec/integration/rspec/raise_error_matcher_spec.rb +605 -226
  39. data/spec/integration/rspec/third_party_matcher_spec.rb +241 -0
  40. data/spec/integration/rspec/unhandled_errors_spec.rb +56 -81
  41. data/spec/spec_helper.rb +18 -7
  42. data/spec/support/integration/helpers.rb +10 -2
  43. data/spec/support/integration/matchers.rb +143 -0
  44. data/spec/support/models/active_record/query.rb +15 -0
  45. data/spec/support/object_id.rb +26 -0
  46. data/spec/support/ruby_versions.rb +4 -0
  47. data/spec/support/shared_examples/active_record.rb +71 -0
  48. data/spec/unit/equality_matcher_spec.rb +8 -8
  49. data/spec/unit/object_inspection_spec.rb +17 -17
  50. data/spec/unit/rspec/matchers/have_predicate_spec.rb +21 -0
  51. data/spec/unit/rspec/matchers/match_array_spec.rb +11 -0
  52. data/super_diff.gemspec +0 -1
  53. metadata +30 -34
  54. data/lib/super_diff/rspec/object_inspection/inspectors/partial_object.rb +0 -21
  55. data/spec/examples.txt +0 -350
@@ -0,0 +1,143 @@
1
+ module SuperDiff
2
+ module IntegrationTests
3
+ def fail_with_indented_multiline_failure_message
4
+ FailWithIndentedMultilineFailureMessageMatcher.new
5
+ end
6
+
7
+ def fail_with_paragraphed_failure_message
8
+ FailWithParagraphedFailureMessageMatcher.new
9
+ end
10
+
11
+ def fail_with_non_indented_multiline_failure_message
12
+ FailWithNonIndentedMultilineFailureMessageMatcher.new
13
+ end
14
+
15
+ def fail_with_singleline_failure_message
16
+ FailWithSinglelineFailureMessageMatcher.new
17
+ end
18
+
19
+ def pass_with_indented_multiline_failure_message
20
+ PassWithIndentedMultilineFailureMessageMatcher.new
21
+ end
22
+
23
+ def pass_with_paragraphed_failure_message
24
+ PassWithParagraphedFailureMessageMatcher.new
25
+ end
26
+
27
+ def pass_with_non_indented_multiline_failure_message
28
+ PassWithNonIndentedMultilineFailureMessageMatcher.new
29
+ end
30
+
31
+ def pass_with_singleline_failure_message
32
+ PassWithSinglelineFailureMessageMatcher.new
33
+ end
34
+
35
+ class FailWithIndentedMultilineFailureMessageMatcher
36
+ def matches?(_)
37
+ false
38
+ end
39
+
40
+ def failure_message
41
+ <<-MESSAGE
42
+ This is a message that spans multiple lines.
43
+ Here is the next line.
44
+ This part is indented, for whatever reason. It just kinda keeps
45
+ going until we finish saying whatever it is we want to say.
46
+ MESSAGE
47
+ end
48
+ end
49
+
50
+ class FailWithParagraphedFailureMessageMatcher
51
+ def matches?(_)
52
+ false
53
+ end
54
+
55
+ def failure_message
56
+ <<-MESSAGE
57
+ This is a message that spans multiple paragraphs.
58
+
59
+ Here is the next paragraph.
60
+ MESSAGE
61
+ end
62
+ end
63
+
64
+ class FailWithNonIndentedMultilineFailureMessageMatcher
65
+ def matches?(_)
66
+ false
67
+ end
68
+
69
+ def failure_message
70
+ <<-MESSAGE
71
+ This is a message that spans multiple lines.
72
+ Here is the next line.
73
+ MESSAGE
74
+ end
75
+ end
76
+
77
+ class FailWithSinglelineFailureMessageMatcher
78
+ def matches?(_)
79
+ false
80
+ end
81
+
82
+ def failure_message
83
+ <<-MESSAGE
84
+ This is a message that spans only one line.
85
+ MESSAGE
86
+ end
87
+ end
88
+
89
+ class PassWithIndentedMultilineFailureMessageMatcher
90
+ def does_not_match?(_)
91
+ false
92
+ end
93
+
94
+ def failure_message_when_negated
95
+ <<-MESSAGE
96
+ This is a message that spans multiple lines.
97
+ Here is the next line.
98
+ This part is indented, for whatever reason. It just kinda keeps
99
+ going until we finish saying whatever it is we want to say.
100
+ MESSAGE
101
+ end
102
+ end
103
+
104
+ class PassWithParagraphedFailureMessageMatcher
105
+ def does_not_match?(_)
106
+ false
107
+ end
108
+
109
+ def failure_message_when_negated
110
+ <<-MESSAGE
111
+ This is a message that spans multiple paragraphs.
112
+
113
+ Here is the next paragraph.
114
+ MESSAGE
115
+ end
116
+ end
117
+
118
+ class PassWithNonIndentedMultilineFailureMessageMatcher
119
+ def does_not_match?(_)
120
+ false
121
+ end
122
+
123
+ def failure_message_when_negated
124
+ <<-MESSAGE
125
+ This is a message that spans multiple lines.
126
+ Here is the next line.
127
+ MESSAGE
128
+ end
129
+ end
130
+
131
+ class PassWithSinglelineFailureMessageMatcher
132
+ def does_not_match?(_)
133
+ false
134
+ end
135
+
136
+ def failure_message_when_negated
137
+ <<-MESSAGE
138
+ This is a message that spans only one line.
139
+ MESSAGE
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,15 @@
1
+ module SuperDiff
2
+ module Test
3
+ module Models
4
+ module ActiveRecord
5
+ class Query
6
+ attr_reader :results
7
+
8
+ def initialize(results:)
9
+ @results = results
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'ruby_versions'
2
+
3
+ if !SuperDiff::Test.jruby? && SuperDiff::Test.version_match?('>= 2.7.0')
4
+ require 'objspace'
5
+ end
6
+
7
+ module SuperDiff
8
+ module Test
9
+ if jruby?
10
+ def self.object_id_hex(object)
11
+ # Source: <https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/RubyBasicObject.java>
12
+ "0x%x" % object.hash
13
+ end
14
+ elsif version_match?('>= 2.7.0')
15
+ def self.object_id_hex(object)
16
+ # Sources: <https://bugs.ruby-lang.org/issues/15408> and <https://bugs.ruby-lang.org/issues/15626#Object-ID>
17
+ address = JSON.parse(ObjectSpace.dump(object))['address']
18
+ "0x%016x" % Integer(address, 16)
19
+ end
20
+ else
21
+ def self.object_id_hex(object)
22
+ "0x%016x" % (object.object_id * 2)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -3,5 +3,9 @@ module SuperDiff
3
3
  def self.jruby?
4
4
  defined?(JRUBY_VERSION)
5
5
  end
6
+
7
+ def self.version_match?(version_string)
8
+ Gem::Requirement.new(version_string).satisfied_by?(Gem::Version.new(RUBY_VERSION))
9
+ end
6
10
  end
7
11
  end
@@ -335,4 +335,75 @@ shared_examples_for "integration with ActiveRecord" do
335
335
  end
336
336
  end
337
337
  end
338
+
339
+ describe "and RSpec's #match matcher" do
340
+ context "when the expected value includes an ActiveRecord object" do
341
+ it "produces the correct output" do
342
+ as_both_colored_and_uncolored do |color_enabled|
343
+ snippet = <<~TEST.strip
344
+ SuperDiff::Test::Models::ActiveRecord::Person.create!(
345
+ name: "Murphy",
346
+ age: 20
347
+ )
348
+
349
+ expected = [
350
+ an_object_having_attributes(
351
+ results: [
352
+ an_object_having_attributes(name: "John", age: 19)
353
+ ]
354
+ )
355
+ ]
356
+
357
+ actual = [
358
+ SuperDiff::Test::Models::ActiveRecord::Query.new(
359
+ results: SuperDiff::Test::Models::ActiveRecord::Person.all
360
+ )
361
+ ]
362
+
363
+ expect(actual).to match(expected)
364
+ TEST
365
+
366
+ program = make_program(snippet, color_enabled: color_enabled)
367
+
368
+ expected_output = build_expected_output(
369
+ color_enabled: color_enabled,
370
+ snippet: %|expect(actual).to match(expected)|,
371
+ newline_before_expectation: true,
372
+ expectation: proc {
373
+ line do
374
+ plain "Expected "
375
+ beta %|[#<SuperDiff::Test::Models::ActiveRecord::Query @results=#<ActiveRecord::Relation [#<SuperDiff::Test::Models::ActiveRecord::Person id: 1, name: "Murphy", age: 20>]>>]|
376
+ end
377
+
378
+ line do
379
+ plain "to match "
380
+ alpha %|[#<an object having attributes (results: [#<an object having attributes (name: "John", age: 19)>])>]|
381
+ end
382
+ },
383
+ diff: proc {
384
+ plain_line %| [|
385
+ plain_line %| #<SuperDiff::Test::Models::ActiveRecord::Query {|
386
+ plain_line %| @results=#<ActiveRecord::Relation [|
387
+ plain_line %| #<SuperDiff::Test::Models::ActiveRecord::Person {|
388
+ plain_line %| id: 1,|
389
+ # alpha_line %|- age: 19,| # TODO
390
+ alpha_line %|- age: 19|
391
+ beta_line %|+ age: 20,|
392
+ alpha_line %|- name: "John"|
393
+ beta_line %|+ name: "Murphy"|
394
+ plain_line %| }>|
395
+ plain_line %| ]>|
396
+ plain_line %| }>|
397
+ plain_line %| ]|
398
+ },
399
+ )
400
+
401
+ expect(program).
402
+ to produce_output_when_run(expected_output).
403
+ in_color(color_enabled).
404
+ removing_object_ids
405
+ end
406
+ end
407
+ end
408
+ end
338
409
  end
@@ -1375,13 +1375,13 @@ RSpec.describe SuperDiff::EqualityMatcher do
1375
1375
  if SuperDiff::Test.jruby?
1376
1376
  # Source: <https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/RubyBasicObject.java>
1377
1377
  colored do
1378
- alpha_line %(Expected: #<SuperDiff::Test::Player:#{"0x%x" % expected.hash} @inventory=["flatline", "purple body shield"], @character="mirage", @handle="martymcfly", @ultimate=0.8, @shields=0.6, @health=0.3>)
1379
- beta_line %( Actual: #<SuperDiff::Test::Player:#{"0x%x" % actual.hash} @inventory=["wingman", "mastiff"], @character="lifeline", @handle="docbrown", @ultimate=0.8, @shields=0.6, @health=0.3>)
1378
+ alpha_line %(Expected: #<SuperDiff::Test::Player:#{SuperDiff::Test.object_id_hex(expected)} @inventory=["flatline", "purple body shield"], @character="mirage", @handle="martymcfly", @ultimate=0.8, @shields=0.6, @health=0.3>)
1379
+ beta_line %( Actual: #<SuperDiff::Test::Player:#{SuperDiff::Test.object_id_hex(actual)} @inventory=["wingman", "mastiff"], @character="lifeline", @handle="docbrown", @ultimate=0.8, @shields=0.6, @health=0.3>)
1380
1380
  end
1381
1381
  else
1382
1382
  colored do
1383
- alpha_line %(Expected: #<SuperDiff::Test::Player:#{"0x%016x" % (expected.object_id * 2)} @handle="martymcfly", @character="mirage", @inventory=["flatline", "purple body shield"], @shields=0.6, @health=0.3, @ultimate=0.8>)
1384
- beta_line %( Actual: #<SuperDiff::Test::Player:#{"0x%016x" % (actual.object_id * 2)} @handle="docbrown", @character="lifeline", @inventory=["wingman", "mastiff"], @shields=0.6, @health=0.3, @ultimate=0.8>)
1383
+ alpha_line %(Expected: #<SuperDiff::Test::Player:#{SuperDiff::Test.object_id_hex(expected)} @handle="martymcfly", @character="mirage", @inventory=["flatline", "purple body shield"], @shields=0.6, @health=0.3, @ultimate=0.8>)
1384
+ beta_line %( Actual: #<SuperDiff::Test::Player:#{SuperDiff::Test.object_id_hex(actual)} @handle="docbrown", @character="lifeline", @inventory=["wingman", "mastiff"], @shields=0.6, @health=0.3, @ultimate=0.8>)
1385
1385
  end
1386
1386
  end
1387
1387
  }
@@ -1466,13 +1466,13 @@ RSpec.describe SuperDiff::EqualityMatcher do
1466
1466
  if SuperDiff::Test.jruby?
1467
1467
  # Source: <https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/RubyBasicObject.java>
1468
1468
  colored do
1469
- alpha_line %(Expected: #<SuperDiff::Test::Item:#{"0x%x" % expected.hash} @name="camera", @quantity=3>)
1470
- beta_line %( Actual: #<SuperDiff::Test::Player:#{"0x%x" % actual.hash} @inventory=["sword"], @character="Jon", @handle="mcmire", @ultimate=true, @shields=11.4, @health=4>)
1469
+ alpha_line %(Expected: #<SuperDiff::Test::Item:#{SuperDiff::Test.object_id_hex(expected)} @name="camera", @quantity=3>)
1470
+ beta_line %( Actual: #<SuperDiff::Test::Player:#{SuperDiff::Test.object_id_hex(actual)} @inventory=["sword"], @character="Jon", @handle="mcmire", @ultimate=true, @shields=11.4, @health=4>)
1471
1471
  end
1472
1472
  else
1473
1473
  colored do
1474
- alpha_line %(Expected: #<SuperDiff::Test::Item:#{"0x%016x" % (expected.object_id * 2)} @name="camera", @quantity=3>)
1475
- beta_line %( Actual: #<SuperDiff::Test::Player:#{"0x%016x" % (actual.object_id * 2)} @handle="mcmire", @character="Jon", @inventory=["sword"], @shields=11.4, @health=4, @ultimate=true>)
1474
+ alpha_line %(Expected: #<SuperDiff::Test::Item:#{SuperDiff::Test.object_id_hex(expected)} @name="camera", @quantity=3>)
1475
+ beta_line %( Actual: #<SuperDiff::Test::Player:#{SuperDiff::Test.object_id_hex(actual)} @handle="mcmire", @character="Jon", @inventory=["sword"], @shields=11.4, @health=4, @ultimate=true>)
1476
1476
  end
1477
1477
  end
1478
1478
  }
@@ -608,9 +608,9 @@ RSpec.describe SuperDiff::ObjectInspection do
608
608
  end
609
609
  end
610
610
 
611
- context "given a partial hash" do
611
+ context "given a hash-including-<something>" do
612
612
  context "given as_single_line: true" do
613
- it "returns a representation of the partial hash on a single line" do
613
+ it "returns a representation of the object on a single line" do
614
614
  inspection = described_class.inspect(
615
615
  a_hash_including(foo: "bar", baz: "qux"),
616
616
  as_single_line: true,
@@ -623,7 +623,7 @@ RSpec.describe SuperDiff::ObjectInspection do
623
623
  end
624
624
 
625
625
  context "given as_single_line: false" do
626
- it "returns a representation of the partial hash across multiple lines" do
626
+ it "returns a representation of the object across multiple lines" do
627
627
  inspection = described_class.inspect(
628
628
  a_hash_including(foo: "bar", baz: "qux"),
629
629
  as_single_line: false,
@@ -639,9 +639,9 @@ RSpec.describe SuperDiff::ObjectInspection do
639
639
  end
640
640
  end
641
641
 
642
- context "given a partial array" do
642
+ context "given a collection-including-<something>" do
643
643
  context "given as_single_line: true" do
644
- it "returns a representation of the partial hash on a single line" do
644
+ it "returns a representation of the object on a single line" do
645
645
  inspection = described_class.inspect(
646
646
  a_collection_including(1, 2, 3),
647
647
  as_single_line: true,
@@ -654,7 +654,7 @@ RSpec.describe SuperDiff::ObjectInspection do
654
654
  end
655
655
 
656
656
  context "given as_single_line: false" do
657
- it "returns a representation of the partial hash across multiple lines" do
657
+ it "returns a representation of the object across multiple lines" do
658
658
  inspection = described_class.inspect(
659
659
  a_collection_including(1, 2, 3),
660
660
  as_single_line: false,
@@ -671,9 +671,9 @@ RSpec.describe SuperDiff::ObjectInspection do
671
671
  end
672
672
  end
673
673
 
674
- context "given a partial object" do
674
+ context "given a fuzzy object" do
675
675
  context "given as_single_line: true" do
676
- it "returns a representation of the partial object on a single line" do
676
+ it "returns a representation of the object on a single line" do
677
677
  inspection = described_class.inspect(
678
678
  an_object_having_attributes(foo: "bar", baz: "qux"),
679
679
  as_single_line: true,
@@ -686,7 +686,7 @@ RSpec.describe SuperDiff::ObjectInspection do
686
686
  end
687
687
 
688
688
  context "given as_single_line: false" do
689
- it "returns a representation of the partial object across multiple lines" do
689
+ it "returns a representation of the object across multiple lines" do
690
690
  inspection = described_class.inspect(
691
691
  an_object_having_attributes(foo: "bar", baz: "qux"),
692
692
  as_single_line: false,
@@ -702,9 +702,9 @@ RSpec.describe SuperDiff::ObjectInspection do
702
702
  end
703
703
  end
704
704
 
705
- context "given a fuzzy object matching a collection containing exactly <something>" do
705
+ context "given a collection-containing-exactly-<something>" do
706
706
  context "given as_single_line: true" do
707
- it "returns a representation of the partial object on a single line" do
707
+ it "returns a representation of the object on a single line" do
708
708
  inspection = described_class.inspect(
709
709
  a_collection_containing_exactly("foo", "bar", "baz"),
710
710
  as_single_line: true,
@@ -717,7 +717,7 @@ RSpec.describe SuperDiff::ObjectInspection do
717
717
  end
718
718
 
719
719
  context "given as_single_line: false" do
720
- it "returns a representation of the partial object across multiple lines" do
720
+ it "returns a representation of the object across multiple lines" do
721
721
  inspection = described_class.inspect(
722
722
  a_collection_containing_exactly("foo", "bar", "baz"),
723
723
  as_single_line: false,
@@ -737,7 +737,7 @@ RSpec.describe SuperDiff::ObjectInspection do
737
737
  context "given a Double" do
738
738
  context "that is anonymous" do
739
739
  context "given as_single_line: true" do
740
- it "returns a representation of the partial object on a single line" do
740
+ it "returns a representation of the object on a single line" do
741
741
  inspection = described_class.inspect(
742
742
  double(foo: "bar", baz: "qux"),
743
743
  as_single_line: true,
@@ -748,7 +748,7 @@ RSpec.describe SuperDiff::ObjectInspection do
748
748
  end
749
749
 
750
750
  context "given as_single_line: false" do
751
- it "returns a representation of the partial object across multiple lines" do
751
+ it "returns a representation of the object across multiple lines" do
752
752
  inspection = described_class.inspect(
753
753
  double(foo: "bar", baz: "qux"),
754
754
  as_single_line: false,
@@ -760,7 +760,7 @@ RSpec.describe SuperDiff::ObjectInspection do
760
760
  end
761
761
  end
762
762
 
763
- context "given an ActiveRecord object" do
763
+ context "given an ActiveRecord object", active_record: true do
764
764
  context "given as_single_line: true" do
765
765
  it "returns a representation of the object on a single line" do
766
766
  inspection = described_class.inspect(
@@ -798,7 +798,7 @@ RSpec.describe SuperDiff::ObjectInspection do
798
798
  end
799
799
  end
800
800
 
801
- context "given an ActiveRecord::Relation object" do
801
+ context "given an ActiveRecord::Relation object", active_record: true do
802
802
  context "given as_single_line: true" do
803
803
  it "returns a representation of the Relation on a single line" do
804
804
  SuperDiff::Test::Models::ActiveRecord::Person.create!(
@@ -855,7 +855,7 @@ RSpec.describe SuperDiff::ObjectInspection do
855
855
  end
856
856
  end
857
857
 
858
- context "given a HashWithIndifferentAccess" do
858
+ context "given a HashWithIndifferentAccess", active_record: true do
859
859
  context "given as_single_line: true" do
860
860
  it "returns a representation of the object on a single line" do
861
861
  inspection = described_class.inspect(
@@ -0,0 +1,21 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "RSpec's `have_<predicate>` matcher" do
4
+ describe "#description" do
5
+ context "given nothing" do
6
+ it "returns the correct output" do
7
+ expect(have_experience.description).to eq(
8
+ "return true for `has_experience?`",
9
+ )
10
+ end
11
+ end
12
+
13
+ context "given an argument" do
14
+ it "returns the correct output" do
15
+ expect(have_ingredients(:sugar).description).to eq(
16
+ "return true for `has_ingredients?(:sugar)`",
17
+ )
18
+ end
19
+ end
20
+ end
21
+ end