rspec-api_helpers 0.2.1 → 1.0.2
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/README.md +95 -16
- data/lib/rspec/api_helpers.rb +21 -0
- data/lib/rspec/api_helpers/adapter/active_model/adapter.rb +19 -0
- data/lib/rspec/api_helpers/adapter/active_model/collection.rb +46 -0
- data/lib/rspec/api_helpers/adapter/active_model/common_helpers.rb +74 -0
- data/lib/rspec/api_helpers/adapter/active_model/resource.rb +77 -0
- data/lib/rspec/api_helpers/adapter/jsonapi/adapter.rb +52 -0
- data/lib/rspec/api_helpers/dispatcher.rb +16 -0
- data/lib/rspec/api_helpers/example_group_methods.rb +83 -142
- data/lib/rspec/api_helpers/example_methods.rb +2 -61
- data/lib/rspec/api_helpers/version.rb +1 -1
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fc01855aa47f3e617fdfca911ccbf4aede2e50a3
|
4
|
+
data.tar.gz: 5db89416125336490daa28757862b0a74887dc86
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b53bd402dd540f09d4b63e471f8bffa5e985a2b56e94ea4ed506c4c9eddbdb5dcd771a9bc378d23dfb7659239b6e5d408365a0509fd317e7701e9bdd097323a
|
7
|
+
data.tar.gz: dfb3344d4d964e568a399157e3587b15893b48d2ce29f3d8ad8ca274c2821b1df850683e52ba6468f5b0ba1c445f56ed44ec4e5e8c679a584864340a067e6ee4
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Rspec::ApiHelpers
|
2
2
|
|
3
|
-
Usefull Rspec helpers for APIs (currently
|
3
|
+
Usefull Rspec helpers for APIs (currently JSONAPI and AM-JSON adapters are supported)
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -29,43 +29,122 @@ In your `rails_helper.rb` add in the top:
|
|
29
29
|
require 'rspec/api_helpers'
|
30
30
|
```
|
31
31
|
|
32
|
-
|
32
|
+
Then you need to specify the adapter you want.
|
33
33
|
|
34
34
|
```ruby
|
35
|
-
|
35
|
+
RSpec.configure do |config|
|
36
|
+
config.include Rspec::ApiHelpers.with(adapter: :json_api)
|
37
|
+
end
|
36
38
|
```
|
39
|
+
Other possible options for the adapter is `active_model`.
|
37
40
|
|
41
|
+
You can also inject your custom made class by providing the class:
|
42
|
+
```ruby
|
43
|
+
RSpec.configure do |config|
|
44
|
+
config.include Rspec::ApiHelpers.with(adapter: Adapter::Hal)
|
45
|
+
end
|
46
|
+
```
|
38
47
|
|
39
48
|
### Examples
|
49
|
+
The library heavily uses dynamic scopes through procs (an alternative to eval).
|
50
|
+
|
51
|
+
|
52
|
+
#### General
|
40
53
|
|
41
54
|
```ruby
|
42
55
|
it_returns_status(200)
|
43
56
|
```
|
44
|
-
It
|
57
|
+
It expects the HTTP response status to be 200.
|
45
58
|
|
46
59
|
```ruby
|
47
|
-
|
60
|
+
it_includes_in_headers('SESSION_TOKEN' => proc{@user.token})
|
61
|
+
```
|
62
|
+
It expects the HTTP response to include this header (and value).
|
63
|
+
|
64
|
+
#### Resource
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
it_returns_attributes(resource: 'user', model: '@user', attrs: [
|
48
68
|
:email, :name
|
49
69
|
])
|
50
70
|
```
|
51
|
-
|
52
|
-
|
71
|
+
|
72
|
+
It expects the JSON resource to contain :email and :name attributes and
|
73
|
+
be equal with with `@user` methods.
|
74
|
+
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
it_returns_attribute_values(
|
78
|
+
resource: 'user', model: proc{@user}, attrs: [
|
79
|
+
:id, :name, :email, :admin, :activated, :created_at, :updated_at
|
80
|
+
], modifiers: {
|
81
|
+
[:created_at, :updated_at] => proc{|i| i.iso8601},
|
82
|
+
:id => proc{|i| i.to_s}
|
83
|
+
}
|
84
|
+
)
|
85
|
+
```
|
86
|
+
It expects the JSON resource to contain specific attribute values as above but for
|
87
|
+
`:updated_at`, `:created_at` and `id` it applies specific methods first, defined in the
|
88
|
+
`modifier` hash (note that the methods are applied in the JSON resource, not in the variable)
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
it_returns_no_attributes(
|
92
|
+
resource: 'user', attrs: [:foo1, :foo2, :foo3]
|
93
|
+
)
|
94
|
+
```
|
95
|
+
It expects the JSON resource to NOT contain any of those attributes.
|
96
|
+
|
97
|
+
#### Collection
|
98
|
+
```ruby
|
99
|
+
it_returns_collection_size(resource: 'users', size: 6)
|
100
|
+
```
|
101
|
+
|
102
|
+
It expects the JSON collection to have `6` resources.
|
53
103
|
|
54
104
|
```ruby
|
55
|
-
|
56
|
-
resource: '
|
57
|
-
|
58
|
-
|
59
|
-
modifier: 'iso8601'
|
105
|
+
it_returns_collection_attributes(
|
106
|
+
resource: 'users', attrs: [
|
107
|
+
:id, :name, :email, :admin, :activated, :created_at, :updated_at
|
108
|
+
]
|
60
109
|
)
|
61
110
|
```
|
62
|
-
|
63
|
-
|
111
|
+
|
112
|
+
It expects the JSON collection to have those attributes (no value checking).
|
113
|
+
|
64
114
|
|
65
115
|
```ruby
|
66
|
-
|
116
|
+
it_returns_no_collection_attributes(
|
117
|
+
resource: 'users', attrs: [
|
118
|
+
:foo
|
119
|
+
]
|
120
|
+
)
|
67
121
|
```
|
68
|
-
|
122
|
+
|
123
|
+
It expects the JSON collection NOT to have those attributes (no value checking).
|
124
|
+
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
it_returns_collection_attribute_values(
|
128
|
+
resource: 'users', model: proc{User.first!}, attrs: [
|
129
|
+
:id, :name, :email, :admin, :activated, :created_at, :updated_at
|
130
|
+
], modifiers: {
|
131
|
+
[:created_at, :updated_at] => proc{|i| i.iso8601},
|
132
|
+
:id => proc{|i| i.to_s}
|
133
|
+
}
|
134
|
+
)
|
135
|
+
```
|
136
|
+
|
137
|
+
It expects the JSON collection to contain specific attribute values as above but for
|
138
|
+
`:updated_at`, `:created_at` and `id` it applies specific methods first, defined in the
|
139
|
+
`modifier` hash (note that the methods are applied in the JSON resource, not in the variable)
|
140
|
+
|
141
|
+
|
142
|
+
## To Do
|
143
|
+
* Enhance documentation
|
144
|
+
* Better dir structure (break example group methods to modules)
|
145
|
+
* Support for nested resources
|
146
|
+
* Add tests
|
147
|
+
* Support for HAL/Siren adapter
|
69
148
|
|
70
149
|
## Contributing
|
71
150
|
|
data/lib/rspec/api_helpers.rb
CHANGED
@@ -1,12 +1,33 @@
|
|
1
1
|
require 'rspec/api_helpers/version'
|
2
|
+
require 'rspec/api_helpers/dispatcher'
|
2
3
|
require 'rspec/api_helpers/example_methods'
|
3
4
|
require 'rspec/api_helpers/example_group_methods'
|
4
5
|
|
5
6
|
module Rspec
|
7
|
+
|
6
8
|
module ApiHelpers
|
9
|
+
class << self;
|
10
|
+
attr_accessor :adapter
|
11
|
+
end
|
12
|
+
|
7
13
|
def self.included(receiver)
|
8
14
|
receiver.extend ExampleGroupMethods
|
9
15
|
receiver.send :include, ExampleMethods
|
10
16
|
end
|
17
|
+
|
18
|
+
def self.with(adapter:)
|
19
|
+
if adapter.is_a?(Class)
|
20
|
+
self.adapter = adapter
|
21
|
+
else
|
22
|
+
case adapter.to_s.to_sym
|
23
|
+
when :active_model
|
24
|
+
self.adapter = Adapter::ActiveModel
|
25
|
+
when :json_api
|
26
|
+
self.adapter = Adapter::JsonApi
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
return self
|
31
|
+
end
|
11
32
|
end
|
12
33
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Adapter
|
2
|
+
class ActiveModel
|
3
|
+
include RSpec::Matchers
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def resource(options, response, _binding)
|
7
|
+
hashed_response = HashWithIndifferentAccess.new(JSON.parse(response.body))
|
8
|
+
|
9
|
+
return Resource.new(options, hashed_response, _binding)
|
10
|
+
end
|
11
|
+
|
12
|
+
def collection(options, response, _binding)
|
13
|
+
hashed_response = HashWithIndifferentAccess.new(JSON.parse(response.body))
|
14
|
+
|
15
|
+
return Collection.new(options, hashed_response, _binding)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class Adapter::ActiveModel::Collection
|
2
|
+
include Adapter::ActiveModel::CommonHelpers
|
3
|
+
include RSpec::Matchers
|
4
|
+
|
5
|
+
def initialize(options, response, _binding)
|
6
|
+
@options = HashWithIndifferentAccess.new(options)
|
7
|
+
@response = response
|
8
|
+
@_binding = _binding
|
9
|
+
end
|
10
|
+
|
11
|
+
def has_size(size)
|
12
|
+
expect(collection.size).to(eq size)
|
13
|
+
end
|
14
|
+
|
15
|
+
def sample(new_options = {})
|
16
|
+
Adapter::ActiveModel::Resource.new(
|
17
|
+
options.merge(new_options), collection.sample.hash, @_binding
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_by(id, new_options = {})
|
22
|
+
resource = collection.find{|i| i.send(id).to_s == model.send(id).to_s}
|
23
|
+
if resource.nil?
|
24
|
+
raise "Resource was not found for id: #{id} (model value: #{model.send(id)})"
|
25
|
+
end
|
26
|
+
|
27
|
+
return Adapter::ActiveModel::Resource.new(
|
28
|
+
options.merge(new_options), resource.hash, @_binding
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
attr_reader :options
|
34
|
+
|
35
|
+
def model
|
36
|
+
@model ||= parse_model(
|
37
|
+
@_binding.instance_exec(&options[:model])
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def collection
|
42
|
+
@collection ||= objectize_collection(
|
43
|
+
@response, root: options[:resource]
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Adapter::ActiveModel::CommonHelpers
|
2
|
+
def objectize_collection(collection, root: nil, existing: true)
|
3
|
+
if root
|
4
|
+
collection = collection[root]
|
5
|
+
end
|
6
|
+
|
7
|
+
return collection.map{|resource|
|
8
|
+
object_hash(resource, existing: existing)
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def objectize_resource(resource, root: nil, existing: true)
|
13
|
+
if root
|
14
|
+
obj = object_hash(resource[root], existing: existing)
|
15
|
+
else
|
16
|
+
obj = object_hash(resource, existing: existing)
|
17
|
+
end
|
18
|
+
|
19
|
+
return obj
|
20
|
+
end
|
21
|
+
|
22
|
+
def object_hash(hash, existing: true)
|
23
|
+
ObjectHash.new(hash, existing: existing)
|
24
|
+
end
|
25
|
+
|
26
|
+
class ObjectHash
|
27
|
+
#existing denotes whether we search for attributes that exist on the
|
28
|
+
#resource or attributes that shouldn't exist
|
29
|
+
attr_accessor :hash, :existing
|
30
|
+
def initialize(hash, existing: true)
|
31
|
+
@hash = HashWithIndifferentAccess.new(hash)
|
32
|
+
@existing = existing
|
33
|
+
end
|
34
|
+
|
35
|
+
def method_missing(name)
|
36
|
+
if existing
|
37
|
+
if hash.key?(name)
|
38
|
+
return hash[name]
|
39
|
+
else
|
40
|
+
return raise KeyError.new("Attribute not found in resource: #{name}")
|
41
|
+
end
|
42
|
+
else
|
43
|
+
if hash.key?(name)
|
44
|
+
return raise(
|
45
|
+
KeyError.new(
|
46
|
+
"Attribute found in resource when it shouldn't: #{name}"
|
47
|
+
)
|
48
|
+
)
|
49
|
+
else
|
50
|
+
return :attribute_not_found
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def superset_mismatch_error(superset, subset)
|
57
|
+
"Expected \n #{subset.to_a.to_s} \n to be included in \n #{superset.to_a.to_s}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def parse_model(model)
|
61
|
+
return model unless model.is_a? Hash
|
62
|
+
|
63
|
+
return object_hash(model)
|
64
|
+
end
|
65
|
+
|
66
|
+
def modifier_for(key)
|
67
|
+
if modifiers_hash[key]
|
68
|
+
return modifiers_hash[key]
|
69
|
+
else
|
70
|
+
return proc{|i| i}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
class Adapter::ActiveModel::Resource
|
2
|
+
include Adapter::ActiveModel::CommonHelpers
|
3
|
+
include RSpec::Matchers
|
4
|
+
|
5
|
+
def initialize(options, response, _binding)
|
6
|
+
@options = HashWithIndifferentAccess.new(options)
|
7
|
+
@response = response
|
8
|
+
@_binding = _binding
|
9
|
+
end
|
10
|
+
|
11
|
+
def resource
|
12
|
+
@resource ||= objectize_resource(
|
13
|
+
@response, root: options[:resource], existing: options[:existing]
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def compare_attribute(attribute)
|
18
|
+
expect(resource.send(attribute)).to(
|
19
|
+
eql(
|
20
|
+
modifier_for(attribute).call(
|
21
|
+
model.send(attribute)
|
22
|
+
)
|
23
|
+
)
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def embedded_collection(new_options = {})
|
28
|
+
raise 'embeds option missing' unless (options[:embeds] || new_options[:embeds])
|
29
|
+
|
30
|
+
Adapter::ActiveModel::Collection.new(
|
31
|
+
options.merge(new_options), resource.send(options[:embeds]), @_binding
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def has_no_attribute(attribute)
|
36
|
+
expect(resource.send(attribute)).to(eql(:attribute_not_found))
|
37
|
+
end
|
38
|
+
|
39
|
+
def has_attribute(attribute)
|
40
|
+
expect(resource.send(attribute)).to_not(eql(:attribute_not_found))
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
attr_reader :options
|
45
|
+
|
46
|
+
def model
|
47
|
+
raise 'model is missing' if options[:model].blank?
|
48
|
+
|
49
|
+
@model ||= parse_model(
|
50
|
+
@_binding.instance_exec(&options[:model])
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
def attrs
|
55
|
+
options[:attrs].map{|i| i.to_s}
|
56
|
+
end
|
57
|
+
|
58
|
+
def attrs?
|
59
|
+
attrs.any?
|
60
|
+
end
|
61
|
+
|
62
|
+
def modifiers_hash
|
63
|
+
return {} if options[:modifiers].blank?
|
64
|
+
|
65
|
+
@modifiers_hash = {}
|
66
|
+
options[:modifiers].each do |key, value|
|
67
|
+
[key].flatten.each do |attr|
|
68
|
+
if attrs?
|
69
|
+
raise "#{attr} missing from :attrs param" unless attrs.include?(attr.to_s)
|
70
|
+
@modifiers_hash[attr] = value
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
return @modifiers_hash
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class Adapter
|
2
|
+
class JsonApi
|
3
|
+
include RSpec::Matchers
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def resource(options, response, _binding)
|
7
|
+
jsonapi_resource = HashWithIndifferentAccess.new(JSON.parse(response.body))
|
8
|
+
|
9
|
+
jsonapi_resource = transform_resource(jsonapi_resource.dig('data') || {})
|
10
|
+
|
11
|
+
return Adapter::ActiveModel::Resource.new(options, jsonapi_resource, _binding)
|
12
|
+
end
|
13
|
+
|
14
|
+
def collection(options, response, _binding)
|
15
|
+
jsonapi_collection = HashWithIndifferentAccess.new(JSON.parse(response.body))
|
16
|
+
|
17
|
+
jsonapi_collection = transform_collection(jsonapi_collection.dig('data') || {})
|
18
|
+
|
19
|
+
return Adapter::ActiveModel::Collection.new(options, jsonapi_collection, _binding)
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
private
|
24
|
+
def transform_resource(jsonapi_hash, root: true)
|
25
|
+
type = jsonapi_hash.dig('type').singularize
|
26
|
+
json_hash = jsonapi_hash.dig('attributes').transform_keys{|key|
|
27
|
+
key.underscore
|
28
|
+
}.merge(id: jsonapi_hash.dig('id').to_s)
|
29
|
+
|
30
|
+
if root
|
31
|
+
return HashWithIndifferentAccess.new({type => json_hash})
|
32
|
+
else
|
33
|
+
return HashWithIndifferentAccess.new(json_hash)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def transform_collection(jsonapi_hash, root: true)
|
38
|
+
type = jsonapi_hash.first.dig('type').pluralize
|
39
|
+
|
40
|
+
json_hash = jsonapi_hash.map{|data|
|
41
|
+
transform_resource(data, root: false)
|
42
|
+
}
|
43
|
+
|
44
|
+
if root
|
45
|
+
return HashWithIndifferentAccess.new({type => json_hash})
|
46
|
+
else
|
47
|
+
return json_hash
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rspec/api_helpers/adapter/jsonapi/adapter'
|
2
|
+
require 'rspec/api_helpers/adapter/active_model/adapter'
|
3
|
+
require 'rspec/api_helpers/adapter/active_model/common_helpers'
|
4
|
+
require 'rspec/api_helpers/adapter/active_model/resource'
|
5
|
+
require 'rspec/api_helpers/adapter/active_model/collection'
|
6
|
+
|
7
|
+
|
8
|
+
module Rspec
|
9
|
+
module ApiHelpers
|
10
|
+
class Dispatcher
|
11
|
+
def adapter
|
12
|
+
Rspec::ApiHelpers.adapter
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -7,56 +7,23 @@ module Rspec
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
model = eval(model)
|
17
|
-
if model.is_a? Hash
|
18
|
-
model = object_hash(model)
|
19
|
-
end
|
20
|
-
|
21
|
-
if only
|
22
|
-
only.each do |attribute|
|
23
|
-
begin
|
24
|
-
if modifier.has_key?(attribute)
|
25
|
-
modifier[attribute] = [modifier[attribute]].flatten
|
26
|
-
|
27
|
-
expect(api_resource.send(attribute)).to(
|
28
|
-
eql(
|
29
|
-
modifier[attribute].inject(
|
30
|
-
model.send(attribute), :send
|
31
|
-
)
|
32
|
-
)
|
33
|
-
)
|
34
|
-
else
|
35
|
-
expect(api_resource.send(attribute)).to(
|
36
|
-
eql(model.send(attribute))
|
37
|
-
)
|
38
|
-
end
|
39
|
-
rescue RSpec::Expectations::ExpectationNotMetError => e
|
40
|
-
e.message << "failed at model attribute: #{attribute}"
|
41
|
-
raise e
|
42
|
-
end
|
43
|
-
end
|
10
|
+
#make rehashable
|
11
|
+
def it_includes_in_headers(headers = {})
|
12
|
+
headers.each do |header, value|
|
13
|
+
it "returns headers #{header} wih value: #{value}" do
|
14
|
+
expect(last_response.headers[header.to_s]).to eq(eval(value))
|
44
15
|
end
|
45
16
|
end
|
46
|
-
|
47
17
|
end
|
48
|
-
alias_method :it_returns_more_attribute_values, :it_returns_attribute_values
|
49
|
-
|
50
18
|
|
51
|
-
def
|
52
|
-
it "expects returned resource (#{resource}) to
|
53
|
-
|
54
|
-
last_response.body, root: resource, existing: false
|
55
|
-
)
|
19
|
+
def it_returns_attribute_values(options = {})
|
20
|
+
it "expects returned resource (#{options[:resource]}) to have model attribute values" do
|
21
|
+
resource = dispatcher.adapter.resource(options.merge(existing: true), last_response, self)
|
56
22
|
|
57
|
-
|
23
|
+
#support proc on attrs
|
24
|
+
options[:attrs].each do |attribute|
|
58
25
|
begin
|
59
|
-
|
26
|
+
resource.compare_attribute(attribute)
|
60
27
|
rescue RSpec::Expectations::ExpectationNotMetError => e
|
61
28
|
e.message << "failed at model attribute: #{attribute}"
|
62
29
|
raise e
|
@@ -64,61 +31,45 @@ module Rspec
|
|
64
31
|
end
|
65
32
|
end
|
66
33
|
end
|
34
|
+
alias_method :it_returns_more_attribute_values, :it_returns_attribute_values
|
67
35
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
71
|
-
expect(last_response.headers[header.to_s]).to eq(eval(value))
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
36
|
+
def it_returns_no_attributes(options = {})
|
37
|
+
it "expects returned resource (#{options[:root]}) to NOT have the following attributes" do
|
38
|
+
resource = dispatcher.adapter.resource(options.merge(existing: false), last_response, self)
|
75
39
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
40
|
+
options[:attrs].each do |attribute|
|
41
|
+
begin
|
42
|
+
resource.has_no_attribute(attribute)
|
43
|
+
rescue RSpec::Expectations::ExpectationNotMetError => e
|
44
|
+
e.message << "failed at model attribute: #{attribute}"
|
45
|
+
raise e
|
46
|
+
end
|
47
|
+
end
|
80
48
|
end
|
81
49
|
end
|
82
50
|
|
83
|
-
def
|
84
|
-
it "returns the correct number of
|
85
|
-
|
86
|
-
expect(resources.sample[embeds].length).to eql(size)
|
87
|
-
end
|
88
|
-
end
|
51
|
+
def it_returns_collection_size(options = {})
|
52
|
+
it "returns the correct number of resources in the #{options[:resource]} collection" do
|
53
|
+
collection = dispatcher.adapter.collection(options.merge(existing: true), last_response, self)
|
89
54
|
|
90
|
-
|
91
|
-
it "returns the correct number of embedded resource #{embeds} in the #{resource} resource" do
|
92
|
-
resource = objectize_resource(last_response.body, root: resource)
|
93
|
-
expect(resource.send(embeds).length).to eql(size)
|
55
|
+
collection.has_size(options[:size])
|
94
56
|
end
|
95
57
|
end
|
96
58
|
|
97
|
-
def it_returns_collection_attributes(
|
98
|
-
it "returns the correct attributes (no value checking) for
|
99
|
-
|
59
|
+
def it_returns_collection_attributes(options = {})
|
60
|
+
it "returns the correct attributes (no value checking) for resources in the #{options[:resource]} collection" do
|
61
|
+
sample_resource = dispatcher.adapter.collection(
|
62
|
+
options, last_response, self
|
63
|
+
).sample(existing: true, resource: nil)
|
100
64
|
|
101
|
-
|
102
|
-
|
103
|
-
)
|
104
|
-
checking_attributes_set = SortedSet.new(attributes.map(&:to_s).to_set)
|
105
|
-
|
106
|
-
if subset
|
65
|
+
#support proc on attrs
|
66
|
+
options[:attrs].each do |attribute|
|
107
67
|
begin
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
raise(
|
113
|
-
$!,
|
114
|
-
superset_mismatch_error(
|
115
|
-
resource_attributes_set, checking_attributes_set
|
116
|
-
),
|
117
|
-
$!.backtrace
|
118
|
-
)
|
68
|
+
sample_resource.has_attribute(attribute)
|
69
|
+
rescue RSpec::Expectations::ExpectationNotMetError => e
|
70
|
+
e.message << "failed at model attribute: #{attribute}"
|
71
|
+
raise e
|
119
72
|
end
|
120
|
-
else
|
121
|
-
expect(resource_attributes_set).to eq(checking_attributes_set)
|
122
73
|
end
|
123
74
|
end
|
124
75
|
end
|
@@ -127,52 +78,35 @@ module Rspec
|
|
127
78
|
:it_returns_collection_attributes
|
128
79
|
)
|
129
80
|
|
130
|
-
def
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
embedded_resource = object_hash(resource.sample.send(embeds.to_sym))
|
136
|
-
|
137
|
-
embedded_resource_attributes_set = SortedSet.new(
|
138
|
-
embedded_resource.hash.keys.map(&:to_s)
|
139
|
-
)
|
140
|
-
checking_attributes_set = SortedSet.new(attributes.map(&:to_s).to_set)
|
81
|
+
def it_returns_no_collection_attributes(options = {})
|
82
|
+
it "expects returned collection (#{options[:resource]}) to NOT have the following attributes" do
|
83
|
+
sample_resource = dispatcher.adapter.collection(
|
84
|
+
options, last_response, self
|
85
|
+
).sample(existing: false, resource: nil)
|
141
86
|
|
142
|
-
|
87
|
+
#support proc on attrs
|
88
|
+
options[:attrs].each do |attribute|
|
143
89
|
begin
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
).to(
|
149
|
-
eq(true)
|
150
|
-
)
|
151
|
-
rescue RSpec::Expectations::ExpectationNotMetError => _
|
152
|
-
raise(
|
153
|
-
$!,
|
154
|
-
superset_mismatch_error(
|
155
|
-
embedded_resource_attributes_set, checking_attributes_set
|
156
|
-
),
|
157
|
-
$!.backtrace
|
158
|
-
)
|
90
|
+
sample_resource.has_no_attribute(attribute)
|
91
|
+
rescue RSpec::Expectations::ExpectationNotMetError => e
|
92
|
+
e.message << "failed at model attribute: #{attribute}"
|
93
|
+
raise e
|
159
94
|
end
|
160
|
-
else
|
161
|
-
expect(resource_attributes_set).to eq(checking_attributes_set)
|
162
95
|
end
|
163
96
|
end
|
164
|
-
|
165
97
|
end
|
166
98
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
99
|
+
#finds_by id
|
100
|
+
def it_returns_collection_attribute_values(options = {})
|
101
|
+
it "expects returned collection (#{options[:resource]}) to have model attribute values" do
|
102
|
+
resource = dispatcher.adapter.collection(
|
103
|
+
options, last_response, self
|
104
|
+
).find_by(:id, existing: true, resource: nil)
|
172
105
|
|
173
|
-
|
106
|
+
#support proc on attrs
|
107
|
+
options[:attrs].each do |attribute|
|
174
108
|
begin
|
175
|
-
|
109
|
+
resource.compare_attribute(attribute)
|
176
110
|
rescue RSpec::Expectations::ExpectationNotMetError => e
|
177
111
|
e.message << "failed at model attribute: #{attribute}"
|
178
112
|
raise e
|
@@ -181,29 +115,36 @@ module Rspec
|
|
181
115
|
end
|
182
116
|
end
|
183
117
|
|
184
|
-
|
185
|
-
|
186
|
-
)
|
187
|
-
it "
|
188
|
-
|
189
|
-
|
190
|
-
)
|
118
|
+
=begin
|
119
|
+
#not tested
|
120
|
+
def it_returns_embedded_collection_size(options = {})
|
121
|
+
it "returns the correct number of embedded resource #{embeds} in the #{resource} resource" do
|
122
|
+
collection = dispatcher.adapter.resource(
|
123
|
+
options.merge(existing: true), last_response, self
|
124
|
+
).embedded_collection
|
191
125
|
|
192
|
-
|
193
|
-
|
194
|
-
|
126
|
+
collection.has_size(options[:size])
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def it_returns_collection_embedded_collection_size(options = {})
|
131
|
+
it "returns the correct number of embedded resource #{embeds} in the #{resource} collection" do
|
195
132
|
|
196
|
-
attributes.each do |attribute|
|
197
|
-
begin
|
198
|
-
expect(embedded_resource.send(attribute)).to eql(:attribute_not_found)
|
199
|
-
rescue RSpec::Expectations::ExpectationNotMetError => e
|
200
|
-
e.message << "failed at model attribute: #{attribute}"
|
201
|
-
raise e
|
202
|
-
end
|
203
|
-
end
|
204
133
|
end
|
205
134
|
end
|
206
135
|
|
136
|
+
def it_returns_collection_embedded_resource_attributes(options = {})
|
137
|
+
it "returns the correct attributes (no value checking) of #{embeds} resource inside #{resource} collection" do
|
138
|
+
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def it_returns_no_collection_embedded_resource_attributes(options = {})
|
143
|
+
it "expects the embedded resource #{embeds} inside the returned collection (#{resource}) to NOT have the following attributes" do
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
147
|
+
=end
|
207
148
|
end
|
208
149
|
end
|
209
150
|
end
|
@@ -3,67 +3,8 @@ require "rspec/api_helpers/version"
|
|
3
3
|
module Rspec
|
4
4
|
module ApiHelpers
|
5
5
|
module ExampleMethods
|
6
|
-
def
|
7
|
-
|
8
|
-
array_hash = HashWithIndifferentAccess.new(JSON.parse(json))
|
9
|
-
|
10
|
-
if root
|
11
|
-
array_hash = array_hash[root]
|
12
|
-
end
|
13
|
-
|
14
|
-
array_hash.each do |resource|
|
15
|
-
array << object_hash(resource, existing: existing)
|
16
|
-
end
|
17
|
-
|
18
|
-
return array
|
19
|
-
end
|
20
|
-
|
21
|
-
def objectize_resource(json, root:, existing: true)
|
22
|
-
hash = HashWithIndifferentAccess.new(JSON.parse(json))
|
23
|
-
if root
|
24
|
-
obj = object_hash(hash[root], existing: existing)
|
25
|
-
else
|
26
|
-
obj = object_hash(hash, existing: existing)
|
27
|
-
end
|
28
|
-
|
29
|
-
return obj
|
30
|
-
end
|
31
|
-
|
32
|
-
def object_hash(hash, existing: true)
|
33
|
-
ObjectHash.new(hash, existing: existing)
|
34
|
-
end
|
35
|
-
|
36
|
-
class ObjectHash
|
37
|
-
#existing denotes whether we search for attributes that exist on the
|
38
|
-
#resource or attributes that shouldn't exist
|
39
|
-
attr_accessor :hash, :existing
|
40
|
-
def initialize(hash, existing: true)
|
41
|
-
@hash = HashWithIndifferentAccess.new(hash)
|
42
|
-
@existing = existing
|
43
|
-
end
|
44
|
-
def method_missing(name)
|
45
|
-
if existing
|
46
|
-
if hash.key?(name)
|
47
|
-
return hash[name]
|
48
|
-
else
|
49
|
-
return raise KeyError.new("Attribute not found in resource: #{name}")
|
50
|
-
end
|
51
|
-
else
|
52
|
-
if hash.key?(name)
|
53
|
-
return raise(
|
54
|
-
KeyError.new(
|
55
|
-
"Attribute found in resource when it shouldn't: #{name}"
|
56
|
-
)
|
57
|
-
)
|
58
|
-
else
|
59
|
-
return :attribute_not_found
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def superset_mismatch_error(superset, subset)
|
66
|
-
"Expected \n #{subset.to_a.to_s} \n to be included in \n #{superset.to_a.to_s}"
|
6
|
+
def dispatcher
|
7
|
+
@dispatcher ||= Rspec::ApiHelpers::Dispatcher.new
|
67
8
|
end
|
68
9
|
end
|
69
10
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec-api_helpers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Filippos Vasilakis
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2016-12-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -53,6 +53,12 @@ files:
|
|
53
53
|
- README.md
|
54
54
|
- Rakefile
|
55
55
|
- lib/rspec/api_helpers.rb
|
56
|
+
- lib/rspec/api_helpers/adapter/active_model/adapter.rb
|
57
|
+
- lib/rspec/api_helpers/adapter/active_model/collection.rb
|
58
|
+
- lib/rspec/api_helpers/adapter/active_model/common_helpers.rb
|
59
|
+
- lib/rspec/api_helpers/adapter/active_model/resource.rb
|
60
|
+
- lib/rspec/api_helpers/adapter/jsonapi/adapter.rb
|
61
|
+
- lib/rspec/api_helpers/dispatcher.rb
|
56
62
|
- lib/rspec/api_helpers/example_group_methods.rb
|
57
63
|
- lib/rspec/api_helpers/example_methods.rb
|
58
64
|
- lib/rspec/api_helpers/version.rb
|
@@ -77,7 +83,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
77
83
|
version: '0'
|
78
84
|
requirements: []
|
79
85
|
rubyforge_project:
|
80
|
-
rubygems_version: 2.
|
86
|
+
rubygems_version: 2.5.1
|
81
87
|
signing_key:
|
82
88
|
specification_version: 4
|
83
89
|
summary: Rspec matchers for APIs
|