rspec-resembles_json_matchers 0.7.2 → 0.9.1

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