README.md in rspec-rails-api-0.5.0 vs README.md in rspec-rails-api-0.6.0
- old
+ new
@@ -70,12 +70,106 @@
end
```
## Configuration
-**TODO: This section is incomplete and the gem has no generator yet**
+**TODO: This section is incomplete and the gem has no generator yet.**
+Here is an example configuration:
+
+```rb
+# spec/rails_helper.rb
+require 'spec/support/rspec_rails_api'
+```
+
+```rb
+# spec/support/acceptance_entities.rb
+
+# This file contains common object definitions
+{
+ error: {
+ error: { type: :string, description: "Error message" }
+ },
+ form_error: {
+ title: { type: :array, required: false, description: "Title errors", of: :string }
+ },
+}.each do |name, attributes|
+ RSpec::Rails::Api::Metadata.add_entity name, attributes
+end
+```
+
+```rb
+# spec/support/rspec_rails_api.rb
+require 'rspec_rails_api'
+require 'support/acceptance_entities'
+
+# Include DSL in RSpec
+RSpec.configure do |config|
+ config.include RSpec::Rails::Api::DSL::Example
+end
+
+# Initialize and configure the renderer
+renderer = RSpec::Rails::Api::OpenApiRenderer.new
+# Server URL for quick reference
+server_url = 'https://example.com'
+
+# Options here should be present for a valid OpenAPI file
+renderer.api_title = 'MyProject API'
+renderer.api_version = '1'
+
+# Options below are optional
+#
+# API description. Markdown supported
+# renderer.api_description = 'Manage data on MyProject'
+#
+# List of servers, to live-test the documentation
+# renderer.api_servers = [{ url: server_url }, { url: 'http://localhost:3000' }]
+#
+# Link to the API terms of service, if any
+# renderer.api_tos = 'http://example.com/tos.html'
+#
+# Contact information
+# renderer.api_contact = { name: 'Admin', email: 'admin@example.com', url: 'http://example.com/contact' }
+#
+# API license information
+# renderer.api_license = { name: 'Apache', url: 'https://opensource.org/licenses/Apache-2.0' }
+#
+# Possible security schemes
+# renderer.add_security_scheme :pkce_code_grant, 'PKCE code grant',
+# type: 'oauth2',
+# flows: {
+# implicit: {
+# authorizationUrl: "#{server_url}/oauth/authorize",
+# scopes: { read: 'will read data on your behalf', write: 'will write data on your behalf' }
+# }
+# }
+# renderer.add_security_scheme :bearer, 'Bearer token',
+# type: 'http',
+# scheme: 'bearer'
+#
+# Declare keys whose values should be filtered in responses.
+# renderer.redact_responses entity_name: { key: 'REDACTED' },
+# other_entity: { other_key: ['REDACTED'] }
+
+
+# We need to merge each context metadata so we can reference to them to build the final file
+RSpec.configuration.after(:context, type: :acceptance) do |context|
+ renderer.merge_context context.class.metadata[:rra].to_h
+ # During development of rspec_rails_api, you may want to dump raw metadata to a file
+ renderer.merge_context context.class.metadata[:rra].to_h, dump_metadata: true
+end
+
+# Skip this block if you don't need the OpenAPI documentation file and only have your responses tested
+RSpec.configuration.after(:suite) do
+ renderer.write_files Rails.root.join('public/swagger') # Write both YAML and prettified JSON files
+ # or
+ renderer.write_files Rails.root.join('public/swagger'), only: [:json] # Prettified JSON only
+ # or
+ renderer.write_files Rails.root.join('public/swagger'), only: [:yaml] # YAML only
+end
+```
+
### Integration with Devise
To use `sign_in` and `sign_out` from Devise in the acceptance tests, create a Devise support file:
```ruby
@@ -122,11 +216,11 @@
end
# In examples
#...
for_code 200, 'Success' do |url|
- sing_in #...
+ sign_in #...
test_response_of url
#...
end
#...
@@ -147,11 +241,11 @@
The idea is to have a simple DSL, and declare things like:
**spec/acceptance/users_spec.rb**
```ruby
-require 'acceptance_helper'
+require 'rails_helper'
RSpec.describe 'Users', type: :acceptance do
resource 'Users', 'Manage users'
entity :user,
@@ -184,10 +278,82 @@
end
end
end
```
+### Entity declarations
+
+You can declare entities locally (in every spec files), but sometimes you will need to use/reference the same entity
+in multiple spec files (e.g.: an error message). In that case, you can create _global_ entities in separate files, and they
+will be picked-up when needed.
+
+Example of a local entity:
+
+```rb
+# spec/acceptance/api/users_acceptance_spec.rb
+require 'rails_helper'
+
+RSpec.describe 'Users', type: :acceptance do
+ resource 'Users', 'Manage users'
+
+ # This is a local entity
+ entity :user,
+ id: { type: :integer, description: 'The id' },
+ email: { type: :string, description: 'The name' },
+ role: { type: :string, description: 'The name' },
+ created_at: { type: :datetime, description: 'Creation date' },
+ updated_at: { type: :datetime, description: 'Modification date' },
+ url: { type: :string, description: 'URL to this category' }
+
+ on_get '/api/users/', 'Users list' do
+ for_code 200, 'Success response', expect_many: :user do |url|
+ test_response_of url
+ end
+ end
+
+ #...
+end
+```
+
+Defining global entities:
+
+```rb
+# spec/support/entities/user.rb
+# This file should be required at some point in the "rails_helper" or "acceptance_helper"
+
+require 'rspec/rails/api/metadata'
+
+RSpec::Rails::Api::Metadata.add_entity :user,
+ id: { type: :integer, description: 'The id' },
+ email: { type: :string, description: 'The name' },
+ role: { type: :string, description: 'The name' },
+ created_at: { type: :datetime, description: 'Creation date' },
+ updated_at: { type: :datetime, description: 'Modification date' },
+ url: { type: :string, description: 'URL to this category' }
+
+```
+
+Organization of the global entities declaration is up to you.
+
+```rb
+# spec/acceptance/api/users_acceptance_spec.rb
+require 'rails_helper'
+
+RSpec.describe 'Users', type: :acceptance do
+ resource 'Users', 'Manage users'
+
+ on_get '/api/users/', 'Users list' do
+ # "user" will use the global user entity
+ for_code 200, 'Success response', expect_many: :user do |url|
+ test_response_of url
+ end
+ end
+
+ #...
+end
+```
+
### DSL
#### Example groups
##### `resource(type, description)`
@@ -195,10 +361,27 @@
Starts a resource description.
- It must be called before any other documentation calls.
- It should be in the first `describe block`
+A resource may be completed across multiple spec files:
+
+```rb
+# an_acceptance_spec.rb
+RSpec.describe 'Something', type: :acceptance do
+ resource 'User', 'Manage users'
+end
+
+# another_acceptance_spec.rb
+RSpec.describe 'Something else', type: :acceptance do
+ resource 'User', 'Another description'
+end
+
+```
+
+The first evaluated `resource` statement will be used as description; all the tests in both files will complete it.
+
##### `entity(type, fields)`
Describes an entity for the documentation. The type is only a reference,
you can put whatever fits (i.e: `:account`, `:user`, ...).
@@ -421,15 +604,34 @@
# ...
end
# ...
```
+##### `requires_security(scheme_references)`
+
+Specifies the valid security schemes to use for this request. Security schemes are declared at the renderer level
+(see [the configuration example](#Configuration)).
+
+```rb
+# Given a previously :basic scheme
+
+# ...
+ on_get '/some/path' do
+ require_security :basic, :implicit
+
+ for_code 200 do |url|
+ #...
+ end
+ end
+# ...
+```
+
#### Examples
Example methods are available in `for_code` blocks
-##### `test_response_of(example, path_params: {}, payload: {}, headers: {})`
+##### `test_response_of(example, path_params: {}, payload: {}, headers: {}, ignore_content_type: false)`
Visits the described URL and:
- Expects the response code to match the described one
- Expects the content type to be `application/json`
@@ -439,9 +641,10 @@
- `path_params`: a hash of overrides for path params (useful if a custom
value is needed)
- `payload`: a hash of values to send. Ignored for GET and DELETE
requests
- `headers`: a hash of custom headers.
+- `ignore_content_type`: whether to ignore response's content-type. By default, checks for a JSON response
```ruby
for_code 200, 'Success' do |url|
test_response_of url
end