json_spec 0.1.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.
- data/.gitignore +5 -0
- data/Gemfile +3 -0
- data/LICENSE.md +20 -0
- data/README.md +224 -0
- data/Rakefile +9 -0
- data/features/equivalence.feature +233 -0
- data/features/paths.feature +43 -0
- data/features/sizes.feature +38 -0
- data/features/step_definitions/steps.rb +7 -0
- data/features/support/env.rb +6 -0
- data/features/types.feature +18 -0
- data/json_spec.gemspec +25 -0
- data/lib/json_spec.rb +9 -0
- data/lib/json_spec/configuration.rb +27 -0
- data/lib/json_spec/cucumber.rb +24 -0
- data/lib/json_spec/errors.rb +13 -0
- data/lib/json_spec/helpers.rb +41 -0
- data/lib/json_spec/matchers.rb +113 -0
- data/lib/json_spec/version.rb +3 -0
- data/spec/json_spec/configuration_spec.rb +52 -0
- data/spec/json_spec/matchers_spec.rb +108 -0
- data/spec/json_spec_spec.rb +5 -0
- data/spec/spec_helper.rb +7 -0
- metadata +113 -0
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright © 2011 Steve Richert
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
JsonSpec
|
2
|
+
========
|
3
|
+
Easily handle JSON in RSpec and Cucumber
|
4
|
+
|
5
|
+
Installation
|
6
|
+
------------
|
7
|
+
gem install json_spec
|
8
|
+
|
9
|
+
or with Bundler:
|
10
|
+
|
11
|
+
gem "json_spec"
|
12
|
+
|
13
|
+
Documentation
|
14
|
+
-------------
|
15
|
+
Please help write documentation!
|
16
|
+
|
17
|
+
[http://rdoc.info/github/collectiveidea/json_spec](http://rdoc.info/github/collectiveidea/json_spec)
|
18
|
+
|
19
|
+
Continuous Integration
|
20
|
+
----------------------
|
21
|
+
Coming soon…
|
22
|
+
|
23
|
+
RSpec
|
24
|
+
--------------
|
25
|
+
JsonSpec defines four new RSpec matchers:
|
26
|
+
|
27
|
+
* `be_json_eql`
|
28
|
+
* `have_json_path`
|
29
|
+
* `have_json_type`
|
30
|
+
* `have_json_size`
|
31
|
+
|
32
|
+
The new matchers could be used in RSpec as follows:
|
33
|
+
|
34
|
+
describe User do
|
35
|
+
let(:user){ User.create!(:first_name => "Steve", :last_name => "Richert") }
|
36
|
+
|
37
|
+
context "#to_json" do
|
38
|
+
let(:json){ user.to_json }
|
39
|
+
|
40
|
+
it "includes the name" do
|
41
|
+
json.should be_json_eql(%({"first_name":"Steve","last_name":"Richert"})).excluding("friends")
|
42
|
+
end
|
43
|
+
|
44
|
+
it "includes the ID" do
|
45
|
+
json.should have_json_path("id")
|
46
|
+
json.should have_json_type(Integer).at_path("id")
|
47
|
+
end
|
48
|
+
|
49
|
+
it "includes friends" do
|
50
|
+
json.should have_json_size(0).at_path("friends")
|
51
|
+
user.friends << User.create!(:first_name => "Catie", :last_name => "Richert")
|
52
|
+
json.should have_json_size(1).at_path("friends")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
### Exclusions
|
58
|
+
|
59
|
+
JsonSpec ignores certain hash keys by default when comparing JSON:
|
60
|
+
|
61
|
+
* `id`
|
62
|
+
* `created_at`
|
63
|
+
* `updated_at`
|
64
|
+
|
65
|
+
It's oftentimes helpful when evaluating JSON representations of newly-created ActiveRecord records
|
66
|
+
so that the new ID and timestamps don't have to be known. These exclusions are globally
|
67
|
+
customizeable:
|
68
|
+
|
69
|
+
JsonSpec.configure do
|
70
|
+
exclude_keys "created_at", "updated_at"
|
71
|
+
end
|
72
|
+
|
73
|
+
Now, the `id` key will be included in JsonSpec's comparisons. Keys can also be excluded/included
|
74
|
+
per matcher by chaining the `excluding` or `including` methods (as shown above) which will add or subtract from
|
75
|
+
the globally excluded keys, respectively.
|
76
|
+
|
77
|
+
### Paths
|
78
|
+
|
79
|
+
Each of JsonSpec's matchers deal with JSON "paths." These are simple strings of "/" separated
|
80
|
+
hash keys and array indexes. For instance, with the following JSON:
|
81
|
+
|
82
|
+
{
|
83
|
+
"first_name": "Steve",
|
84
|
+
"last_name": "Richert",
|
85
|
+
"friends": [
|
86
|
+
{
|
87
|
+
"first_name": "Catie",
|
88
|
+
"last_name": "Richert"
|
89
|
+
}
|
90
|
+
]
|
91
|
+
}
|
92
|
+
|
93
|
+
We could access the first friend's first name with the path `"friends/0/first_name"`.
|
94
|
+
|
95
|
+
Cucumber
|
96
|
+
--------
|
97
|
+
JsonSpec provides Cucumber steps that utilize its RSpec matchers and that's where JsonSpec really
|
98
|
+
shines. This is perfect for testing your app's JSON API.
|
99
|
+
|
100
|
+
In order to use the Cucumber steps, in your `env.rb` you must:
|
101
|
+
|
102
|
+
require "json_spec/cucumber"
|
103
|
+
|
104
|
+
You also need to define a `last_json` method. If you're using Capybara, it could be as simple as:
|
105
|
+
|
106
|
+
def last_json
|
107
|
+
page.source
|
108
|
+
end
|
109
|
+
|
110
|
+
Now, you can use the JsonSpec steps in your features:
|
111
|
+
|
112
|
+
Feature: User API
|
113
|
+
Background:
|
114
|
+
Given the following users exist:
|
115
|
+
| id | first_name | last_name |
|
116
|
+
| 1 | Steve | Richert |
|
117
|
+
| 2 | Catie | Richert |
|
118
|
+
And "Steve Richert" is friends with "Catie Richert"
|
119
|
+
|
120
|
+
Scenario: Index action
|
121
|
+
When I visit "/users.json"
|
122
|
+
Then the JSON response should have 2 users
|
123
|
+
And the JSON response at "0/id" should be 1
|
124
|
+
And the JSON response at "1/id" should be 2
|
125
|
+
|
126
|
+
Scenario: Show action
|
127
|
+
When I visit "/users/1.json"
|
128
|
+
Then the JSON response at "first_name" should be "Steve"
|
129
|
+
And the JSON response at "last_name" should be "Richert"
|
130
|
+
And the JSON response should have "created_at"
|
131
|
+
And the JSON response at "created_at" should be a string
|
132
|
+
And the JSON response at "friends" should be:
|
133
|
+
"""
|
134
|
+
[
|
135
|
+
{
|
136
|
+
"id": 2,
|
137
|
+
"first_name": "Catie",
|
138
|
+
"last_name": "Richert"
|
139
|
+
}
|
140
|
+
]
|
141
|
+
"""
|
142
|
+
|
143
|
+
The background steps above aren't provided by JsonSpec and the "visit" steps are provided by
|
144
|
+
Capybara. The remaining steps all stem from the five steps that JsonSpec provides. They're
|
145
|
+
versatile and can be used in plenty of different formats:
|
146
|
+
|
147
|
+
Then the JSON should be:
|
148
|
+
"""
|
149
|
+
{
|
150
|
+
"key": "value"
|
151
|
+
}
|
152
|
+
"""
|
153
|
+
Then the JSON at "path" should be:
|
154
|
+
"""
|
155
|
+
[
|
156
|
+
"entry",
|
157
|
+
"entry"
|
158
|
+
]
|
159
|
+
"""
|
160
|
+
|
161
|
+
Then the JSON should be {"key":"value"}
|
162
|
+
Then the JSON at "path" should be {"key":"value"}
|
163
|
+
Then the JSON should be ["entry","entry"]
|
164
|
+
Then the JSON at "path" should be ["entry","entry"]
|
165
|
+
Then the JSON at "path" should be "string"
|
166
|
+
Then the JSON at "path" should be 10
|
167
|
+
Then the JSON at "path" should be 10.0
|
168
|
+
Then the JSON at "path" should be 1e+1
|
169
|
+
Then the JSON at "path" should be true
|
170
|
+
Then the JSON at "path" should be false
|
171
|
+
Then the JSON at "path" should be null
|
172
|
+
|
173
|
+
Then the JSON should have "path"
|
174
|
+
|
175
|
+
Then the JSON should be a hash
|
176
|
+
Then the JSON at "path" should be an array
|
177
|
+
Then the JSON at "path" should be a float
|
178
|
+
|
179
|
+
Then the JSON should have 1 entry
|
180
|
+
Then the JSON at "path" should have 2 entries
|
181
|
+
Then the JSON should have 3 keys
|
182
|
+
Then the JSON should have 4 whatevers
|
183
|
+
|
184
|
+
_All instances of "JSON" above could also be downcased and/or followed by "response."_
|
185
|
+
|
186
|
+
Contributing
|
187
|
+
------------
|
188
|
+
In the spirit of [free software](http://www.fsf.org/licensing/essays/free-sw.html), **everyone** is encouraged to help improve this project.
|
189
|
+
|
190
|
+
Here are some ways *you* can contribute:
|
191
|
+
|
192
|
+
* using alpha, beta, and prerelease versions
|
193
|
+
* reporting bugs
|
194
|
+
* suggesting new features
|
195
|
+
* writing or editing documentation
|
196
|
+
* writing specifications
|
197
|
+
* writing code (**no patch is too small**: fix typos, add comments, clean up inconsistent whitespace)
|
198
|
+
* refactoring code
|
199
|
+
* closing [issues](https://github.com/collectiveidea/json_spec/issues)
|
200
|
+
* reviewing patches
|
201
|
+
|
202
|
+
Submitting an Issue
|
203
|
+
-------------------
|
204
|
+
We use the [GitHub issue tracker](https://github.com/collectiveidea/json_spec/issues) to track bugs
|
205
|
+
and features. Before submitting a bug report or feature request, check to make sure it hasn't already
|
206
|
+
been submitted. You can indicate support for an existing issuse by voting it up. When submitting a
|
207
|
+
bug report, please include a [Gist](https://gist.github.com/) that includes a stack trace and any
|
208
|
+
details that may be necessary to reproduce the bug, including your gem version, Ruby version, and
|
209
|
+
operating system. Ideally, a bug report should include a pull request with failing specs.
|
210
|
+
|
211
|
+
Submitting a Pull Request
|
212
|
+
-------------------------
|
213
|
+
1. Fork the project.
|
214
|
+
2. Create a topic branch.
|
215
|
+
3. Implement your feature or bug fix.
|
216
|
+
4. Add specs for your feature or bug fix.
|
217
|
+
5. Run <tt>bundle exec rake</tt>. If your changes are not 100% covered and passing, go back to step 4.
|
218
|
+
6. Commit and push your changes.
|
219
|
+
7. Submit a pull request. Please do not include changes to the gemspec, version, or history file. (If you want to create your own version for some reason, please do so in a separate commit.)
|
220
|
+
|
221
|
+
Copyright
|
222
|
+
---------
|
223
|
+
Copyright © 2011 Steve Richert
|
224
|
+
See [LICENSE](https://github.com/collectiveidea/json_spec/blob/master/LICENSE.md) for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
Feature: Equivalence
|
2
|
+
Background:
|
3
|
+
Given the JSON is:
|
4
|
+
"""
|
5
|
+
{
|
6
|
+
"array": [
|
7
|
+
"json",
|
8
|
+
"spec"
|
9
|
+
],
|
10
|
+
"created_at": "2011-07-08 02:27:34",
|
11
|
+
"empty_array": [
|
12
|
+
|
13
|
+
],
|
14
|
+
"empty_hash": {
|
15
|
+
},
|
16
|
+
"false": false,
|
17
|
+
"float": 10.0,
|
18
|
+
"hash": {
|
19
|
+
"json": "spec"
|
20
|
+
},
|
21
|
+
"id": 1,
|
22
|
+
"integer": 10,
|
23
|
+
"negative": -10,
|
24
|
+
"null": null,
|
25
|
+
"string": "json_spec",
|
26
|
+
"true": true,
|
27
|
+
"updated_at": "2011-07-08 02:28:50"
|
28
|
+
}
|
29
|
+
"""
|
30
|
+
|
31
|
+
Scenario: Identical JSON
|
32
|
+
When I get the JSON
|
33
|
+
Then the JSON should be:
|
34
|
+
"""
|
35
|
+
{
|
36
|
+
"array": [
|
37
|
+
"json",
|
38
|
+
"spec"
|
39
|
+
],
|
40
|
+
"created_at": "2011-07-08 02:27:34",
|
41
|
+
"empty_array": [
|
42
|
+
|
43
|
+
],
|
44
|
+
"empty_hash": {
|
45
|
+
},
|
46
|
+
"false": false,
|
47
|
+
"float": 10.0,
|
48
|
+
"hash": {
|
49
|
+
"json": "spec"
|
50
|
+
},
|
51
|
+
"id": 1,
|
52
|
+
"integer": 10,
|
53
|
+
"negative": -10,
|
54
|
+
"null": null,
|
55
|
+
"string": "json_spec",
|
56
|
+
"true": true,
|
57
|
+
"updated_at": "2011-07-08 02:28:50"
|
58
|
+
}
|
59
|
+
"""
|
60
|
+
|
61
|
+
Scenario: Reverse order
|
62
|
+
When I get the JSON
|
63
|
+
Then the JSON should be:
|
64
|
+
"""
|
65
|
+
{
|
66
|
+
"updated_at": "2011-07-08 02:28:50",
|
67
|
+
"true": true,
|
68
|
+
"string": "json_spec",
|
69
|
+
"null": null,
|
70
|
+
"negative": -10,
|
71
|
+
"integer": 10,
|
72
|
+
"id": 1,
|
73
|
+
"hash": {
|
74
|
+
"json": "spec"
|
75
|
+
},
|
76
|
+
"float": 10.0,
|
77
|
+
"false": false,
|
78
|
+
"empty_hash": {
|
79
|
+
},
|
80
|
+
"empty_array": [
|
81
|
+
|
82
|
+
],
|
83
|
+
"created_at": "2011-07-08 02:27:34",
|
84
|
+
"array": [
|
85
|
+
"json",
|
86
|
+
"spec"
|
87
|
+
]
|
88
|
+
}
|
89
|
+
"""
|
90
|
+
|
91
|
+
Scenario: Excluding keys
|
92
|
+
When I get the JSON
|
93
|
+
Then the JSON should be:
|
94
|
+
"""
|
95
|
+
{
|
96
|
+
"array": [
|
97
|
+
"json",
|
98
|
+
"spec"
|
99
|
+
],
|
100
|
+
"empty_array": [
|
101
|
+
|
102
|
+
],
|
103
|
+
"empty_hash": {
|
104
|
+
},
|
105
|
+
"false": false,
|
106
|
+
"float": 10.0,
|
107
|
+
"hash": {
|
108
|
+
"json": "spec"
|
109
|
+
},
|
110
|
+
"integer": 10,
|
111
|
+
"negative": -10,
|
112
|
+
"null": null,
|
113
|
+
"string": "json_spec",
|
114
|
+
"true": true
|
115
|
+
}
|
116
|
+
"""
|
117
|
+
|
118
|
+
Scenario: String
|
119
|
+
When I get the JSON
|
120
|
+
Then the JSON at "string" should be "json_spec"
|
121
|
+
And the JSON at "string" should be:
|
122
|
+
"""
|
123
|
+
"json_spec"
|
124
|
+
"""
|
125
|
+
|
126
|
+
Scenario: Integer
|
127
|
+
When I get the JSON
|
128
|
+
Then the JSON at "integer" should be 10
|
129
|
+
And the JSON at "integer" should be:
|
130
|
+
"""
|
131
|
+
10
|
132
|
+
"""
|
133
|
+
|
134
|
+
Scenario: Negative integer
|
135
|
+
When I get the JSON
|
136
|
+
Then the JSON at "negative" should be -10
|
137
|
+
And the JSON at "negative" should be:
|
138
|
+
"""
|
139
|
+
-10
|
140
|
+
"""
|
141
|
+
|
142
|
+
Scenario: Float
|
143
|
+
When I get the JSON
|
144
|
+
Then the JSON at "float" should be 10.0
|
145
|
+
And the JSON at "float" should be 10.0e0
|
146
|
+
And the JSON at "float" should be 10.0e+0
|
147
|
+
And the JSON at "float" should be 10.0e-0
|
148
|
+
And the JSON at "float" should be 10e0
|
149
|
+
And the JSON at "float" should be 10e+0
|
150
|
+
And the JSON at "float" should be 10e-0
|
151
|
+
And the JSON at "float" should be 1.0e1
|
152
|
+
And the JSON at "float" should be 1.0e+1
|
153
|
+
And the JSON at "float" should be 1e1
|
154
|
+
And the JSON at "float" should be 1e+1
|
155
|
+
And the JSON at "float" should be 100.0e-1
|
156
|
+
And the JSON at "float" should be 100e-1
|
157
|
+
And the JSON at "float" should be:
|
158
|
+
"""
|
159
|
+
10.0
|
160
|
+
"""
|
161
|
+
|
162
|
+
Scenario: Array
|
163
|
+
When I get the JSON
|
164
|
+
Then the JSON at "array" should be ["json","spec"]
|
165
|
+
And the JSON at "array/0" should be "json"
|
166
|
+
And the JSON at "array/1" should be "spec"
|
167
|
+
And the JSON at "array" should be:
|
168
|
+
"""
|
169
|
+
[
|
170
|
+
"json",
|
171
|
+
"spec"
|
172
|
+
]
|
173
|
+
"""
|
174
|
+
|
175
|
+
Scenario: Empty array
|
176
|
+
When I get the JSON
|
177
|
+
Then the JSON at "empty_array" should be []
|
178
|
+
And the JSON at "empty_array" should be:
|
179
|
+
"""
|
180
|
+
[
|
181
|
+
|
182
|
+
]
|
183
|
+
"""
|
184
|
+
|
185
|
+
Scenario: Hash
|
186
|
+
When I get the JSON
|
187
|
+
Then the JSON at "hash" should be {"json":"spec"}
|
188
|
+
And the JSON at "hash/json" should be "spec"
|
189
|
+
And the JSON at "hash" should be:
|
190
|
+
"""
|
191
|
+
{
|
192
|
+
"json": "spec"
|
193
|
+
}
|
194
|
+
"""
|
195
|
+
|
196
|
+
Scenario: Empty hash
|
197
|
+
When I get the JSON
|
198
|
+
Then the JSON at "empty_hash" should be {}
|
199
|
+
And the JSON at "empty_hash" should be:
|
200
|
+
"""
|
201
|
+
{
|
202
|
+
}
|
203
|
+
"""
|
204
|
+
|
205
|
+
Scenario: True
|
206
|
+
When I get the JSON
|
207
|
+
Then the JSON at "true" should be true
|
208
|
+
And the JSON at "true" should be:
|
209
|
+
"""
|
210
|
+
true
|
211
|
+
"""
|
212
|
+
|
213
|
+
Scenario: False
|
214
|
+
When I get the JSON
|
215
|
+
Then the JSON at "false" should be false
|
216
|
+
And the JSON at "false" should be:
|
217
|
+
"""
|
218
|
+
false
|
219
|
+
"""
|
220
|
+
|
221
|
+
Scenario: Null
|
222
|
+
When I get the JSON
|
223
|
+
Then the JSON at "null" should be null
|
224
|
+
And the JSON at "null" should be:
|
225
|
+
"""
|
226
|
+
null
|
227
|
+
"""
|
228
|
+
|
229
|
+
Scenario: Excluded value
|
230
|
+
When I get the JSON
|
231
|
+
Then the JSON at "created_at" should be "2011-07-08 02:27:34"
|
232
|
+
And the JSON at "id" should be 1
|
233
|
+
And the JSON at "updated_at" should be "2011-07-08 02:28:50"
|
@@ -0,0 +1,43 @@
|
|
1
|
+
Feature: Paths
|
2
|
+
Background:
|
3
|
+
Given the JSON is:
|
4
|
+
"""
|
5
|
+
{
|
6
|
+
"array": [
|
7
|
+
{
|
8
|
+
"one": 1,
|
9
|
+
"two": 2
|
10
|
+
},
|
11
|
+
{
|
12
|
+
"four": 4,
|
13
|
+
"three": 3
|
14
|
+
}
|
15
|
+
],
|
16
|
+
"hash": {
|
17
|
+
"even": [
|
18
|
+
6,
|
19
|
+
8
|
20
|
+
],
|
21
|
+
"odd": [
|
22
|
+
5,
|
23
|
+
7
|
24
|
+
]
|
25
|
+
},
|
26
|
+
"id": null
|
27
|
+
}
|
28
|
+
"""
|
29
|
+
|
30
|
+
Scenario: Nested paths
|
31
|
+
When I get the JSON
|
32
|
+
Then the JSON should have "array/0/one"
|
33
|
+
And the JSON should have "array/0/two"
|
34
|
+
And the JSON should have "array/1/four"
|
35
|
+
And the JSON should have "array/1/three"
|
36
|
+
And the JSON should have "hash/even/0"
|
37
|
+
And the JSON should have "hash/even/1"
|
38
|
+
And the JSON should have "hash/odd/0"
|
39
|
+
And the JSON should have "hash/odd/1"
|
40
|
+
|
41
|
+
Scenario: Ignored paths
|
42
|
+
When I get the JSON
|
43
|
+
Then the JSON should have "id"
|
@@ -0,0 +1,38 @@
|
|
1
|
+
Feature: Sizes
|
2
|
+
Background:
|
3
|
+
Given the JSON is:
|
4
|
+
"""
|
5
|
+
{
|
6
|
+
"id": null,
|
7
|
+
"one": [
|
8
|
+
1
|
9
|
+
],
|
10
|
+
"three": [
|
11
|
+
1,
|
12
|
+
2,
|
13
|
+
3
|
14
|
+
],
|
15
|
+
"two": [
|
16
|
+
1,
|
17
|
+
2
|
18
|
+
],
|
19
|
+
"zero": [
|
20
|
+
|
21
|
+
]
|
22
|
+
}
|
23
|
+
"""
|
24
|
+
|
25
|
+
Scenario: Hash
|
26
|
+
When I get the JSON
|
27
|
+
Then the JSON should have 5 keys
|
28
|
+
And the JSON should have 5 values
|
29
|
+
|
30
|
+
Scenario: Empty array
|
31
|
+
When I get the JSON
|
32
|
+
Then the JSON at "zero" should have 0 entries
|
33
|
+
|
34
|
+
Scenario: Array
|
35
|
+
When I get the JSON
|
36
|
+
Then the JSON at "one" should have 1 entry
|
37
|
+
And the JSON at "two" should have 2 values
|
38
|
+
And the JSON at "three" should have 3 numbers
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Feature: Types
|
2
|
+
Scenario: All types
|
3
|
+
Given the JSON is:
|
4
|
+
"""
|
5
|
+
{
|
6
|
+
"array": [],
|
7
|
+
"float": 10.0,
|
8
|
+
"hash": {},
|
9
|
+
"integer": 10,
|
10
|
+
"string": "json_spec"
|
11
|
+
}
|
12
|
+
"""
|
13
|
+
When I get the JSON
|
14
|
+
Then the JSON should be a hash
|
15
|
+
And the JSON at "array" should be an array
|
16
|
+
And the JSON at "float" should be a float
|
17
|
+
And the JSON at "hash" should be a hash
|
18
|
+
And the JSON at "integer" should be an integer
|
data/json_spec.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "json_spec/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "json_spec"
|
7
|
+
s.version = JsonSpec::VERSION
|
8
|
+
s.authors = ["Steve Richert"]
|
9
|
+
s.email = ["steve.richert@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/collectiveidea/json_spec"
|
11
|
+
s.summary = "Easily handle JSON in RSpec and Cucumber"
|
12
|
+
s.description = "Easily handle JSON in RSpec and Cucumber"
|
13
|
+
|
14
|
+
s.rubyforge_project = "json_spec"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_dependency "json", "~> 1.0"
|
22
|
+
s.add_dependency "rspec", "~> 2.0"
|
23
|
+
|
24
|
+
s.add_development_dependency "cucumber", "~> 1.0"
|
25
|
+
end
|
data/lib/json_spec.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
module JsonSpec
|
4
|
+
module Configuration
|
5
|
+
DEFAULT_EXCLUDED_KEYS = %w(id created_at updated_at)
|
6
|
+
|
7
|
+
def configure(&block)
|
8
|
+
instance_eval(&block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def excluded_keys
|
12
|
+
@excluded_keys ||= DEFAULT_EXCLUDED_KEYS
|
13
|
+
end
|
14
|
+
|
15
|
+
def excluded_keys=(keys)
|
16
|
+
@excluded_keys = keys.map{|k| k.to_s }.uniq
|
17
|
+
end
|
18
|
+
|
19
|
+
def exclude_keys(*keys)
|
20
|
+
self.excluded_keys = keys
|
21
|
+
end
|
22
|
+
|
23
|
+
def reset
|
24
|
+
instance_variables.each{|iv| remove_instance_variable(iv) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.expand_path("../../json_spec", __FILE__)
|
2
|
+
|
3
|
+
World(JsonSpec::Helpers)
|
4
|
+
|
5
|
+
Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should be:$/ do |path, json|
|
6
|
+
last_json.should be_json_eql(json).at_path(path)
|
7
|
+
end
|
8
|
+
|
9
|
+
Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should be (".*"|\-?\d+(?:\.\d+)?(?:[eE][\+\-]?\d+)?|\[.*\]|\{.*\}|true|false|null)$/ do |path, value|
|
10
|
+
last_json.should be_json_eql(value).at_path(path)
|
11
|
+
end
|
12
|
+
|
13
|
+
Then /^the (?:JSON|json)(?: response)? should have "(.*)"$/ do |path|
|
14
|
+
last_json.should have_json_path(path)
|
15
|
+
end
|
16
|
+
|
17
|
+
Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should be an? (.*)$/ do |path, type|
|
18
|
+
klass = Module.const_get(type.gsub(/^./){|x| x.upcase })
|
19
|
+
last_json.should have_json_type(klass).at_path(path)
|
20
|
+
end
|
21
|
+
|
22
|
+
Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should have (\d+)/ do |path, size|
|
23
|
+
last_json.should have_json_size(size.to_i).at_path(path)
|
24
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module JsonSpec
|
2
|
+
module Helpers
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def json_at_path(json, path)
|
6
|
+
pretty_json_value(ruby_at_json_path(json, path))
|
7
|
+
end
|
8
|
+
|
9
|
+
def pretty_json_value(ruby)
|
10
|
+
case ruby
|
11
|
+
when Hash, Array then JSON.pretty_generate(ruby)
|
12
|
+
when NilClass then "null"
|
13
|
+
else ruby.inspect
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def ruby_at_json_path(json, path)
|
18
|
+
json_path_to_keys(path).inject(parse_json_value(json)) do |value, key|
|
19
|
+
case value
|
20
|
+
when Hash, Array then value.fetch(key){ missing_json_path!(path) }
|
21
|
+
else missing_json_path!(path)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def json_path_to_keys(path)
|
27
|
+
path.to_s.gsub(/(?:^\/|\/$)/, "").split("/").map{|k| k =~ /^\d+$/ ? k.to_i : k }
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse_json_value(json)
|
31
|
+
JSON.parse(%({"root":#{json}}))["root"]
|
32
|
+
rescue JSON::ParserError
|
33
|
+
# Re-raise more appropriate parsing error
|
34
|
+
JSON.parse(json)
|
35
|
+
end
|
36
|
+
|
37
|
+
def missing_json_path!(path)
|
38
|
+
raise JsonSpec::MissingPathError.new(path)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require "json"
|
2
|
+
require "rspec"
|
3
|
+
|
4
|
+
RSpec::Matchers.define :be_json_eql do |expected_json|
|
5
|
+
include JsonSpec::Helpers
|
6
|
+
|
7
|
+
diffable
|
8
|
+
|
9
|
+
match do |actual_json|
|
10
|
+
@actual, @expected = scrub(actual_json, @path), [scrub(expected_json)]
|
11
|
+
@actual == @expected.first
|
12
|
+
end
|
13
|
+
|
14
|
+
chain :at_path do |path|
|
15
|
+
@path = path
|
16
|
+
end
|
17
|
+
|
18
|
+
chain :excluding do |*keys|
|
19
|
+
excluded_keys.add(*keys.map{|k| k.to_s })
|
20
|
+
end
|
21
|
+
|
22
|
+
chain :including do |*keys|
|
23
|
+
excluded_keys.subtract(keys.map{|k| k.to_s })
|
24
|
+
end
|
25
|
+
|
26
|
+
failure_message_for_should do
|
27
|
+
message = "Expected equivalent JSON"
|
28
|
+
message << %( at path "#{@path}") if @path
|
29
|
+
message
|
30
|
+
end
|
31
|
+
|
32
|
+
def scrub(json, path = nil)
|
33
|
+
ruby = path ? ruby_at_json_path(json, path) : parse_json_value(json)
|
34
|
+
pretty_json_value(exclude_keys(ruby)).chomp + "\n"
|
35
|
+
end
|
36
|
+
|
37
|
+
def exclude_keys(ruby)
|
38
|
+
case ruby
|
39
|
+
when Hash
|
40
|
+
ruby.sort.inject({}) do |hash, (key, value)|
|
41
|
+
hash[key] = exclude_keys(value) unless exclude_key?(key)
|
42
|
+
hash
|
43
|
+
end
|
44
|
+
when Array
|
45
|
+
ruby.map{|v| exclude_keys(v) }
|
46
|
+
else ruby
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def exclude_key?(key)
|
51
|
+
excluded_keys.include?(key)
|
52
|
+
end
|
53
|
+
|
54
|
+
def excluded_keys
|
55
|
+
@excluded_keys ||= Set.new(JsonSpec.excluded_keys)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
RSpec::Matchers.define :have_json_path do |path|
|
60
|
+
include JsonSpec::Helpers
|
61
|
+
|
62
|
+
match do |json|
|
63
|
+
begin
|
64
|
+
ruby_at_json_path(json, path)
|
65
|
+
true
|
66
|
+
rescue JsonSpec::MissingPathError
|
67
|
+
false
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
failure_message_for_should do
|
72
|
+
%(Expected JSON path "#{path}")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
RSpec::Matchers.define :have_json_type do |klass|
|
77
|
+
include JsonSpec::Helpers
|
78
|
+
|
79
|
+
match do |json|
|
80
|
+
ruby = @path ? ruby_at_json_path(json, @path) : parse_json_value(json)
|
81
|
+
ruby.is_a?(klass)
|
82
|
+
end
|
83
|
+
|
84
|
+
chain :at_path do |path|
|
85
|
+
@path = path
|
86
|
+
end
|
87
|
+
|
88
|
+
failure_message_for_should do
|
89
|
+
message = "Expected JSON value type of #{klass}"
|
90
|
+
message << %( at path "#{@path}") if @path
|
91
|
+
message
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
RSpec::Matchers.define :have_json_size do |expected_size|
|
96
|
+
include JsonSpec::Helpers
|
97
|
+
|
98
|
+
match do |json|
|
99
|
+
ruby = @path ? ruby_at_json_path(json, @path) : parse_json_value(json)
|
100
|
+
actual_size = ruby.is_a?(Enumerable) ? ruby.size : 1
|
101
|
+
actual_size == expected_size
|
102
|
+
end
|
103
|
+
|
104
|
+
chain :at_path do |path|
|
105
|
+
@path = path
|
106
|
+
end
|
107
|
+
|
108
|
+
failure_message_for_should do
|
109
|
+
message = "Expected JSON value size of #{expected_size}"
|
110
|
+
message << %( at path "#{@path}") if @path
|
111
|
+
message
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe JsonSpec::Configuration do
|
4
|
+
it "excludes id and timestamps by default" do
|
5
|
+
JsonSpec.excluded_keys.should == ["id", "created_at", "updated_at"]
|
6
|
+
end
|
7
|
+
|
8
|
+
it "excludes custom keys" do
|
9
|
+
JsonSpec.exclude_keys("token")
|
10
|
+
JsonSpec.excluded_keys.should == ["token"]
|
11
|
+
end
|
12
|
+
|
13
|
+
it "excludes custom keys via setter" do
|
14
|
+
JsonSpec.excluded_keys = ["token"]
|
15
|
+
JsonSpec.excluded_keys.should == ["token"]
|
16
|
+
end
|
17
|
+
|
18
|
+
it "excludes custom keys via block" do
|
19
|
+
JsonSpec.configure{|c| c.exclude_keys("token") }
|
20
|
+
JsonSpec.excluded_keys.should == ["token"]
|
21
|
+
end
|
22
|
+
|
23
|
+
it "excludes custom keys via block setter" do
|
24
|
+
JsonSpec.configure{|c| c.excluded_keys = ["token"] }
|
25
|
+
JsonSpec.excluded_keys.should == ["token"]
|
26
|
+
end
|
27
|
+
|
28
|
+
it "excludes custom keys via instance-evaluated block" do
|
29
|
+
JsonSpec.configure{ exclude_keys("token") }
|
30
|
+
JsonSpec.excluded_keys.should == ["token"]
|
31
|
+
end
|
32
|
+
|
33
|
+
it "ensures its excluded keys are strings" do
|
34
|
+
JsonSpec.exclude_keys(:token)
|
35
|
+
JsonSpec.excluded_keys.should == ["token"]
|
36
|
+
end
|
37
|
+
|
38
|
+
it "ensures its excluded keys are unique" do
|
39
|
+
JsonSpec.exclude_keys("token", :token)
|
40
|
+
JsonSpec.excluded_keys.should == ["token"]
|
41
|
+
end
|
42
|
+
|
43
|
+
it "resets its excluded keys" do
|
44
|
+
original = JsonSpec.excluded_keys
|
45
|
+
|
46
|
+
JsonSpec.exclude_keys("token")
|
47
|
+
JsonSpec.excluded_keys.should_not == original
|
48
|
+
|
49
|
+
JsonSpec.reset
|
50
|
+
JsonSpec.excluded_keys.should == original
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Matchers:" do
|
4
|
+
context "be_json_eql" do
|
5
|
+
it "matches identical JSON" do
|
6
|
+
%({"json":"spec"}).should be_json_eql(%({"json":"spec"}))
|
7
|
+
end
|
8
|
+
|
9
|
+
it "matches differently-formatted JSON" do
|
10
|
+
%({"json": "spec"}).should be_json_eql(%({"json":"spec"}))
|
11
|
+
end
|
12
|
+
|
13
|
+
it "matches out-of-order hashes" do
|
14
|
+
%({"laser":"lemon","json":"spec"}).should be_json_eql(%({"json":"spec","laser":"lemon"}))
|
15
|
+
end
|
16
|
+
|
17
|
+
it "doesn't match out-of-order arrays" do
|
18
|
+
%(["json","spec"]).should_not be_json_eql(%(["spec","json"]))
|
19
|
+
end
|
20
|
+
|
21
|
+
it "ignores excluded-by-default hash keys" do
|
22
|
+
JsonSpec.excluded_keys.should_not be_empty
|
23
|
+
|
24
|
+
actual = expected = {"json" => "spec"}
|
25
|
+
JsonSpec.excluded_keys.each{|k| actual[k] = k }
|
26
|
+
actual.to_json.should be_json_eql(expected.to_json)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "ignores custom excluded hash keys" do
|
30
|
+
JsonSpec.exclude_keys("ignore")
|
31
|
+
%({"json":"spec","ignore":"please"}).should be_json_eql(%({"json":"spec"}))
|
32
|
+
end
|
33
|
+
|
34
|
+
it "ignores nested, excluded hash keys" do
|
35
|
+
JsonSpec.exclude_keys("ignore")
|
36
|
+
%({"json":"spec","please":{"ignore":"this"}}).should be_json_eql(%({"json":"spec","please":{}}))
|
37
|
+
end
|
38
|
+
|
39
|
+
it "ignores hash keys when included in the expected value" do
|
40
|
+
JsonSpec.exclude_keys("ignore")
|
41
|
+
%({"json":"spec","ignore":"please"}).should be_json_eql(%({"json":"spec","ignore":"this"}))
|
42
|
+
end
|
43
|
+
|
44
|
+
it "doesn't match Ruby-equivalent, JSON-inequivalent values" do
|
45
|
+
%({"one":1}).should_not be_json_eql(%({"one":1.0}))
|
46
|
+
end
|
47
|
+
|
48
|
+
it "excludes extra hash keys per matcher" do
|
49
|
+
JsonSpec.excluded_keys = %w(ignore)
|
50
|
+
%({"id":1,"json":"spec","ignore":"please"}).should be_json_eql(%({"id":"2","json":"spec","ignore":"this"})).excluding("id")
|
51
|
+
end
|
52
|
+
|
53
|
+
it "excludes extra hash keys given as symbols" do
|
54
|
+
JsonSpec.excluded_keys = []
|
55
|
+
%({"id":1,"json":"spec"}).should be_json_eql(%({"id":2,"json":"spec"})).excluding(:id)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "includes globally-excluded hash keys per matcher" do
|
59
|
+
JsonSpec.excluded_keys = %w(id ignore)
|
60
|
+
%({"id":1,"json":"spec","ignore":"please"}).should_not be_json_eql(%({"id":"2","json":"spec","ignore":"this"})).including("id")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "includes globally-included hash keys given as symbols" do
|
64
|
+
JsonSpec.excluded_keys = %w(id)
|
65
|
+
%({"id":1,"json":"spec"}).should_not be_json_eql(%({"id":2,"json":"spec"})).including(:id)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "have_json_size" do
|
70
|
+
it "counts array entries" do
|
71
|
+
%([1,2,3]).should have_json_size(3)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "counts null array entries" do
|
75
|
+
%([1,null,3]).should have_json_size(3)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "counts hash key/value pairs" do
|
79
|
+
%({"one":1,"two":2,"three":3}).should have_json_size(3)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "counts null hash values" do
|
83
|
+
%({"one":1,"two":null,"three":3}).should have_json_size(3)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "have_json_path" do
|
88
|
+
it "matches hash keys" do
|
89
|
+
%({"one":{"two":{"three":4}}}).should have_json_path("one/two/three")
|
90
|
+
end
|
91
|
+
|
92
|
+
it "doesn't match values" do
|
93
|
+
%({"one":{"two":{"three":4}}}).should_not have_json_path("one/two/three/4")
|
94
|
+
end
|
95
|
+
|
96
|
+
it "matches array indexes" do
|
97
|
+
%([1,[1,2,[1,2,3,4]]]).should have_json_path("1/2/3")
|
98
|
+
end
|
99
|
+
|
100
|
+
it "respects null array values" do
|
101
|
+
%([null,[null,null,[null,null,null,null]]]).should have_json_path("1/2/3")
|
102
|
+
end
|
103
|
+
|
104
|
+
it "matches hash keys and array indexes" do
|
105
|
+
%({"one":[1,2,{"three":4}]}).should have_json_path("one/2/three")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: json_spec
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Steve Richert
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-07-08 00:00:00.000000000 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: json
|
17
|
+
requirement: &2153487840 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '1.0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *2153487840
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rspec
|
28
|
+
requirement: &2153487340 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *2153487340
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: cucumber
|
39
|
+
requirement: &2153486880 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '1.0'
|
45
|
+
type: :development
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *2153486880
|
48
|
+
description: Easily handle JSON in RSpec and Cucumber
|
49
|
+
email:
|
50
|
+
- steve.richert@gmail.com
|
51
|
+
executables: []
|
52
|
+
extensions: []
|
53
|
+
extra_rdoc_files: []
|
54
|
+
files:
|
55
|
+
- .gitignore
|
56
|
+
- Gemfile
|
57
|
+
- LICENSE.md
|
58
|
+
- README.md
|
59
|
+
- Rakefile
|
60
|
+
- features/equivalence.feature
|
61
|
+
- features/paths.feature
|
62
|
+
- features/sizes.feature
|
63
|
+
- features/step_definitions/steps.rb
|
64
|
+
- features/support/env.rb
|
65
|
+
- features/types.feature
|
66
|
+
- json_spec.gemspec
|
67
|
+
- lib/json_spec.rb
|
68
|
+
- lib/json_spec/configuration.rb
|
69
|
+
- lib/json_spec/cucumber.rb
|
70
|
+
- lib/json_spec/errors.rb
|
71
|
+
- lib/json_spec/helpers.rb
|
72
|
+
- lib/json_spec/matchers.rb
|
73
|
+
- lib/json_spec/version.rb
|
74
|
+
- spec/json_spec/configuration_spec.rb
|
75
|
+
- spec/json_spec/matchers_spec.rb
|
76
|
+
- spec/json_spec_spec.rb
|
77
|
+
- spec/spec_helper.rb
|
78
|
+
has_rdoc: true
|
79
|
+
homepage: https://github.com/collectiveidea/json_spec
|
80
|
+
licenses: []
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ! '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubyforge_project: json_spec
|
99
|
+
rubygems_version: 1.6.2
|
100
|
+
signing_key:
|
101
|
+
specification_version: 3
|
102
|
+
summary: Easily handle JSON in RSpec and Cucumber
|
103
|
+
test_files:
|
104
|
+
- features/equivalence.feature
|
105
|
+
- features/paths.feature
|
106
|
+
- features/sizes.feature
|
107
|
+
- features/step_definitions/steps.rb
|
108
|
+
- features/support/env.rb
|
109
|
+
- features/types.feature
|
110
|
+
- spec/json_spec/configuration_spec.rb
|
111
|
+
- spec/json_spec/matchers_spec.rb
|
112
|
+
- spec/json_spec_spec.rb
|
113
|
+
- spec/spec_helper.rb
|