rspec-rails-api 0.1.5 → 0.2.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 +4 -4
- data/.gitignore +2 -0
- data/.gitlab-ci.yml +7 -0
- data/.rubocop.yml +5 -1
- data/CHANGELOG.md +21 -0
- data/README.md +131 -68
- data/bin/setup +2 -0
- data/lib/rspec/rails/api/dsl/example_group.rb +20 -3
- data/lib/rspec/rails/api/metadata.rb +11 -4
- data/lib/rspec/rails/api/open_api_renderer.rb +5 -4
- data/lib/rspec/rails/api/version.rb +1 -1
- data/rspec-rails-api.gemspec +1 -1
- metadata +2 -3
- data/examples/commented.rb +0 -167
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0861008371cafa6cab0cda5c165acda54b5a3047f152713bb684ae0c7c7be0ff'
|
4
|
+
data.tar.gz: d3c8877c5ef1e43c902010b68b9e483feefa6e57251d67aba325394a9682aee7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e1009f40d78187888e546c74e62e88d7557f45a4a3cfed78a27785d63b401f673ed1796928f1d32823a665694b86761653bb87edc68522418d6117c4b576e362
|
7
|
+
data.tar.gz: 10ffebb4902456c4671b39b7dd707ea8ca870b9a27054c3139cae94bb3dcebfc3f88abd41a298ed870f98d7953c1195171cbf8f077db2dcd6530fe761e354a5a
|
data/.gitignore
CHANGED
data/.gitlab-ci.yml
CHANGED
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,27 @@
|
|
1
1
|
# Change Log
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
|
+
## Not released
|
5
|
+
|
6
|
+
## 0.2.0 - 2019-11-02
|
7
|
+
|
8
|
+
### Added
|
9
|
+
|
10
|
+
- `parameters` method to define path or requests params for the whole file.
|
11
|
+
- A simple Rails application now acts as example and is used to generate some
|
12
|
+
of the fixtures.
|
13
|
+
|
14
|
+
### Changed
|
15
|
+
|
16
|
+
- method `path_params` can now use a reference to a previously defined parameters
|
17
|
+
set. The method has a new signature: `path_params(fields: nil, defined: nil)`. Update
|
18
|
+
the existing calls accordingly. To use params defined with `parameters`, use the
|
19
|
+
`defined` option: `path_params defined: :common_path_params`
|
20
|
+
- method `request_params` can now use a reference to a previously defined parameters
|
21
|
+
set. The method has a new signature: `request_params(attributes: nil, defined: nil)`. Update
|
22
|
+
the existing calls accordingly. To use params defined with `parameters`, use the
|
23
|
+
`defined` option: `request_params defined: :common_form_params`
|
24
|
+
|
4
25
|
## 0.1.5 - 2019-10-31
|
5
26
|
|
6
27
|
### Fixed
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# RSpec-rails-api
|
2
2
|
|
3
3
|
> An RSpec plugin to test Rails api responses and generate swagger
|
4
4
|
> documentation
|
@@ -13,7 +13,7 @@ in order to test it.
|
|
13
13
|
|
14
14
|
Add this line to your application's Gemfile:
|
15
15
|
|
16
|
-
```
|
16
|
+
```ruby
|
17
17
|
gem 'rspec-rails-api'
|
18
18
|
```
|
19
19
|
|
@@ -29,7 +29,7 @@ Configuration should be made manually for now:
|
|
29
29
|
|
30
30
|
**spec/acceptance_helper.rb**
|
31
31
|
|
32
|
-
```
|
32
|
+
```ruby
|
33
33
|
require 'rails_helper'
|
34
34
|
require 'rspec_rails_api'
|
35
35
|
|
@@ -38,13 +38,15 @@ RSpec.configure do |config|
|
|
38
38
|
end
|
39
39
|
|
40
40
|
renderer = RSpec::Rails::Api::OpenApiRenderer.new
|
41
|
-
|
42
|
-
renderer.api_title = '
|
41
|
+
# Options here should be customized
|
42
|
+
renderer.api_title = 'YourProject API'
|
43
43
|
renderer.api_version = '1'
|
44
|
-
renderer.api_description = '
|
45
|
-
#
|
46
|
-
|
47
|
-
|
44
|
+
renderer.api_description = 'Manage data on YourProject'
|
45
|
+
# Options below are optional
|
46
|
+
renderer.api_servers = [{ url: 'https://example.com' }]
|
47
|
+
renderer.api_tos = 'http://example.com/tos.html'
|
48
|
+
renderer.api_contact = { name: 'Admin', email: 'admin@example.com', url: 'http://example.com/contact' }
|
49
|
+
renderer.api_license = { name: 'Apache', url: 'https://opensource.org/licenses/Apache-2.0' }
|
48
50
|
|
49
51
|
RSpec.configuration.after(:context, type: :acceptance) do |context|
|
50
52
|
renderer.merge_context context.class.metadata[:rrad].to_h
|
@@ -58,14 +60,14 @@ end
|
|
58
60
|
|
59
61
|
**spec/rails_helper.rb**
|
60
62
|
|
61
|
-
```
|
63
|
+
```ruby
|
62
64
|
# ...
|
63
65
|
|
64
66
|
RSpec::Rails::DIRECTORY_MAPPINGS[:acceptance] = %w[spec acceptance]
|
65
67
|
|
66
68
|
RSpec.configure do |config|
|
67
69
|
# ...
|
68
|
-
config.include RSpec::Rails::RequestExampleGroup, :
|
70
|
+
config.include RSpec::Rails::RequestExampleGroup, type: :acceptance
|
69
71
|
end
|
70
72
|
```
|
71
73
|
|
@@ -77,7 +79,7 @@ end
|
|
77
79
|
|
78
80
|
To use `sign_in` and `sign_out` from Devise in the acceptance tests, create a Devise support file:
|
79
81
|
|
80
|
-
```
|
82
|
+
```ruby
|
81
83
|
# spec/support/devise.rb
|
82
84
|
module DeviseAcceptanceSpecHelpers
|
83
85
|
include Warden::Test::Helpers
|
@@ -97,7 +99,7 @@ end
|
|
97
99
|
|
98
100
|
Load this file in `rails_helper.rb`:
|
99
101
|
|
100
|
-
```
|
102
|
+
```ruby
|
101
103
|
#...
|
102
104
|
# Add additional requires below this line. Rails is not loaded until this point!
|
103
105
|
require 'support/devise'
|
@@ -106,7 +108,7 @@ require 'support/devise'
|
|
106
108
|
|
107
109
|
Include the helper for acceptance specs:
|
108
110
|
|
109
|
-
```
|
111
|
+
```ruby
|
110
112
|
RSpec.configure do |config|
|
111
113
|
config.include DeviseAcceptanceSpecHelpers, type: :acceptance
|
112
114
|
end
|
@@ -114,7 +116,7 @@ end
|
|
114
116
|
|
115
117
|
You can now use the methods as usual:
|
116
118
|
|
117
|
-
```
|
119
|
+
```ruby
|
118
120
|
# In a before block
|
119
121
|
before do
|
120
122
|
sign_in #...
|
@@ -122,9 +124,9 @@ end
|
|
122
124
|
|
123
125
|
# In examples
|
124
126
|
#...
|
125
|
-
for_code 200, 'Success' do |
|
127
|
+
for_code 200, 'Success' do |url|
|
126
128
|
sing_in #...
|
127
|
-
visit
|
129
|
+
visit url
|
128
130
|
|
129
131
|
#...
|
130
132
|
end
|
@@ -142,24 +144,20 @@ If you want to generate the documentation without testing the endpoints
|
|
142
144
|
(and thus, without examples in generated files), use the `DOC_ONLY`
|
143
145
|
environment variable:
|
144
146
|
|
145
|
-
```
|
147
|
+
```sh
|
146
148
|
DOC_ONLY=true bundle exec rails spec
|
147
149
|
```
|
148
150
|
|
149
|
-
For now, files are saved as `tmp/out.json` and `tmp/out.yml`.
|
150
|
-
|
151
|
-
There is nothing to customize the file headers (info, license, ...) yet.
|
152
|
-
|
153
151
|
## Writing specs
|
154
152
|
|
155
|
-
There is a [commented example](
|
156
|
-
`
|
153
|
+
There is a [commented example](dummy/spec/acceptance/posts_spec.rb) available in
|
154
|
+
`dummy/spec/acceptance`.
|
157
155
|
|
158
156
|
The idea is to have a simple DSL, and declare things like:
|
159
157
|
|
160
158
|
**spec/acceptance/users_spec.rb**
|
161
159
|
|
162
|
-
```
|
160
|
+
```ruby
|
163
161
|
require 'acceptance_helper'
|
164
162
|
|
165
163
|
RSpec.describe 'Users', type: :acceptance do
|
@@ -174,8 +172,8 @@ RSpec.describe 'Users', type: :acceptance do
|
|
174
172
|
url: { type: :string, description: 'URL to this category' }
|
175
173
|
|
176
174
|
on_get '/api/users/', 'Users list' do
|
177
|
-
for_code 200, 'Success response' do |
|
178
|
-
visit
|
175
|
+
for_code 200, 'Success response' do |url|
|
176
|
+
visit url
|
179
177
|
expect(response).to have_many defined :user
|
180
178
|
end
|
181
179
|
end
|
@@ -191,8 +189,8 @@ RSpec.describe 'Users', type: :acceptance do
|
|
191
189
|
}
|
192
190
|
}
|
193
191
|
|
194
|
-
for_code 200, 'Success response' do |
|
195
|
-
visit
|
192
|
+
for_code 200, 'Success response' do |url|
|
193
|
+
visit url
|
196
194
|
expect(response).to have_one defined :user
|
197
195
|
end
|
198
196
|
end
|
@@ -203,25 +201,24 @@ end
|
|
203
201
|
|
204
202
|
#### Example groups
|
205
203
|
|
206
|
-
##### `resource(
|
204
|
+
##### `resource(type, description)`
|
207
205
|
|
208
206
|
Starts a resource description.
|
209
207
|
|
210
208
|
- It must be called before any other documentation calls.
|
211
209
|
- It should be in the first `describe block`
|
212
210
|
|
213
|
-
##### `entity(
|
211
|
+
##### `entity(type, fields)`
|
214
212
|
|
215
|
-
Describes an entity for the documentation. The
|
216
|
-
you can put whatever fits (i.e: `:account`, `:user
|
217
|
-
differs)
|
213
|
+
Describes an entity for the documentation. The type is only a reference,
|
214
|
+
you can put whatever fits (i.e: `:account`, `:user`, ...).
|
218
215
|
|
219
|
-
They
|
216
|
+
They should be in the main `describe` block.
|
220
217
|
|
221
|
-
- `
|
218
|
+
- `type` is a symbol
|
222
219
|
- `description` is a hash of attributes
|
223
220
|
|
224
|
-
```
|
221
|
+
```ruby
|
225
222
|
{
|
226
223
|
id: { type: :integer, desc: 'The resource identifier' },
|
227
224
|
name: { type: :string, desc: 'The resource name' },
|
@@ -253,7 +250,7 @@ An attribute should have the following form:
|
|
253
250
|
To describe complex structures, use `:object` with `:attributes` and
|
254
251
|
`:array` `:of` something:
|
255
252
|
|
256
|
-
```
|
253
|
+
```ruby
|
257
254
|
entity :friend,
|
258
255
|
name: { type: :string, required: false, description: 'Friend name' }
|
259
256
|
|
@@ -277,6 +274,15 @@ inline.
|
|
277
274
|
Both `:of` and `attributes` may be a hash of fields or a symbol. If they
|
278
275
|
are omitted, they will be documented, but responses won't be validated.
|
279
276
|
|
277
|
+
##### `parameters(type, fields)`
|
278
|
+
Describe path or request parameters. The type is only a reference,
|
279
|
+
use whatever makes sense. These parameters will be present in
|
280
|
+
documentation, only if they are referenced by a `request_params` or
|
281
|
+
`path_params` call.
|
282
|
+
|
283
|
+
Fields have the structure of the hash you would give to `request_params`
|
284
|
+
or `path_params` (see each method later in this documentation).
|
285
|
+
|
280
286
|
##### `on_<xxx>(url, description, &block)`
|
281
287
|
|
282
288
|
Defines an URL.
|
@@ -294,15 +300,16 @@ For now, only these methods are available:
|
|
294
300
|
- `on_patch`
|
295
301
|
- `on_delete`
|
296
302
|
|
297
|
-
##### `path_params(
|
303
|
+
##### `path_params(fields: nil, defined: nil)`
|
298
304
|
|
299
305
|
Defines the path parameters that are used in the URL.
|
300
306
|
|
301
|
-
```
|
307
|
+
```ruby
|
302
308
|
on_get '/api/users/:id/posts/:post_slug?full=:full_post' do
|
303
|
-
path_params
|
304
|
-
|
305
|
-
|
309
|
+
path_params fields: {
|
310
|
+
id: { type: :integer, description: 'The user ID' },
|
311
|
+
post_slug: { type: :string, description: 'The post slug' },
|
312
|
+
full_post: { type: :boolean, required: false, description: 'Returns the full post if `true`, or only an excerpt' } }
|
306
313
|
|
307
314
|
# ...
|
308
315
|
end
|
@@ -313,18 +320,33 @@ end
|
|
313
320
|
[CommonMark](https://commonmark.org/)
|
314
321
|
- `required` is optional an defaults to `true`.
|
315
322
|
|
316
|
-
|
323
|
+
Alternative with defined parameters:
|
324
|
+
|
325
|
+
```ruby
|
326
|
+
parameters :users_post_path_params,
|
327
|
+
id: { type: :integer, description: 'The user ID' },
|
328
|
+
post_slug: { type: :string, description: 'The post slug' }
|
329
|
+
|
330
|
+
on_get '/api/users/:id/posts/:post_slug' do
|
331
|
+
path_params defined: :users_post_path_params
|
332
|
+
|
333
|
+
#...
|
334
|
+
end
|
335
|
+
```
|
336
|
+
|
337
|
+
##### `request_params(attributes: nil, defined: nil)`
|
317
338
|
|
318
339
|
Defines the format of the JSON payload. Type `object` is supported, so
|
319
340
|
nested elements can be described:
|
320
341
|
|
321
|
-
```
|
342
|
+
```ruby
|
322
343
|
on_post '/api/items' do
|
323
|
-
request_params
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
344
|
+
request_params attributes: {
|
345
|
+
item: { type: :object, required: true, properties: {
|
346
|
+
name: { type: integer, description: 'The name of the new item', required: true },
|
347
|
+
notes: { type: string, description: 'Additional notes' }
|
348
|
+
} }
|
349
|
+
}
|
328
350
|
#...
|
329
351
|
end
|
330
352
|
```
|
@@ -342,6 +364,23 @@ An attribute should have the following form:
|
|
342
364
|
- `properties` is a hash of params and is only used if `type: :object`
|
343
365
|
- `of` is a hash of params and is only used if `type: :array`
|
344
366
|
|
367
|
+
Alternative with defined parameters:
|
368
|
+
|
369
|
+
```ruby
|
370
|
+
parameters :item_form_params,
|
371
|
+
item: { type: :object, required: true, properties: {
|
372
|
+
name: { type: integer, description: 'The name of the new item', required: true },
|
373
|
+
notes: { type: string, description: 'Additional notes' }
|
374
|
+
}
|
375
|
+
}
|
376
|
+
|
377
|
+
on_post '/api/items' do
|
378
|
+
request_params defined: :item_form_params
|
379
|
+
|
380
|
+
#...
|
381
|
+
end
|
382
|
+
```
|
383
|
+
|
345
384
|
##### `for_code(http_status, description, doc_only: false &block)`
|
346
385
|
|
347
386
|
Describes the desired output for a precedently defined URL.
|
@@ -367,10 +406,10 @@ to test.
|
|
367
406
|
Once again, you have to pass an argument to the block if you use
|
368
407
|
`visit`.
|
369
408
|
|
370
|
-
```
|
409
|
+
```ruby
|
371
410
|
# ...
|
372
|
-
for_code 200 'A successful response' do |
|
373
|
-
visit
|
411
|
+
for_code 200, 'A successful response' do |url|
|
412
|
+
visit url
|
374
413
|
# ...
|
375
414
|
end
|
376
415
|
# ...
|
@@ -395,9 +434,9 @@ Visits the described URL and:
|
|
395
434
|
requests
|
396
435
|
- `headers`: a hash of custom headers.
|
397
436
|
|
398
|
-
```
|
399
|
-
for_code 200, 'Success' do |
|
400
|
-
visit
|
437
|
+
```ruby
|
438
|
+
for_code 200, 'Success' do |url|
|
439
|
+
visit url
|
401
440
|
end
|
402
441
|
```
|
403
442
|
|
@@ -410,9 +449,9 @@ defined entity.
|
|
410
449
|
|
411
450
|
It should be compared against a hash or a `response` object:
|
412
451
|
|
413
|
-
```
|
452
|
+
```ruby
|
414
453
|
#...
|
415
|
-
entity user
|
454
|
+
entity :user,
|
416
455
|
id: { type: :integer, desc: 'The id' },
|
417
456
|
name: { type: :string, desc: 'The name' }
|
418
457
|
|
@@ -433,11 +472,11 @@ as a defined entity.
|
|
433
472
|
|
434
473
|
It should be compared against an array or a `response` object:
|
435
474
|
|
436
|
-
```
|
475
|
+
```ruby
|
437
476
|
#...
|
438
|
-
entity user
|
477
|
+
entity :user,
|
439
478
|
id: { type: :integer, desc: 'The id' },
|
440
|
-
name: { type: :string, desc: 'The name }
|
479
|
+
name: { type: :string, desc: 'The name' }
|
441
480
|
|
442
481
|
#...
|
443
482
|
|
@@ -456,7 +495,7 @@ expect(response).to have_many defined :user
|
|
456
495
|
Contexts will break the thing. This is due to how the gem builds its
|
457
496
|
metadata, relying on the parents metadata. You have to stick to the DSL.
|
458
497
|
|
459
|
-
```
|
498
|
+
```ruby
|
460
499
|
RSpec.describe 'Categories', type: :request do
|
461
500
|
describe 'Categories'
|
462
501
|
|
@@ -494,14 +533,38 @@ There is no support for file fields yet.
|
|
494
533
|
## Development
|
495
534
|
|
496
535
|
After checking out the repo, run `bin/setup` to install dependencies.
|
497
|
-
Then, run `
|
536
|
+
Then, run `bundle exec rspec` to run the tests. You can also run `bin/console`
|
498
537
|
for an interactive prompt that will allow you to experiment.
|
499
538
|
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
539
|
+
### Testing
|
540
|
+
|
541
|
+
#### Dummy application
|
542
|
+
|
543
|
+
A small Rails application is available as an example, in the `dummy` directory.
|
544
|
+
If you write new features in the library, you'll have to update the examples to
|
545
|
+
make them pass their tests:
|
546
|
+
|
547
|
+
```shell
|
548
|
+
cd dummy
|
549
|
+
bundle exec rspec
|
550
|
+
```
|
551
|
+
|
552
|
+
Doing so will also update the fixtures used by some of the library tests.
|
553
|
+
|
554
|
+
Please don't commit the fixtures unless the changes in them are directly related
|
555
|
+
to the changes in the library.
|
556
|
+
|
557
|
+
#### Code linting
|
558
|
+
|
559
|
+
We use Rubocop here, with a releset for the library, and another for the dummy
|
560
|
+
application:
|
561
|
+
|
562
|
+
```shell
|
563
|
+
bundle exec rubocop
|
564
|
+
|
565
|
+
cd dummy
|
566
|
+
bundle exec rubocop
|
567
|
+
```
|
505
568
|
|
506
569
|
## Contributing
|
507
570
|
|
data/bin/setup
CHANGED
@@ -21,13 +21,30 @@ module RSpec
|
|
21
21
|
metadata[:rrad].add_entity type, fields
|
22
22
|
end
|
23
23
|
|
24
|
+
# Used to describe a request or path param which will be available as reference
|
25
|
+
def parameters(type, fields)
|
26
|
+
metadata[:rrad].add_parameter type, fields
|
27
|
+
end
|
28
|
+
|
24
29
|
# Used to describe query parameters
|
25
|
-
def path_params(fields)
|
30
|
+
def path_params(fields: nil, defined: nil)
|
31
|
+
if defined && !metadata[:rrad].parameters[defined]
|
32
|
+
raise "Parameter #{defined} was not defined with the 'parameters' method"
|
33
|
+
end
|
34
|
+
|
35
|
+
fields ||= metadata[:rrad].parameters[defined]
|
36
|
+
|
26
37
|
metadata[:rrad].add_path_params fields
|
27
38
|
end
|
28
39
|
|
29
|
-
def request_params(
|
30
|
-
metadata[:rrad].
|
40
|
+
def request_params(attributes: nil, defined: nil)
|
41
|
+
if defined && !metadata[:rrad].parameters[defined]
|
42
|
+
raise "Parameter #{defined} was not defined with the 'parameters' method"
|
43
|
+
end
|
44
|
+
|
45
|
+
attributes ||= metadata[:rrad].parameters[defined]
|
46
|
+
|
47
|
+
metadata[:rrad].add_request_params attributes
|
31
48
|
end
|
32
49
|
|
33
50
|
def on_get(url, description = nil, &block)
|
@@ -9,11 +9,12 @@ module RSpec
|
|
9
9
|
module Api
|
10
10
|
# Handles contexts and examples metadatas.
|
11
11
|
class Metadata # rubocop:disable Metrics/ClassLength
|
12
|
-
attr_reader :entities, :resources, :current_resource
|
12
|
+
attr_reader :entities, :resources, :current_resource, :parameters
|
13
13
|
|
14
14
|
def initialize
|
15
|
-
@resources
|
16
|
-
@entities
|
15
|
+
@resources = {}
|
16
|
+
@entities = {}
|
17
|
+
@parameters = {}
|
17
18
|
# Only used when building metadata during RSpec boot
|
18
19
|
@current_resource = nil
|
19
20
|
@current_method = nil
|
@@ -32,6 +33,12 @@ module RSpec
|
|
32
33
|
EntityConfig.new(fields))
|
33
34
|
end
|
34
35
|
|
36
|
+
def add_parameter(type, fields)
|
37
|
+
raise "Parameter #{type} is already defined" if @parameters[type]
|
38
|
+
|
39
|
+
@parameters[type] = fields
|
40
|
+
end
|
41
|
+
|
35
42
|
def add_path_params(fields) # rubocop:disable Metrics/MethodLength
|
36
43
|
check_current_context :resource, :url
|
37
44
|
|
@@ -138,7 +145,7 @@ module RSpec
|
|
138
145
|
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength, Style/GuardClause
|
139
146
|
|
140
147
|
def path_param_scope(url_chunks, name)
|
141
|
-
if /:#{name}
|
148
|
+
if /:#{name}/.match?(url_chunks[0])
|
142
149
|
:path
|
143
150
|
elsif url_chunks[1] && /:#{name}/ =~ url_chunks[1]
|
144
151
|
:query
|
@@ -19,7 +19,7 @@ module RSpec
|
|
19
19
|
def initialize
|
20
20
|
@metadata = { resources: {}, entities: {} }
|
21
21
|
@api_infos = {}
|
22
|
-
@api_servers =
|
22
|
+
@api_servers = []
|
23
23
|
@api_paths = {}
|
24
24
|
@api_components = {}
|
25
25
|
@api_tags = []
|
@@ -27,11 +27,12 @@ module RSpec
|
|
27
27
|
@api_license = {}
|
28
28
|
end
|
29
29
|
|
30
|
-
def merge_context(context)
|
30
|
+
def merge_context(context, dump_metadata: false)
|
31
31
|
@metadata[:resources].deep_merge! context[:resources]
|
32
32
|
@metadata[:entities].deep_merge! context[:entities]
|
33
|
-
|
34
|
-
#
|
33
|
+
|
34
|
+
# Save context for debug and fixtures
|
35
|
+
File.write ::Rails.root.join('tmp', 'rra_metadata.yaml'), context.to_yaml if dump_metadata
|
35
36
|
end
|
36
37
|
|
37
38
|
def write_files(path = nil, only: %i[yaml json])
|
data/rspec-rails-api.gemspec
CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
# Specify which files should be added to the gem when it is released.
|
27
27
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
28
28
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
29
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
29
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|dummy)/}) }
|
30
30
|
end
|
31
31
|
spec.bindir = 'exe'
|
32
32
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec-rails-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Manuel Tancoigne
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-11-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -144,7 +144,6 @@ files:
|
|
144
144
|
- Rakefile
|
145
145
|
- bin/console
|
146
146
|
- bin/setup
|
147
|
-
- examples/commented.rb
|
148
147
|
- lib/rspec/rails/api/dsl/example.rb
|
149
148
|
- lib/rspec/rails/api/dsl/example_group.rb
|
150
149
|
- lib/rspec/rails/api/entity_config.rb
|
data/examples/commented.rb
DELETED
@@ -1,167 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'acceptance_helper'
|
4
|
-
|
5
|
-
RSpec.describe 'Categories', type: :acceptance do
|
6
|
-
# Needed to initialize documentation for this type of request.
|
7
|
-
# It should come before all the other calls (it will let the metadata
|
8
|
-
# class know which resource we're dealing with)
|
9
|
-
resource 'Categories', 'Manage categories in a group'
|
10
|
-
|
11
|
-
# Define an entity that can be returned by an URL.
|
12
|
-
entity :category,
|
13
|
-
id: { type: :integer, description: 'The id' },
|
14
|
-
name: { type: :string, description: 'The name' },
|
15
|
-
group_id: { type: :number, description: 'Concerned group' },
|
16
|
-
created_at: { type: :datetime, description: 'Creation date' },
|
17
|
-
updated_at: { type: :datetime, description: 'Modification date' },
|
18
|
-
url: { type: :string, description: 'URL to this category' }
|
19
|
-
|
20
|
-
entity :error,
|
21
|
-
error: { type: :string, description: 'The error' }
|
22
|
-
|
23
|
-
entity :form_error,
|
24
|
-
errors: {
|
25
|
-
type: :object, description: 'Form errors', attributes: {
|
26
|
-
name: { type: :array, description: 'Name errors' },
|
27
|
-
group: { type: :array, description: 'Group errors' },
|
28
|
-
}
|
29
|
-
}
|
30
|
-
|
31
|
-
# Some vars to use in the tests
|
32
|
-
let(:current_user) { FactoryBot.create :user_active }
|
33
|
-
|
34
|
-
let(:group) { current_user.groups.first }
|
35
|
-
let(:group_id) { group.id }
|
36
|
-
let(:category) { FactoryBot.create :category, user: current_user, group: group }
|
37
|
-
|
38
|
-
# Declare a path accessed with a given method
|
39
|
-
# Their values can be declared with a "let(:var){ value }" statement,
|
40
|
-
# or passed to "visit" method (see "for_code 404")
|
41
|
-
on_get '/api/groups/:group_id/categories', 'Categories defined in the given group' do
|
42
|
-
# Declare path parameters for documentation
|
43
|
-
path_params group_id: { type: :integer, description: 'Target group identifier' }
|
44
|
-
|
45
|
-
# Expectations for the given HTTP status
|
46
|
-
for_code 200, 'Success' do |example|
|
47
|
-
FactoryBot.create_list :category, 2, user: current_user, group: group
|
48
|
-
|
49
|
-
# This method is not built-in; check: ../README.md
|
50
|
-
sign_in current_user
|
51
|
-
|
52
|
-
# Actually visit the path. It will expect the given code and a
|
53
|
-
# content of type "application/JSON" (except for 204: no content
|
54
|
-
# statuses)
|
55
|
-
visit example
|
56
|
-
|
57
|
-
# Custom matcher: have_many will expect a body with an array of
|
58
|
-
# the given entity. All entries in the response will have its keys
|
59
|
-
# checked (not the content type).
|
60
|
-
# The `defined` method will get the correct entity for comparison
|
61
|
-
expect(response).to have_many defined :category
|
62
|
-
|
63
|
-
# Other expectations
|
64
|
-
expect(JSON.parse(response.body).count).to eq 2
|
65
|
-
end
|
66
|
-
|
67
|
-
for_code 404, 'Not found (not owned)' do |example|
|
68
|
-
sign_in current_user
|
69
|
-
|
70
|
-
group_id = FactoryBot.create(:user_active).groups.first.id
|
71
|
-
|
72
|
-
visit example, path_params: { group_id: group_id }
|
73
|
-
expect(response).to have_one defined :error
|
74
|
-
end
|
75
|
-
|
76
|
-
for_code 401, 'Not authorized' do |example|
|
77
|
-
visit example
|
78
|
-
expect(response).to have_one defined :error
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
on_get '/api/groups/:group_id/categories/:id', 'Get category' do
|
83
|
-
path_params group_id: { type: :integer, description: 'Target group identifier' },
|
84
|
-
id: { type: :integer, description: 'Category id' }
|
85
|
-
|
86
|
-
let(:id) { category.id }
|
87
|
-
|
88
|
-
for_code 200, 'Success' do |example|
|
89
|
-
sign_in current_user
|
90
|
-
|
91
|
-
visit example
|
92
|
-
expect(response).to have_one defined :category
|
93
|
-
end
|
94
|
-
|
95
|
-
for_code 401, 'Not authorized' do |example|
|
96
|
-
visit example
|
97
|
-
expect(response).to have_one defined :error
|
98
|
-
end
|
99
|
-
|
100
|
-
for_code 404, 'Not found' do |example|
|
101
|
-
sign_in current_user
|
102
|
-
|
103
|
-
visit example, path_params: { id: 0 }
|
104
|
-
expect(response).to have_one defined :error
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
on_post '/api/groups/:group_id/categories', 'Creates a category' do
|
109
|
-
path_params group_id: { type: :integer, description: 'Target group identifier' }
|
110
|
-
|
111
|
-
request_params category: {
|
112
|
-
type: :object, required: true, attributes: {
|
113
|
-
name: { type: :string, required: true, description: 'The name' },
|
114
|
-
}
|
115
|
-
}
|
116
|
-
|
117
|
-
let(:id) { category.id }
|
118
|
-
|
119
|
-
for_code 201, 'Success' do |example|
|
120
|
-
sign_in current_user
|
121
|
-
|
122
|
-
visit example, payload: { category: { name: 'New category' } }
|
123
|
-
expect(response).to have_one defined :category
|
124
|
-
end
|
125
|
-
|
126
|
-
for_code 422, 'Form error' do |example|
|
127
|
-
sign_in current_user
|
128
|
-
|
129
|
-
visit example, payload: { category: {} }
|
130
|
-
expect(response).to have_one defined :form_error
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
on_put '/api/groups/:group_id/categories/:id', 'Updates a category' do
|
135
|
-
path_params group_id: { type: :integer, description: 'Target group identifier' },
|
136
|
-
id: { type: :integer, description: 'Category id' }
|
137
|
-
|
138
|
-
let(:id) { category.id }
|
139
|
-
|
140
|
-
request_params category: {
|
141
|
-
type: :object, required: true, description: 'New category attributes', attributes: {
|
142
|
-
name: { type: :string, required: false, description: 'The new name' },
|
143
|
-
}
|
144
|
-
}
|
145
|
-
|
146
|
-
for_code 200, 'Success' do |example|
|
147
|
-
sign_in current_user
|
148
|
-
|
149
|
-
visit example, payload: { category: { name: 'New name' } }
|
150
|
-
expect(response).to have_one defined :category
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
on_delete '/api/groups/:group_id/categories/:id', 'Deletes a category' do
|
155
|
-
path_params group_id: { type: :integer, description: 'Target group identifier' },
|
156
|
-
id: { type: :integer, description: 'Category id' }
|
157
|
-
|
158
|
-
let(:id) { category.id }
|
159
|
-
|
160
|
-
for_code 204, 'Success' do |example|
|
161
|
-
sign_in current_user
|
162
|
-
|
163
|
-
visit example
|
164
|
-
# Not checking content for 204 responses
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|