smart_rspec 0.1.3 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 2ff36ef01dd0d51c6bab2614a6613201973dd7c7
4
- data.tar.gz: cdf57909342d77f99b5ccf3b0fe3766343da5591
2
+ SHA256:
3
+ metadata.gz: f654ae1cc1194b77fafaf9efd5084b2c1346201ea076baca1e20d6c7f8182517
4
+ data.tar.gz: ee86c887ebf82fdce3818de765eb5ae4748b7f7efec18534303a30b34c98b3fd
5
5
  SHA512:
6
- metadata.gz: 8e1f3afcc473a3fb8b25a21da76bbc6a4cbfd09d7e93746f15b3e224f8b4f24f438f42266906e99e4389e9ffde7f07311790d16133d2e892117f11efc22f3e72
7
- data.tar.gz: ec5b1078402f12aa22cd125eceef70a1ea96ce4d639186c20505733dc7045d9a9e2be90b5f9abc4571c78ec54480a42bbd12e32994084596948835f13a4a7fbe
6
+ metadata.gz: 459bc117fd8ff6f167c89fd94ed8dc2af1a6b7ce93d8d65fdb0cba4a2e2b2616e81c03bf371602d3c8549a5b5876d2c76afcd6b42b4a1754807df3801ad0bd2d
7
+ data.tar.gz: fa832dc1c221c3a46e0eb316324bb0f7ccb35d7363e87200c7533a3e896de4f8120096ad8c2e1b682b9e5ea875f3d6c156ed7a9827486402418a3e53abb24e03
data/.travis.yml CHANGED
@@ -1,6 +1,3 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.9.3
4
- - 2.0.0
5
- - 2.1.0
6
- - 2.2.0
3
+ - 2.3.3
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
@@ -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
@@ -13,4 +13,3 @@ module SmartRspec
13
13
  end
14
14
  end
15
15
  end
16
-
@@ -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
@@ -1,3 +1,3 @@
1
1
  module SmartRspec
2
- VERSION = '0.1.3'
2
+ VERSION = '0.2.0'.freeze
3
3
  end
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
-
data/smart_rspec.gemspec CHANGED
@@ -3,8 +3,7 @@ lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'smart_rspec/version'
5
5
 
6
- Gem::Specification.new do |spec|
7
- spec.name = 'smart_rspec'
6
+ Gem::Specification.new do |spec| spec.name = 'smart_rspec'
8
7
  spec.version = SmartRspec::VERSION
9
8
  spec.authors = ['Tiago Guedes']
10
9
  spec.email = ['tiagopog@gmail.com']
@@ -18,11 +17,11 @@ Gem::Specification.new do |spec|
18
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
18
  spec.require_paths = ['lib']
20
19
 
21
- spec.add_runtime_dependency 'activesupport', '~> 4.1'
20
+ spec.add_runtime_dependency 'activesupport', '~> 6.0'
22
21
  spec.add_runtime_dependency 'rspec-collection_matchers', '~> 1.1', '>= 1.1.2'
23
22
 
24
- spec.add_development_dependency 'bundler', '~> 1.6'
25
- spec.add_development_dependency 'faker', '~> 1.4'
26
- spec.add_development_dependency 'rake', '~> 10.0'
27
- spec.add_development_dependency 'rspec', '~> 3.2'
23
+ spec.add_development_dependency 'bundler', '~> 2.0'
24
+ spec.add_development_dependency 'faker', '~> 2.0'
25
+ spec.add_development_dependency 'rake', '~> 13.0'
26
+ spec.add_development_dependency 'rspec', '~> 3.5'
28
27
  end
@@ -0,0 +1,11 @@
1
+ module Fixtures
2
+ class Response
3
+ def initialize
4
+ @file = File.read('spec/fixtures/users.json')
5
+ end
6
+
7
+ def body
8
+ @file
9
+ end
10
+ end
11
+ end
@@ -1,6 +1,6 @@
1
1
  require 'smart_rspec/support/regexes'
2
2
 
3
- module Factories
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, :errors
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 locale=(locale)
39
- [:en, :pt].include?(locale) && @locale = locale
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 [:en, :pt].include?(locale)
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
- other_user = @@collection.select { |e| e.name == name && e.username == username && e.id != id }.first
77
- if username && (other_user || %w(foo bar).include?(username))
78
- @errors.merge!({ username: @@error_message[other_user ? :uniqueness : :exclusion] })
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 }.each do |key, value|
85
- eval "@#{key} ||= #{value.inspect}"
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
- before(:all) do
7
- attrs = {
8
- email: Faker::Internet.email,
9
- name: Faker::Name.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
- user = User.new(email: Faker::Internet.email)
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: true
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: { scope: :name, mock: user }, exclusion: { in: %w(foo bar) }
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
-