cuke_sniffer 0.0.2 → 0.0.3

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.
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