rspec-api_helpers 0.2.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|