rspec-rails-api 0.1.5 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|