rspec-rails-api 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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.gitlab-ci.yml +36 -0
- data/.rspec +3 -0
- data/.rubocop.yml +29 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +457 -0
- data/Rakefile +8 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/examples/commented.rb +167 -0
- data/lib/rspec/rails/api/dsl/example.rb +97 -0
- data/lib/rspec/rails/api/dsl/example_group.rb +90 -0
- data/lib/rspec/rails/api/entity_config.rb +44 -0
- data/lib/rspec/rails/api/field_config.rb +52 -0
- data/lib/rspec/rails/api/matchers.rb +40 -0
- data/lib/rspec/rails/api/metadata.rb +178 -0
- data/lib/rspec/rails/api/open_api_renderer.rb +215 -0
- data/lib/rspec/rails/api/utils.rb +88 -0
- data/lib/rspec/rails/api/version.rb +9 -0
- data/lib/rspec_rails_api.rb +37 -0
- data/rspec-rails-api.gemspec +37 -0
- metadata +168 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0d84f33b2eb3f005afce4a2712e2fd3523687f5ba098573d93e386439aa97639
|
4
|
+
data.tar.gz: e013cc905ed66797cd78c6d6c833c87ba30e939a0c12b950cb1209639789a3fd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 32a8012f525f7319555e5aada0b1019e37939461034522193e939a2a028a3fb5897a133e0fb5fdd93a4ef8b77d3a62d2092884bae9c6e3194e50da8b5bd3572d
|
7
|
+
data.tar.gz: 5703b53a8d61d35c4e8124e615056da5c909bbe47e96fe9aeeb5362479a80db8d5c6999094ebb06d8502d1937f3520908d6554a66e3360ffb43d747c962d1021
|
data/.gitignore
ADDED
data/.gitlab-ci.yml
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
---
|
2
|
+
image: ruby:2.4
|
3
|
+
|
4
|
+
stages:
|
5
|
+
- prepare
|
6
|
+
- test
|
7
|
+
|
8
|
+
bundle:
|
9
|
+
stage: prepare
|
10
|
+
script:
|
11
|
+
- bundle install --path='vendor/bundle'
|
12
|
+
artifacts:
|
13
|
+
untracked: true
|
14
|
+
expire_in: 1 hour
|
15
|
+
paths:
|
16
|
+
- 'vendor/'
|
17
|
+
cache:
|
18
|
+
untracked: true
|
19
|
+
paths:
|
20
|
+
- 'vendor/'
|
21
|
+
|
22
|
+
rubocop:
|
23
|
+
stage: test
|
24
|
+
script:
|
25
|
+
- bundle install --path='vendor/bundle'
|
26
|
+
- bundle exec rubocop
|
27
|
+
dependencies:
|
28
|
+
- bundle
|
29
|
+
|
30
|
+
rspec:
|
31
|
+
stage: test
|
32
|
+
script:
|
33
|
+
- bundle install --path='vendor/bundle'
|
34
|
+
- bundle exec rspec
|
35
|
+
dependencies:
|
36
|
+
- bundle
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
---
|
2
|
+
require:
|
3
|
+
- rubocop-performance
|
4
|
+
|
5
|
+
Metrics/BlockLength:
|
6
|
+
Exclude:
|
7
|
+
- examples/**/*.rb
|
8
|
+
- rspec-rails-api.gemspec
|
9
|
+
- spec/**/*_spec.rb
|
10
|
+
|
11
|
+
Metrics/LineLength:
|
12
|
+
Max: 120
|
13
|
+
Exclude:
|
14
|
+
- spec/**/*_spec.rb
|
15
|
+
|
16
|
+
Naming/UncommunicativeMethodParamName:
|
17
|
+
AllowedNames:
|
18
|
+
- of
|
19
|
+
|
20
|
+
Layout/AlignHash:
|
21
|
+
EnforcedColonStyle: table
|
22
|
+
EnforcedHashRocketStyle: table
|
23
|
+
|
24
|
+
Style/TrailingCommaInArrayLiteral:
|
25
|
+
EnforcedStyleForMultiline: comma
|
26
|
+
|
27
|
+
Style/TrailingCommaInHashLiteral:
|
28
|
+
EnforcedStyleForMultiline: comma
|
29
|
+
|
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at m.tancoigne@gmail.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 Manuel Tancoigne
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,457 @@
|
|
1
|
+
# RspecRailsApiDoc
|
2
|
+
|
3
|
+
> An RSpec plugin to test Rails api responses and generate swagger
|
4
|
+
> documentation
|
5
|
+
|
6
|
+
**This is a work in progress** but you're welcome to help, test, submit
|
7
|
+
issues, ...
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
As the gem is not yet published, you have to specify its git repository
|
12
|
+
in order to test it.
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
```rb
|
17
|
+
gem 'rspec-rails-api'
|
18
|
+
```
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
```sh
|
23
|
+
bundle
|
24
|
+
```
|
25
|
+
|
26
|
+
### Rails configuration
|
27
|
+
|
28
|
+
Configuration should be made manually for now:
|
29
|
+
|
30
|
+
**spec/acceptance_helper.rb**
|
31
|
+
|
32
|
+
```rb
|
33
|
+
require 'rails_helper'
|
34
|
+
require 'rspec_rails_api'
|
35
|
+
|
36
|
+
RSpec.configure do |config|
|
37
|
+
config.include Rspec::Rails::Api::DSL::Example
|
38
|
+
end
|
39
|
+
|
40
|
+
renderer = Rspec::Rails::Api::OpenApiRenderer.new
|
41
|
+
|
42
|
+
RSpec.configuration.after(:context, type: :acceptance) do |context|
|
43
|
+
renderer.merge_context context.class.metadata[:rrad].to_h
|
44
|
+
end
|
45
|
+
|
46
|
+
RSpec.configuration.after(:suite) do
|
47
|
+
renderer.write_files
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
**spec/rails_helper.rb**
|
52
|
+
|
53
|
+
```rb
|
54
|
+
# ...
|
55
|
+
|
56
|
+
RSpec::Rails::DIRECTORY_MAPPINGS[:acceptance] = %w[spec acceptance]
|
57
|
+
|
58
|
+
RSpec.configure do |config|
|
59
|
+
# ...
|
60
|
+
config.include RSpec::Rails::RequestExampleGroup, :type => :acceptance
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
## Configuration
|
65
|
+
|
66
|
+
TODO
|
67
|
+
|
68
|
+
## Usage
|
69
|
+
|
70
|
+
Write some spec files and run RSpec as you would usually do.
|
71
|
+
|
72
|
+
If you want to generate the documentation without testing the endpoints
|
73
|
+
(and thus, without examples in generated files), use the `DOC_ONLY`
|
74
|
+
environment variable:
|
75
|
+
|
76
|
+
```rb
|
77
|
+
DOC_ONLY=true bundle exec rails spec
|
78
|
+
```
|
79
|
+
|
80
|
+
For now, files are saved as `tmp/out.json` and `tmp/out.yml`.
|
81
|
+
|
82
|
+
There is nothing to customize the file headers (info, license, ...) yet.
|
83
|
+
|
84
|
+
## Writing specs
|
85
|
+
|
86
|
+
There is a [commented example](examples/commented.rb) available in
|
87
|
+
`doc/`.
|
88
|
+
|
89
|
+
The idea is to have a simple DSL, and declare things like:
|
90
|
+
|
91
|
+
**spec/acceptance/users_spec.rb**
|
92
|
+
|
93
|
+
```rb
|
94
|
+
require 'acceptance_helper'
|
95
|
+
|
96
|
+
RSpec.describe 'Users', type: :acceptance do
|
97
|
+
resource 'Users', 'Manage users'
|
98
|
+
|
99
|
+
entity :user,
|
100
|
+
id: { type: :integer, description: 'The id' },
|
101
|
+
email: { type: :string, description: 'The name' },
|
102
|
+
role: { type: :string, description: 'The name' },
|
103
|
+
created_at: { type: :datetime, description: 'Creation date' },
|
104
|
+
updated_at: { type: :datetime, description: 'Modification date' },
|
105
|
+
url: { type: :string, description: 'URL to this category' }
|
106
|
+
|
107
|
+
on_get '/api/users/', 'Users list' do
|
108
|
+
for_code 200, 'Success response' do |example|
|
109
|
+
visit example
|
110
|
+
expect(response).to have_many defined :user
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
on_put '/api/users/:id', 'Users list' do
|
115
|
+
path_param id: { type: :integer, description: 'User Id' }
|
116
|
+
|
117
|
+
request_params user: {
|
118
|
+
type: :object, required: true, properties: {
|
119
|
+
name: { type: :string, required: false, description: 'New name' },
|
120
|
+
email: { type: :string, required: false, description: 'New email' },
|
121
|
+
role: { type: :string, required: false, description: 'New role' },
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
for_code 200, 'Success response' do |example|
|
126
|
+
visit example
|
127
|
+
expect(response).to have_one defined :user
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
### DSL
|
134
|
+
|
135
|
+
#### Example groups
|
136
|
+
|
137
|
+
##### `resource(name, description)`
|
138
|
+
|
139
|
+
Starts a resource description.
|
140
|
+
|
141
|
+
- It must be called before any other documentation calls.
|
142
|
+
- It should be in the first `describe block`
|
143
|
+
|
144
|
+
##### `entity(name, fields)`
|
145
|
+
|
146
|
+
Describes an entity for the documentation. The name is not visible, so
|
147
|
+
you can put whatever fits (i.e: `:account`, `:user` if the content
|
148
|
+
differs)
|
149
|
+
|
150
|
+
They are ideally in the main `describe` block.
|
151
|
+
|
152
|
+
- `name` is a symbol
|
153
|
+
- `description` is a hash of attributes
|
154
|
+
|
155
|
+
```rb
|
156
|
+
{
|
157
|
+
id: { type: :integer, desc: 'The resource identifier' },
|
158
|
+
name: { type: :string, desc: 'The resource name' },
|
159
|
+
# ...
|
160
|
+
}
|
161
|
+
```
|
162
|
+
|
163
|
+
An attribute should have the following form:
|
164
|
+
|
165
|
+
```
|
166
|
+
<field_name>: {type: <type>, desc: <description>}
|
167
|
+
```
|
168
|
+
|
169
|
+
- `type` can be any of the accepted
|
170
|
+
[OpenAPI types](http://spec.openapis.org/oas/v3.0.2#dataTypeFormat):
|
171
|
+
- `:integer`, `:int32`, `:int64`
|
172
|
+
- `:number`, `:float`, `:double`
|
173
|
+
- `:string`, `:byte`, `:binary`
|
174
|
+
- `:boolean`
|
175
|
+
- `:date`, `:datetime`
|
176
|
+
- `:password`
|
177
|
+
- `:object`, `:array`
|
178
|
+
|
179
|
+
- `description` should be some valid
|
180
|
+
[CommonMark](https://commonmark.org/)
|
181
|
+
|
182
|
+
###### Objects and arrays
|
183
|
+
|
184
|
+
To describe complex structures, use `:object` with `:attributes` and
|
185
|
+
`:array` `:of` something:
|
186
|
+
|
187
|
+
```rb
|
188
|
+
entity :friend,
|
189
|
+
name: { type: :string, required: false, description: 'Friend name' }
|
190
|
+
|
191
|
+
entity :user,
|
192
|
+
id: { type: :number, required: false, description: 'Identifier' },
|
193
|
+
name: { type: :string, required: false, description: 'The name' },
|
194
|
+
friends: { type: :array, of: :friend, required: false, description: 'Friends list'},
|
195
|
+
dog: { type: :object, required: false, description: 'The dog', attributes: :dog },
|
196
|
+
cat: {
|
197
|
+
type: :object, required: false, description: 'The cat', attributes: {
|
198
|
+
name: { type: :string, required: false, description: 'Cat name' },
|
199
|
+
}
|
200
|
+
}
|
201
|
+
```
|
202
|
+
|
203
|
+
In this example, there is an `:array, of: :friend`, which is a reference
|
204
|
+
to the `:friend` entity described above; an `:object` with `:dog`
|
205
|
+
attributes (reference too); and a cat object with its attributes defined
|
206
|
+
inline.
|
207
|
+
|
208
|
+
Both `:of` and `attributes` may be a hash of fields or a symbol. If they
|
209
|
+
are omitted, they will be documented, but responses won't be validated.
|
210
|
+
|
211
|
+
##### `on_<xxx>(url, description, &block)`
|
212
|
+
|
213
|
+
Defines an URL.
|
214
|
+
|
215
|
+
- `url` should be a relative URL to an existing endpoint (i.e.:
|
216
|
+
`/api/users`)
|
217
|
+
- `description` should be some valid
|
218
|
+
[CommonMark](https://commonmark.org/)
|
219
|
+
|
220
|
+
For now, only these methods are available:
|
221
|
+
|
222
|
+
- `on_get`
|
223
|
+
- `on_post`
|
224
|
+
- `on_put`
|
225
|
+
- `on_patch`
|
226
|
+
- `on_delete`
|
227
|
+
|
228
|
+
##### `path_params(<hash_of_attributes>)`
|
229
|
+
|
230
|
+
Defines the path parameters that are used in the URL.
|
231
|
+
|
232
|
+
```rb
|
233
|
+
on_get '/api/users/:id/posts/:post_slug?full=:full_post' do
|
234
|
+
path_params id: type: :integer, description: 'The user ID',
|
235
|
+
post_slug: type: :string, description: 'The post slug',
|
236
|
+
full_post: type: :boolean, required: false, description: 'Returns the full post if `true`, or only an excerpt',
|
237
|
+
|
238
|
+
# ...
|
239
|
+
end
|
240
|
+
```
|
241
|
+
|
242
|
+
- `type` is the field type (check _entity definition_ for a list).
|
243
|
+
- `description` should be some valid
|
244
|
+
[CommonMark](https://commonmark.org/)
|
245
|
+
- `required` is optional an defaults to `true`.
|
246
|
+
|
247
|
+
##### `request_params(<hash_of_attributes>)`
|
248
|
+
|
249
|
+
Defines the format of the JSON payload. Type `object` is supported, so
|
250
|
+
nested elements can be described:
|
251
|
+
|
252
|
+
```rb
|
253
|
+
on_post '/api/items' do
|
254
|
+
request_params item: { type: :object, required: true, properties: {
|
255
|
+
name: { type: integer, description: 'The name of the new item', required: true },
|
256
|
+
notes: { type: string, description: 'Additional notes' }
|
257
|
+
},
|
258
|
+
}
|
259
|
+
#...
|
260
|
+
end
|
261
|
+
```
|
262
|
+
|
263
|
+
An attribute should have the following form:
|
264
|
+
|
265
|
+
```
|
266
|
+
<attr_name>: {type: <type>, desc: <description>, required: <required>, properties: <another_hash> }
|
267
|
+
```
|
268
|
+
|
269
|
+
- `attr_name` is the attribute name (sic)
|
270
|
+
- `type` is the field type (check _entity definition_ for a list).
|
271
|
+
`type` can be `:object` if the attribute contains other attributes.
|
272
|
+
- `required` is optional an defaults to `false`.
|
273
|
+
- `properties` is a hash of params and is only used if `type: :object`
|
274
|
+
|
275
|
+
##### `for_code(http_status, description, doc_only: false &block)`
|
276
|
+
|
277
|
+
Describes the desired output for a precedently defined URL.
|
278
|
+
|
279
|
+
Block takes one required argument, that should be passed to `visit`.
|
280
|
+
This argument will contain the block context and allow `visit` to access
|
281
|
+
the metadatas.
|
282
|
+
|
283
|
+
- `http_status` is an integer representing an
|
284
|
+
[HTTP status](https://httpstat.us/)
|
285
|
+
- `description` should be some valid
|
286
|
+
[CommonMark](https://commonmark.org/)
|
287
|
+
- `doc_only` can be set to true to temporarily disable block execution
|
288
|
+
and only create the documentation (without examples).
|
289
|
+
- `block` where additional tests can be performed. If `visit()` is
|
290
|
+
called within the block, its output will be used in documentation
|
291
|
+
examples, and the response type and code will actually be tested.
|
292
|
+
|
293
|
+
If no block is passed, only the documentation will be generated, without
|
294
|
+
examples. This can be useful to document endpoints that are impossible
|
295
|
+
to test.
|
296
|
+
|
297
|
+
Once again, you have to pass an argument to the block if you use
|
298
|
+
`visit`.
|
299
|
+
|
300
|
+
```rb
|
301
|
+
# ...
|
302
|
+
for_code 200 'A successful response' do |example|
|
303
|
+
visit example
|
304
|
+
# ...
|
305
|
+
end
|
306
|
+
# ...
|
307
|
+
```
|
308
|
+
|
309
|
+
#### Examples
|
310
|
+
|
311
|
+
Example methods are available in `for_code` blocks
|
312
|
+
|
313
|
+
##### `visit(example, path_params: {}, payload: {})`
|
314
|
+
|
315
|
+
Visits the described URL and:
|
316
|
+
|
317
|
+
- Expects the response code to match the described one
|
318
|
+
- Expects the content type to be `application/json`
|
319
|
+
|
320
|
+
- `example` is required and should be the block context (yep, i'll never
|
321
|
+
say it enough)
|
322
|
+
- `path_params`: a hash of overrides for path params (useful if a custom
|
323
|
+
value is needed)
|
324
|
+
- `payload`: a hash of values to send. Ignored for GET and DELETE
|
325
|
+
requests
|
326
|
+
|
327
|
+
```rb
|
328
|
+
for_code 200, 'Success' do |example|
|
329
|
+
visit example
|
330
|
+
end
|
331
|
+
```
|
332
|
+
|
333
|
+
#### Matchers
|
334
|
+
|
335
|
+
##### `have_one(type)`
|
336
|
+
|
337
|
+
Expects the compared content to be a hash with the same keys as a
|
338
|
+
defined entity.
|
339
|
+
|
340
|
+
It should be compared against a hash or a `response` object:
|
341
|
+
|
342
|
+
```rb
|
343
|
+
#...
|
344
|
+
entity user:
|
345
|
+
id: { type: :integer, desc: 'The id',
|
346
|
+
name: { type: :string, desc: 'The name'
|
347
|
+
|
348
|
+
#...
|
349
|
+
|
350
|
+
expect({name: 'John'}).to have_one defined :user # Fails because `id` is missing
|
351
|
+
|
352
|
+
# OR
|
353
|
+
expect(response).to have_one defined :user
|
354
|
+
```
|
355
|
+
|
356
|
+
`defined` will get the correct entity.
|
357
|
+
|
358
|
+
##### `have_many(type)`
|
359
|
+
|
360
|
+
Expects the compared content to be an array of hashes with the same keys
|
361
|
+
as a defined entity.
|
362
|
+
|
363
|
+
It should be compared against an array or a `response` object:
|
364
|
+
|
365
|
+
```rb
|
366
|
+
#...
|
367
|
+
entity user:
|
368
|
+
id: { type: :integer, desc: 'The id',
|
369
|
+
name: { type: :string, desc: 'The name'
|
370
|
+
|
371
|
+
#...
|
372
|
+
|
373
|
+
expect([{id: 2, name: 'Jessica'}, {name: 'John'}]).to have_many defined :user # Fails because `id` is missing in the second entry
|
374
|
+
|
375
|
+
# OR
|
376
|
+
expect(response).to have_many defined :user
|
377
|
+
```
|
378
|
+
|
379
|
+
`defined` will get the correct entity.
|
380
|
+
|
381
|
+
## Limitations
|
382
|
+
|
383
|
+
### Contexts
|
384
|
+
|
385
|
+
Contexts will break the thing. This is due to how the gem builds its
|
386
|
+
metadata, relying on the parents metadata. You have to stick to the DSL.
|
387
|
+
|
388
|
+
```rb
|
389
|
+
RSpec.describe 'Categories', type: :request do
|
390
|
+
describe 'Categories'
|
391
|
+
|
392
|
+
context 'Authenticated' do
|
393
|
+
on_get '/api/categories', 'List all categories' do
|
394
|
+
# ...
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
# or
|
399
|
+
|
400
|
+
on_get '/api/categories', 'List all categories' do
|
401
|
+
context 'Authenticated' do
|
402
|
+
# ...
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
# won't work as expected.
|
407
|
+
end
|
408
|
+
```
|
409
|
+
|
410
|
+
MRs to change this are welcome.
|
411
|
+
|
412
|
+
### Request parameters
|
413
|
+
|
414
|
+
Arrays of objects are not supported yet (i.e.: to describe nested
|
415
|
+
attributes of an `has_many` relation)
|
416
|
+
|
417
|
+
MRs to improve this are welcome.
|
418
|
+
|
419
|
+
### Headers
|
420
|
+
|
421
|
+
There is no way to have custom headers yet. This means, no token-based
|
422
|
+
auth.
|
423
|
+
|
424
|
+
### Files
|
425
|
+
|
426
|
+
There is no support for file fields yet.
|
427
|
+
|
428
|
+
## Development
|
429
|
+
|
430
|
+
After checking out the repo, run `bin/setup` to install dependencies.
|
431
|
+
Then, run `rake spec` to run the tests. You can also run `bin/console`
|
432
|
+
for an interactive prompt that will allow you to experiment.
|
433
|
+
|
434
|
+
To install this gem onto your local machine, run `bundle exec rake
|
435
|
+
install`. To release a new version, update the version number in
|
436
|
+
`version.rb`, and then run `bundle exec rake release`, which will create
|
437
|
+
a git tag for the version, push git commits and tags, and push the
|
438
|
+
`.gem` file to [rubygems.org](https://rubygems.org).
|
439
|
+
|
440
|
+
## Contributing
|
441
|
+
|
442
|
+
Bug reports and pull requests are welcome on GitLab at
|
443
|
+
https://gitlab.com/experimentslabs/rspec-rails-api/issues. This
|
444
|
+
project is intended to be a safe, welcoming space for collaboration, and
|
445
|
+
contributors are expected to adhere to the
|
446
|
+
[Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
447
|
+
|
448
|
+
## License
|
449
|
+
|
450
|
+
The gem is available as open source under the terms of the
|
451
|
+
[MIT License](https://opensource.org/licenses/MIT).
|
452
|
+
|
453
|
+
## Code of Conduct
|
454
|
+
|
455
|
+
Everyone interacting in the RspecRailsApiDoc project’s codebases, issue
|
456
|
+
trackers, chat rooms and mailing lists is expected to follow the
|
457
|
+
[code of conduct](https://gitlab.com/experimentslabs/rspec-rails-api/blob/master/CODE_OF_CONDUCT.md).
|