rspec-resembles_json_matchers 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/rspec-json_api_matchers.svg)](https://badge.fury.io/rb/rspec-json_api_matchers)[![Build Status](https://travis-ci.org/paul/rspec-json_api_matchers.svg?branch=master)](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.
|