super_diff 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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