spec_producer 0.11.0 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -0
- data/CHANGELOG.md +15 -0
- data/README.md +73 -24
- data/lib/configuration.rb +41 -0
- data/lib/generators/spec_producer/install/install_generator.rb +13 -0
- data/lib/generators/spec_producer/install/templates/spec_producer +1 -0
- data/lib/spec_producer.rb +140 -47
- data/lib/spec_producer/factories_production_module.rb +17 -11
- data/lib/spec_producer/missing_files_module.rb +104 -41
- data/lib/spec_producer/missing_gems_module.rb +77 -0
- data/lib/spec_producer/producers.rb +10 -0
- data/lib/spec_producer/producers/base.rb +129 -0
- data/lib/spec_producer/producers/controllers_producer.rb +31 -0
- data/lib/spec_producer/producers/helpers_producer.rb +26 -0
- data/lib/spec_producer/producers/jobs_producer.rb +34 -0
- data/lib/spec_producer/producers/mailers_producer.rb +24 -0
- data/lib/spec_producer/producers/models_producer.rb +89 -0
- data/lib/spec_producer/producers/registry.rb +66 -0
- data/lib/spec_producer/producers/routes_producer.rb +44 -0
- data/lib/spec_producer/producers/serializers_producer.rb +46 -0
- data/lib/spec_producer/producers/views_producer.rb +25 -0
- data/lib/spec_producer/railtie.rb +13 -0
- data/lib/spec_producer/rspec_builders.rb +1 -0
- data/lib/spec_producer/rspec_builders/base.rb +148 -0
- data/lib/spec_producer/rspec_builders/builder.rb +220 -0
- data/lib/spec_producer/rspec_builders/matchers.rb +256 -0
- data/lib/spec_producer/spec_production_module.rb +80 -331
- data/lib/spec_producer/spec_runner.rb +14 -0
- data/lib/spec_producer/utils.rb +1 -0
- data/lib/spec_producer/utils/file_utils.rb +69 -0
- data/lib/spec_producer/version.rb +1 -1
- data/lib/tasks/spec_producer_tasks.rake +103 -0
- data/spec_producer.gemspec +6 -0
- metadata +111 -2
@@ -0,0 +1,220 @@
|
|
1
|
+
module SpecProducer
|
2
|
+
module RspecBuilders
|
3
|
+
module Builder
|
4
|
+
# == RspecBuilder \Builder
|
5
|
+
#
|
6
|
+
# Provides the building blocks to create an rspec text using
|
7
|
+
# <tt>RspecProducer::RspecBuilders::Base</tt> class. Currently
|
8
|
+
# it supports the following building spec blocks:
|
9
|
+
#
|
10
|
+
|
11
|
+
|
12
|
+
# Call build on an <tt>RspecBuilder::Base instance</tt>
|
13
|
+
#
|
14
|
+
# Example
|
15
|
+
#
|
16
|
+
# builder = RspecBuilders::Base.new('Init Text')
|
17
|
+
# builder.build do |b|
|
18
|
+
# # Other method calls here
|
19
|
+
# end
|
20
|
+
def build(&block)
|
21
|
+
block.call(self)
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
# Call build on a <tt>RspecBuilders::Base</tt> class
|
26
|
+
#
|
27
|
+
# Example
|
28
|
+
#
|
29
|
+
# builder = RspecBuilders::Base.build do |b|
|
30
|
+
# # ...
|
31
|
+
# b.context('my awsome spec') do
|
32
|
+
# be.it { should_be(:valid) }
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
def self.build(&block)
|
37
|
+
instance = new
|
38
|
+
block.call(instance)
|
39
|
+
instance
|
40
|
+
end
|
41
|
+
|
42
|
+
# Creates a new spec. This is the header block required by all spec files
|
43
|
+
#
|
44
|
+
# Example
|
45
|
+
#
|
46
|
+
# builder = RspecBuilders::Base.build do |b|
|
47
|
+
# b.spec 'User', 'models'
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# Calling
|
51
|
+
# puts builder
|
52
|
+
#
|
53
|
+
# Returns the following string:
|
54
|
+
#
|
55
|
+
# describe User, type: :model do
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
def spec(klass, type, &block)
|
59
|
+
new_line
|
60
|
+
|
61
|
+
if type == 'routing'
|
62
|
+
add "describe '#{klass} routes', type: :#{type} do"
|
63
|
+
elsif type == 'view'
|
64
|
+
add "describe '#{klass}', type: :#{type} do"
|
65
|
+
else
|
66
|
+
add "describe #{klass}, type: :#{type} do"
|
67
|
+
end
|
68
|
+
|
69
|
+
increase_intent
|
70
|
+
new_line
|
71
|
+
|
72
|
+
block.call(self)
|
73
|
+
|
74
|
+
decrease_intent
|
75
|
+
add 'end'
|
76
|
+
end
|
77
|
+
|
78
|
+
# Adds a subject { 'some' } block to buffer
|
79
|
+
# it also adds a new line for the next spec to be added to the new line
|
80
|
+
#
|
81
|
+
# Example
|
82
|
+
#
|
83
|
+
# RspecBuilders::Based.build do |b|
|
84
|
+
# b.subject { 'User.new' }
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
# Produces:
|
88
|
+
# subject { User.new }
|
89
|
+
#
|
90
|
+
def subject(s)
|
91
|
+
add "subject { #{s} }"
|
92
|
+
new_line
|
93
|
+
end
|
94
|
+
|
95
|
+
# Adds a pending 'some' block to buffer
|
96
|
+
# it also adds a new line for the next spec to be added to the new line
|
97
|
+
#
|
98
|
+
# Example
|
99
|
+
#
|
100
|
+
# RspecBuilders::Based.build do |b|
|
101
|
+
# b.pending { 'some missing spec' }
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# Produces:
|
105
|
+
# pending 'some missing spec'
|
106
|
+
#
|
107
|
+
def pending(s)
|
108
|
+
add "pending '#{s}'"
|
109
|
+
new_line
|
110
|
+
end
|
111
|
+
|
112
|
+
# Adds a before { render } block to buffer
|
113
|
+
#
|
114
|
+
# Example
|
115
|
+
#
|
116
|
+
# RspecBuilders::Based.build do |b|
|
117
|
+
# b.before_render
|
118
|
+
# end
|
119
|
+
#
|
120
|
+
# Produces:
|
121
|
+
# before do
|
122
|
+
# render
|
123
|
+
# end
|
124
|
+
#
|
125
|
+
def before_render
|
126
|
+
add "before do"
|
127
|
+
increase_intent
|
128
|
+
new_line
|
129
|
+
|
130
|
+
add "render"
|
131
|
+
new_line
|
132
|
+
|
133
|
+
decrease_intent
|
134
|
+
add 'end'
|
135
|
+
new_line
|
136
|
+
new_line
|
137
|
+
end
|
138
|
+
|
139
|
+
# Adds a context or describe (alias) block for the spec.
|
140
|
+
# the method is responsible to handle the intentation management
|
141
|
+
# (increase / decrease).
|
142
|
+
#
|
143
|
+
# Usage
|
144
|
+
#
|
145
|
+
# RspecBuilders::Base.build do |b|
|
146
|
+
# b.context 'first context' do
|
147
|
+
# b.context 'first nested' do
|
148
|
+
# end
|
149
|
+
# end
|
150
|
+
#
|
151
|
+
# b.context 'second context' do
|
152
|
+
# b.context 'second nested' {}
|
153
|
+
# end
|
154
|
+
# end
|
155
|
+
#
|
156
|
+
# Which produces the following spec:
|
157
|
+
#
|
158
|
+
# context 'first context' do
|
159
|
+
# context 'first nested' do
|
160
|
+
# end
|
161
|
+
# end
|
162
|
+
#
|
163
|
+
# context 'second context' do
|
164
|
+
# context 'second nested do
|
165
|
+
#
|
166
|
+
# end
|
167
|
+
# end
|
168
|
+
#
|
169
|
+
def context(name, &block)
|
170
|
+
new_line
|
171
|
+
add "context \"#{name}\" do"
|
172
|
+
increase_intent
|
173
|
+
new_line
|
174
|
+
|
175
|
+
block.call
|
176
|
+
|
177
|
+
decrease_intent
|
178
|
+
add 'end'
|
179
|
+
new_line
|
180
|
+
end
|
181
|
+
alias_method :describe, :context
|
182
|
+
|
183
|
+
# Provides an it expectation.
|
184
|
+
#
|
185
|
+
# Usage example
|
186
|
+
#
|
187
|
+
# user = User.new(name: 'alex', email: 'some@some.com')
|
188
|
+
#
|
189
|
+
# RspecBuilders::Base.build do |b|
|
190
|
+
# b.subject { "build(:user, name: 'Alex', email: 'some@some.com')" }
|
191
|
+
#
|
192
|
+
# # Matchers provided from RspecBuilders::Matchers
|
193
|
+
# b.it { validates_presence_of(:name) }
|
194
|
+
# b.it { expect('subject.name').to eq user.name }
|
195
|
+
# b.it { expect('subject.email').to eq 'name' }
|
196
|
+
#
|
197
|
+
# # Or provide the expecation as a string
|
198
|
+
# b.it('expect(subject).to be_a User')
|
199
|
+
# end
|
200
|
+
#
|
201
|
+
# produces the following spec
|
202
|
+
#
|
203
|
+
# subject { 'builder(:user, anme: 'Alex', email: 'some@some.com')' }
|
204
|
+
# it { is_expected.to validate_presence_of(:name) }
|
205
|
+
# it { expect(subject.name).to eq 'Alex' }
|
206
|
+
# it { expect(subject.email).to eq 'some@some.com' }
|
207
|
+
# it { expect(subject).to be_a User }
|
208
|
+
#
|
209
|
+
def it(*args, &block)
|
210
|
+
expectation = args.shift
|
211
|
+
if expectation
|
212
|
+
add "it { #{expectation} }"
|
213
|
+
new_line
|
214
|
+
else
|
215
|
+
instance_eval(&block)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
@@ -0,0 +1,256 @@
|
|
1
|
+
module SpecProducer
|
2
|
+
module RspecBuilders
|
3
|
+
module Matchers
|
4
|
+
# \RspecBuilders \Matchers
|
5
|
+
# Provides some helper methods to build Rspec matchers.
|
6
|
+
|
7
|
+
# AR reflections to RSpec mathcers
|
8
|
+
REFLECTION_MACRO_MAPPINGS = {
|
9
|
+
belongs_to: 'belong_to',
|
10
|
+
has_one: 'have_one',
|
11
|
+
has_many: 'have_many',
|
12
|
+
has_and_belongs_to_many: 'have_and_belongs_to_many',
|
13
|
+
inverse_of: 'inverse_of',
|
14
|
+
autosave: 'autosave',
|
15
|
+
through: 'through',
|
16
|
+
class_name: 'class_name',
|
17
|
+
foreign_key: 'with_foreign_key',
|
18
|
+
primary_key: 'with_primary_key',
|
19
|
+
source: 'source',
|
20
|
+
dependent: 'dependent'
|
21
|
+
}
|
22
|
+
|
23
|
+
MATCHERS = {
|
24
|
+
presence: -> (name) {"is_expected.to validate_presence_of(:#{name})" },
|
25
|
+
belong_to: ->(name) { "is_expected.to belong_to(:#{name})" },
|
26
|
+
have_one: ->(name) { "is_expected.to have_one(:#{name})" },
|
27
|
+
has_many: ->(name) { "is_expected.to have_many(:#{name})" },
|
28
|
+
db_column: ->(name) { "is_expected.to have_db_column(:#{name})" },
|
29
|
+
}
|
30
|
+
|
31
|
+
def M(type)
|
32
|
+
MATCHERS[type]
|
33
|
+
end
|
34
|
+
|
35
|
+
# Writes an expectation for an AR association given an AR reflection.
|
36
|
+
# Example
|
37
|
+
#
|
38
|
+
# class User < ActiveRecord::Base
|
39
|
+
# has_many :posts, class_name: 'Post', dependent: :destroy
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# user_reflection = User.reflections.first
|
43
|
+
# puts has_assosication(user_reflection)
|
44
|
+
# => it { is_expected.to have_many(:posts).class_name(:Post).dependent(:destroy) }
|
45
|
+
#
|
46
|
+
# RspecBuilders::Base.build do |b|
|
47
|
+
# b.it { has_associations(user_reflection) }
|
48
|
+
# end
|
49
|
+
def has_association(reflection)
|
50
|
+
matcher = "#{REFLECTION_MACRO_MAPPINGS[reflection.macro.to_sym]}(:#{reflection.name})"
|
51
|
+
options = association_options_for(reflection)
|
52
|
+
matcher += options if options.present?
|
53
|
+
expectation = "it { is_expected.to #{matcher} }"
|
54
|
+
write expectation
|
55
|
+
new_line
|
56
|
+
end
|
57
|
+
|
58
|
+
def json_parse_for_serialized_object(object)
|
59
|
+
"JSON.parse(#{object.name}.new(#{factory_build_for_object(object)}).to_json)"
|
60
|
+
end
|
61
|
+
|
62
|
+
def initialize_serializer_for_object(object)
|
63
|
+
"#{object.name}.new(#{factory_build_for_object(object)})"
|
64
|
+
end
|
65
|
+
|
66
|
+
def factory_build_for_object(object)
|
67
|
+
"FactoryGirl.build(:#{object.name.underscore.gsub('_serializer', '')})"
|
68
|
+
end
|
69
|
+
|
70
|
+
def association_options_for(reflection)
|
71
|
+
return if reflection.options.empty?
|
72
|
+
options = []
|
73
|
+
|
74
|
+
reflection.options.each_pair do |key, value|
|
75
|
+
options << (SpecProducer::RspecBuilders::Matchers::REFLECTION_MACRO_MAPPINGS[key] || key.to_s) + "(:#{value})"
|
76
|
+
end
|
77
|
+
options.reject(&:nil?).join('.').prepend('.')
|
78
|
+
end
|
79
|
+
private :association_options_for
|
80
|
+
|
81
|
+
# Writes all responds_to expectations for the fiven arguments.
|
82
|
+
#
|
83
|
+
# Example:
|
84
|
+
#
|
85
|
+
# RspecBuilders::Base.build do |b|
|
86
|
+
# b.it { responds_to(:name, :age) }
|
87
|
+
# # or
|
88
|
+
# b.responds_to(:name, :age)
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# will produce
|
92
|
+
# it { is_expected.to respond_to(:name, :age) }
|
93
|
+
#
|
94
|
+
def responds_to(*args)
|
95
|
+
args = args.map!{ |arg| ":#{arg.gsub(":", '')}" }.split.join(',')
|
96
|
+
it "is_expected.to respond_to(#{args})"
|
97
|
+
new_line
|
98
|
+
end
|
99
|
+
|
100
|
+
# provides a should_be_* matchers
|
101
|
+
#
|
102
|
+
# Example
|
103
|
+
# RspecBuilders::Base.build do |b|
|
104
|
+
# b.should_be(:present)
|
105
|
+
# b.it { should_be(:valid) }
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# will produce:
|
109
|
+
# it { is_expected.to be_present }
|
110
|
+
# it { is_expected.to be_valid }
|
111
|
+
#
|
112
|
+
def should_be(type)
|
113
|
+
it "is_expected.to be_#{type}"
|
114
|
+
new_line
|
115
|
+
end
|
116
|
+
|
117
|
+
# Given a validator naeme (ex. present) and the attribute
|
118
|
+
# validate it will produce the corresponding shoulda matcher
|
119
|
+
#
|
120
|
+
# Example
|
121
|
+
#
|
122
|
+
# RspecBuilders::Base.build do |b|
|
123
|
+
# b.validates_with(:presence, :name)
|
124
|
+
# b.it { vlaidates_with(:presence, :age) }
|
125
|
+
# end
|
126
|
+
#
|
127
|
+
# Will produce
|
128
|
+
# it { is_expected.to validate_presence_of(:name) }
|
129
|
+
# it { is_expected.to validate_presence_of(:age) }
|
130
|
+
#
|
131
|
+
# Currently no validation options are supported.
|
132
|
+
# We should probably change the method to accept an
|
133
|
+
# <tt>ActiveModel</tt> validator instead like we do
|
134
|
+
# with association expectation where we accept an
|
135
|
+
# <tt>ActiveRecord::Reflection</tt>
|
136
|
+
#
|
137
|
+
def validates_with(kind, attr)
|
138
|
+
it "is_expected.to validate_#{kind}_of(:#{attr})"
|
139
|
+
new_line
|
140
|
+
end
|
141
|
+
|
142
|
+
# Combine the methods below to add an Rspec like
|
143
|
+
# expectation.
|
144
|
+
#
|
145
|
+
# Example
|
146
|
+
#
|
147
|
+
# var = 10
|
148
|
+
# RspecBuidlers::Base.build do
|
149
|
+
# it { expect('subject.age').to eq var }
|
150
|
+
# it { expect('subject.name').to eq 'me' }
|
151
|
+
# end
|
152
|
+
#
|
153
|
+
#############################################
|
154
|
+
# it { expect(some).to eq instance.age }
|
155
|
+
def expect(attr)
|
156
|
+
@_expected = attr
|
157
|
+
self
|
158
|
+
end
|
159
|
+
def to actual
|
160
|
+
it "expect(#{@_expected}).to eq(#{actual})"
|
161
|
+
new_line
|
162
|
+
end
|
163
|
+
def eq(value)
|
164
|
+
value
|
165
|
+
end
|
166
|
+
#############################################
|
167
|
+
|
168
|
+
# Given an name atrribute it writes the:
|
169
|
+
# Example
|
170
|
+
# builder.should_validate_presence_of('age')
|
171
|
+
#
|
172
|
+
# Produes
|
173
|
+
# it { is_expeted.to validate_presnce_of(:age) }
|
174
|
+
#
|
175
|
+
def should_validate_presence_of(name)
|
176
|
+
expectation = M(:presence)[name]
|
177
|
+
it(expectation)
|
178
|
+
new_line
|
179
|
+
end
|
180
|
+
alias_method :validates_presence_of, :should_validate_presence_of
|
181
|
+
|
182
|
+
# Given a :user attribute it writes the belongs to expectation
|
183
|
+
#
|
184
|
+
# Example
|
185
|
+
# builder.should_belong_to(:user)
|
186
|
+
#
|
187
|
+
# Produces
|
188
|
+
# it { is_expected.to belong_to(:user) }
|
189
|
+
#
|
190
|
+
def should_belong_to(name)
|
191
|
+
expectation = M(:belong_to)[name]
|
192
|
+
it(expectation)
|
193
|
+
new_line
|
194
|
+
end
|
195
|
+
|
196
|
+
# Should have one shoulda matcher producer
|
197
|
+
#
|
198
|
+
# Example
|
199
|
+
# builder.should_have_one(:profile)
|
200
|
+
#
|
201
|
+
# Produces
|
202
|
+
# it { is_expected.to have_one(:profile) }
|
203
|
+
#
|
204
|
+
def should_have_one(name)
|
205
|
+
expectation = M(:have_one)[name]
|
206
|
+
it(expectation)
|
207
|
+
new_line
|
208
|
+
end
|
209
|
+
|
210
|
+
# have_many shoulda matcher expectation
|
211
|
+
#
|
212
|
+
# Example
|
213
|
+
# builder.should_have_many(:posts)
|
214
|
+
#
|
215
|
+
# Produces
|
216
|
+
# it { is_expected.to have_many(:posts) }
|
217
|
+
#
|
218
|
+
def should_have_many(name)
|
219
|
+
expectation = M(:has_many)[name]
|
220
|
+
it(expectation)
|
221
|
+
new_line
|
222
|
+
end
|
223
|
+
alias_method :has_many, :should_have_many
|
224
|
+
|
225
|
+
# have_db_column spec producer
|
226
|
+
#
|
227
|
+
# example
|
228
|
+
# builder.has_db_column(:user_id)
|
229
|
+
#
|
230
|
+
# produces
|
231
|
+
# it { is_expected.to have_db_column(:user_id) }
|
232
|
+
#
|
233
|
+
def has_db_column(name)
|
234
|
+
expectation = M(:db_column)[name]
|
235
|
+
it(expectation)
|
236
|
+
new_line
|
237
|
+
end
|
238
|
+
|
239
|
+
# Factories spec producer
|
240
|
+
# Given the resource name of the factory to build
|
241
|
+
# it checks to see if a valid factory exists for the resource name
|
242
|
+
#
|
243
|
+
# example
|
244
|
+
#
|
245
|
+
# build.has_valid_factory(:user)
|
246
|
+
#
|
247
|
+
# produces
|
248
|
+
# it { expect(FactoryGirl.build(:user)).to be_valid }
|
249
|
+
#
|
250
|
+
def has_valid_factory(name)
|
251
|
+
it "expect(FactoryGirl.build(:#{name})).to be_valid"
|
252
|
+
new_line
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|