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 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 html (name) : Runs CukeSniffer then outputs an
10
- html file in the current
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
@@ -1,3 +1,5 @@
1
+ require 'nokogiri'
2
+ require 'roxml'
1
3
 
2
4
  require 'cuke_sniffer/constants'
3
5
  require 'cuke_sniffer/rule_config'
@@ -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
- attr_accessor :features_location,:step_definitions_location, :features, :scenarios, :step_definitions, :summary
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
- counter = 1
191
- scenario.steps.each do |step|
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
- dead_steps = []
264
+ dead_steps_hash = {}
219
265
  @step_definitions.each do |step_definition|
220
- dead_steps << step_definition if step_definition.calls.empty?
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
- dead_steps.sort_by{|step| step.location}
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
@@ -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, :total_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
- @feature_rules_hash = {}
16
+ @RULES_hash = {}
14
17
  @scenarios_score = 0
15
18
  @total_score = 0
16
- split_feature(file_name)
17
- evaluate_score
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 split_feature(file_name)
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(FEATURE_RULES[:no_scenarios]) if @scenarios.empty?
100
+ store_rule(RULES[:no_scenarios]) if @scenarios.empty?
91
101
  end
92
102
 
93
103
  def rule_too_many_scenarios
94
- rule = FEATURE_RULES[:too_many_scenarios]
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( FEATURE_RULES[:background_with_no_scenarios]) if @scenarios.empty? and !@background.nil?
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(FEATURE_RULES[:background_with_one_scenario]) if @scenarios.size == 1 and !@background.nil?
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 = SHARED_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 = SHARED_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 = SHARED_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 = SHARED_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
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: <%= cuke_sniffer.get_dead_steps.count %></div>
214
- <% cuke_sniffer.get_dead_steps.each do |dead_step| %>
215
- <div style="font:14pt;padding-left:5%;padding-bottom: 1%;">
216
- <div><%= "/" + dead_step.regex.to_s.match(/\(\?\-mix\:(?<regex>.*)\)/)[:regex] + "/" %></div>
217
- <div style="padding-left:1%"><%= dead_step.location %></div>
218
- </div>
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 and !step_definition.calls.empty? %>
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 = 100 #will prevent suite from executing properly
7
- ERROR = 25 #will cause problem with debugging
8
- WARNING = 10 #readibility/misuse of cucumber
9
- INFO = 1 #Small improvements that can be made
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
- STEP_DEFINITION_RULES = {
132
- :no_code => {
133
- :enabled => true,
134
- :phrase => "No code in Step Definition.",
135
- :score => ERROR,
136
- },
137
- :too_many_parameters => {
138
- :enabled => true,
139
- :phrase => "Too many parameters in Step Definition.",
140
- :score => WARNING,
141
- :max => 4
142
- },
143
- :nested_step => {
144
- :enabled => true,
145
- :phrase => "Nested step call.",
146
- :score => INFO,
147
- },
148
- :recursive_nested_step => {
149
- :enabled => true,
150
- :phrase => "Recursive nested step call.",
151
- :score => FATAL,
152
- },
153
- :commented_code => {
154
- :enabled => true,
155
- :phrase => "Commented code in Step Definition.",
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
- attr_accessor :location, :score, :rules_hash
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
- attr_accessor :start_line, :type, :steps, :inline_tables, :examples_table
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(SCENARIO_RULES[:no_steps]) if @steps.empty?
110
+ store_rule(RULES[:no_steps]) if @steps.empty?
97
111
  end
98
112
 
99
113
  def rule_too_many_steps
100
- rule = SCENARIO_RULES[:too_many_steps]
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(SCENARIO_RULES[:out_of_order_steps]) unless step_order == %w(Given When Then) or step_order == %w(When Then)
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(SCENARIO_RULES[:invalid_first_step]) if %w(And But).include?(first_step)
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(SCENARIO_RULES[:asterisk_step]) }
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(SCENARIO_RULES[:commented_step]) if is_comment?(step)
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 = SHARED_RULES[:implementation_word]
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(SCENARIO_RULES[:date_used]) if step =~ DATE_REGEX
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(SCENARIO_RULES[:no_examples_table]) if @examples_table.empty?
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(SCENARIO_RULES[:no_examples]) if @examples_table.size == 1
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(SCENARIO_RULES[:one_example]) if @examples_table.size == 2 and !is_comment?(@examples_table[1])
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(SCENARIO_RULES[:too_many_examples]) if (@examples_table.size - 1) >= 8
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(SCENARIO_RULES[:commented_example]) if is_comment?(example)
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
- SAME_LINE_COMPLEX_STEP_REGEX = /^steps\s%Q?{#{STEP_STYLES}(?<step_string>.*)}$/
8
- START_COMPLEX_STEP_REGEX = /^steps\s%Q?\{\s*/
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*)/)[:line].to_i
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(STEP_DEFINITION_RULES[:no_code]) if code.empty?
177
+ store_rule(RULES[:no_code]) if code.empty?
119
178
  end
120
179
 
121
180
  def rule_too_many_parameters
122
- rule = STEP_DEFINITION_RULES[:too_many_parameters]
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(STEP_DEFINITION_RULES[:nested_step]) unless nested_steps.empty?
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(STEP_DEFINITION_RULES[:recursive_nested_step]) if nested_step =~ regex
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(STEP_DEFINITION_RULES[:commented_code]) if is_comment?(line)
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.2
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-02-15 00:00:00.000000000 Z
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