spec_producer 0.11.0 → 0.13.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 +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
|