smart_rspec 0.1.3 → 0.1.4
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 +0 -15
- data/lib/smart_rspec/macros.rb +2 -3
- data/lib/smart_rspec/matchers/be_matchers.rb +0 -6
- data/lib/smart_rspec/matchers/json_api_matchers.rb +43 -0
- data/lib/smart_rspec/matchers/other_matchers.rb +0 -1
- data/lib/smart_rspec/matchers.rb +3 -1
- data/lib/smart_rspec/support/controller/response.rb +47 -0
- data/lib/smart_rspec/support/model/assertions.rb +112 -0
- data/lib/smart_rspec/support/model/expectations.rb +43 -0
- data/lib/smart_rspec/version.rb +1 -1
- data/lib/smart_rspec.rb +2 -3
- data/spec/fixtures/response.rb +11 -0
- data/spec/{factories → fixtures}/user.rb +31 -16
- data/spec/fixtures/users.json +81 -0
- data/spec/smart_rspec/macros_spec.rb +13 -12
- data/spec/smart_rspec/matchers_spec.rb +182 -121
- data/spec/spec_helper.rb +3 -3
- metadata +13 -7
- data/lib/smart_rspec/support/assertions.rb +0 -119
- data/lib/smart_rspec/support/expectations.rb +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b6913c78ea542aba3c5bb09a26bbb2b5aaa8767
|
4
|
+
data.tar.gz: 7a94b954a2e829efa220e487f94f3eb94894bdc1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57e4f5b9b6cb2b9d55aa42c8d6e4cbdace08b94c0ae36cbcd311d74f8569dc0c88b842e42be6b362bcf087e2a9e486ab2b944d9b29bdda5ecb79774d7ada7c65
|
7
|
+
data.tar.gz: ebc06947e17c4c84aa5c0ba1f788dcc4f77ab6e3f09804b210694fda390f0667b627bd25ab47a130d458c5c6d58f727161ade7abeef039694d81ed8a121294f4
|
data/README.md
CHANGED
@@ -51,7 +51,6 @@ end
|
|
51
51
|
* [be_a_list_of](#be_a_list_of)
|
52
52
|
* [be_ascending](#be_ascending)
|
53
53
|
* [be_descending](#be_descending)
|
54
|
-
* [be_a_bad_request](#be_a_bad_request)
|
55
54
|
* ["Have" matchers](#have-matchers)
|
56
55
|
* [have](#have)
|
57
56
|
* [have_at_least](#have_at_least)
|
@@ -180,20 +179,6 @@ it { expect([4, 3, 2, 1]).to be_descending }
|
|
180
179
|
it { expect([1, 2, 3, 4]).not_to be_descending }
|
181
180
|
```
|
182
181
|
|
183
|
-
##### be_a_bad_request
|
184
|
-
``` ruby
|
185
|
-
context 'unauthenticated' do
|
186
|
-
subject { get :profile }
|
187
|
-
it { is_expected.to be_a_bad_request }
|
188
|
-
end
|
189
|
-
|
190
|
-
context 'authenticated' do
|
191
|
-
before { sign_in user }
|
192
|
-
subject { get :profile }
|
193
|
-
it { is_expected.to_not be_a_bad_request }
|
194
|
-
end
|
195
|
-
```
|
196
|
-
|
197
182
|
#### Have matchers
|
198
183
|
|
199
184
|
##### have(x).items
|
data/lib/smart_rspec/macros.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
require 'smart_rspec/support/assertions'
|
1
|
+
require 'smart_rspec/support/model/assertions'
|
2
2
|
|
3
3
|
module SmartRspec::Macros
|
4
|
-
include SmartRspec::Support::Assertions
|
4
|
+
include SmartRspec::Support::Model::Assertions
|
5
5
|
|
6
6
|
def belongs_to(*associations)
|
7
7
|
assert_association :belongs_to, associations
|
@@ -31,4 +31,3 @@ module SmartRspec::Macros
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
34
|
-
|
@@ -7,12 +7,6 @@ module SmartRspec
|
|
7
7
|
match { |actual| actual == actual.sort }
|
8
8
|
end
|
9
9
|
|
10
|
-
matcher :be_a_bad_request do
|
11
|
-
match do |response|
|
12
|
-
response.code.to_s =~ /^4/
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
10
|
matcher :be_a_list_of do |klass|
|
17
11
|
match do |collection|
|
18
12
|
collection.all? { |e| e.is_a?(klass) }
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module SmartRspec
|
2
|
+
module Matchers
|
3
|
+
module JsonApiMatchers
|
4
|
+
extend RSpec::Matchers::DSL
|
5
|
+
include SmartRspec::Support::Controller::Response
|
6
|
+
|
7
|
+
matcher :have_primary_data do |expected|
|
8
|
+
match do |response|
|
9
|
+
json(response).collection.all? do |record|
|
10
|
+
!record['id'].to_s.empty? && record['type'] == expected
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
matcher :have_data_attributes do |fields|
|
16
|
+
match do |response|
|
17
|
+
json(response).check_keys_in('attributes', fields)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
matcher :have_relationships do |relationships|
|
22
|
+
match do |response|
|
23
|
+
json(response).check_keys_in('relationships', relationships)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
matcher :have_included_relationships do
|
28
|
+
match do |response|
|
29
|
+
json(response)
|
30
|
+
return false if included_data.empty? || relationship_data.empty?
|
31
|
+
included_data.size == relationship_data.size &&
|
32
|
+
(included_data - relationship_data).empty?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
matcher :have_meta_record_count do |count|
|
37
|
+
match do |response|
|
38
|
+
json(response).meta_record_count == count
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/smart_rspec/matchers.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
require 'smart_rspec/support/regexes'
|
2
|
+
require 'smart_rspec/support/controller/response'
|
2
3
|
require 'smart_rspec/matchers/be_matchers'
|
4
|
+
require 'smart_rspec/matchers/json_api_matchers'
|
3
5
|
require 'smart_rspec/matchers/other_matchers'
|
4
6
|
|
5
7
|
module SmartRspec
|
6
8
|
module Matchers
|
7
9
|
include SmartRspec::Support::Regexes
|
8
10
|
include SmartRspec::Matchers::BeMatchers
|
11
|
+
include SmartRspec::Matchers::JsonApiMatchers
|
9
12
|
include SmartRspec::Matchers::OtherMatchers
|
10
13
|
end
|
11
14
|
end
|
12
|
-
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module SmartRspec
|
4
|
+
module Support
|
5
|
+
module Controller
|
6
|
+
module Response
|
7
|
+
def json(response)
|
8
|
+
@json ||= JSON.parse(response.body)
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def error
|
13
|
+
@error ||= @json['errors'].first
|
14
|
+
end
|
15
|
+
|
16
|
+
def collection
|
17
|
+
@collection ||= [@json['data']].flatten
|
18
|
+
end
|
19
|
+
|
20
|
+
def meta_record_count
|
21
|
+
@json['meta']['record_count']
|
22
|
+
end
|
23
|
+
|
24
|
+
def relationship_data
|
25
|
+
@relationship_data ||= collection.flat_map do |record|
|
26
|
+
record['relationships'].flat_map do |_, relation|
|
27
|
+
[relation['data']].flatten.map { |data| data.slice('type', 'id') }
|
28
|
+
end.compact
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def included_data
|
33
|
+
return [] if @json['included'].nil?
|
34
|
+
@included_data ||= @json['included'].flat_map do |record|
|
35
|
+
record.slice('type', 'id')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def check_keys_in(member, keys)
|
40
|
+
collection.all? do |record|
|
41
|
+
record[member].keys.sort == keys.sort
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module SmartRspec
|
2
|
+
module Support
|
3
|
+
module Model
|
4
|
+
module Assertions
|
5
|
+
def validates_email_of(attr, validation)
|
6
|
+
it 'has an invalid format' do
|
7
|
+
%w(foobar foobar@ @foobar foo@bar).each do |e|
|
8
|
+
be_valid_expectation(attr, e, subject.dup)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def validates_exclusion_of(attr, validation)
|
14
|
+
it 'has a reserved value' do
|
15
|
+
be_valid_expectation(attr, validation[:in].sample)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def validates_format_of(attr, validation)
|
20
|
+
it 'does not match the required format' do
|
21
|
+
mock, with =
|
22
|
+
validation.values_at(:mock).first,
|
23
|
+
validation.values_at(:with).first
|
24
|
+
|
25
|
+
if mock && with && with !~ mock
|
26
|
+
be_valid_expectation(attr, mock)
|
27
|
+
else
|
28
|
+
raise ArgumentError, ':with and :mock are required when using the :format validation'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def validates_inclusion_of(attr, validation)
|
34
|
+
it 'is out of the scope of possible values' do
|
35
|
+
begin
|
36
|
+
value = SecureRandom.hex
|
37
|
+
end while validation[:in].include?(value)
|
38
|
+
be_valid_expectation(attr, value)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def validates_length_of(attr, validation)
|
43
|
+
validation.each do |key, value|
|
44
|
+
next unless [:in, :is, :maximum, :minimum, :within].include?(key)
|
45
|
+
txt, n = build_length_validation(key, value)
|
46
|
+
it txt do
|
47
|
+
be_valid_expectation(attr, 'x' * n)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def validates_presence_of(attr, validation)
|
53
|
+
it 'is blank' do
|
54
|
+
be_valid_expectation(attr, nil, subject.dup)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def validates_uniqueness_of(attr, validation)
|
59
|
+
it 'is already in use' do
|
60
|
+
if !validation.is_a?(Hash) || !validation.has_key?(:mock)
|
61
|
+
raise ArgumentError, 'A "mock" must be set when validating the uniqueness of a record'
|
62
|
+
elsif subject.persisted? || subject.save
|
63
|
+
mock, scope = validation.values_at(:mock, :scope)
|
64
|
+
mock.send("#{scope}=", subject.send(scope)) unless scope.to_s.empty?
|
65
|
+
be_valid_expectation(attr, subject.send(attr), mock)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def assert_has_attributes(attrs, options) type_str = build_type_str(options)
|
71
|
+
attrs.each do |attr|
|
72
|
+
it %Q(has an attribute named "#{attr}"#{type_str}) do
|
73
|
+
expect(subject).to respond_to(attr)
|
74
|
+
has_attributes_expectation(attr, options)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def assert_association(type, associations)
|
80
|
+
associations.each do |model|
|
81
|
+
it "#{type.to_s.gsub('_', ' ')} #{model}" do
|
82
|
+
expect(subject).to respond_to(model)
|
83
|
+
association_expectation(type, model)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def build_length_validation(key, value)
|
89
|
+
case key
|
90
|
+
when :in, :within then ['is out of the length range', value.max + 1]
|
91
|
+
when :is, :minimum then ["is #{key == :is ? 'invalid' : 'too short'}", value - 1]
|
92
|
+
when :maximum then ['is too long', value + 1]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def build_type_str(options)
|
97
|
+
if !options.nil? && options[:type]
|
98
|
+
" (%s%s%s)" % [
|
99
|
+
('Enumerated ' if options[:enum]),
|
100
|
+
options[:type],
|
101
|
+
(", default: #{options[:default]}" if options[:default])
|
102
|
+
]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# def scoped_validation?(validation)
|
107
|
+
# validation.is_a?(Hash) && ([:scope, :mock] - validation.keys).empty?
|
108
|
+
# end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module SmartRspec
|
2
|
+
module Support
|
3
|
+
module Model
|
4
|
+
module Expectations
|
5
|
+
def be_valid_expectation(attr, value = nil, mock = nil)
|
6
|
+
mock ||= subject
|
7
|
+
mock.send("#{attr}=", value)
|
8
|
+
|
9
|
+
expect(mock).not_to be_valid
|
10
|
+
expect(mock).to have_error_on(attr)
|
11
|
+
end
|
12
|
+
|
13
|
+
def default_expectation(attr, value)
|
14
|
+
expect(subject.send(attr)).to eq(value)
|
15
|
+
end
|
16
|
+
|
17
|
+
def enum_expectation(attr, value)
|
18
|
+
expect(value).to include(subject.send(attr).to_sym)
|
19
|
+
end
|
20
|
+
|
21
|
+
def type_expectation(attr, value)
|
22
|
+
assert_type = value != :Boolean ? be_kind_of(Kernel.const_get(value)) : be_boolean
|
23
|
+
expect(subject.send(attr)).to assert_type
|
24
|
+
end
|
25
|
+
|
26
|
+
def has_attributes_expectation(attr, options) options.each do |key, value|
|
27
|
+
send("#{key}_expectation", attr, value)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def association_expectation(type, model)
|
32
|
+
if type == :has_many
|
33
|
+
expect(subject).to respond_to("#{model.to_s.singularize}_ids")
|
34
|
+
elsif type == :belongs_to
|
35
|
+
%W(#{model}= #{model}_id #{model}_id=).each do |method|
|
36
|
+
expect(subject).to respond_to(method)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/smart_rspec/version.rb
CHANGED
data/lib/smart_rspec.rb
CHANGED
@@ -2,7 +2,7 @@ require 'active_support/concern'
|
|
2
2
|
require 'rspec/collection_matchers'
|
3
3
|
require 'rspec/matchers'
|
4
4
|
|
5
|
-
%w(macros matchers support/expectations).each { |f| require "smart_rspec/#{f}" }
|
5
|
+
%w(macros matchers support/model/expectations).each { |f| require "smart_rspec/#{f}" }
|
6
6
|
|
7
7
|
include SmartRspec::Matchers
|
8
8
|
|
@@ -10,11 +10,10 @@ module SmartRspec
|
|
10
10
|
extend ActiveSupport::Concern
|
11
11
|
|
12
12
|
included do
|
13
|
-
include SmartRspec::Support::Expectations
|
13
|
+
include SmartRspec::Support::Model::Expectations
|
14
14
|
end
|
15
15
|
|
16
16
|
module ClassMethods
|
17
17
|
include SmartRspec::Macros
|
18
18
|
end
|
19
19
|
end
|
20
|
-
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'smart_rspec/support/regexes'
|
2
2
|
|
3
|
-
module
|
3
|
+
module Fixtures
|
4
4
|
class User
|
5
5
|
include SmartRspec::Support::Regexes
|
6
6
|
|
@@ -17,30 +17,42 @@ module Factories
|
|
17
17
|
|
18
18
|
attr_accessor :email, :system, :system_id, :project, :project_id,
|
19
19
|
:name, :username, :is_admin, :score, :admin, :father,
|
20
|
-
:mother, :articles, :rates
|
20
|
+
:mother, :articles, :rates, :errors
|
21
21
|
|
22
|
-
attr_reader :id
|
22
|
+
attr_reader :id
|
23
|
+
|
24
|
+
attr_writer :locale
|
23
25
|
|
24
26
|
def initialize(attrs = {})
|
25
27
|
attrs.each { |key, value| self.send("#{key}=", value) }
|
26
28
|
set_defaults
|
27
|
-
@@collection << self
|
28
29
|
end
|
29
30
|
|
30
31
|
class << self
|
31
32
|
attr_reader :collection
|
33
|
+
|
34
|
+
def create(attrs)
|
35
|
+
user = User.new(attrs)
|
36
|
+
@@collection << user && user
|
37
|
+
end
|
38
|
+
|
39
|
+
def find_by(key, value)
|
40
|
+
@@collection.find { |e| e.send(key) == value }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def save
|
45
|
+
@@collection << self
|
32
46
|
end
|
33
47
|
|
34
48
|
def locale
|
35
49
|
@locale.to_s unless @locale.nil?
|
36
50
|
end
|
37
51
|
|
38
|
-
def
|
39
|
-
|
52
|
+
def persisted?
|
53
|
+
User.find_by(:id, id)
|
40
54
|
end
|
41
55
|
|
42
|
-
def persisted?; true end
|
43
|
-
|
44
56
|
def valid?
|
45
57
|
%w(email father locale name username).each { |e| send("check_#{e}") }
|
46
58
|
@errors.nil?
|
@@ -51,6 +63,8 @@ module Factories
|
|
51
63
|
def check_email
|
52
64
|
if !email || (email && email !~ build_regex(:email))
|
53
65
|
@errors.merge!({ email: @@error_message[:blank] })
|
66
|
+
elsif User.find_by(:email, email)
|
67
|
+
@errors.merge!({ email: @@error_message[:uniqueness] })
|
54
68
|
end
|
55
69
|
end
|
56
70
|
|
@@ -61,7 +75,7 @@ module Factories
|
|
61
75
|
end
|
62
76
|
|
63
77
|
def check_locale
|
64
|
-
unless
|
78
|
+
unless %w(en pt).include?(locale)
|
65
79
|
@errors.merge!({ locale: @@error_message[:inclusion] })
|
66
80
|
end
|
67
81
|
end
|
@@ -73,18 +87,19 @@ module Factories
|
|
73
87
|
end
|
74
88
|
|
75
89
|
def check_username
|
76
|
-
|
77
|
-
|
78
|
-
|
90
|
+
if username.to_s.empty?
|
91
|
+
@errors.merge!({ username: @@error_message[:blank] })
|
92
|
+
elsif %w(foo bar).include?(username)
|
93
|
+
@errors.merge!({ username: @@error_message[:exclusion] })
|
94
|
+
elsif User.find_by(:username, username)
|
95
|
+
@errors.merge!({ username: @@error_message[:uniqueness] })
|
79
96
|
end
|
80
97
|
end
|
81
98
|
|
82
99
|
def set_defaults
|
83
100
|
@@last_id = @id = @@last_id + 1
|
84
|
-
{ errors: {}, is_admin: false, score: 0, locale: :en }
|
85
|
-
|
86
|
-
end
|
101
|
+
attrs = { errors: {}, is_admin: false, score: 0, locale: :en }
|
102
|
+
attrs.each { |key, value| send("#{key}=", value) }
|
87
103
|
end
|
88
104
|
end
|
89
105
|
end
|
90
|
-
|
@@ -0,0 +1,81 @@
|
|
1
|
+
{
|
2
|
+
"data": [
|
3
|
+
{
|
4
|
+
"id": "1",
|
5
|
+
"type": "users",
|
6
|
+
"links": {
|
7
|
+
"self": "http://api.myawesomesite.com/users/1"
|
8
|
+
},
|
9
|
+
"attributes": {
|
10
|
+
"first_name": "Tiago",
|
11
|
+
"last_name": "Guedes",
|
12
|
+
"full_name": "Tiago Guedes",
|
13
|
+
"birthday": "1988-22-12"
|
14
|
+
},
|
15
|
+
"relationships": {
|
16
|
+
"posts": {
|
17
|
+
"links": {
|
18
|
+
"self": "http://api.myawesomesite.com/users/1/relationships/posts",
|
19
|
+
"related": "http://api.myawesomesite.com/users/1/posts"
|
20
|
+
},
|
21
|
+
"data": [
|
22
|
+
{
|
23
|
+
"type": "posts",
|
24
|
+
"id": "1"
|
25
|
+
}
|
26
|
+
]
|
27
|
+
}
|
28
|
+
}
|
29
|
+
},
|
30
|
+
{
|
31
|
+
"id": "2",
|
32
|
+
"type": "users",
|
33
|
+
"links": {
|
34
|
+
"self": "http://api.myawesomesite.com/users/2"
|
35
|
+
},
|
36
|
+
"attributes": {
|
37
|
+
"first_name": "Douglas",
|
38
|
+
"last_name": "André",
|
39
|
+
"full_name": "Douglas André",
|
40
|
+
"birthday": null
|
41
|
+
},
|
42
|
+
"relationships": {
|
43
|
+
"posts": {
|
44
|
+
"links": {
|
45
|
+
"self": "http://api.myawesomesite.com/users/2/relationships/posts",
|
46
|
+
"related": "http://api.myawesomesite.com/users/2/posts"
|
47
|
+
},
|
48
|
+
"data": []
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
],
|
53
|
+
"included": [
|
54
|
+
{
|
55
|
+
"id": "1",
|
56
|
+
"type": "posts",
|
57
|
+
"links": {
|
58
|
+
"self": "http://api.myawesomesite.com/posts/1"
|
59
|
+
},
|
60
|
+
"attributes": {
|
61
|
+
"title": "An awesome post",
|
62
|
+
"body": "Lorem ipsum dolot sit amet"
|
63
|
+
},
|
64
|
+
"relationships": {
|
65
|
+
"author": {
|
66
|
+
"links": {
|
67
|
+
"self": "http://api.myawesomesite.com/posts/1/relationships/author",
|
68
|
+
"related": "http://api.myawesomesite.com/posts/1/author"
|
69
|
+
}
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
],
|
74
|
+
"meta": {
|
75
|
+
"record_count": 2
|
76
|
+
},
|
77
|
+
"links": {
|
78
|
+
"first": "http://api.myawesomesite.com/users?include=posts&page%5Blimit%5D=2&page%5Boffset%5D=0",
|
79
|
+
"last": "http://api.myawesomesite.com/users?include=posts&page%5Blimit%5D=2&page%5Boffset%5D=0"
|
80
|
+
}
|
81
|
+
}
|
@@ -3,17 +3,14 @@ require 'spec_helper'
|
|
3
3
|
describe SmartRspec::Macros do
|
4
4
|
include SmartRspec
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
email:
|
9
|
-
name:
|
6
|
+
subject(:user) do
|
7
|
+
User.create({
|
8
|
+
email: Faker::Internet.email,
|
9
|
+
name: Faker::Name.name,
|
10
10
|
username: Faker::Internet.user_name
|
11
|
-
}
|
12
|
-
@user = User.new(attrs)
|
11
|
+
})
|
13
12
|
end
|
14
13
|
|
15
|
-
subject { @user }
|
16
|
-
|
17
14
|
describe '#belongs_to' do
|
18
15
|
context 'when it receives a single arg' do
|
19
16
|
belongs_to :system
|
@@ -59,11 +56,16 @@ describe SmartRspec::Macros do
|
|
59
56
|
|
60
57
|
describe '#fails_validation_of' do
|
61
58
|
context 'when it receives a single arg' do
|
62
|
-
|
59
|
+
new_user =
|
60
|
+
User.new({
|
61
|
+
email: Faker::Internet.email,
|
62
|
+
name: Faker::Name.name,
|
63
|
+
username: Faker::Internet.user_name
|
64
|
+
})
|
63
65
|
|
64
|
-
fails_validation_of :email, presence: true, email: true, uniqueness:
|
66
|
+
fails_validation_of :email, presence: true, email: true, uniqueness: { mock: new_user }
|
65
67
|
fails_validation_of :name, length: { maximum: 80 }
|
66
|
-
fails_validation_of :username, uniqueness: {
|
68
|
+
fails_validation_of :username, uniqueness: { mock: new_user }, exclusion: { in: %w(foo bar) }
|
67
69
|
fails_validation_of :locale, inclusion: { in: %w(en pt) }
|
68
70
|
fails_validation_of :father, format: { with: /foo/, mock: 'bar' }
|
69
71
|
end
|
@@ -73,4 +75,3 @@ describe SmartRspec::Macros do
|
|
73
75
|
end
|
74
76
|
end
|
75
77
|
end
|
76
|
-
|
@@ -1,173 +1,234 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe
|
4
|
-
describe
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
describe SmartRspec::Matchers do
|
4
|
+
describe BeMatchers do
|
5
|
+
describe '#be_ascending' do
|
6
|
+
context 'positive assertion' do
|
7
|
+
it { expect([1, 2, 3, 4]).to be_ascending }
|
8
|
+
end
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
context 'negative assertion' do
|
11
|
+
it { expect([1, 4, 2, 3]).not_to be_ascending }
|
12
|
+
end
|
11
13
|
end
|
12
|
-
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
describe '#be_boolean' do
|
16
|
+
context 'positive assertion' do
|
17
|
+
it { expect(true).to be_boolean }
|
18
|
+
it { expect(false).to be_boolean }
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
context 'negative assertion' do
|
22
|
+
it { expect('true').not_to be_boolean }
|
23
|
+
it { expect(1).not_to be_boolean }
|
24
|
+
it { expect(%w(foo bar)).not_to be_boolean }
|
25
|
+
end
|
24
26
|
end
|
25
|
-
end
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
describe '#be_descending' do
|
29
|
+
context 'positive assertion' do
|
30
|
+
it { expect([4, 3, 2, 1]).to be_descending }
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
-
|
33
|
+
context 'negative assertion' do
|
34
|
+
it { expect([1, 2, 3, 4]).not_to be_descending }
|
35
|
+
end
|
34
36
|
end
|
35
|
-
end
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
describe '#be_email' do
|
39
|
+
context 'positive assertion' do
|
40
|
+
it { expect(Faker::Internet.email).to be_email }
|
41
|
+
it { expect('tiagopog@gmail.com').to be_email }
|
42
|
+
it { expect('foo@bar.com.br').to be_email }
|
43
|
+
end
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
45
|
+
context 'negative assertion' do
|
46
|
+
it { expect('foo@bar').not_to be_email }
|
47
|
+
it { expect('foo@').not_to be_email }
|
48
|
+
it { expect('@bar').not_to be_email }
|
49
|
+
it { expect('@bar.com').not_to be_email }
|
50
|
+
it { expect('foo bar@bar.com').not_to be_email }
|
51
|
+
end
|
50
52
|
end
|
51
|
-
end
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
54
|
+
describe '#be_url' do
|
55
|
+
context 'positive assertion' do
|
56
|
+
it { expect(Faker::Internet.url).to be_url }
|
57
|
+
it { expect('http://adtangerine.com').to be_url }
|
58
|
+
it { expect('http://www.facebook.com').to be_url }
|
59
|
+
it { expect('www.twitflink.com').to be_url }
|
60
|
+
it { expect('google.com.br').to be_url }
|
61
|
+
end
|
61
62
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
63
|
+
context 'negative assertion' do
|
64
|
+
it { expect('foobar.bar').not_to be_url }
|
65
|
+
it { expect('foobar').not_to be_url }
|
66
|
+
it { expect('foo bar.com.br').not_to be_url }
|
67
|
+
end
|
66
68
|
end
|
67
|
-
end
|
68
69
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
70
|
+
describe '#be_image_url' do
|
71
|
+
context 'positive assertion' do
|
72
|
+
it { expect(Faker::Company.logo).to be_image_url }
|
73
|
+
it { expect('http://foobar.com/foo.jpg').to be_image_url }
|
74
|
+
it { expect('http://foobar.com/foo.jpg').to be_image_url(:jpg) }
|
75
|
+
it { expect('http://foobar.com/foo.gif').to be_image_url(:gif) }
|
76
|
+
it { expect('http://foobar.com/foo.png').to be_image_url(:png) }
|
77
|
+
it { expect('http://foobar.com/foo.png').to be_image_url([:jpg, :png]) }
|
78
|
+
it { expect('http://foobar.com/foo/bar?image=foo.jpg').to be_image_url }
|
79
|
+
end
|
79
80
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
81
|
+
context 'negative assertion' do
|
82
|
+
it { expect('http://foobar.com').not_to be_image_url }
|
83
|
+
it { expect('http://foobar.com/foo.jpg').not_to be_image_url(:gif) }
|
84
|
+
it { expect('http://foobar.com/foo.gif').not_to be_image_url(:png) }
|
85
|
+
it { expect('http://foobar.com/foo.png').not_to be_image_url(:jpg) }
|
86
|
+
it { expect('http://foobar.com/foo.gif').not_to be_image_url([:jpg, :png]) }
|
87
|
+
end
|
86
88
|
end
|
87
|
-
end
|
88
89
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
90
|
+
describe '#be_a_list_of' do
|
91
|
+
context 'positive assertion' do
|
92
|
+
subject { Array.new(3, User.new) }
|
93
|
+
it { is_expected.to be_a_list_of(User) }
|
94
|
+
end
|
94
95
|
|
95
|
-
|
96
|
-
|
97
|
-
|
96
|
+
context 'negative assertion' do
|
97
|
+
subject { Array.new(3, User.new) << nil }
|
98
|
+
it { is_expected.to_not be_a_list_of(User) }
|
99
|
+
end
|
98
100
|
end
|
99
101
|
end
|
100
102
|
|
101
|
-
describe
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
103
|
+
describe JsonApiMatchers do
|
104
|
+
subject(:response) { Fixtures::Response.new }
|
105
|
+
|
106
|
+
describe '#have_primary_data' do
|
107
|
+
context 'positive assertion' do
|
108
|
+
it do
|
109
|
+
expect(response).to have_primary_data('users')
|
110
|
+
end
|
111
|
+
end
|
106
112
|
|
107
|
-
|
108
|
-
|
109
|
-
|
113
|
+
context 'negative assertion' do
|
114
|
+
it do
|
115
|
+
expect(response).not_to have_primary_data('foobar')
|
116
|
+
end
|
117
|
+
end
|
110
118
|
end
|
111
|
-
end
|
112
119
|
|
113
|
-
|
114
|
-
|
120
|
+
describe '#have_data_attributes' do
|
121
|
+
let(:fields) { %w(first_name last_name full_name birthday) }
|
122
|
+
|
123
|
+
context 'positive assertion' do
|
124
|
+
it do
|
125
|
+
expect(response).to have_data_attributes(fields)
|
126
|
+
end
|
127
|
+
end
|
115
128
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
129
|
+
context 'negative assertion' do
|
130
|
+
it do
|
131
|
+
expect(response).not_to have_data_attributes(fields + %w(foobar))
|
132
|
+
end
|
120
133
|
end
|
121
134
|
end
|
122
135
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
136
|
+
describe '#have_relationships' do
|
137
|
+
let(:relationships) { %w(posts) }
|
138
|
+
|
139
|
+
context 'positive assertion' do
|
140
|
+
it do
|
141
|
+
expect(response).to have_relationships(relationships)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'negative assertion' do
|
146
|
+
it do
|
147
|
+
expect(response).not_to have_relationships(relationships + %w(foobar))
|
148
|
+
end
|
127
149
|
end
|
128
150
|
end
|
129
|
-
end
|
130
151
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
152
|
+
describe '#have_included_relationships' do
|
153
|
+
let(:relationships) { %w(posts) }
|
154
|
+
|
155
|
+
context 'positive assertion' do
|
156
|
+
it do
|
157
|
+
expect(response).to have_included_relationships
|
158
|
+
end
|
159
|
+
end
|
136
160
|
end
|
137
161
|
|
138
|
-
|
139
|
-
|
162
|
+
describe '#have_meta_record_count' do
|
163
|
+
context 'positive assertion' do
|
164
|
+
it do
|
165
|
+
expect(response).to have_meta_record_count(2)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context 'negative assertion' do
|
170
|
+
it do
|
171
|
+
expect(response).not_to have_meta_record_count(3)
|
172
|
+
end
|
173
|
+
end
|
140
174
|
end
|
141
175
|
end
|
142
176
|
|
143
|
-
describe
|
144
|
-
|
145
|
-
subject {
|
146
|
-
|
177
|
+
describe OtherMatchers do
|
178
|
+
describe '#have_error_on' do
|
179
|
+
subject { User.new(email: nil, name: Faker::Name.name) }
|
180
|
+
|
181
|
+
context 'positive assertion' do
|
182
|
+
it do
|
183
|
+
subject.valid?
|
184
|
+
is_expected.to have_error_on(:email)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
context 'negative assertion' do
|
189
|
+
it do
|
190
|
+
subject.valid?
|
191
|
+
is_expected.not_to have_error_on(:name)
|
192
|
+
end
|
193
|
+
end
|
147
194
|
end
|
148
195
|
|
149
|
-
|
150
|
-
|
151
|
-
|
196
|
+
describe '#include_items' do
|
197
|
+
context 'positive assertion' do
|
198
|
+
it { expect(%w(foo bar foobar)).to include_items(%w(foo bar foobar)) }
|
199
|
+
it { expect(%w(lorem ipsum)).to include_items('lorem', 'ipsum') }
|
200
|
+
it { expect([1, 'foo', ['bar']]).to include_items([1, 'foo', ['bar']]) }
|
201
|
+
end
|
202
|
+
|
203
|
+
context 'negative assertion' do
|
204
|
+
it { expect(%w(foo bar foobar)).not_to include_items(%w(lorem)) }
|
205
|
+
end
|
152
206
|
end
|
153
207
|
end
|
154
208
|
|
155
|
-
describe
|
156
|
-
|
209
|
+
describe ::RSpec::CollectionMatchers do
|
210
|
+
describe '#have' do
|
211
|
+
context 'positive assertion' do
|
212
|
+
it { expect([1]).to have(1).item }
|
213
|
+
it { expect(%w(foo bar)).to have(2).items }
|
214
|
+
end
|
157
215
|
|
158
|
-
|
159
|
-
|
160
|
-
|
216
|
+
context 'negative assertion' do
|
217
|
+
it { expect([1]).not_to have(2).items }
|
218
|
+
it { expect(%w(foo bar)).not_to have(1).item }
|
219
|
+
end
|
161
220
|
end
|
162
221
|
|
163
|
-
|
164
|
-
|
165
|
-
|
222
|
+
describe '#have_at_least' do
|
223
|
+
context 'positive assertion' do
|
224
|
+
it { expect(%w(foo bar foobar)).to have_at_least(3).items }
|
225
|
+
end
|
166
226
|
end
|
167
227
|
|
168
|
-
|
169
|
-
|
170
|
-
|
228
|
+
describe '#have_at_most' do
|
229
|
+
context 'positive assertion' do
|
230
|
+
it { expect(%w(foo bar foobar)).to have_at_most(3).items }
|
231
|
+
end
|
171
232
|
end
|
172
233
|
end
|
173
234
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_rspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tiago Guedes
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-05-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -119,13 +119,17 @@ files:
|
|
119
119
|
- lib/smart_rspec/macros.rb
|
120
120
|
- lib/smart_rspec/matchers.rb
|
121
121
|
- lib/smart_rspec/matchers/be_matchers.rb
|
122
|
+
- lib/smart_rspec/matchers/json_api_matchers.rb
|
122
123
|
- lib/smart_rspec/matchers/other_matchers.rb
|
123
|
-
- lib/smart_rspec/support/
|
124
|
-
- lib/smart_rspec/support/
|
124
|
+
- lib/smart_rspec/support/controller/response.rb
|
125
|
+
- lib/smart_rspec/support/model/assertions.rb
|
126
|
+
- lib/smart_rspec/support/model/expectations.rb
|
125
127
|
- lib/smart_rspec/support/regexes.rb
|
126
128
|
- lib/smart_rspec/version.rb
|
127
129
|
- smart_rspec.gemspec
|
128
|
-
- spec/
|
130
|
+
- spec/fixtures/response.rb
|
131
|
+
- spec/fixtures/user.rb
|
132
|
+
- spec/fixtures/users.json
|
129
133
|
- spec/smart_rspec/macros_spec.rb
|
130
134
|
- spec/smart_rspec/matchers_spec.rb
|
131
135
|
- spec/spec_helper.rb
|
@@ -149,12 +153,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
149
153
|
version: '0'
|
150
154
|
requirements: []
|
151
155
|
rubyforge_project:
|
152
|
-
rubygems_version: 2.
|
156
|
+
rubygems_version: 2.6.2
|
153
157
|
signing_key:
|
154
158
|
specification_version: 4
|
155
159
|
summary: Macros and matchers to make your RSpec tests even more amazing.
|
156
160
|
test_files:
|
157
|
-
- spec/
|
161
|
+
- spec/fixtures/response.rb
|
162
|
+
- spec/fixtures/user.rb
|
163
|
+
- spec/fixtures/users.json
|
158
164
|
- spec/smart_rspec/macros_spec.rb
|
159
165
|
- spec/smart_rspec/matchers_spec.rb
|
160
166
|
- spec/spec_helper.rb
|
@@ -1,119 +0,0 @@
|
|
1
|
-
module SmartRspec
|
2
|
-
module Support
|
3
|
-
module Assertions
|
4
|
-
private_class_method
|
5
|
-
|
6
|
-
def validates_email_of(attr, validation)
|
7
|
-
it 'has an invalid format' do
|
8
|
-
%w(foobar foobar@ @foobar foo@bar).each do |e|
|
9
|
-
validation_expectation(attr, e)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def validates_exclusion_of(attr, validation)
|
15
|
-
it 'has a reserved value' do
|
16
|
-
validation_expectation(attr, validation[:in].sample)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def validates_format_of(attr, validation)
|
21
|
-
it 'does not match the required format' do
|
22
|
-
mock, with =
|
23
|
-
validation.values_at(:mock).first,
|
24
|
-
validation.values_at(:with).first
|
25
|
-
|
26
|
-
if mock && with && with !~ mock
|
27
|
-
validation_expectation(attr, mock)
|
28
|
-
else
|
29
|
-
raise ArgumentError, ':with and :mock are required when using the :format validation'
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def validates_inclusion_of(attr, validation)
|
35
|
-
it 'is out of the scope of possible values' do
|
36
|
-
begin
|
37
|
-
value = SecureRandom.hex
|
38
|
-
end while validation[:in].include?(value)
|
39
|
-
validation_expectation(attr, value)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def validates_length_of(attr, validation)
|
44
|
-
validation.each do |key, value|
|
45
|
-
next unless [:in, :is, :maximum, :minimum, :within].include?(key)
|
46
|
-
txt, n = build_length_validation(key, value)
|
47
|
-
it txt do
|
48
|
-
validation_expectation(attr, 'x' * n)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def validates_presence_of(attr, validation)
|
54
|
-
it 'is blank' do
|
55
|
-
validation_expectation(attr, nil)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def validates_uniqueness_of(attr, validation)
|
60
|
-
scoped = scoped_validation?(validation)
|
61
|
-
it "is already in use#{" (scope: #{validation[:scope]})" if scoped}" do
|
62
|
-
mock =
|
63
|
-
if scoped
|
64
|
-
copy = subject.send(validation[:scope])
|
65
|
-
validation[:mock].send("#{validation[:scope]}=", copy)
|
66
|
-
validation[:mock]
|
67
|
-
else
|
68
|
-
subject.dup
|
69
|
-
end
|
70
|
-
subject.save unless subject.persisted?
|
71
|
-
validation_expectation(attr, subject.send(attr), mock)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def assert_has_attributes(attrs, options)
|
76
|
-
type_str = build_type_str(options)
|
77
|
-
|
78
|
-
attrs.each do |attr|
|
79
|
-
it %Q(has an attribute named "#{attr}"#{type_str}) do
|
80
|
-
expect(subject).to respond_to(attr)
|
81
|
-
has_attributes_expectation(attr, options)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def assert_association(type, associations)
|
87
|
-
associations.each do |model|
|
88
|
-
it "#{type.to_s.gsub('_', ' ')} #{model}" do
|
89
|
-
expect(subject).to respond_to(model)
|
90
|
-
association_expectation(type, model)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def build_length_validation(key, value)
|
96
|
-
case key
|
97
|
-
when :in, :within then ['is out of the length range', value.max + 1]
|
98
|
-
when :is, :minimum then ["is #{key == :is ? 'invalid' : 'too short'}", value - 1]
|
99
|
-
when :maximum then ['is too long', value + 1]
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def build_type_str(options)
|
104
|
-
if !options.nil? && options[:type]
|
105
|
-
" (%s%s%s)" % [
|
106
|
-
('Enumerated ' if options[:enum]),
|
107
|
-
options[:type],
|
108
|
-
(", default: #{options[:default]}" if options[:default])
|
109
|
-
]
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def scoped_validation?(validation)
|
114
|
-
validation.is_a?(Hash) && ([:scope, :mock] - validation.keys).empty?
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
@@ -1,42 +0,0 @@
|
|
1
|
-
module SmartRspec
|
2
|
-
module Support
|
3
|
-
module Expectations
|
4
|
-
def validation_expectation(attr, value = nil, mock = nil)
|
5
|
-
mock ||= subject
|
6
|
-
mock.send("#{attr}=", value)
|
7
|
-
|
8
|
-
expect(mock).not_to be_valid
|
9
|
-
expect(mock).to have_error_on(attr)
|
10
|
-
end
|
11
|
-
|
12
|
-
def default_expectation(attr, value)
|
13
|
-
expect(subject.send(attr)).to eq(value)
|
14
|
-
end
|
15
|
-
|
16
|
-
def enum_expectation(attr, value)
|
17
|
-
expect(value).to include(subject.send(attr).to_sym)
|
18
|
-
end
|
19
|
-
|
20
|
-
def type_expectation(attr, value)
|
21
|
-
assert_type = value != :Boolean ? be_kind_of(Kernel.const_get(value)) : be_boolean
|
22
|
-
expect(subject.send(attr)).to assert_type
|
23
|
-
end
|
24
|
-
|
25
|
-
def has_attributes_expectation(attr, options) options.each do |key, value|
|
26
|
-
send("#{key}_expectation", attr, value)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def association_expectation(type, model)
|
31
|
-
if type == :has_many
|
32
|
-
expect(subject).to respond_to("#{model.to_s.singularize}_ids")
|
33
|
-
elsif type == :belongs_to
|
34
|
-
%W(#{model}= #{model}_id #{model}_id=).each do |method|
|
35
|
-
expect(subject).to respond_to(method)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|