cuke_sniffer 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/cuke_sniffer.rb +11 -2
- data/lib/cuke_sniffer.rb +2 -0
- data/lib/cuke_sniffer/cli.rb +79 -11
- data/lib/cuke_sniffer/feature.rb +20 -10
- data/lib/cuke_sniffer/feature_rules_evaluator.rb +69 -69
- data/lib/cuke_sniffer/report/markup.rhtml +16 -7
- data/lib/cuke_sniffer/rule_config.rb +183 -152
- data/lib/cuke_sniffer/rules_evaluator.rb +4 -1
- data/lib/cuke_sniffer/scenario.rb +32 -18
- data/lib/cuke_sniffer/step_definition.rb +75 -16
- metadata +20 -10
data/bin/cuke_sniffer.rb
CHANGED
@@ -6,8 +6,9 @@ Calling CukeSniffer with no arguments will run it against the current directory.
|
|
6
6
|
Other Options for Running include:
|
7
7
|
<feature_file_path>, <step_def_file_path> : Runs CukeSniffer against the
|
8
8
|
specified paths.
|
9
|
-
-o, --out
|
10
|
-
|
9
|
+
-o, --out <type> (name) : Where <type> is 'html' or 'xml'.
|
10
|
+
Runs CukeSniffer then outputs an
|
11
|
+
html/xml file in the current
|
11
12
|
directory (with optional name).
|
12
13
|
-h, --help : You get this lovely document."
|
13
14
|
|
@@ -41,6 +42,14 @@ if ARGV.include? "--out" or ARGV.include? "-o"
|
|
41
42
|
file_name = file_name + ".html" unless file_name =~ /\.html$/
|
42
43
|
cuke_sniffer.output_html(file_name)
|
43
44
|
end
|
45
|
+
when "xml"
|
46
|
+
file_name = ARGV[index + 2]
|
47
|
+
if file_name.nil?
|
48
|
+
cuke_sniffer.output_xml
|
49
|
+
else
|
50
|
+
file_name = file_name + ".xml" unless file_name =~ /\.xml$/
|
51
|
+
cuke_sniffer.output_xml(file_name)
|
52
|
+
end
|
44
53
|
else
|
45
54
|
print_results(cuke_sniffer)
|
46
55
|
end
|
data/lib/cuke_sniffer.rb
CHANGED
data/lib/cuke_sniffer/cli.rb
CHANGED
@@ -1,10 +1,30 @@
|
|
1
1
|
require 'erb'
|
2
|
+
require 'roxml'
|
2
3
|
|
3
4
|
module CukeSniffer
|
4
5
|
class CLI
|
5
6
|
include CukeSniffer::Constants
|
7
|
+
include ROXML
|
6
8
|
|
7
|
-
|
9
|
+
class SummaryNode
|
10
|
+
include ROXML
|
11
|
+
xml_accessor :score
|
12
|
+
xml_accessor :count
|
13
|
+
xml_accessor :average
|
14
|
+
xml_accessor :good
|
15
|
+
xml_accessor :bad
|
16
|
+
xml_accessor :threshold
|
17
|
+
end
|
18
|
+
|
19
|
+
xml_name "cuke_sniffer"
|
20
|
+
xml_accessor :features_summary, :as => SummaryNode
|
21
|
+
xml_accessor :scenarios_summary, :as => SummaryNode
|
22
|
+
xml_accessor :step_definitions_summary, :as => SummaryNode
|
23
|
+
xml_accessor :improvement_list, :as => {:key => "rule", :value => "total"}, :in => "improvement_list", :from => "improvement"
|
24
|
+
xml_accessor :features, :as => [CukeSniffer::Feature], :in => "features"
|
25
|
+
xml_accessor :step_definitions, :as => [CukeSniffer::StepDefinition], :in => "step_definitions"
|
26
|
+
|
27
|
+
attr_accessor :summary, :features_location, :step_definitions_location, :scenarios
|
8
28
|
|
9
29
|
def initialize(features_location = Dir.getwd, step_definitions_location = Dir.getwd)
|
10
30
|
@features_location = features_location
|
@@ -50,6 +70,21 @@ module CukeSniffer
|
|
50
70
|
catalog_step_calls
|
51
71
|
puts "\nAssessing Score: "
|
52
72
|
assess_score
|
73
|
+
@improvement_list = @summary[:improvement_list]
|
74
|
+
@features_summary = load_summary_data(@summary[:features])
|
75
|
+
@scenarios_summary = load_summary_data(@summary[:scenarios])
|
76
|
+
@step_definitions_summary = load_summary_data(@summary[:step_definitions])
|
77
|
+
end
|
78
|
+
|
79
|
+
def load_summary_data(summary_hash)
|
80
|
+
summary_node = SummaryNode.new
|
81
|
+
summary_node.count = summary_hash[:total]
|
82
|
+
summary_node.score = summary_hash[:total_score]
|
83
|
+
summary_node.average = summary_hash[:average]
|
84
|
+
summary_node.threshold = summary_hash[:threshold]
|
85
|
+
summary_node.good = summary_hash[:good]
|
86
|
+
summary_node.bad = summary_hash[:bad]
|
87
|
+
summary_node
|
53
88
|
end
|
54
89
|
|
55
90
|
def good?
|
@@ -183,16 +218,27 @@ module CukeSniffer
|
|
183
218
|
output
|
184
219
|
end
|
185
220
|
|
221
|
+
def extract_steps_hash(scenario)
|
222
|
+
steps_hash = {}
|
223
|
+
counter = 1
|
224
|
+
scenario.steps.each do |step|
|
225
|
+
location = scenario.location.gsub(/:\d*$/, ":#{scenario.start_line + counter}")
|
226
|
+
steps_hash[location] = step
|
227
|
+
counter += 1
|
228
|
+
end
|
229
|
+
steps_hash
|
230
|
+
end
|
231
|
+
|
186
232
|
def get_all_steps
|
187
233
|
steps = {}
|
188
234
|
@features.each do |feature|
|
235
|
+
unless feature.background.nil?
|
236
|
+
background_steps = extract_steps_hash(feature.background)
|
237
|
+
background_steps.each_key { |key| steps[key] = background_steps[key] }
|
238
|
+
end
|
189
239
|
feature.scenarios.each do |scenario|
|
190
|
-
|
191
|
-
|
192
|
-
location = scenario.location.gsub(/:\d*$/, ":#{scenario.start_line + counter}")
|
193
|
-
steps[location] = step
|
194
|
-
counter += 1
|
195
|
-
end
|
240
|
+
scenario_steps = extract_steps_hash(scenario)
|
241
|
+
scenario_steps.each_key { |key| steps[key] = scenario_steps[key] }
|
196
242
|
end
|
197
243
|
end
|
198
244
|
@step_definitions.each do |definition|
|
@@ -215,12 +261,25 @@ module CukeSniffer
|
|
215
261
|
end
|
216
262
|
|
217
263
|
def get_dead_steps
|
218
|
-
|
264
|
+
dead_steps_hash = {}
|
219
265
|
@step_definitions.each do |step_definition|
|
220
|
-
|
266
|
+
location_match = step_definition.location.match(/(?<file>.*).rb:(?<line>\d+)/)
|
267
|
+
file_name = location_match[:file]
|
268
|
+
regex = step_definition.regex.to_s.match(/\(\?\-mix\:(?<regex>.*)\)/)[:regex]
|
269
|
+
dead_steps_hash[file_name] ||= []
|
270
|
+
dead_steps_hash[file_name] << "#{location_match[:line]}: /#{regex}/" if step_definition.calls.empty?
|
221
271
|
end
|
222
|
-
|
223
|
-
|
272
|
+
total = 0
|
273
|
+
dead_steps_hash.each_key do |key|
|
274
|
+
unless dead_steps_hash[key] == []
|
275
|
+
total += dead_steps_hash[key].size
|
276
|
+
dead_steps_hash[key].sort_by! {|row| row[/^\d+/].to_i}
|
277
|
+
else
|
278
|
+
dead_steps_hash.delete(key)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
dead_steps_hash[:total] = total
|
282
|
+
dead_steps_hash
|
224
283
|
end
|
225
284
|
|
226
285
|
def extract_markup
|
@@ -242,5 +301,14 @@ module CukeSniffer
|
|
242
301
|
f.write(output)
|
243
302
|
end
|
244
303
|
end
|
304
|
+
|
305
|
+
def output_xml(file_name = "cuke_sniffer.xml")
|
306
|
+
doc = Nokogiri::XML::Document.new
|
307
|
+
doc.root = self.to_xml
|
308
|
+
open(file_name, "w") do |file|
|
309
|
+
file << doc.serialize
|
310
|
+
end
|
311
|
+
|
312
|
+
end
|
245
313
|
end
|
246
314
|
end
|
data/lib/cuke_sniffer/feature.rb
CHANGED
@@ -2,28 +2,38 @@ module CukeSniffer
|
|
2
2
|
class Feature < FeatureRulesEvaluator
|
3
3
|
include CukeSniffer::Constants
|
4
4
|
include CukeSniffer::RuleConfig
|
5
|
+
include ROXML
|
6
|
+
|
7
|
+
xml_accessor :scenarios, :as => [CukeSniffer::FeatureRulesEvaluator], :in => "scenarios"
|
5
8
|
|
6
9
|
SCENARIO_TITLE_REGEX = /#{COMMENT_REGEX}#{SCENARIO_TITLE_STYLES}(?<name>.*)/
|
7
10
|
|
8
|
-
attr_accessor :background, :scenarios, :scenarios_score
|
11
|
+
attr_accessor :background, :scenarios, :scenarios_score,:total_score
|
9
12
|
|
10
13
|
def initialize(file_name)
|
11
14
|
super(file_name)
|
12
15
|
@scenarios = []
|
13
|
-
@
|
16
|
+
@RULES_hash = {}
|
14
17
|
@scenarios_score = 0
|
15
18
|
@total_score = 0
|
16
|
-
|
17
|
-
|
19
|
+
feature_lines = extract_feature_from_file(file_name)
|
20
|
+
if feature_lines == []
|
21
|
+
store_rule(RULES[:empty_feature])
|
22
|
+
else
|
23
|
+
split_feature(file_name, feature_lines)
|
24
|
+
evaluate_score
|
25
|
+
end
|
18
26
|
end
|
19
27
|
|
20
|
-
def
|
28
|
+
def extract_feature_from_file(file_name)
|
21
29
|
feature_lines = []
|
22
|
-
|
23
30
|
feature_file = File.open(file_name)
|
24
31
|
feature_file.each_line { |line| feature_lines << line }
|
25
32
|
feature_file.close
|
33
|
+
feature_lines
|
34
|
+
end
|
26
35
|
|
36
|
+
def split_feature(file_name, feature_lines)
|
27
37
|
index = 0
|
28
38
|
until feature_lines[index].match /Feature:\s*(?<name>.*)/
|
29
39
|
update_tag_list(feature_lines[index])
|
@@ -87,20 +97,20 @@ module CukeSniffer
|
|
87
97
|
end
|
88
98
|
|
89
99
|
def rule_no_scenarios
|
90
|
-
store_rule(
|
100
|
+
store_rule(RULES[:no_scenarios]) if @scenarios.empty?
|
91
101
|
end
|
92
102
|
|
93
103
|
def rule_too_many_scenarios
|
94
|
-
rule =
|
104
|
+
rule = RULES[:too_many_scenarios]
|
95
105
|
store_rule(rule) if @scenarios.size >= rule[:max]
|
96
106
|
end
|
97
107
|
|
98
108
|
def rule_background_with_no_scenarios
|
99
|
-
store_rule(
|
109
|
+
store_rule( RULES[:background_with_no_scenarios]) if @scenarios.empty? and !@background.nil?
|
100
110
|
end
|
101
111
|
|
102
112
|
def rule_background_with_one_scenario
|
103
|
-
store_rule(
|
113
|
+
store_rule(RULES[:background_with_one_scenario]) if @scenarios.size == 1 and !@background.nil?
|
104
114
|
end
|
105
115
|
|
106
116
|
end
|
@@ -1,69 +1,69 @@
|
|
1
|
-
require 'cuke_sniffer/constants'
|
2
|
-
require 'cuke_sniffer/rule_config'
|
3
|
-
require 'cuke_sniffer/rules_evaluator'
|
4
|
-
|
5
|
-
module CukeSniffer
|
6
|
-
class FeatureRulesEvaluator < RulesEvaluator
|
7
|
-
include CukeSniffer::Constants
|
8
|
-
include CukeSniffer::RuleConfig
|
9
|
-
|
10
|
-
attr_accessor :tags, :name
|
11
|
-
|
12
|
-
def initialize(location)
|
13
|
-
@name = ""
|
14
|
-
@tags = []
|
15
|
-
super(location)
|
16
|
-
end
|
17
|
-
|
18
|
-
def create_name(line, filter)
|
19
|
-
line.gsub!(/#{COMMENT_REGEX}#{filter}/, "")
|
20
|
-
line.strip!
|
21
|
-
@name += " " unless @name.empty? or line.empty?
|
22
|
-
@name += line
|
23
|
-
end
|
24
|
-
|
25
|
-
def update_tag_list(line)
|
26
|
-
if TAG_REGEX.match(line) && !is_comment?(line)
|
27
|
-
line.scan(TAG_REGEX).each { |tag| @tags << tag[0] }
|
28
|
-
else
|
29
|
-
@tags << line.strip unless line.empty?
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def evaluate_score
|
34
|
-
super
|
35
|
-
cls_name = self.class.to_s.gsub('CukeSniffer::', '')
|
36
|
-
rule_too_many_tags(cls_name)
|
37
|
-
rule_no_description(cls_name)
|
38
|
-
rule_numbers_in_name(cls_name)
|
39
|
-
rule_long_name(cls_name)
|
40
|
-
end
|
41
|
-
|
42
|
-
def rule_too_many_tags(type)
|
43
|
-
rule =
|
44
|
-
store_updated_rule(rule, rule[:phrase].gsub(/{.*}/, type)) if tags.size >= rule[:max]
|
45
|
-
end
|
46
|
-
|
47
|
-
def rule_no_description(type)
|
48
|
-
rule =
|
49
|
-
store_updated_rule(rule, rule[:phrase].gsub(/{.*}/, type)) if name.empty?
|
50
|
-
end
|
51
|
-
|
52
|
-
def rule_numbers_in_name(type)
|
53
|
-
rule =
|
54
|
-
store_updated_rule(rule, rule[:phrase].gsub(/{.*}/, type)) if name =~ /\d/
|
55
|
-
end
|
56
|
-
|
57
|
-
def rule_long_name(type)
|
58
|
-
rule =
|
59
|
-
store_updated_rule(rule, rule[:phrase].gsub(/{.*}/, type)) if name.size >= rule[:max]
|
60
|
-
end
|
61
|
-
|
62
|
-
def == (comparison_object)
|
63
|
-
super(comparison_object)
|
64
|
-
comparison_object.name == name
|
65
|
-
comparison_object.tags == tags
|
66
|
-
end
|
67
|
-
|
68
|
-
end
|
69
|
-
end
|
1
|
+
require 'cuke_sniffer/constants'
|
2
|
+
require 'cuke_sniffer/rule_config'
|
3
|
+
require 'cuke_sniffer/rules_evaluator'
|
4
|
+
|
5
|
+
module CukeSniffer
|
6
|
+
class FeatureRulesEvaluator < RulesEvaluator
|
7
|
+
include CukeSniffer::Constants
|
8
|
+
include CukeSniffer::RuleConfig
|
9
|
+
|
10
|
+
attr_accessor :tags, :name
|
11
|
+
|
12
|
+
def initialize(location)
|
13
|
+
@name = ""
|
14
|
+
@tags = []
|
15
|
+
super(location)
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_name(line, filter)
|
19
|
+
line.gsub!(/#{COMMENT_REGEX}#{filter}/, "")
|
20
|
+
line.strip!
|
21
|
+
@name += " " unless @name.empty? or line.empty?
|
22
|
+
@name += line
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_tag_list(line)
|
26
|
+
if TAG_REGEX.match(line) && !is_comment?(line)
|
27
|
+
line.scan(TAG_REGEX).each { |tag| @tags << tag[0] }
|
28
|
+
else
|
29
|
+
@tags << line.strip unless line.empty?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def evaluate_score
|
34
|
+
super
|
35
|
+
cls_name = self.class.to_s.gsub('CukeSniffer::', '')
|
36
|
+
rule_too_many_tags(cls_name)
|
37
|
+
rule_no_description(cls_name)
|
38
|
+
rule_numbers_in_name(cls_name)
|
39
|
+
rule_long_name(cls_name)
|
40
|
+
end
|
41
|
+
|
42
|
+
def rule_too_many_tags(type)
|
43
|
+
rule = RULES[:too_many_tags]
|
44
|
+
store_updated_rule(rule, rule[:phrase].gsub(/{.*}/, type)) if tags.size >= rule[:max]
|
45
|
+
end
|
46
|
+
|
47
|
+
def rule_no_description(type)
|
48
|
+
rule = RULES[:no_description]
|
49
|
+
store_updated_rule(rule, rule[:phrase].gsub(/{.*}/, type)) if name.empty?
|
50
|
+
end
|
51
|
+
|
52
|
+
def rule_numbers_in_name(type)
|
53
|
+
rule = RULES[:numbers_in_description]
|
54
|
+
store_updated_rule(rule, rule[:phrase].gsub(/{.*}/, type)) if name =~ /\d/
|
55
|
+
end
|
56
|
+
|
57
|
+
def rule_long_name(type)
|
58
|
+
rule = RULES[:long_name]
|
59
|
+
store_updated_rule(rule, rule[:phrase].gsub(/{.*}/, type)) if name.size >= rule[:max]
|
60
|
+
end
|
61
|
+
|
62
|
+
def == (comparison_object)
|
63
|
+
super(comparison_object)
|
64
|
+
comparison_object.name == name
|
65
|
+
comparison_object.tags == tags
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -209,13 +209,22 @@
|
|
209
209
|
|
210
210
|
<div class="sections">
|
211
211
|
<div name="dead_steps" class="shrink_section" id="dead_steps">
|
212
|
+
<% dead_steps = cuke_sniffer.get_dead_steps %>
|
213
|
+
<br>
|
212
214
|
<div style="font:bold;padding-left:3%;font:bold;font-size:14pt;">Total Dead
|
213
|
-
Steps: <%=
|
214
|
-
<%
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
215
|
+
Steps: <%= dead_steps[:total] %></div>
|
216
|
+
<% dead_steps.delete :total%>
|
217
|
+
<% dead_steps.each_key do |step_file| %>
|
218
|
+
<div style="font:bold;padding-left:3%;font-size:14pt;">
|
219
|
+
<%= step_file %>
|
220
|
+
<br>
|
221
|
+
<div style="padding-left:3%;font-size:12pt;">
|
222
|
+
<%dead_steps[step_file].each do |regex|%>
|
223
|
+
<%= regex %>
|
224
|
+
<br>
|
225
|
+
<% end %>
|
226
|
+
</div></div>
|
227
|
+
<br>
|
219
228
|
<% end %>
|
220
229
|
</div>
|
221
230
|
</div>
|
@@ -320,7 +329,7 @@
|
|
320
329
|
<% i=0 %>
|
321
330
|
<% cuke_sniffer.step_definitions.each do |step_definition| %>
|
322
331
|
<div style="cursor: hand;" onclick="toggle(this,4)" class="feature <%= i%2==0 ? "even" : "odd" %>">
|
323
|
-
<% next if step_definition.score <= 0
|
332
|
+
<% next if step_definition.score <= 0 or step_definition.calls.empty? %>
|
324
333
|
<div style="float:left;padding:4px;font-weight:bold;color:red;"><%= step_definition.score %></div>
|
325
334
|
<div style="width:55%;padding:4px; float:left;">
|
326
335
|
|
@@ -1,160 +1,191 @@
|
|
1
|
-
|
2
|
-
|
3
1
|
module CukeSniffer
|
4
2
|
module RuleConfig
|
5
3
|
|
6
|
-
FATAL =
|
7
|
-
ERROR =
|
8
|
-
WARNING = 10
|
9
|
-
INFO =
|
10
|
-
|
11
|
-
SHARED_RULES = {
|
12
|
-
:too_many_tags => {
|
13
|
-
:enabled => true,
|
14
|
-
:phrase => "{class} has too many tags.",
|
15
|
-
:score => INFO,
|
16
|
-
:max => 8
|
17
|
-
},
|
18
|
-
:no_description => {
|
19
|
-
:enabled => true,
|
20
|
-
:phrase => "{class} has no description.",
|
21
|
-
:score => ERROR,
|
22
|
-
},
|
23
|
-
:numbers_in_description => {
|
24
|
-
:enabled => true,
|
25
|
-
:phrase => "{class} has numbers in the description.",
|
26
|
-
:score => WARNING,
|
27
|
-
},
|
28
|
-
:long_name => {
|
29
|
-
:enabled => true,
|
30
|
-
:phrase => "{class} has a long description.",
|
31
|
-
:score => INFO,
|
32
|
-
:max => 180
|
33
|
-
},
|
34
|
-
:implementation_word => {
|
35
|
-
:enabled => true,
|
36
|
-
:phrase => "Implementation word used: {word}.",
|
37
|
-
:score => INFO,
|
38
|
-
:words => ["page", "site", "url", "button", "drop down", "dropdown", "select list", "click", "text box", "radio button", "check box", "xml", "window", "pop up", "pop-up", "screen"]
|
39
|
-
}
|
40
|
-
}
|
41
|
-
|
42
|
-
FEATURE_RULES = {
|
43
|
-
:background_with_no_scenarios => {
|
44
|
-
:enabled => true,
|
45
|
-
:phrase => "Feature has a background with no scenarios.",
|
46
|
-
:score => WARNING,
|
47
|
-
},
|
48
|
-
:background_with_one_scenario => {
|
49
|
-
:enabled => true,
|
50
|
-
:phrase => "Feature has a background with one scenario.",
|
51
|
-
:score => WARNING,
|
52
|
-
},
|
53
|
-
:no_scenarios => {
|
54
|
-
:enabled => true,
|
55
|
-
:phrase => "Feature with no scenarios.",
|
56
|
-
:score => ERROR,
|
57
|
-
},
|
58
|
-
:too_many_scenarios => {
|
59
|
-
:enabled => true,
|
60
|
-
:phrase => "Feature with too many scenarios.",
|
61
|
-
:score => INFO,
|
62
|
-
:max => 10,
|
63
|
-
},
|
64
|
-
}
|
65
|
-
|
66
|
-
SCENARIO_RULES = {
|
67
|
-
:too_many_steps => {
|
68
|
-
:enabled => true,
|
69
|
-
:phrase => "Scenario with too many steps.",
|
70
|
-
:score => WARNING,
|
71
|
-
:max => 7,
|
72
|
-
},
|
73
|
-
:out_of_order_steps => {
|
74
|
-
:enabled => true,
|
75
|
-
:phrase => "Scenario steps out of Given/When/Then order.",
|
76
|
-
:score => WARNING,
|
77
|
-
},
|
78
|
-
:invalid_first_step => {
|
79
|
-
:enabled => true,
|
80
|
-
:phrase => "Invalid first step. Began with And/But.",
|
81
|
-
:score => WARNING,
|
82
|
-
},
|
83
|
-
:asterisk_step => {
|
84
|
-
:enabled => true,
|
85
|
-
:phrase => "Step includes a * instead of Given/When/Then/And/But.",
|
86
|
-
:score => WARNING,
|
87
|
-
},
|
88
|
-
:commented_step => {
|
89
|
-
:enabled => true,
|
90
|
-
:phrase => "Commented step.",
|
91
|
-
:score => ERROR,
|
92
|
-
},
|
93
|
-
:commented_example => {
|
94
|
-
:enabled => true,
|
95
|
-
:phrase => "Commented example.",
|
96
|
-
:score => ERROR,
|
97
|
-
},
|
98
|
-
:no_examples => {
|
99
|
-
:enabled => true,
|
100
|
-
:phrase => "Scenario Outline with no examples.",
|
101
|
-
:score => FATAL,
|
102
|
-
},
|
103
|
-
:one_example => {
|
104
|
-
:enabled => true,
|
105
|
-
:phrase => "Scenario Outline with only one example.",
|
106
|
-
:score => WARNING,
|
107
|
-
},
|
108
|
-
:no_examples_table => {
|
109
|
-
:enabled => true,
|
110
|
-
:phrase => "Scenario Outline with no examples table.",
|
111
|
-
:score => FATAL,
|
112
|
-
},
|
113
|
-
:too_many_examples => {
|
114
|
-
:enabled => true,
|
115
|
-
:phrase => "Scenario Outline with too many examples.",
|
116
|
-
:score => WARNING,
|
117
|
-
:max => 10
|
118
|
-
},
|
119
|
-
:date_used => {
|
120
|
-
:enabled => true,
|
121
|
-
:phrase => "Date used.",
|
122
|
-
:score => INFO,
|
123
|
-
},
|
124
|
-
:no_steps => {
|
125
|
-
:enabled => true,
|
126
|
-
:phrase => "No steps in Scenario.",
|
127
|
-
:score => ERROR,
|
128
|
-
},
|
129
|
-
}
|
4
|
+
FATAL = 100 #will prevent suite from executing properly
|
5
|
+
ERROR = 25 #will cause problem with debugging
|
6
|
+
WARNING = 10 #readibility/misuse of cucumber
|
7
|
+
INFO = 1 #Small improvements that can be made
|
130
8
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
:
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
:
|
155
|
-
|
9
|
+
RULES = {
|
10
|
+
:too_many_tags => {
|
11
|
+
:enabled => true,
|
12
|
+
:phrase => "{class} has too many tags.",
|
13
|
+
:score => INFO,
|
14
|
+
:max => 8
|
15
|
+
},
|
16
|
+
:no_description => {
|
17
|
+
:enabled => true,
|
18
|
+
:phrase => "{class} has no description.",
|
19
|
+
:score => ERROR,
|
20
|
+
},
|
21
|
+
:numbers_in_description => {
|
22
|
+
:enabled => true,
|
23
|
+
:phrase => "{class} has numbers in the description.",
|
24
|
+
:score => WARNING,
|
25
|
+
},
|
26
|
+
:long_name => {
|
27
|
+
:enabled => true,
|
28
|
+
:phrase => "{class} has a long description.",
|
29
|
+
:score => INFO,
|
30
|
+
:max => 180
|
31
|
+
},
|
32
|
+
:implementation_word => {
|
33
|
+
:enabled => true,
|
34
|
+
:phrase => "Implementation word used: {word}.",
|
35
|
+
:score => INFO,
|
36
|
+
:words => ["page", "site", "url", "button", "drop down", "dropdown", "select list", "click", "text box", "radio button", "check box", "xml", "window", "pop up", "pop-up", "screen"]
|
37
|
+
},
|
38
|
+
:empty_feature => {
|
39
|
+
:enabled => true,
|
40
|
+
:phrase => "Feature file has no content.",
|
41
|
+
:score => WARNING,
|
42
|
+
},
|
43
|
+
:background_with_no_scenarios => {
|
44
|
+
:enabled => true,
|
45
|
+
:phrase => "Feature has a background with no scenarios.",
|
46
|
+
:score => WARNING,
|
47
|
+
},
|
48
|
+
:background_with_one_scenario => {
|
49
|
+
:enabled => true,
|
50
|
+
:phrase => "Feature has a background with one scenario.",
|
51
|
+
:score => WARNING,
|
52
|
+
},
|
53
|
+
:no_scenarios => {
|
54
|
+
:enabled => true,
|
55
|
+
:phrase => "Feature with no scenarios.",
|
56
|
+
:score => ERROR,
|
57
|
+
},
|
58
|
+
:too_many_scenarios => {
|
59
|
+
:enabled => true,
|
60
|
+
:phrase => "Feature with too many scenarios.",
|
61
|
+
:score => INFO,
|
62
|
+
:max => 10,
|
63
|
+
},
|
64
|
+
:too_many_steps => {
|
65
|
+
:enabled => true,
|
66
|
+
:phrase => "Scenario with too many steps.",
|
67
|
+
:score => WARNING,
|
68
|
+
:max => 7,
|
69
|
+
},
|
70
|
+
:out_of_order_steps => {
|
71
|
+
:enabled => true,
|
72
|
+
:phrase => "Scenario steps out of Given/When/Then order.",
|
73
|
+
:score => WARNING,
|
74
|
+
},
|
75
|
+
:invalid_first_step => {
|
76
|
+
:enabled => true,
|
77
|
+
:phrase => "Invalid first step. Began with And/But.",
|
78
|
+
:score => WARNING,
|
79
|
+
},
|
80
|
+
:asterisk_step => {
|
81
|
+
:enabled => true,
|
82
|
+
:phrase => "Step includes a * instead of Given/When/Then/And/But.",
|
83
|
+
:score => WARNING,
|
84
|
+
},
|
85
|
+
:commented_step => {
|
86
|
+
:enabled => true,
|
87
|
+
:phrase => "Commented step.",
|
88
|
+
:score => ERROR,
|
89
|
+
},
|
90
|
+
:commented_example => {
|
91
|
+
:enabled => true,
|
92
|
+
:phrase => "Commented example.",
|
93
|
+
:score => ERROR,
|
94
|
+
},
|
95
|
+
:no_examples => {
|
96
|
+
:enabled => true,
|
97
|
+
:phrase => "Scenario Outline with no examples.",
|
98
|
+
:score => FATAL,
|
99
|
+
},
|
100
|
+
:one_example => {
|
101
|
+
:enabled => true,
|
102
|
+
:phrase => "Scenario Outline with only one example.",
|
103
|
+
:score => WARNING,
|
104
|
+
},
|
105
|
+
:no_examples_table => {
|
106
|
+
:enabled => true,
|
107
|
+
:phrase => "Scenario Outline with no examples table.",
|
108
|
+
:score => FATAL,
|
109
|
+
},
|
110
|
+
:too_many_examples => {
|
111
|
+
:enabled => true,
|
112
|
+
:phrase => "Scenario Outline with too many examples.",
|
113
|
+
:score => WARNING,
|
114
|
+
:max => 10
|
115
|
+
},
|
116
|
+
:date_used => {
|
117
|
+
:enabled => true,
|
118
|
+
:phrase => "Date used.",
|
119
|
+
:score => INFO,
|
120
|
+
},
|
121
|
+
:no_steps => {
|
122
|
+
:enabled => true,
|
123
|
+
:phrase => "No steps in Scenario.",
|
124
|
+
:score => ERROR,
|
125
|
+
},
|
126
|
+
:one_word_step => {
|
127
|
+
:enabled => true,
|
128
|
+
:phrase => "Step that is only one word long.",
|
129
|
+
:score => ERROR,
|
130
|
+
},
|
131
|
+
:multiple_given_when_then => {
|
132
|
+
:enabled => true,
|
133
|
+
:phrase => "Given/When/Then used multiple times in the same scenario.",
|
134
|
+
:score => WARNING,
|
135
|
+
},
|
136
|
+
:no_code => {
|
137
|
+
:enabled => true,
|
138
|
+
:phrase => "No code in Step Definition.",
|
139
|
+
:score => ERROR,
|
140
|
+
},
|
141
|
+
:too_many_parameters => {
|
142
|
+
:enabled => true,
|
143
|
+
:phrase => "Too many parameters in Step Definition.",
|
144
|
+
:score => WARNING,
|
145
|
+
:max => 4
|
146
|
+
},
|
147
|
+
:nested_step => {
|
148
|
+
:enabled => true,
|
149
|
+
:phrase => "Nested step call.",
|
150
|
+
:score => INFO,
|
151
|
+
},
|
152
|
+
:recursive_nested_step => {
|
153
|
+
:enabled => true,
|
154
|
+
:phrase => "Recursive nested step call.",
|
155
|
+
:score => FATAL,
|
156
|
+
},
|
157
|
+
:commented_code => {
|
158
|
+
:enabled => true,
|
159
|
+
:phrase => "Commented code in Step Definition.",
|
160
|
+
:score => INFO,
|
161
|
+
},
|
162
|
+
:lazy_debugging => {
|
163
|
+
:enabled => true,
|
164
|
+
:phrase => "Lazy Debugging through puts, p, or print",
|
165
|
+
:score => WARNING,
|
166
|
+
},
|
167
|
+
:pending => {
|
168
|
+
:enabled => true,
|
169
|
+
:phrase => "Pending step definition. Implement or remove.",
|
170
|
+
:score => WARNING,
|
171
|
+
},
|
172
|
+
:small_sleep => {
|
173
|
+
:enabled => true,
|
174
|
+
:phrase => "Small sleeps used. Use a wait_until like method.",
|
175
|
+
:score => INFO,
|
176
|
+
:max => 2
|
177
|
+
},
|
178
|
+
:large_sleep => {
|
179
|
+
:enabled => true,
|
180
|
+
:phrase => "Large sleeps used. Use a wait_until like method.",
|
156
181
|
:score => INFO,
|
157
|
-
|
182
|
+
:min => 2
|
183
|
+
},
|
184
|
+
:todo => {
|
185
|
+
:enabled => true,
|
186
|
+
:phrase => "Todo found. Resolve it.",
|
187
|
+
:score => INFO,
|
188
|
+
},
|
158
189
|
}
|
159
190
|
|
160
191
|
end
|
@@ -1,6 +1,9 @@
|
|
1
|
+
require 'roxml'
|
1
2
|
module CukeSniffer
|
2
3
|
class RulesEvaluator
|
3
|
-
|
4
|
+
include ROXML
|
5
|
+
xml_accessor :score, :location
|
6
|
+
xml_accessor :rules_hash, :as => {:key => "phrase", :value => "score"}, :in => "rules", :from => "rule"
|
4
7
|
|
5
8
|
def initialize(location)
|
6
9
|
@location = location
|
@@ -1,12 +1,13 @@
|
|
1
|
-
require 'cuke_sniffer/constants'
|
2
|
-
require 'cuke_sniffer/rule_config'
|
3
|
-
|
4
1
|
module CukeSniffer
|
5
2
|
class Scenario < FeatureRulesEvaluator
|
6
3
|
include CukeSniffer::Constants
|
7
4
|
include CukeSniffer::RuleConfig
|
8
|
-
|
9
|
-
|
5
|
+
|
6
|
+
xml_accessor :start_line
|
7
|
+
xml_accessor :steps, :as => [], :in => "steps"
|
8
|
+
xml_accessor :examples_table, :as => [], :in => "examples"
|
9
|
+
|
10
|
+
attr_accessor :type, :inline_tables
|
10
11
|
|
11
12
|
def initialize(location, scenario)
|
12
13
|
super(location)
|
@@ -81,9 +82,22 @@ module CukeSniffer
|
|
81
82
|
rule_commented_step
|
82
83
|
rule_implementation_words
|
83
84
|
rule_date_used_in_step
|
85
|
+
rule_one_word_step
|
86
|
+
rule_multiple_given_when_then
|
84
87
|
evaluate_outline_scores if type == "Scenario Outline"
|
85
88
|
end
|
86
89
|
|
90
|
+
def rule_multiple_given_when_then
|
91
|
+
step_order = get_step_order
|
92
|
+
%w(Given When Then).each { |type| store_rule(RULES[:multiple_given_when_then]) if step_order.count(type) > 1 }
|
93
|
+
end
|
94
|
+
|
95
|
+
def rule_one_word_step
|
96
|
+
@steps.each do |step|
|
97
|
+
store_rule(RULES[:one_word_step]) if step.split.count == 2
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
87
101
|
def evaluate_outline_scores
|
88
102
|
rule_no_examples_table
|
89
103
|
rule_no_examples
|
@@ -93,37 +107,37 @@ module CukeSniffer
|
|
93
107
|
end
|
94
108
|
|
95
109
|
def rule_empty_scenario
|
96
|
-
store_rule(
|
110
|
+
store_rule(RULES[:no_steps]) if @steps.empty?
|
97
111
|
end
|
98
112
|
|
99
113
|
def rule_too_many_steps
|
100
|
-
rule =
|
114
|
+
rule = RULES[:too_many_steps]
|
101
115
|
store_rule(rule) if @steps.size >= rule[:max]
|
102
116
|
end
|
103
117
|
|
104
118
|
def rule_step_order
|
105
119
|
step_order = get_step_order.uniq
|
106
120
|
%w(But * And).each { |type| step_order.delete(type) }
|
107
|
-
store_rule(
|
121
|
+
store_rule(RULES[:out_of_order_steps]) unless step_order == %w(Given When Then) or step_order == %w(When Then)
|
108
122
|
end
|
109
123
|
|
110
124
|
def rule_invalid_first_step
|
111
125
|
first_step = get_step_order.first
|
112
|
-
store_rule(
|
126
|
+
store_rule(RULES[:invalid_first_step]) if %w(And But).include?(first_step)
|
113
127
|
end
|
114
128
|
|
115
129
|
def rule_asterisk_step
|
116
|
-
get_step_order.count('*').times { store_rule(
|
130
|
+
get_step_order.count('*').times { store_rule(RULES[:asterisk_step]) }
|
117
131
|
end
|
118
132
|
|
119
133
|
def rule_commented_step
|
120
134
|
@steps.each do |step|
|
121
|
-
store_rule(
|
135
|
+
store_rule(RULES[:commented_step]) if is_comment?(step)
|
122
136
|
end
|
123
137
|
end
|
124
138
|
|
125
139
|
def rule_implementation_words
|
126
|
-
rule =
|
140
|
+
rule = RULES[:implementation_word]
|
127
141
|
@steps.each do |step|
|
128
142
|
next if is_comment?(step)
|
129
143
|
rule[:words].each do |word|
|
@@ -134,29 +148,29 @@ module CukeSniffer
|
|
134
148
|
|
135
149
|
def rule_date_used_in_step
|
136
150
|
@steps.each do |step|
|
137
|
-
store_rule(
|
151
|
+
store_rule(RULES[:date_used]) if step =~ DATE_REGEX
|
138
152
|
end
|
139
153
|
end
|
140
154
|
|
141
155
|
def rule_no_examples_table
|
142
|
-
store_rule(
|
156
|
+
store_rule(RULES[:no_examples_table]) if @examples_table.empty?
|
143
157
|
end
|
144
158
|
|
145
159
|
def rule_no_examples
|
146
|
-
store_rule(
|
160
|
+
store_rule(RULES[:no_examples]) if @examples_table.size == 1
|
147
161
|
end
|
148
162
|
|
149
163
|
def rule_one_example
|
150
|
-
store_rule(
|
164
|
+
store_rule(RULES[:one_example]) if @examples_table.size == 2 and !is_comment?(@examples_table[1])
|
151
165
|
end
|
152
166
|
|
153
167
|
def rule_too_many_examples
|
154
|
-
store_rule(
|
168
|
+
store_rule(RULES[:too_many_examples]) if (@examples_table.size - 1) >= 8
|
155
169
|
end
|
156
170
|
|
157
171
|
def rule_commented_example
|
158
172
|
@examples_table.each do |example|
|
159
|
-
store_rule(
|
173
|
+
store_rule(RULES[:commented_example]) if is_comment?(example)
|
160
174
|
end
|
161
175
|
end
|
162
176
|
|
@@ -1,15 +1,22 @@
|
|
1
|
+
require 'roxml'
|
1
2
|
module CukeSniffer
|
2
3
|
class StepDefinition < RulesEvaluator
|
3
4
|
include CukeSniffer::Constants
|
4
5
|
include CukeSniffer::RuleConfig
|
5
6
|
|
7
|
+
xml_accessor :start_line
|
8
|
+
xml_accessor :regex
|
9
|
+
xml_accessor :parameters, :as => [], :in => "parameters"
|
10
|
+
xml_accessor :nested_steps, :as => {:key => 'location', :value => 'call'}, :in => "nested_steps"
|
11
|
+
xml_accessor :calls, :as => {:key => 'location', :value => 'call'}, :in => "calls"
|
12
|
+
xml_accessor :code, :as => [], :in => "code"
|
13
|
+
|
6
14
|
SIMPLE_NESTED_STEP_REGEX = /steps\s"#{STEP_STYLES}(?<step_string>.*)"$/
|
7
|
-
|
8
|
-
|
15
|
+
START_COMPLEX_STEP_REGEX = /^steps\s%(q|Q)?\{\s*/
|
16
|
+
SAME_LINE_COMPLEX_STEP_REGEX = /#{START_COMPLEX_STEP_REGEX}#{STEP_STYLES}(?<step_string>.*)}$/
|
9
17
|
END_COMPLEX_STEP_REGEX = /}$/
|
10
|
-
START_COMPLEX_WITH_STEP_REGEX = /steps\s%Q?\{#{STEP_STYLES}(?<step_string>.*)$/
|
18
|
+
START_COMPLEX_WITH_STEP_REGEX = /steps\s%(q|Q)?\{#{STEP_STYLES}(?<step_string>.*)$/
|
11
19
|
END_COMPLEX_WITH_STEP_REGEX = /#{STEP_STYLES}(?<step_string>.*)}$/
|
12
|
-
attr_accessor :start_line, :regex, :code, :parameters, :calls, :nested_steps
|
13
20
|
|
14
21
|
def initialize(location, raw_code)
|
15
22
|
super(location)
|
@@ -17,7 +24,7 @@ module CukeSniffer
|
|
17
24
|
@parameters = []
|
18
25
|
@calls = {}
|
19
26
|
@nested_steps = {}
|
20
|
-
@start_line = location.match(/:(?<line>\d*)
|
27
|
+
@start_line = location.match(/:(?<line>\d*)$/)[:line].to_i
|
21
28
|
|
22
29
|
end_match_index = (raw_code.size - 1) - raw_code.reverse.index("end")
|
23
30
|
@code = raw_code[1...end_match_index]
|
@@ -46,13 +53,26 @@ module CukeSniffer
|
|
46
53
|
case line
|
47
54
|
when SIMPLE_NESTED_STEP_REGEX
|
48
55
|
regex = SIMPLE_NESTED_STEP_REGEX
|
56
|
+
when START_COMPLEX_WITH_STEP_REGEX
|
57
|
+
if line =~ /\}$/
|
58
|
+
if line.include?('#{')
|
59
|
+
reversed_line = line.reverse
|
60
|
+
last_capture = reversed_line[0..reversed_line.index('#')].reverse
|
61
|
+
if last_capture =~ /{.*}$/
|
62
|
+
multi_line_step_flag = true
|
63
|
+
regex = START_COMPLEX_WITH_STEP_REGEX
|
64
|
+
else
|
65
|
+
regex = SAME_LINE_COMPLEX_STEP_REGEX
|
66
|
+
end
|
67
|
+
else
|
68
|
+
regex = SAME_LINE_COMPLEX_STEP_REGEX
|
69
|
+
end
|
70
|
+
else
|
71
|
+
multi_line_step_flag = true
|
72
|
+
regex = START_COMPLEX_WITH_STEP_REGEX
|
73
|
+
end
|
49
74
|
when SAME_LINE_COMPLEX_STEP_REGEX
|
50
75
|
regex = SAME_LINE_COMPLEX_STEP_REGEX
|
51
|
-
when START_COMPLEX_WITH_STEP_REGEX
|
52
|
-
multi_line_step_flag = true
|
53
|
-
regex = START_COMPLEX_WITH_STEP_REGEX
|
54
|
-
when START_COMPLEX_STEP_REGEX
|
55
|
-
multi_line_step_flag = true
|
56
76
|
when END_COMPLEX_WITH_STEP_REGEX
|
57
77
|
if line =~ /[#]{.*}$/ && multi_line_step_flag
|
58
78
|
regex = STEP_REGEX
|
@@ -60,6 +80,8 @@ module CukeSniffer
|
|
60
80
|
regex = END_COMPLEX_WITH_STEP_REGEX
|
61
81
|
multi_line_step_flag = false
|
62
82
|
end
|
83
|
+
when START_COMPLEX_STEP_REGEX
|
84
|
+
multi_line_step_flag = true
|
63
85
|
when STEP_REGEX
|
64
86
|
regex = STEP_REGEX if multi_line_step_flag
|
65
87
|
when END_COMPLEX_STEP_REGEX
|
@@ -89,7 +111,7 @@ module CukeSniffer
|
|
89
111
|
|
90
112
|
def condensed_call_list
|
91
113
|
condensed_list = {}
|
92
|
-
@calls.each{|call, step_string|
|
114
|
+
@calls.each { |call, step_string|
|
93
115
|
condensed_list[step_string] ||= []
|
94
116
|
condensed_list[step_string] << call
|
95
117
|
}
|
@@ -112,30 +134,67 @@ module CukeSniffer
|
|
112
134
|
rule_nested_steps
|
113
135
|
rule_recursive_nested_step
|
114
136
|
rule_commented_code
|
137
|
+
rule_lazy_debugging
|
138
|
+
rule_pending
|
139
|
+
rule_todo
|
140
|
+
sleep_rules
|
141
|
+
end
|
142
|
+
|
143
|
+
def rule_todo
|
144
|
+
code.each do |line|
|
145
|
+
store_rule(RULES[:todo]) if line =~ /\#(TODO|todo)/
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def sleep_rules
|
150
|
+
code.each do |line|
|
151
|
+
match_data = line.match /^\s*sleep(\s|\()(?<sleep_time>.*)\)?/
|
152
|
+
unless match_data.nil?
|
153
|
+
sleep_value = match_data[:sleep_time].to_f
|
154
|
+
store_rule(RULES[:small_sleep]) if sleep_value <= RULES[:small_sleep][:max]
|
155
|
+
store_rule(RULES[:large_sleep]) if sleep_value > RULES[:large_sleep][:min]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def rule_pending
|
161
|
+
code.each do |line|
|
162
|
+
if line =~ /^\s*pending\s*$/
|
163
|
+
store_rule(RULES[:pending])
|
164
|
+
return
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def rule_lazy_debugging
|
170
|
+
code.each do |line|
|
171
|
+
next if is_comment?(line)
|
172
|
+
store_rule(RULES[:lazy_debugging]) if line.strip =~ /^(p|puts)( |\()('|"|%(q|Q)?\{)/
|
173
|
+
end
|
115
174
|
end
|
116
175
|
|
117
176
|
def rule_no_code
|
118
|
-
store_rule(
|
177
|
+
store_rule(RULES[:no_code]) if code.empty?
|
119
178
|
end
|
120
179
|
|
121
180
|
def rule_too_many_parameters
|
122
|
-
rule =
|
181
|
+
rule = RULES[:too_many_parameters]
|
123
182
|
store_rule(rule) if parameters.size >= rule[:max]
|
124
183
|
end
|
125
184
|
|
126
185
|
def rule_nested_steps
|
127
|
-
store_rule(
|
186
|
+
store_rule(RULES[:nested_step]) unless nested_steps.empty?
|
128
187
|
end
|
129
188
|
|
130
189
|
def rule_recursive_nested_step
|
131
190
|
nested_steps.each_value do |nested_step|
|
132
|
-
store_rule(
|
191
|
+
store_rule(RULES[:recursive_nested_step]) if nested_step =~ regex
|
133
192
|
end
|
134
193
|
end
|
135
194
|
|
136
195
|
def rule_commented_code
|
137
196
|
code.each do |line|
|
138
|
-
store_rule(
|
197
|
+
store_rule(RULES[:commented_code]) if is_comment?(line)
|
139
198
|
end
|
140
199
|
end
|
141
200
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cuke_sniffer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,10 +11,26 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2013-
|
15
|
-
dependencies:
|
14
|
+
date: 2013-03-15 00:00:00.000000000 Z
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: roxml
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ! '>='
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: '0'
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
28
|
+
requirements:
|
29
|
+
- - ! '>='
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '0'
|
16
32
|
description: A ruby library used to root out smells in your cukes.
|
17
|
-
email:
|
33
|
+
email:
|
18
34
|
executables:
|
19
35
|
- cuke_sniffer.rb
|
20
36
|
extensions: []
|
@@ -43,18 +59,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
43
59
|
- - ! '>='
|
44
60
|
- !ruby/object:Gem::Version
|
45
61
|
version: '0'
|
46
|
-
segments:
|
47
|
-
- 0
|
48
|
-
hash: -320967053
|
49
62
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
63
|
none: false
|
51
64
|
requirements:
|
52
65
|
- - ! '>='
|
53
66
|
- !ruby/object:Gem::Version
|
54
67
|
version: '0'
|
55
|
-
segments:
|
56
|
-
- 0
|
57
|
-
hash: -320967053
|
58
68
|
requirements: []
|
59
69
|
rubyforge_project:
|
60
70
|
rubygems_version: 1.8.24
|