rspec-resembles_json_matchers 0.7.2 → 0.9.1

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +155 -0
  4. data/.travis.yml +5 -6
  5. data/Appraisals +7 -5
  6. data/Changelog.md +16 -0
  7. data/Gemfile +3 -1
  8. data/Guardfile +6 -5
  9. data/LICENSE.txt +13 -0
  10. data/README.md +5 -0
  11. data/Rakefile +2 -0
  12. data/examples/example_spec.rb +67 -63
  13. data/gemfiles/.bundle/config +5 -0
  14. data/gemfiles/rails_5.gemfile +2 -2
  15. data/gemfiles/rails_5.gemfile.lock +100 -67
  16. data/gemfiles/{rails_42.gemfile → rails_6.gemfile} +3 -3
  17. data/gemfiles/rails_6.gemfile.lock +236 -0
  18. data/lib/rspec/resembles_json_matchers.rb +16 -15
  19. data/lib/rspec/resembles_json_matchers/attribute_differ.rb +75 -55
  20. data/lib/rspec/resembles_json_matchers/attribute_matcher.rb +38 -18
  21. data/lib/rspec/resembles_json_matchers/helpers.rb +4 -3
  22. data/lib/rspec/resembles_json_matchers/json_matcher.rb +22 -33
  23. data/lib/rspec/resembles_json_matchers/matcherizer.rb +2 -3
  24. data/lib/rspec/resembles_json_matchers/resembles_any_of_matcher.rb +16 -15
  25. data/lib/rspec/resembles_json_matchers/resembles_array_matcher.rb +2 -4
  26. data/lib/rspec/resembles_json_matchers/resembles_boolean_matcher.rb +4 -1
  27. data/lib/rspec/resembles_json_matchers/resembles_class_matcher.rb +2 -1
  28. data/lib/rspec/resembles_json_matchers/resembles_date_matcher.rb +2 -0
  29. data/lib/rspec/resembles_json_matchers/resembles_hash_matcher.rb +11 -11
  30. data/lib/rspec/resembles_json_matchers/resembles_matcher.rb +8 -16
  31. data/lib/rspec/resembles_json_matchers/resembles_nil_matcher.rb +1 -0
  32. data/lib/rspec/resembles_json_matchers/resembles_numeric_matcher.rb +2 -1
  33. data/lib/rspec/resembles_json_matchers/resembles_route_matcher.rb +6 -3
  34. data/lib/rspec/resembles_json_matchers/resembles_string_matcher.rb +1 -0
  35. data/lib/rspec/resembles_json_matchers/string_indent.rb +4 -2
  36. data/lib/rspec/resembles_json_matchers/version.rb +3 -1
  37. data/rspec-resembles_json_matchers.gemspec +14 -9
  38. metadata +68 -24
  39. data/gemfiles/rails_42.gemfile.lock +0 -183
@@ -1,20 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rspec/resembles_json_matchers/version"
2
4
  require "rspec/resembles_json_matchers/string_indent"
3
5
 
4
6
  module RSpec
5
7
  module ResemblesJsonMatchers
6
- autoload :AttributeMatcher, "rspec/resembles_json_matchers/attribute_matcher"
7
- autoload :JsonMatcher, "rspec/resembles_json_matchers/json_matcher"
8
- autoload :Helpers, "rspec/resembles_json_matchers/helpers"
9
-
10
- autoload :ResemblesAnyOfMatcher, "rspec/resembles_json_matchers/resembles_any_of_matcher"
11
- autoload :ResemblesRouteMatcher, "rspec/resembles_json_matchers/resembles_route_matcher"
12
- autoload :ResemblesDateMatcher, "rspec/resembles_json_matchers/resembles_date_matcher"
13
- autoload :ResemblesNumericMatcher, "rspec/resembles_json_matchers/resembles_numeric_matcher"
14
- autoload :ResemblesStringMatcher, "rspec/resembles_json_matchers/resembles_string_matcher"
15
- autoload :ResemblesBooleanMatcher, "rspec/resembles_json_matchers/resembles_boolean_matcher"
16
- autoload :ResemblesNilMatcher, "rspec/resembles_json_matchers/resembles_nil_matcher"
17
- autoload :ResemblesClassMatcher, "rspec/resembles_json_matchers/resembles_class_matcher"
8
+ require "rspec/resembles_json_matchers/helpers"
9
+ require "rspec/resembles_json_matchers/attribute_matcher"
10
+ require "rspec/resembles_json_matchers/json_matcher"
11
+
12
+ require "rspec/resembles_json_matchers/resembles_any_of_matcher"
13
+ require "rspec/resembles_json_matchers/resembles_route_matcher"
14
+ require "rspec/resembles_json_matchers/resembles_date_matcher"
15
+ require "rspec/resembles_json_matchers/resembles_numeric_matcher"
16
+ require "rspec/resembles_json_matchers/resembles_string_matcher"
17
+ require "rspec/resembles_json_matchers/resembles_boolean_matcher"
18
+ require "rspec/resembles_json_matchers/resembles_nil_matcher"
19
+ require "rspec/resembles_json_matchers/resembles_class_matcher"
18
20
 
