smart_rspec 0.1.3 → 0.2.0

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