rspec-resembles_json_matchers 0.6.0 → 0.7.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.
- checksums.yaml +5 -5
- data/Changelog.md +3 -0
- data/Guardfile +8 -1
- data/README.md +87 -117
- data/bin/rspec +21 -0
- data/examples/example_spec.rb +59 -1
- data/lib/rspec/resembles_json_matchers/attribute_differ.rb +150 -0
- data/lib/rspec/resembles_json_matchers/attribute_matcher.rb +26 -35
- data/lib/rspec/resembles_json_matchers/json_matcher.rb +46 -43
- data/lib/rspec/resembles_json_matchers/resembles_any_of_matcher.rb +11 -15
- data/lib/rspec/resembles_json_matchers/resembles_class_matcher.rb +2 -0
- data/lib/rspec/resembles_json_matchers/resembles_date_matcher.rb +3 -4
- data/lib/rspec/resembles_json_matchers/resembles_hash_matcher.rb +6 -6
- data/lib/rspec/resembles_json_matchers/resembles_numeric_matcher.rb +7 -3
- data/lib/rspec/resembles_json_matchers/resembles_string_matcher.rb +2 -4
- data/lib/rspec/resembles_json_matchers/version.rb +1 -1
- data/lib/rspec/resembles_json_matchers.rb +3 -12
- data/rspec-resembles_json_matchers.gemspec +3 -0
- metadata +53 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 41ce9ddec87f87a2b3bbdca185b9987b6b61c89813c1e1a4b5a23f5af677d636
|
4
|
+
data.tar.gz: 1f04f3a51ffd0c508a551661946466f1910eb22e427205b49fb87e689e7d24a9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4bff40b1f820583202eecfc003676f46e63533ab9c8dc635a87616b95ce5169a478ed0cad7c2aa692717859320ad0d66a1d8cb898eec091aa955537155091d44
|
7
|
+
data.tar.gz: 725fc52f95deab1a7fb909191b9b1ca14c5f19f1f45316bcd59b5aa014e419cfaee0c7cb865620a79faea9e28bd9b48910b7d4c1aef435fb1806dc689bbf7ba8
|
data/Changelog.md
CHANGED
data/Guardfile
CHANGED
@@ -24,7 +24,14 @@
|
|
24
24
|
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
25
25
|
# * 'just' rspec: 'rspec'
|
26
26
|
|
27
|
-
guard :rspec,
|
27
|
+
guard :rspec,
|
28
|
+
# cmd: "bin/rspec --no-profile --fail-fast",
|
29
|
+
cmd: "bin/rspec --no-profile",
|
30
|
+
failed_mode: :keep,
|
31
|
+
run_all: { cmd: "bin/rspec --no-profile --tag ~type:feature" }, # skip feature specs on "all"
|
32
|
+
notification: true,
|
33
|
+
all_on_start: false,
|
34
|
+
all_on_pass: false do
|
28
35
|
require "guard/rspec/dsl"
|
29
36
|
dsl = Guard::RSpec::Dsl.new(self)
|
30
37
|
|
data/README.md
CHANGED
@@ -2,79 +2,97 @@
|
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/rspec-json_api_matchers)[](https://travis-ci.org/paul/rspec-json_api_matchers)
|
4
4
|
|
5
|
-
This gem provides a set of matchers that make testing JSON documents (actually
|
6
|
-
the hashes parsed from them) simpler and more elegant.
|
5
|
+
This gem provides a set of matchers that make testing JSON documents (actually the hashes parsed from them) simpler and more elegant.
|
7
6
|
|
8
|
-
|
7
|
+
## `resemble` Matcher
|
9
8
|
|
10
|
-
|
9
|
+
Oftentimes when testing your JSON API responses, you don't care about the actual values matching exactly, just that they "resemble" your expected values. This gem provides a variety of matchers to just get close:
|
11
10
|
|
12
|
-
|
13
|
-
match against its value.
|
11
|
+
### Numbers
|
14
12
|
|
15
|
-
|
13
|
+
Anything that's a Ruby `Numeric` will match:
|
16
14
|
|
17
15
|
```ruby
|
18
|
-
|
19
|
-
|
16
|
+
# These pass
|
17
|
+
expect(10).to resemble 42
|
18
|
+
expect(3.14).to resemble 42
|
19
|
+
expect(10).to resemble 42.4
|
20
|
+
|
21
|
+
# These fail
|
22
|
+
expect("string").to resemble 42
|
23
|
+
expect(Time.now).to resemble 42
|
24
|
+
```
|
20
25
|
|
21
|
-
|
22
|
-
{
|
23
|
-
author: "Paul",
|
24
|
-
gems_published: 42,
|
25
|
-
created_at: "2016-01-01T00:00:00Z"
|
26
|
-
}
|
27
|
-
end
|
26
|
+
I haven't needed it yet, but I'm open to discussing if more accurate matches would be needed. For example:
|
28
27
|
|
29
|
-
|
30
|
-
|
28
|
+
* Does 1_000_000_000 "resemble" 1?
|
29
|
+
* Does a float "resemble" an integer?
|
31
30
|
|
32
|
-
# Test the value by using another matcher
|
33
|
-
it { should have_attribute :author, eq("Paul") }
|
34
|
-
it { should have_attribute :author, match(/Paul/) }
|
35
|
-
it { should have_attribute :gems_published, be > 40 }
|
36
|
-
it { should have_attribute :created_at, iso8601_timestamp }
|
37
|
-
end
|
38
|
-
```
|
39
31
|
|
40
|
-
|
41
|
-
failure messages:
|
32
|
+
### Dates/Times
|
42
33
|
|
34
|
+
Anything that is a Ruby `Date`/`Time`/`DateTime`, or a string that can be parsed by `Time.iso8601` will match:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
# These pass
|
38
|
+
expect(Time.now).to resemble "2018-01-01T00:00:00Z"
|
39
|
+
expect(Time.now.iso8601).to resemble "2018-01-01T00:00:00Z"
|
40
|
+
|
41
|
+
# These fail
|
42
|
+
expect("Some string").to resemble "2018-01-01T00:00:00Z"
|
43
43
|
```
|
44
|
-
my hash
|
45
|
-
should have attribute :author be present
|
46
|
-
should have attribute :author eq "Paul"
|
47
|
-
should have attribute :author match /Paul/
|
48
|
-
should have attribute :gems_published be > 40
|
49
|
-
should have attribute :created_at match /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/
|
50
|
-
```
|
51
44
|
|
45
|
+
Open questions:
|
46
|
+
|
47
|
+
* Does `"2018-01-01T00:00:00-0700"` "resemble" `"2018-01-01T00:00:00Z"`? That is, should it ensure the timezone matches?
|
48
|
+
* Do non-ISO8601 datetimes "resemble" ISO8601 ones?
|
49
|
+
* Is there a permissible time range? Does the year 1600 "resemble" 2017? Does `"0000-00-00T00:00:00Z"`? Does `Time.at(0)`?
|
50
|
+
|
51
|
+
### Rails routes
|
52
|
+
|
53
|
+
If you're using Rails (specifically `ActionDispatch`), we can check that routes resemble each other:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
# These pass
|
57
|
+
expect("/posts/1").to resemble posts_path(post)
|
58
|
+
expect("/posts/1000000").to resemble posts_path(post)
|
59
|
+
expect("https://example.com/posts/1").to resemble posts_path(post)
|
60
|
+
|
61
|
+
# These fail
|
62
|
+
expect("/users/1").to resemble posts_path(post)
|
63
|
+
expect("Some string").to resemble posts_path(post)
|
52
64
|
```
|
53
|
-
Failures:
|
54
65
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
66
|
+
### Strings
|
67
|
+
|
68
|
+
Any string that didn't match one of the other resembles matchers will match:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
# These pass
|
72
|
+
expect("Some string").to resemble "some other string"
|
73
|
+
expect("").to resemble "some other string"
|
74
|
+
expect("a" * 100_000).to resemble "some other string"
|
75
|
+
|
76
|
+
# These fail
|
77
|
+
expect(42).to resemble "42"
|
78
|
+
expect(Time.now).to resemble "Time.now
|
59
79
|
|
60
|
-
2) my hash should have attribute :author match /paul/
|
61
|
-
Failure/Error: it { should have_attribute :author, match(/paul/) }
|
62
|
-
Expected value of attribute :author to match /paul/ but it was "Paul"
|
63
|
-
# ./spec/examples_spec.rb:25:in `block (2 levels) in <top (required)>'
|
64
80
|
```
|
65
81
|
|
66
|
-
|
82
|
+
Open questions:
|
83
|
+
|
84
|
+
* Should there be some heuristic to decide if a string resembles another? Does length matter?
|
67
85
|
|
68
|
-
|
86
|
+
## `#resemble_json/match_json`
|
69
87
|
|
70
|
-
|
88
|
+
The resembles matchers are nice on their own, but their power shines when used with the `resembles_json` matcher. This allows you to write an example JSON document in your spec, and match it against the output from a request. Any values in the "expected" document that aren't already matchers will be converted to the best `resembles` matcher. You can write plain json documents as the expected, or be explicit by specifying matchers.
|
71
89
|
|
72
|
-
See the examples folder for more.
|
73
90
|
|
74
91
|
### Example Usage
|
75
92
|
|
76
93
|
```ruby
|
77
94
|
RSpec.describe "a basic json document" do
|
95
|
+
# This would probably actually come from something like `JSON.parse(response.body)`
|
78
96
|
let(:document) do
|
79
97
|
{
|
80
98
|
"@id": "/posts/2016/test1",
|
@@ -89,19 +107,19 @@ RSpec.describe "a basic json document" do
|
|
89
107
|
specify do
|
90
108
|
expect(document).to resemble_json(
|
91
109
|
{
|
92
|
-
"@id":
|
93
|
-
"@type": eq("Post"),
|
94
|
-
"title":
|
95
|
-
"body": "lorem ipsum",
|
96
|
-
"created_at": "2016-03-01T00:03:42",
|
97
|
-
"published_at":
|
110
|
+
"@id": post_path(post), # resembles route
|
111
|
+
"@type": eq("Post"), # using an explicit matcher to match exactly
|
112
|
+
"title": match(/^Hello/), # another explicit matcher
|
113
|
+
"body": "lorem ipsum", # resembles string
|
114
|
+
"created_at": "2016-03-01T00:03:42", # resembles time
|
115
|
+
"published_at": post.published_at # Also resembles time
|
98
116
|
}
|
99
117
|
)
|
100
118
|
end
|
101
119
|
end
|
102
120
|
```
|
103
121
|
|
104
|
-
|
122
|
+
It provides good descriptions if you run `rspec` with `--format=documentation`:
|
105
123
|
|
106
124
|
```
|
107
125
|
a basic json document
|
@@ -116,21 +134,18 @@ Again, it provides good descriptions:
|
|
116
134
|
}
|
117
135
|
```
|
118
136
|
|
119
|
-
|
137
|
+
It also provides a failure message as a diff of the JSON object:
|
120
138
|
|
121
139
|
```
|
122
|
-
|
123
|
-
|
124
|
-
1) The resembles json matcher a basic json document with several attributes that failed to match should resemble json
|
140
|
+
1) The resembles json matcher a basic json document with several attributes that failed to match should have json that looks like
|
125
141
|
{
|
126
|
-
"@id": /posts/:year/:title,
|
142
|
+
"@id": "/posts/:year/:title",
|
127
143
|
"@type": "PostCollection",
|
128
144
|
"title": 42.0,
|
129
|
-
"body": lorem ipsum,
|
145
|
+
"body": "lorem ipsum",
|
130
146
|
"created_at": "2016-03-01T00:03:42",
|
131
147
|
"published_at": "2016-03-10T15:35:00"
|
132
148
|
}
|
133
|
-
|
134
149
|
Failure/Error:
|
135
150
|
expect(document).to resemble_json(
|
136
151
|
{
|
@@ -143,65 +158,20 @@ Failures:
|
|
143
158
|
}
|
144
159
|
)
|
145
160
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
161
|
+
{
|
162
|
+
"@id": "/posts/:year/:title",
|
163
|
+
- "@type": eq "PostCollection",
|
164
|
+
+ "@type": "Post",
|
165
|
+
- "title": 42.0,
|
166
|
+
+ "title": "Hello World!",
|
167
|
+
"body": "lorem ipsum",
|
168
|
+
"created_at": "2016-03-01T00:03:42",
|
169
|
+
"published_at": "2016-03-10T15:35:00"
|
170
|
+
}
|
152
171
|
# ./examples/example_spec.rb:40:in `block (4 levels) in <top (required)>'
|
153
|
-
|
154
|
-
2) The resembles json matcher a basic json document when the matcher is missing a field that is present in the document should resemble json
|
155
|
-
{
|
156
|
-
"@id": /posts/:year/:title,
|
157
|
-
"@type": "Post",
|
158
|
-
"body": lorem ipsum,
|
159
|
-
"created_at": "2016-03-01T00:03:42",
|
160
|
-
"published_at": "2016-03-10T15:35:00"
|
161
|
-
}
|
162
|
-
|
163
|
-
Failure/Error:
|
164
|
-
expect(document).to resemble_json(
|
165
|
-
{
|
166
|
-
"@id": "/posts/:year/:title",
|
167
|
-
"@type": eq("Post"),
|
168
|
-
"body": "lorem ipsum",
|
169
|
-
"created_at": "2016-03-01T00:03:42",
|
170
|
-
"published_at": "2016-03-10T15:35:00"
|
171
|
-
}
|
172
|
-
)
|
173
|
-
|
174
|
-
failed because
|
175
|
-
attribute "title":
|
176
|
-
is present, but no matcher provided to match it
|
177
|
-
# ./examples/example_spec.rb:55:in `block (4 levels) in <top (required)>'
|
178
|
-
|
179
|
-
3) The resembles json matcher a basic json document when the document is missing a field that is present in the matcher should resemble json
|
180
|
-
{
|
181
|
-
"@id": /posts/:year/:title,
|
182
|
-
"@type": "Post",
|
183
|
-
"body": lorem ipsum,
|
184
|
-
"created_at": "2016-03-01T00:03:42",
|
185
|
-
"published_at": "2016-03-10T15:35:00"
|
186
|
-
}
|
187
|
-
|
188
|
-
Failure/Error:
|
189
|
-
expect(document).to resemble_json(
|
190
|
-
{
|
191
|
-
"@id": "/posts/:year/:title",
|
192
|
-
"@type": eq("Post"),
|
193
|
-
"body": "lorem ipsum",
|
194
|
-
"created_at": "2016-03-01T00:03:42",
|
195
|
-
"published_at": "2016-03-10T15:35:00"
|
196
|
-
}
|
197
|
-
)
|
198
|
-
|
199
|
-
failed because
|
200
|
-
attribute "title":
|
201
|
-
is present, but no matcher provided to match it
|
202
|
-
# ./examples/example_spec.rb:69:in `block (4 levels) in <top (required)>'
|
203
172
|
```
|
204
173
|
|
174
|
+
It can also handle nested JSON documents and Arrays, showing the proper diffs. See the `./examples` directory, but it works pretty much how you'd expect.
|
205
175
|
|
206
176
|
# Installation
|
207
177
|
|
data/bin/rspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rspec' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
12
|
+
load(bundle_binstub) if File.file?(bundle_binstub)
|
13
|
+
|
14
|
+
require "pathname"
|
15
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
16
|
+
Pathname.new(__FILE__).realpath)
|
17
|
+
|
18
|
+
require "rubygems"
|
19
|
+
require "bundler/setup"
|
20
|
+
|
21
|
+
load Gem.bin_path("rspec-core", "rspec")
|
data/examples/example_spec.rb
CHANGED
@@ -4,7 +4,7 @@ RSpec.describe "The resembles json matcher" do
|
|
4
4
|
include RSpec::ResemblesJsonMatchers
|
5
5
|
|
6
6
|
before :all do
|
7
|
-
puts
|
7
|
+
puts <<~WARNING
|
8
8
|
NOTE: some of these are expected to fail, they are meant to demonstrate
|
9
9
|
the output generated by failing specs
|
10
10
|
WARNING
|
@@ -117,7 +117,65 @@ RSpec.describe "The resembles json matcher" do
|
|
117
117
|
}
|
118
118
|
)
|
119
119
|
end
|
120
|
+
end
|
120
121
|
|
122
|
+
describe "empty nested documents" do
|
123
|
+
let(:document) do
|
124
|
+
{
|
125
|
+
"@id": "/posts/2016/test1",
|
126
|
+
"@type": "Post",
|
127
|
+
"title": "Hello World!",
|
128
|
+
"body": "lorem ipsum",
|
129
|
+
"created_at": "2016-03-01T00:03:42",
|
130
|
+
"published_at": "2016-03-10T15:35:00"
|
131
|
+
}
|
132
|
+
end
|
133
|
+
|
134
|
+
specify do
|
135
|
+
expect(document).to resemble_json(
|
136
|
+
{
|
137
|
+
"@id": "/posts/:year/:title",
|
138
|
+
"@type": eq("Post"),
|
139
|
+
"title": "Hello World!",
|
140
|
+
"body": "lorem ipsum",
|
141
|
+
"author": {
|
142
|
+
"@id": "/authors/:id",
|
143
|
+
"name": eq("Paul")
|
144
|
+
},
|
145
|
+
"created_at": "2016-03-01T00:03:42",
|
146
|
+
"published_at": "2016-03-10T15:35:00"
|
147
|
+
}
|
148
|
+
)
|
149
|
+
end
|
150
|
+
end
|
121
151
|
|
152
|
+
describe "empty nested array documents" do
|
153
|
+
let(:document) do
|
154
|
+
{
|
155
|
+
"@id": "/posts",
|
156
|
+
"@type": "PostCollection",
|
157
|
+
"nextPage": "/posts?page=2"
|
158
|
+
}
|
159
|
+
end
|
160
|
+
|
161
|
+
specify do
|
162
|
+
expect(document).to resemble_json(
|
163
|
+
{
|
164
|
+
"@id": "/posts",
|
165
|
+
"@type": eq("PostCollection"),
|
166
|
+
"nextPage": "/posts?page=2",
|
167
|
+
"members": [
|
168
|
+
{
|
169
|
+
"@id": "/posts/:year/:title",
|
170
|
+
"@type": eq("Post"),
|
171
|
+
"title": "Hello World!",
|
172
|
+
"body": "lorem ipsum",
|
173
|
+
"created_at": "2016-03-01T00:03:42",
|
174
|
+
"published_at": "2016-03-10T15:35:00"
|
175
|
+
}
|
176
|
+
]
|
177
|
+
}
|
178
|
+
)
|
179
|
+
end
|
122
180
|
end
|
123
181
|
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require "active_support/inflector"
|
2
|
+
|
3
|
+
module RSpec::ResemblesJsonMatchers
|
4
|
+
class AttributeDiffer
|
5
|
+
def initialize(matcher)
|
6
|
+
@matcher = matcher
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
@buffer = StringIO.new
|
11
|
+
@buffer.puts NEUTRAL_COLOR + "Diff:"
|
12
|
+
render(@matcher)
|
13
|
+
@buffer.string
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def render(matcher, **opts)
|
19
|
+
class_name = ActiveSupport::Inflector.demodulize(matcher.class.to_s)
|
20
|
+
method_name = :"render_#{class_name}"
|
21
|
+
send method_name, matcher, **opts
|
22
|
+
end
|
23
|
+
|
24
|
+
def render_JsonMatcher(matcher, prefix: "", starts_on_newline: false, **opts)
|
25
|
+
@buffer.print prefix if starts_on_newline
|
26
|
+
@buffer.print NORMAL_COLOR unless prefix.include?("-")
|
27
|
+
@buffer.puts "{"
|
28
|
+
matcher.expected_matchers.each do |key, attr_matcher|
|
29
|
+
last = (matcher.expected_matchers.keys.last == key)
|
30
|
+
render(attr_matcher, prefix: prefix, last: last, **opts)
|
31
|
+
end
|
32
|
+
if matcher.actual.nil?
|
33
|
+
@buffer.print REMOVED_COLOR
|
34
|
+
if prefix.include? "-"
|
35
|
+
@buffer.print prefix + "}"
|
36
|
+
else
|
37
|
+
@buffer.print prefix + "- }"
|
38
|
+
end
|
39
|
+
else
|
40
|
+
@buffer.print NORMAL_COLOR unless prefix.include?("-")
|
41
|
+
@buffer.print prefix + "}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def render_AttributeMatcher(matcher, prefix: "", last: false)
|
46
|
+
if matcher.matched?
|
47
|
+
@buffer.print NORMAL_COLOR
|
48
|
+
@buffer.print prefix + " " + "#{matcher.attribute_name.to_json}: "
|
49
|
+
render(matcher.value_matcher, prefix: prefix + " ")
|
50
|
+
@buffer.print(",") unless last
|
51
|
+
@buffer.puts
|
52
|
+
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
|
68
|
+
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
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def render_ResemblesAnyOfMatcher(matcher, prefix: "", **opts)
|
94
|
+
@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
|
99
|
+
else
|
100
|
+
matcher.attempted_matchers.each do |attempted_matcher|
|
101
|
+
last = (matcher.attempted_matchers.last == attempted_matcher)
|
102
|
+
render attempted_matcher, prefix: prefix + " ", starts_on_newline: true
|
103
|
+
@buffer.print(",") unless last
|
104
|
+
@buffer.puts
|
105
|
+
end
|
106
|
+
end
|
107
|
+
@buffer.print prefix + "]"
|
108
|
+
end
|
109
|
+
|
110
|
+
def render_ResemblesStringMatcher(matcher, **opts)
|
111
|
+
@buffer.print matcher.expected.to_json
|
112
|
+
end
|
113
|
+
|
114
|
+
def render_ResemblesDateMatcher(matcher, **opts)
|
115
|
+
@buffer.print matcher.expected.to_json
|
116
|
+
end
|
117
|
+
|
118
|
+
def render_ResemblesNumericMatcher(matcher, **opts)
|
119
|
+
@buffer.print matcher.expected.to_json
|
120
|
+
end
|
121
|
+
|
122
|
+
def render_ResemblesClassMatcher(matcher, **opts)
|
123
|
+
@buffer.print matcher.expected.inspect
|
124
|
+
end
|
125
|
+
|
126
|
+
def method_missing(method_name, *args, &block)
|
127
|
+
if method_name.to_s.start_with?("render_")
|
128
|
+
raise NoMethodError, method_name if method_name.to_s.end_with?("Matcher")
|
129
|
+
@buffer.print RSpec::Support::ObjectFormatter.format(args.first)
|
130
|
+
else
|
131
|
+
super
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def respond_to_missing?(method_name, include_private = false)
|
136
|
+
method_name.to_s.start_with?("render_")
|
137
|
+
end
|
138
|
+
|
139
|
+
def nested_matcher?(matcher)
|
140
|
+
matcher.is_a?(JsonMatcher) || matcher.is_a?(ResemblesAnyOfMatcher)
|
141
|
+
end
|
142
|
+
|
143
|
+
NORMAL_COLOR = "\e[0m".freeze
|
144
|
+
REMOVED_COLOR = "\e[31m".freeze # Red
|
145
|
+
ADDED_COLOR = "\e[32m".freeze # Green
|
146
|
+
NEUTRAL_COLOR = "\e[34m".freeze # Blue
|
147
|
+
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
@@ -18,30 +18,39 @@ module RSpec::ResemblesJsonMatchers
|
|
18
18
|
def matches?(document)
|
19
19
|
@document = document.with_indifferent_access
|
20
20
|
|
21
|
-
@
|
22
|
-
expected.matches?(@document.fetch(attribute_name, nil))
|
21
|
+
@matched = !missing_attribute? && expected.matches?(actual_value)
|
23
22
|
end
|
24
23
|
|
25
24
|
def failure_message
|
26
|
-
if
|
27
|
-
|
28
|
-
attribute_name.inspect,
|
29
|
-
"to be present"]
|
25
|
+
if missing_attribute?
|
26
|
+
%{Expected document to have attribute #{attribute_name.inspect}}
|
30
27
|
else
|
31
|
-
|
32
|
-
attribute_name.inspect,
|
33
|
-
"to",
|
34
|
-
expected.description,
|
35
|
-
"but it was",
|
36
|
-
document[attribute_name].inspect]
|
28
|
+
%{Expected attribute #{attribute_name.inspect} to #{value_matcher.description}, but it was #{actual_value.inspect}}
|
37
29
|
end
|
38
|
-
sentencize(*msgs)
|
39
30
|
end
|
40
31
|
|
41
|
-
def
|
42
|
-
|
43
|
-
|
44
|
-
|
32
|
+
def matched?
|
33
|
+
@matched
|
34
|
+
end
|
35
|
+
|
36
|
+
def value_matcher
|
37
|
+
@expected
|
38
|
+
end
|
39
|
+
|
40
|
+
def expected_value
|
41
|
+
value_matcher.expected
|
42
|
+
end
|
43
|
+
|
44
|
+
def actual_value
|
45
|
+
document.fetch(attribute_name, nil)
|
46
|
+
end
|
47
|
+
|
48
|
+
def missing_attribute?
|
49
|
+
!has_attribute?
|
50
|
+
end
|
51
|
+
|
52
|
+
def has_attribute?
|
53
|
+
document && document.key?(attribute_name)
|
45
54
|
end
|
46
55
|
|
47
56
|
NullMatcher = Class.new do
|
@@ -63,23 +72,5 @@ module RSpec::ResemblesJsonMatchers
|
|
63
72
|
end
|
64
73
|
end.new
|
65
74
|
|
66
|
-
def matcherize(expected)
|
67
|
-
if matcher? expected
|
68
|
-
expected
|
69
|
-
|
70
|
-
elsif expected.respond_to? :===
|
71
|
-
RSpec::Matchers::Builtin::Match.new(expected)
|
72
|
-
|
73
|
-
else
|
74
|
-
RSpec::Matchers::Builtin::Eq.new(expected)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def matcher?(obj)
|
79
|
-
obj.respond_to(:matches?) && (obj.respond_to?(:failure_message) ||
|
80
|
-
obj.respond_to?(:failure_message_for_should))
|
81
|
-
end
|
82
|
-
|
83
75
|
end
|
84
|
-
|
85
76
|
end
|
@@ -1,58 +1,50 @@
|
|
1
|
-
require "active_support/core_ext/hash/keys"
|
2
|
-
|
1
|
+
require "active_support/core_ext/hash/keys" # stringify_keys
|
3
2
|
require "json"
|
4
3
|
|
4
|
+
require "rspec/resembles_json_matchers/attribute_differ"
|
5
|
+
|
5
6
|
module RSpec::ResemblesJsonMatchers
|
6
7
|
class JsonMatcher
|
7
8
|
include RSpec::Matchers::Composable
|
8
|
-
include
|
9
|
+
include Helpers
|
10
|
+
|
11
|
+
def self.can_match?(hash)
|
12
|
+
hash.is_a? Hash
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :expected, :actual
|
9
16
|
|
10
17
|
def initialize(expected_json)
|
11
|
-
@
|
12
|
-
@failed_matchers = {}
|
18
|
+
@expected = expected_json.deep_stringify_keys
|
13
19
|
end
|
14
20
|
|
15
21
|
def matches?(actual_json)
|
16
|
-
@
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
22
|
+
@actual = actual_json.deep_stringify_keys
|
23
|
+
all_passed = true
|
24
|
+
expected_matchers.each do |key, attr_matcher|
|
25
|
+
result = attr_matcher.matches?(actual)
|
26
|
+
all_passed &&= result
|
21
27
|
end
|
22
|
-
|
28
|
+
all_passed
|
23
29
|
end
|
24
30
|
|
25
31
|
def description
|
26
32
|
# TODO Figure out how to discover the right indent level
|
27
|
-
"have json that looks like\n#{expected_formatted.indent(
|
33
|
+
"have json that looks like\n#{expected_formatted.indent(2)}"
|
28
34
|
end
|
29
35
|
|
30
36
|
def failure_message
|
31
|
-
|
32
|
-
pretty_actual.indent(2),
|
33
|
-
"To match:",
|
34
|
-
expected_formatted.indent(2),
|
35
|
-
"Failures:",
|
36
|
-
pretty_errors.indent(2) ]
|
37
|
-
msgs.join("\n")
|
37
|
+
AttributeDiffer.new(self).to_s
|
38
38
|
end
|
39
39
|
|
40
|
-
def
|
41
|
-
|
42
|
-
end
|
43
|
-
|
44
|
-
def expected_formatted
|
45
|
-
pretty_json(@expected_json)
|
46
|
-
end
|
47
|
-
|
48
|
-
def pretty_actual
|
49
|
-
pretty_json(@actual_json)
|
40
|
+
def to_json
|
41
|
+
failure_message
|
50
42
|
end
|
51
43
|
|
52
44
|
def expected_matchers
|
53
45
|
@expected_matchers ||= {}.tap do |hsh|
|
54
|
-
|
55
|
-
hsh[k] =
|
46
|
+
expected.each do |k,v|
|
47
|
+
hsh[k.to_s] = AttributeMatcher.new(k, matcherize(v))
|
56
48
|
end
|
57
49
|
end
|
58
50
|
end
|
@@ -60,23 +52,34 @@ module RSpec::ResemblesJsonMatchers
|
|
60
52
|
def expected_formatted
|
61
53
|
out = "{\n"
|
62
54
|
out << expected_matchers.map do |k,v|
|
63
|
-
|
64
|
-
when RSpec::Matchers::BuiltIn::Eq
|
65
|
-
%{"#{k}": #{v.expected_formatted}}.indent(2)
|
66
|
-
else
|
67
|
-
%{"#{k}": #{v.description}}.indent(2)
|
68
|
-
end
|
55
|
+
%{"#{k}": #{RSpec::Support::ObjectFormatter.format(v.expected_value)}}.indent(2)
|
69
56
|
end.join(",\n")
|
70
57
|
out << "\n}"
|
71
58
|
end
|
72
59
|
|
73
|
-
def
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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"
|
79
71
|
end
|
80
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)
|
83
|
+
end
|
81
84
|
end
|
82
85
|
end
|
@@ -8,13 +8,16 @@ module RSpec::ResemblesJsonMatchers
|
|
8
8
|
array.is_a? Array
|
9
9
|
end
|
10
10
|
|
11
|
+
attr_reader :expected, :actual
|
12
|
+
|
11
13
|
def initialize(expected)
|
12
|
-
@expected = expected
|
14
|
+
@expected = expected.map { |e| matcherize(e) }
|
13
15
|
end
|
14
16
|
|
15
17
|
def matches?(actual)
|
16
|
-
Array.wrap(actual)
|
17
|
-
|
18
|
+
@actual = Array.wrap(actual)
|
19
|
+
@actual.all? do |a|
|
20
|
+
expected_matchers.any? { |m| attempted_matchers << m; m.matches? a }
|
18
21
|
end
|
19
22
|
end
|
20
23
|
|
@@ -27,17 +30,15 @@ module RSpec::ResemblesJsonMatchers
|
|
27
30
|
end
|
28
31
|
|
29
32
|
def failure_message
|
30
|
-
sentencize ["Expected every item to match one of:\n",
|
31
|
-
expected_formatted,
|
32
|
-
"The item at",
|
33
|
-
# failed_item_indexes,
|
34
|
-
"did not because:\n",
|
35
|
-
failure_messages]
|
36
33
|
|
37
34
|
end
|
38
35
|
|
39
36
|
def expected_matchers
|
40
|
-
@expected
|
37
|
+
@expected
|
38
|
+
end
|
39
|
+
|
40
|
+
def attempted_matchers
|
41
|
+
@attempted_matchers ||= []
|
41
42
|
end
|
42
43
|
|
43
44
|
def expected_formatted
|
@@ -52,10 +53,5 @@ module RSpec::ResemblesJsonMatchers
|
|
52
53
|
end.join("\n")
|
53
54
|
end << "\n"
|
54
55
|
end
|
55
|
-
|
56
|
-
def failure_messages
|
57
|
-
end
|
58
|
-
|
59
56
|
end
|
60
|
-
|
61
57
|
end
|
@@ -20,8 +20,11 @@ module RSpec::ResemblesJsonMatchers
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
+
attr_reader :expected
|
24
|
+
|
23
25
|
def initialize(expected)
|
24
26
|
@expected = expected
|
27
|
+
# @expected = expected.is_a?(String) ? Time.parse(expected) : expected
|
25
28
|
end
|
26
29
|
|
27
30
|
def description
|
@@ -33,10 +36,6 @@ module RSpec::ResemblesJsonMatchers
|
|
33
36
|
self.class.can_match?(actual)
|
34
37
|
end
|
35
38
|
|
36
|
-
def expected_formatted
|
37
|
-
@expected.inspect.to_s
|
38
|
-
end
|
39
|
-
|
40
39
|
def failure_message
|
41
40
|
"#{@actual.inspect} does not resemble a Date or Timestamp"
|
42
41
|
end
|
@@ -20,7 +20,7 @@ module RSpec::ResemblesJsonMatchers
|
|
20
20
|
expected_matchers.each do |expected_key, value_matcher|
|
21
21
|
@matched_keys << expected_key
|
22
22
|
|
23
|
-
attr_matcher =
|
23
|
+
attr_matcher = AttributeMatcher.new(expected_key, value_matcher)
|
24
24
|
match = attr_matcher.matches?(actual)
|
25
25
|
|
26
26
|
if match
|
@@ -42,11 +42,11 @@ module RSpec::ResemblesJsonMatchers
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def failure_message
|
45
|
-
"failed because\n" +
|
46
|
-
pretty_failed_matches.indent(2)
|
47
|
-
pretty_unmatched_keys.indent(2)
|
48
|
-
pretty_unmatched_matchers.indent(2)
|
49
|
-
|
45
|
+
"failed because\n" + [
|
46
|
+
pretty_failed_matches.indent(2),
|
47
|
+
pretty_unmatched_keys.indent(2),
|
48
|
+
pretty_unmatched_matchers.indent(2)
|
49
|
+
].join("\n") + "\n"
|
50
50
|
end
|
51
51
|
|
52
52
|
protected
|
@@ -1,12 +1,12 @@
|
|
1
1
|
|
2
2
|
module RSpec::ResemblesJsonMatchers
|
3
3
|
class ResemblesNumericMatcher
|
4
|
-
NUMBER_REGEX = /\A\d+\Z/.freeze
|
5
4
|
def self.can_match?(number)
|
6
|
-
number.is_a?(Numeric)
|
7
|
-
number =~ NUMBER_REGEX
|
5
|
+
number.is_a?(Numeric)
|
8
6
|
end
|
9
7
|
|
8
|
+
attr_reader :expected
|
9
|
+
|
10
10
|
def initialize(expected)
|
11
11
|
@expected = expected
|
12
12
|
end
|
@@ -25,6 +25,10 @@ module RSpec::ResemblesJsonMatchers
|
|
25
25
|
@expected
|
26
26
|
end
|
27
27
|
|
28
|
+
def to_json
|
29
|
+
@expected
|
30
|
+
end
|
31
|
+
|
28
32
|
def failure_message
|
29
33
|
"#{@actual.inspect} does not resemble a number"
|
30
34
|
end
|
@@ -5,6 +5,8 @@ module RSpec::ResemblesJsonMatchers
|
|
5
5
|
string.is_a? String
|
6
6
|
end
|
7
7
|
|
8
|
+
attr_reader :expected
|
9
|
+
|
8
10
|
def initialize(expected)
|
9
11
|
@expected = expected
|
10
12
|
end
|
@@ -17,9 +19,5 @@ module RSpec::ResemblesJsonMatchers
|
|
17
19
|
def matches?(actual)
|
18
20
|
self.class.can_match?(actual)
|
19
21
|
end
|
20
|
-
|
21
|
-
def expected_formatted
|
22
|
-
@expected
|
23
|
-
end
|
24
22
|
end
|
25
23
|
end
|
@@ -4,13 +4,9 @@ require "rspec/resembles_json_matchers/string_indent"
|
|
4
4
|
module RSpec
|
5
5
|
module ResemblesJsonMatchers
|
6
6
|
autoload :AttributeMatcher, "rspec/resembles_json_matchers/attribute_matcher"
|
7
|
-
# autoload :ResemblesMatcher, "rspec/resembles_json_matchers/resembles_matcher"
|
8
7
|
autoload :JsonMatcher, "rspec/resembles_json_matchers/json_matcher"
|
9
8
|
autoload :Helpers, "rspec/resembles_json_matchers/helpers"
|
10
|
-
# autoload :Matcherizer, "rspec/resembles_json_matchers/matcherizer"
|
11
9
|
|
12
|
-
autoload :ResemblesHashMatcher, "rspec/resembles_json_matchers/resembles_hash_matcher"
|
13
|
-
autoload :ResemblesArrayMatcher, "rspec/resembles_json_matchers/resembles_array_matcher"
|
14
10
|
autoload :ResemblesAnyOfMatcher, "rspec/resembles_json_matchers/resembles_any_of_matcher"
|
15
11
|
autoload :ResemblesRouteMatcher, "rspec/resembles_json_matchers/resembles_route_matcher"
|
16
12
|
autoload :ResemblesDateMatcher, "rspec/resembles_json_matchers/resembles_date_matcher"
|
@@ -27,22 +23,17 @@ module RSpec
|
|
27
23
|
def match_json(*a)
|
28
24
|
JsonMatcher.new(*a)
|
29
25
|
end
|
30
|
-
|
31
|
-
def have_attribute(*a)
|
32
|
-
AttributeMatcher.new(*a)
|
33
|
-
end
|
26
|
+
alias resemble_json match_json
|
34
27
|
|
35
28
|
def resembles(*a)
|
36
29
|
RSpec::ResemblesJsonMatchers.resembles_matcher_for(*a).new(*a)
|
37
30
|
end
|
38
31
|
alias resemble resembles
|
39
|
-
alias resemble_json resembles
|
40
32
|
|
41
33
|
def self.resembles_matcher_candidates
|
42
34
|
# Order matters
|
43
35
|
@candidates ||= [
|
44
|
-
|
45
|
-
#ResemblesArrayMatcher,
|
36
|
+
JsonMatcher,
|
46
37
|
ResemblesAnyOfMatcher,
|
47
38
|
ResemblesRouteMatcher,
|
48
39
|
ResemblesDateMatcher,
|
@@ -55,7 +46,7 @@ module RSpec
|
|
55
46
|
end
|
56
47
|
|
57
48
|
def self.resembles_matcher_for(expected, **a)
|
58
|
-
resembles_matcher_candidates.detect { |candidate| candidate.can_match?(expected) } ||
|
49
|
+
resembles_matcher_candidates.detect { |candidate| candidate.can_match?(expected) } || RSpec::Matchers::BuiltIn::Eq
|
59
50
|
end
|
60
51
|
|
61
52
|
end
|
@@ -31,9 +31,12 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.add_development_dependency "appraisal"
|
32
32
|
spec.add_development_dependency "guard"
|
33
33
|
spec.add_development_dependency "guard-rspec"
|
34
|
+
spec.add_development_dependency "pry-byebug"
|
35
|
+
spec.add_development_dependency "pry-state"
|
34
36
|
spec.add_development_dependency "awesome_print"
|
35
37
|
|
36
38
|
spec.add_runtime_dependency "rspec", ">= 2.0", "< 4.0.0.a"
|
37
39
|
spec.add_runtime_dependency "rspec-expectations", ">= 2.0", "< 4.0.0.a"
|
40
|
+
spec.add_runtime_dependency "rspec-support", ">= 2.0", "< 4.0.0.a"
|
38
41
|
spec.add_runtime_dependency "activesupport", ">= 3.0" # For core extensions
|
39
42
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec-resembles_json_matchers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Sadauskas
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-12-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -94,6 +94,34 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry-byebug
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: pry-state
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
97
125
|
- !ruby/object:Gem::Dependency
|
98
126
|
name: awesome_print
|
99
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -148,6 +176,26 @@ dependencies:
|
|
148
176
|
- - "<"
|
149
177
|
- !ruby/object:Gem::Version
|
150
178
|
version: 4.0.0.a
|
179
|
+
- !ruby/object:Gem::Dependency
|
180
|
+
name: rspec-support
|
181
|
+
requirement: !ruby/object:Gem::Requirement
|
182
|
+
requirements:
|
183
|
+
- - ">="
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
version: '2.0'
|
186
|
+
- - "<"
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
version: 4.0.0.a
|
189
|
+
type: :runtime
|
190
|
+
prerelease: false
|
191
|
+
version_requirements: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - ">="
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: '2.0'
|
196
|
+
- - "<"
|
197
|
+
- !ruby/object:Gem::Version
|
198
|
+
version: 4.0.0.a
|
151
199
|
- !ruby/object:Gem::Dependency
|
152
200
|
name: activesupport
|
153
201
|
requirement: !ruby/object:Gem::Requirement
|
@@ -178,6 +226,7 @@ files:
|
|
178
226
|
- Guardfile
|
179
227
|
- README.md
|
180
228
|
- Rakefile
|
229
|
+
- bin/rspec
|
181
230
|
- examples/example_spec.rb
|
182
231
|
- gemfiles/rails_32.gemfile
|
183
232
|
- gemfiles/rails_32.gemfile.lock
|
@@ -188,6 +237,7 @@ files:
|
|
188
237
|
- gemfiles/rails_5.gemfile
|
189
238
|
- gemfiles/rails_5.gemfile.lock
|
190
239
|
- lib/rspec/resembles_json_matchers.rb
|
240
|
+
- lib/rspec/resembles_json_matchers/attribute_differ.rb
|
191
241
|
- lib/rspec/resembles_json_matchers/attribute_matcher.rb
|
192
242
|
- lib/rspec/resembles_json_matchers/helpers.rb
|
193
243
|
- lib/rspec/resembles_json_matchers/json_matcher.rb
|
@@ -225,7 +275,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
225
275
|
version: '0'
|
226
276
|
requirements: []
|
227
277
|
rubyforge_project:
|
228
|
-
rubygems_version: 2.
|
278
|
+
rubygems_version: 2.7.3
|
229
279
|
signing_key:
|
230
280
|
specification_version: 4
|
231
281
|
summary: Helpful matchers for comparing JSON documents.
|