19
21
  def iso8601_timestamp
20
22
  match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/)
@@ -32,7 +34,7 @@ module RSpec
32
34
 
33
35
  def self.resembles_matcher_candidates
34
36
  # Order matters
35
- @candidates ||= [
37
+ @resembles_matcher_candidates ||= [
36
38
  JsonMatcher,
37
39
  ResemblesAnyOfMatcher,
38
40
  ResemblesRouteMatcher,
@@ -45,9 +47,8 @@ module RSpec
45
47
  ].freeze
46
48
  end
47
49
 
48
- def self.resembles_matcher_for(expected, **a)
50
+ def self.resembles_matcher_for(expected, **_a)
49
51
  resembles_matcher_candidates.detect { |candidate| candidate.can_match?(expected) } || RSpec::Matchers::BuiltIn::Eq
50
52
  end
51
-
52
53
  end
53
54
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/inflector"
2
4
 
3
5
  module RSpec::ResemblesJsonMatchers
@@ -47,59 +49,70 @@ module RSpec::ResemblesJsonMatchers
47
49
  @buffer.print NORMAL_COLOR
48
50
  @buffer.print prefix + " " + "#{matcher.attribute_name.to_json}: "
49
51
  render(matcher.value_matcher, prefix: prefix + " ")
50
- @buffer.print(",") unless last
51
- @buffer.puts
52
+ elsif nested_matcher?(matcher.value_matcher)
53
+ @buffer.print NORMAL_COLOR
54
+ @buffer.print prefix + " " + "#{matcher.attribute_name.to_json}: "
55
+ render(matcher.value_matcher, prefix: prefix + " ")
52
56
  else
53
- if nested_matcher?(matcher.value_matcher)
54
- if matcher.missing_attribute?
55
- prefix = prefix + "- "
56
- @buffer.print REMOVED_COLOR
57
- @buffer.print prefix + "#{matcher.attribute_name.to_json}: "
58
- render(matcher.value_matcher, prefix: prefix)
59
- @buffer.print(",") unless last
60
- @buffer.puts
61
- else
62
- @buffer.print NORMAL_COLOR
63
- @buffer.print prefix + " " + "#{matcher.attribute_name.to_json}: "
64
- render(matcher.value_matcher, prefix: prefix + " ")
65
- @buffer.print(",") unless last
66
- @buffer.puts
67
- end
57
+ @buffer.print REMOVED_COLOR
58
+ @buffer.print prefix
59
+ if prefix.include? "-"
60
+ @buffer.print " "
68
61
  else
69
- @buffer.print REMOVED_COLOR
70
- @buffer.print prefix
71
- if prefix.include? "-"
72
- @buffer.print " "
73
- else
74
- @buffer.print "- "
75
- end
76
- @buffer.print "#{matcher.attribute_name.to_json}: "
77
- render(matcher.value_matcher, prefix: prefix + " ")
78
- @buffer.print NORMAL_COLOR
79
- @buffer.print(",") unless last
80
- @buffer.puts
81
- unless matcher.missing_attribute?
82
- @buffer.print ADDED_COLOR
83
- @buffer.print prefix + "+ #{matcher.attribute_name.to_json}: "
84
- render(matcher.actual_value, prefix: prefix + " ")
85
- @buffer.print NORMAL_COLOR
86
- @buffer.print(",") unless last
87
- @buffer.puts
88
- end
62
+ @buffer.print "- "
89
63
  end
64
+ @buffer.print "#{matcher.attribute_name.to_json}: "
65
+ render(matcher.value_matcher, prefix: prefix + " ")
66
+ @buffer.print NORMAL_COLOR
67
+ @buffer.print(",") unless last
68
+ @buffer.puts
69
+ @buffer.print ADDED_COLOR
70
+ @buffer.print prefix + "+ #{matcher.attribute_name.to_json}: "
71
+ render(matcher.actual_value, prefix: prefix + " ")
72
+ @buffer.print NORMAL_COLOR
90
73
  end
74
+ @buffer.print(",") unless last
75
+ @buffer.puts
76
+ end
77
+
78
+ def render_MissingAttributeMatcher(matcher, prefix: "", last: false)
79
+ prefix += (prefix.include?("-") ? " " : "- ")
80
+ @buffer.print REMOVED_COLOR
81
+ @buffer.print prefix + "#{matcher.attribute_name.to_json}: "
82
+ render(matcher.value_matcher, prefix: prefix)
83
+ @buffer.print(",") unless last
84
+ @buffer.puts
85
+ end
86
+
87
+ def render_ExtraAttributeMatcher(matcher, prefix: "", last: false)
88
+ prefix += "+ "
89
+ @buffer.print ADDED_COLOR
90
+ @buffer.print prefix + matcher.attribute_name.to_json + ": "
91
+ render(matcher.actual_value, prefix: prefix)
92
+ @buffer.print "," unless last
93
+ @buffer.puts
91
94
  end
92
95
 
93
- def render_ResemblesAnyOfMatcher(matcher, prefix: "", **opts)
96
+ def render_ResemblesAnyOfMatcher(matcher, prefix: "", **_opts)
94
97
  @buffer.puts "["
95
- if matcher.actual.nil? || matcher.actual.empty?
96
- example_matcher = matcher.expected.first
97
- render example_matcher, prefix: prefix + " ", starts_on_newline: true
98
- @buffer.puts
98
+ if matcher.matched?
99
+ matcher.original_expected.each do |item|
100
+ @buffer.print JSON.pretty_generate(item).indent(1, NORMAL_COLOR + prefix + "- ")
101
+ last = (matcher.original_expected.last == item)
102
+ @buffer.print(",") unless last
103
+ @buffer.puts
104
+ end
105
+ elsif matcher.actual.nil? || matcher.actual.empty?
106
+ matcher.expected.each do |expected_matcher|
107
+ render expected_matcher, prefix: prefix + " ", starts_on_newline: true
108
+ last = (matcher.expected.last == expected_matcher)
109
+ @buffer.print(",") unless last
110
+ @buffer.puts
111
+ end
99
112
  else
100
113
  matcher.attempted_matchers.each do |attempted_matcher|
101
- last = (matcher.attempted_matchers.last == attempted_matcher)
102
114
  render attempted_matcher, prefix: prefix + " ", starts_on_newline: true
115
+ last = (matcher.attempted_matchers.last == attempted_matcher)
103
116
  @buffer.print(",") unless last
104
117
  @buffer.puts
105
118
  end
@@ -107,36 +120,45 @@ module RSpec::ResemblesJsonMatchers
107
120
  @buffer.print prefix + "]"
108
121
  end
109
122
 
110
- def render_ResemblesStringMatcher(matcher, **opts)
123
+ def render_ResemblesBooleanMatcher(matcher, **_opts)
111
124
  @buffer.print matcher.expected.to_json
112
125
  end
113
126
 
114
- def render_ResemblesDateMatcher(matcher, **opts)
127
+ def render_ResemblesStringMatcher(matcher, **_opts)
115
128
  @buffer.print matcher.expected.to_json
116
129
  end
117
130
 
118
- def render_ResemblesNumericMatcher(matcher, **opts)
131
+ def render_ResemblesDateMatcher(matcher, **_opts)
119
132
  @buffer.print matcher.expected.to_json
120
133
  end
121
134
 
122
- def render_ResemblesClassMatcher(matcher, **opts)
135
+ def render_ResemblesNumericMatcher(matcher, **_opts)
136
+ @buffer.print matcher.expected.to_json
137
+ end
138
+
139
+ def render_ResemblesClassMatcher(matcher, **_opts)
123
140
  @buffer.print matcher.expected.inspect
124
141
  end
125
142
 
126
- def render_ResemblesNilMatcher(matcher, **opts)
143
+ def render_ResemblesNilMatcher(_matcher, **_opts)
127
144
  @buffer.print "null"
128
145
  end
129
146
 
147
+ def render_ResemblesRouteMatcher(matcher, **_opts)
148
+ @buffer.print matcher.expected.inspect
149
+ end
150
+
130
151
  def method_missing(method_name, *args, &block)
131
152
  if method_name.to_s.start_with?("render_")
132
153
  raise NoMethodError, method_name if method_name.to_s.end_with?("Matcher")
154
+
133
155
  @buffer.print RSpec::Support::ObjectFormatter.format(args.first)
134
156
  else
135
157
  super
136
158
  end
137
159
  end
138
160
 
139
- def respond_to_missing?(method_name, include_private = false)
161
+ def respond_to_missing?(method_name, _include_private = false)
140
162
  method_name.to_s.start_with?("render_")
141
163
  end
142
164
 
@@ -144,11 +166,9 @@ module RSpec::ResemblesJsonMatchers
144
166
  matcher.is_a?(JsonMatcher) || matcher.is_a?(ResemblesAnyOfMatcher)
145
167
  end
146
168
 
147
- NORMAL_COLOR = "\e[0m".freeze
148
- REMOVED_COLOR = "\e[31m".freeze # Red
149
- ADDED_COLOR = "\e[32m".freeze # Green
150
- NEUTRAL_COLOR = "\e[34m".freeze # Blue
151
-
169
+ NORMAL_COLOR = "\e[0m"
170
+ REMOVED_COLOR = "\e[31m" # Red
171
+ ADDED_COLOR = "\e[32m" # Green
172
+ NEUTRAL_COLOR = "\e[34m" # Blue
152
173
  end
153
174
  end
154
-
@@ -1,13 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/hash/indifferent_access"
4
+ require "active_support/core_ext/object/try"
2
5
 
3
6
  module RSpec::ResemblesJsonMatchers
4
-
5
7
  class AttributeMatcher
6
8
  include RSpec::ResemblesJsonMatchers::Helpers
9
+ Undefined = Object.new # TODO use Dry::Core::Constants::Undefined
7
10
 
8
11
  attr_reader :attribute_name, :expected, :document
9
12
 
10
- def initialize(attribute_name, expected = NullMatcher)
13
+ def initialize(attribute_name, expected = NullMatcher.new)
11
14
  @attribute_name, @expected = attribute_name, expected
12
15
  end
13
16
 
@@ -16,17 +19,13 @@ module RSpec::ResemblesJsonMatchers
16
19
  end
17
20
 
18
21
  def matches?(document)
19
- @document = document.with_indifferent_access
22
+ @document = document.try(:with_indifferent_access)
20
23
 
21
- @matched = !missing_attribute? && expected.matches?(actual_value)
24
+ @matched = document.key?(attribute_name) && expected.matches?(actual_value)
22
25
  end
23
26
 
24
27
  def failure_message
25
- if missing_attribute?
26
- %{Expected document to have attribute #{attribute_name.inspect}}
27
- else
28
- %{Expected attribute #{attribute_name.inspect} to #{value_matcher.description}, but it was #{actual_value.inspect}}
29
- end
28
+ %{Expected attribute #{attribute_name.inspect} to #{value_matcher.description}, but it was #{actual_value.inspect}}
30
29
  end
31
30
 
32
31
  def matched?
@@ -45,14 +44,6 @@ module RSpec::ResemblesJsonMatchers
45
44
  document.fetch(attribute_name, nil)
46
45
  end
47
46
 
48
- def missing_attribute?
49
- !has_attribute?
50
- end
51
-
52
- def has_attribute?
53
- document && document.key?(attribute_name)
54
- end
55
-
56
47
  NullMatcher = Class.new do
57
48
  def matches?(*_args)
58
49
  true
@@ -70,7 +61,36 @@ module RSpec::ResemblesJsonMatchers
70
61
  def ===(_other)
71
62
  true
72
63
  end
73
- end.new
64
+ end
65
+ end
74
66
 
67
+ class MissingAttributeMatcher < AttributeMatcher
68
+ def matches?(document)
69
+ @document = document.try(:with_indifferent_access)
70
+ false
71
+ end
72
+
73
+ def failure_message
74
+ "Document had is missing attribute #{attribute_name.inspect}"
75
+ end
76
+
77
+ def description
78
+ "be present"
79
+ end
80
+ end
81
+
82
+ class ExtraAttributeMatcher < AttributeMatcher
83
+ def matches?(document)
84
+ @document = document.try(:with_indifferent_access)
85
+ false
86
+ end
87
+
88
+ def failure_message
89
+ "Document had unexpected attribute #{attribute_name.inspect}"
90
+ end
91
+
92
+ def description
93
+ "not be present"
94
+ end
75
95
  end
76
96
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "json"
2
4
 
3
5
  module RSpec::ResemblesJsonMatchers
@@ -14,16 +16,15 @@ module RSpec::ResemblesJsonMatchers
14
16
  end
15
17
 
16
18
  def matcherize(expected)
17
- if is_matcher? expected
19
+ if matcher?(expected)
18
20
  expected
19
21
  else
20
22
  RSpec::ResemblesJsonMatchers.resembles_matcher_for(expected).new(expected)
21
23
  end
22
24
  end
23
25
 
24
- def is_matcher?(obj)
26
+ def matcher?(obj)
25
27
  obj.respond_to?(:matches?) && obj.respond_to?(:description)
26
28
  end
27
-
28
29
  end
29
30
  end
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/hash/keys" # stringify_keys
2
4
  require "json"
3
5
 
6
+ require "rspec/matchers"
4
7
  require "rspec/resembles_json_matchers/attribute_differ"
5
8
 
6
9
  module RSpec::ResemblesJsonMatchers
@@ -12,17 +15,18 @@ module RSpec::ResemblesJsonMatchers
12
15
  hash.is_a? Hash
13
16
  end
14
17
 
15
- attr_reader :expected, :actual
18
+ attr_reader :expected
16
19
 
17
20
  def initialize(expected_json)
18
- @expected = expected_json.deep_stringify_keys
21
+ @expected = expected_json.try(:deep_stringify_keys)
19
22
  end
20
23
 
21
24
  def matches?(actual_json)
22
- @actual = actual_json.deep_stringify_keys
25
+ @actual = actual_json.try(:deep_stringify_keys)
26
+ # Can't use #all? because it stops on the first false
23
27
  all_passed = true
24
- expected_matchers.each do |key, attr_matcher|
25
- result = attr_matcher.matches?(actual)
28
+ expected_matchers.each do |_key, matcher|
29
+ result = matcher.matches?(actual)
26
30
  all_passed &&= result
27
31
  end
28
32
  all_passed
@@ -37,49 +41,34 @@ module RSpec::ResemblesJsonMatchers
37
41
  AttributeDiffer.new(self).to_s
38
42
  end
39
43
 
40
- def to_json
44
+ def to_json(*_args)
41
45
  failure_message
42
46
  end
43
47
 
44
48
  def expected_matchers
45
49
  @expected_matchers ||= {}.tap do |hsh|
46
- expected.each do |k,v|
47
- hsh[k.to_s] = AttributeMatcher.new(k, matcherize(v))
50
+ (expected.keys + actual.keys).uniq.each do |key|
51
+ expected_value = matcherize(expected[key])
52
+ hsh[key.to_s] =
53
+ if !expected.key?(key) then ExtraAttributeMatcher.new(key, expected_value)
54
+ elsif !actual.key?(key) then MissingAttributeMatcher.new(key, expected_value)
55
+ else
56
+ AttributeMatcher.new(key, expected_value)
57
+ end
48
58
  end
49
59
  end
50
60
  end
51
61
 
52
62
  def expected_formatted
53
- out = "{\n"
54
- out << expected_matchers.map do |k,v|
63
+ out = +"{\n"
64
+ out << expected_matchers.map do |k, v|
55
65
  %{"#{k}": #{RSpec::Support::ObjectFormatter.format(v.expected_value)}}.indent(2)
56
66
  end.join(",\n")
57
67
  out << "\n}"
58
68
  end
59
69
 
60
- def color_lines(text)
61
- text.split("\n").map do |line|
62
- case line.chr[0]
63
- when "-" then red line
64
- when "+" then green line
65
- end
66
- end.compact
67
- end
68
-
69
- def color(text, color_code)
70
- "\e[#{color_code}m#{text}\e[0m"
71
- end
72
-
73
- def red(text)
74
- color(text, 31)
75
- end
76
-
77
- def green(text)
78
- color(text, 32)
79
- end
80
-
81
- def normal(text)
82
- color(text, 0)
70
+ def actual
71
+ @actual ||= {}
83
72
  end
84
73
  end
85
74
  end