cuke_sniffer 0.0.5 → 0.0.6

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.
Files changed (31) hide show
  1. data/bin/cuke_sniffer +85 -63
  2. data/lib/cuke_sniffer.rb +23 -16
  3. data/lib/cuke_sniffer/cli.rb +269 -523
  4. data/lib/cuke_sniffer/constants.rb +5 -2
  5. data/lib/cuke_sniffer/cuke_sniffer_helper.rb +165 -0
  6. data/lib/cuke_sniffer/dead_steps_helper.rb +54 -0
  7. data/lib/cuke_sniffer/feature.rb +13 -81
  8. data/lib/cuke_sniffer/feature_rules_evaluator.rb +56 -115
  9. data/lib/cuke_sniffer/formatter.rb +142 -0
  10. data/lib/cuke_sniffer/hook.rb +77 -160
  11. data/lib/cuke_sniffer/report/dead_steps.html.erb +36 -0
  12. data/lib/cuke_sniffer/report/features.html.erb +60 -0
  13. data/lib/cuke_sniffer/report/hooks.html.erb +49 -0
  14. data/lib/cuke_sniffer/report/improvement_list.html.erb +18 -0
  15. data/lib/cuke_sniffer/report/legend.html.erb +167 -0
  16. data/lib/cuke_sniffer/report/min_template.html.erb +125 -0
  17. data/lib/cuke_sniffer/report/rules.html.erb +9 -0
  18. data/lib/cuke_sniffer/report/standard_template.html.erb +137 -0
  19. data/lib/cuke_sniffer/report/step_definitions.html.erb +49 -0
  20. data/lib/cuke_sniffer/report/sub_rules.html.erb +24 -0
  21. data/lib/cuke_sniffer/report/summary.html.erb +71 -0
  22. data/lib/cuke_sniffer/rule.rb +28 -0
  23. data/lib/cuke_sniffer/rule_config.rb +241 -48
  24. data/lib/cuke_sniffer/rule_target.rb +62 -0
  25. data/lib/cuke_sniffer/rules_evaluator.rb +65 -70
  26. data/lib/cuke_sniffer/scenario.rb +102 -238
  27. data/lib/cuke_sniffer/step_definition.rb +163 -239
  28. data/lib/cuke_sniffer/summary_helper.rb +91 -0
  29. data/lib/cuke_sniffer/summary_node.rb +19 -0
  30. metadata +23 -5
  31. data/lib/cuke_sniffer/report/markup.rhtml +0 -353
@@ -0,0 +1,62 @@
1
+ require 'roxml'
2
+ module CukeSniffer
3
+
4
+ # Author:: Robert Cochran (mailto:cochrarj@miamioh.edu)
5
+ # Copyright:: Copyright (C) 2013 Robert Cochran
6
+ # License:: Distributes under the MIT License
7
+ # Parent class for all objects that have rules executed against it
8
+ # Mixins: CukeSniffer::Constants, CukeSniffer::RuleConfig, ROXML
9
+ class RuleTarget
10
+ include CukeSniffer::Constants
11
+ include CukeSniffer::RuleConfig
12
+ include ROXML
13
+
14
+ xml_accessor :score, :location
15
+ xml_accessor :rules_hash, :as => {:key => "phrase", :value => "score"}, :in => "rules", :from => "rule"
16
+
17
+ # int: Sum of the rules fired
18
+ attr_accessor :score
19
+
20
+ # string: Location in which the object was found
21
+ attr_accessor :location
22
+
23
+ # hash: Contains the phrase every rule fired against the object and times it fired
24
+ # * Key: string
25
+ # * Value: int
26
+ attr_accessor :rules_hash
27
+
28
+ # Location must be in the format of "file_path\file_name.rb:line_number"
29
+ def initialize(location)
30
+ @location = location
31
+ @score = 0
32
+ @rules_hash = {}
33
+ @class_type = self.class.to_s.gsub(/.*::/, "")
34
+ end
35
+
36
+ # Compares the score against the objects threshold
37
+ # If a score is below the threshold it is good and returns true
38
+ # Return: Boolean
39
+ def good?
40
+ score <= Constants::THRESHOLDS[@class_type]
41
+ end
42
+
43
+ # Calculates the score to threshold percentage of an object
44
+ # Return: Float
45
+ def problem_percentage
46
+ score.to_f / Constants::THRESHOLDS[@class_type].to_f
47
+ end
48
+
49
+ def == (comparison_object) # :nodoc:
50
+ comparison_object.location == location &&
51
+ comparison_object.score == score &&
52
+ comparison_object.rules_hash == rules_hash
53
+ end
54
+
55
+ private
56
+
57
+ #TODO Abstraction needed for this regex matcher (constants?)
58
+ def is_comment?(line)
59
+ true if line =~ /^\#.*$/
60
+ end
61
+ end
62
+ end
@@ -1,70 +1,65 @@
1
- require 'roxml'
2
- module CukeSniffer
3
-
4
- # Author:: Robert Cochran (mailto:cochrarj@miamioh.edu)
5
- # Copyright:: Copyright (C) 2013 Robert Cochran
6
- # License:: Distributes under the MIT License
7
- # Parent class for all objects that have rules executed against it
8
- # Mixins: CukeSniffer::Constants, CukeSniffer::RuleConfig, ROXML
9
- class RulesEvaluator
10
- include CukeSniffer::Constants
11
- include CukeSniffer::RuleConfig
12
- include ROXML
13
-
14
- xml_accessor :score, :location
15
- xml_accessor :rules_hash, :as => {:key => "phrase", :value => "score"}, :in => "rules", :from => "rule"
16
-
17
- # int: Sum of the rules fired
18
- attr_accessor :score
19
-
20
- # string: Location in which the object was found
21
- attr_accessor :location
22
-
23
- # hash: Contains the phrase every rule fired against the object and times it fired
24
- # * Key: string
25
- # * Value: int
26
- attr_accessor :rules_hash
27
-
28
- # Location must be in the format of "file_path\file_name.rb:line_number"
29
- def initialize(location)
30
- @location = location
31
- @score = 0
32
- @rules_hash = {}
33
- @class_type = self.class.to_s.gsub(/.*::/, "")
34
- end
35
-
36
- # Compares the score against the objects threshold
37
- # If a score is below the threshold it is good and returns true
38
- # Return: Boolean
39
- def good?
40
- score <= Constants::THRESHOLDS[@class_type]
41
- end
42
-
43
- # Calculates the score to threshold percentage of an object
44
- # Return: Float
45
- def problem_percentage
46
- score.to_f / Constants::THRESHOLDS[@class_type].to_f
47
- end
48
-
49
- def == (comparison_object) # :nodoc:
50
- comparison_object.location == location &&
51
- comparison_object.score == score &&
52
- comparison_object.rules_hash == rules_hash
53
- end
54
-
55
- def store_rule(rule, phrase = rule[:phrase])
56
- if rule[:enabled]
57
- @score += rule[:score]
58
- @rules_hash[phrase] ||= 0
59
- @rules_hash[phrase] += 1
60
- end
61
- end
62
-
63
- private
64
-
65
- #TODO Abstraction needed for this regex matcher (constants?)
66
- def is_comment?(line)
67
- true if line =~ /^\#.*$/
68
- end
69
- end
70
- end
1
+ require 'roxml'
2
+
3
+ module CukeSniffer
4
+
5
+ # Author:: Robert Cochran (mailto:cochrarj@miamioh.edu)
6
+ # Copyright:: Copyright (C) 2013 Robert Cochran
7
+ # License:: Distributes under the MIT License
8
+ # Evaluates all cucumber components found in CukeSniffer with the passed rules
9
+ class RulesEvaluator
10
+ include CukeSniffer::Constants
11
+ attr_accessor :rules
12
+
13
+ def initialize(cli, rules)
14
+ raise "A CLI must be provided for evaluation." if cli.nil?
15
+ raise "Rules must be provided for evaluation." if rules.nil? or rules.empty?
16
+ @rules = rules
17
+ judge_features(cli.features)
18
+ judge_objects(cli.step_definitions, "StepDefinition")
19
+ judge_objects(cli.hooks, "Hook")
20
+ end
21
+
22
+ private
23
+
24
+ def judge_features(features)
25
+ features.each do |feature|
26
+ judge_feature(feature)
27
+ end
28
+ end
29
+
30
+ def judge_feature(feature)
31
+ judge_object(feature, "Feature")
32
+ judge_object(feature.background, "Background") unless feature.background.nil?
33
+ judge_objects(feature.scenarios, "Scenario")
34
+ feature.total_score += feature.update_score
35
+
36
+ end
37
+
38
+ def judge_objects(objects, type)
39
+ objects.each do | object |
40
+ judge_object(object, type)
41
+ end
42
+ end
43
+
44
+ def judge_object(object, type)
45
+ @rules.each do |rule|
46
+ fail "No targets for rule: #{rule.phrase}" if rule.targets.nil? or rule.targets.empty?
47
+ next unless rule.targets.include? type and rule.enabled
48
+ if eval(rule.reason) == true
49
+ phrase = rule.phrase.gsub("{class}", type)
50
+ store_rule(object, rule, phrase)
51
+ end
52
+ end
53
+ end
54
+
55
+ def store_rule(object, rule, phrase = rule.phrase)
56
+ object.score += rule.score
57
+ object.rules_hash[phrase] ||= 0
58
+ object.rules_hash[phrase] += 1
59
+ end
60
+
61
+ def is_comment?(line)
62
+ true if line =~ /^\#.*$/
63
+ end
64
+ end
65
+ end
@@ -1,238 +1,102 @@
1
- module CukeSniffer
2
-
3
- # Author:: Robert Cochran (mailto:cochrarj@miamioh.edu)
4
- # Copyright:: Copyright (C) 2013 Robert Cochran
5
- # License:: Distributes under the MIT License
6
- # This class is a representation of the cucumber objects
7
- # Background, Scenario, Scenario Outline
8
- #
9
- # Extends CukeSniffer::FeatureRulesEvaluator
10
- class Scenario < FeatureRulesEvaluator
11
-
12
- xml_accessor :start_line
13
- xml_accessor :steps, :as => [], :in => "steps"
14
- xml_accessor :examples_table, :as => [], :in => "examples"
15
-
16
- # int: Line on which the scenario begins
17
- attr_accessor :start_line
18
-
19
- # string: The type of scenario
20
- # Background, Scenario, Scenario Outline
21
- attr_accessor :type
22
-
23
- # string array: List of each step call in a scenario
24
- attr_accessor :steps
25
-
26
- # hash: Keeps each location and content of an inline table
27
- # * Key: Step string the inline table is attached to
28
- # * Value: Array of all of the lines in the table
29
- attr_accessor :inline_tables
30
-
31
- # string array: List of each example row in a scenario outline
32
- attr_accessor :examples_table
33
-
34
- # Location must be in the format of "file_path\file_name.rb:line_number"
35
- # Scenario must be a string array containing everything from the first tag to the last example table
36
- # where applicable.
37
- def initialize(location, scenario)
38
- super(location)
39
- @start_line = location.match(/:(?<line>\d*)$/)[:line].to_i
40
- @steps = []
41
- @inline_tables = {}
42
- @examples_table = []
43
- split_scenario(scenario)
44
- evaluate_score
45
- end
46
-
47
- def ==(comparison_object) # :nodoc:
48
- super(comparison_object) &&
49
- comparison_object.steps == steps &&
50
- comparison_object.examples_table == examples_table
51
- end
52
-
53
- private
54
-
55
- def split_scenario(scenario)
56
- index = 0
57
- until index >= scenario.length or scenario[index] =~ SCENARIO_TITLE_STYLES
58
- update_tag_list(scenario[index])
59
- index += 1
60
- end
61
-
62
- until index >= scenario.length or scenario[index].match STEP_REGEX or scenario[index].include?("Examples:")
63
- match = scenario[index].match(SCENARIO_TITLE_STYLES)
64
- @type = match[:type] unless match.nil?
65
- create_name(scenario[index], SCENARIO_TITLE_STYLES)
66
- index += 1
67
- end
68
-
69
- until index >= scenario.length or scenario[index].include?("Examples:")
70
- if scenario[index] =~ /^\|.*\|/
71
- step = scenario[index - 1]
72
- @inline_tables[step] = []
73
- until index >= scenario.length or scenario[index] =~ /(#{STEP_REGEX}|^\s*Examples:)/
74
- @inline_tables[step] << scenario[index]
75
- index += 1
76
- end
77
- else
78
- @steps << scenario[index] if scenario[index] =~ STEP_REGEX
79
- index += 1
80
- end
81
- end
82
-
83
- if index < scenario.length and scenario[index].include?("Examples:")
84
- index += 1
85
- until index >= scenario.length
86
- index += 2 if scenario[index].include?("Examples:")
87
- @examples_table << scenario[index] if scenario[index] =~ /#{COMMENT_REGEX}\|.*\|/
88
- index += 1
89
- end
90
- end
91
- end
92
-
93
- def get_step_order
94
- order = []
95
- @steps.each do |line|
96
- next if is_comment?(line)
97
- match = line.match(STEP_REGEX)
98
- order << match[:style] unless match.nil?
99
- end
100
- order
101
- end
102
-
103
- def evaluate_score
104
- if type == "Background"
105
- rule_numbers_in_name(type)
106
- rule_long_name(type)
107
- rule_tagged_background(type)
108
- else
109
- super
110
- rule_step_order
111
- end
112
-
113
- rule_empty_scenario
114
- rule_too_many_steps
115
- rule_invalid_first_step
116
- rule_asterisk_step
117
- rule_commented_step
118
- rule_implementation_words
119
- rule_date_used_in_step
120
- rule_one_word_step
121
- rule_multiple_given_when_then
122
- evaluate_outline_scores if type == "Scenario Outline"
123
- end
124
-
125
- def evaluate_outline_scores
126
- rule_no_examples_table
127
- rule_no_examples
128
- rule_one_example
129
- rule_too_many_examples
130
- rule_commented_example
131
- end
132
-
133
- def rule_multiple_given_when_then
134
- step_order = get_step_order
135
- rule = RULES[:multiple_given_when_then]
136
- phrase = rule[:phrase].gsub(/{.*}/, type)
137
- ["Given", "When", "Then"].each { |step| store_rule(rule, phrase) if step_order.count(step) > 1 }
138
- end
139
-
140
- def rule_one_word_step
141
- @steps.each do |step|
142
- rule = RULES[:one_word_step]
143
- store_rule(rule) if step.split.count == 2
144
- end
145
- end
146
-
147
- def rule_step_order
148
- step_order = get_step_order.uniq
149
- ["But", "*", "And"].each { |type| step_order.delete(type) }
150
- rule = RULES[:out_of_order_steps]
151
- store_rule(rule) unless step_order == %w(Given When Then) or step_order == %w(When Then)
152
- end
153
-
154
- def rule_asterisk_step
155
- get_step_order.count('*').times do
156
- rule = RULES[:asterisk_step]
157
- store_rule(rule)
158
- end
159
- end
160
-
161
- def rule_commented_step
162
- @steps.each do |step|
163
- rule = RULES[:commented_step]
164
- store_rule(rule) if is_comment?(step)
165
- end
166
- end
167
-
168
- def rule_date_used_in_step
169
- @steps.each do |step|
170
- rule = RULES[:date_used]
171
- store_rule(rule) if step =~ DATE_REGEX
172
- end
173
- end
174
-
175
- def rule_no_examples_table
176
- rule = RULES[:no_examples_table]
177
- store_rule(rule) if @examples_table.empty?
178
- end
179
-
180
- def rule_no_examples
181
- rule = RULES[:no_examples]
182
- store_rule(rule) if @examples_table.size == 1
183
- end
184
-
185
- def rule_one_example
186
- rule = RULES[:one_example]
187
- store_rule(rule) if @examples_table.size == 2 and !is_comment?(@examples_table[1])
188
- end
189
-
190
- def rule_too_many_examples
191
- rule = RULES[:too_many_examples]
192
- store_rule(rule) if (@examples_table.size - 1) >= 8
193
- end
194
-
195
- def rule_commented_example
196
- @examples_table.each do |example|
197
- rule = RULES[:commented_example]
198
- store_rule(rule) if is_comment?(example)
199
- end
200
- end
201
-
202
- def rule_implementation_words
203
- rule = RULES[:implementation_word]
204
- @steps.each do |step|
205
- next if is_comment?(step)
206
- rule[:words].each do |word|
207
- rule_phrase = rule[:phrase].gsub(/{.*}/, word)
208
- store_rule(rule, rule_phrase) if step.include?(word)
209
- end
210
- end
211
- end
212
-
213
- def rule_tagged_background(type)
214
- rule = RULES[:background_with_tag]
215
- rule_phrase = rule[:phrase].gsub(/{.*}/, type)
216
- store_rule(rule, rule_phrase) if tags.size > 0
217
- end
218
-
219
- def rule_invalid_first_step
220
- first_step = get_step_order.first
221
- rule = RULES[:invalid_first_step]
222
- rule_phrase = rule[:phrase].gsub(/{.*}/, type)
223
- store_rule(rule, rule_phrase) if %w(And But).include?(first_step)
224
- end
225
-
226
- def rule_empty_scenario
227
- rule = RULES[:no_steps]
228
- rule_phrase = rule[:phrase].gsub(/{.*}/, type)
229
- store_rule(rule, rule_phrase) if @steps.empty?
230
- end
231
-
232
- def rule_too_many_steps
233
- rule = RULES[:too_many_steps]
234
- rule_phrase = rule[:phrase].gsub(/{.*}/, type)
235
- store_rule(rule, rule_phrase) if @steps.size >= rule[:max]
236
- end
237
- end
238
- end
1
+ module CukeSniffer
2
+
3
+ # Author:: Robert Cochran (mailto:cochrarj@miamioh.edu)
4
+ # Copyright:: Copyright (C) 2013 Robert Cochran
5
+ # License:: Distributes under the MIT License
6
+ # This class is a representation of the cucumber objects
7
+ # Background, Scenario, Scenario Outline
8
+ #
9
+ # Extends CukeSniffer::FeatureRulesEvaluator
10
+ class Scenario < FeatureRuleTarget
11
+
12
+ xml_accessor :start_line
13
+ xml_accessor :steps, :as => [], :in => "steps"
14
+ xml_accessor :examples_table, :as => [], :in => "examples"
15
+
16
+ # int: Line on which the scenario begins
17
+ attr_accessor :start_line
18
+
19
+ # string: The type of scenario
20
+ # Background, Scenario, Scenario Outline
21
+ attr_accessor :type
22
+
23
+ # string array: List of each step call in a scenario
24
+ attr_accessor :steps
25
+
26
+ # hash: Keeps each location and content of an inline table
27
+ # * Key: Step string the inline table is attached to
28
+ # * Value: Array of all of the lines in the table
29
+ attr_accessor :inline_tables
30
+
31
+ # string array: List of each example row in a scenario outline
32
+ attr_accessor :examples_table
33
+
34
+ # Location must be in the format of "file_path\file_name.rb:line_number"
35
+ # Scenario must be a string array containing everything from the first tag to the last example table
36
+ # where applicable.
37
+ def initialize(location, scenario)
38
+ super(location)
39
+ @start_line = location.match(/:(?<line>\d*)$/)[:line].to_i
40
+ @steps = []
41
+ @inline_tables = {}
42
+ @examples_table = []
43
+ split_scenario(scenario)
44
+ end
45
+
46
+ def ==(comparison_object) # :nodoc:
47
+ super(comparison_object) &&
48
+ comparison_object.steps == steps &&
49
+ comparison_object.examples_table == examples_table
50
+ end
51
+
52
+ def get_step_order
53
+ order = []
54
+ @steps.each do |line|
55
+ next if is_comment?(line)
56
+ match = line.match(STEP_REGEX)
57
+ order << match[:style] unless match.nil?
58
+ end
59
+ order
60
+ end
61
+
62
+ private
63
+
64
+ def split_scenario(scenario)
65
+ index = 0
66
+ until index >= scenario.length or scenario[index] =~ SCENARIO_TITLE_STYLES
67
+ update_tag_list(scenario[index])
68
+ index += 1
69
+ end
70
+
71
+ until index >= scenario.length or scenario[index].match STEP_REGEX or scenario[index].include?("Examples:")
72
+ match = scenario[index].match(SCENARIO_TITLE_STYLES)
73
+ @type = match[:type] unless match.nil?
74
+ create_name(scenario[index], SCENARIO_TITLE_STYLES)
75
+ index += 1
76
+ end
77
+
78
+ until index >= scenario.length or scenario[index].include?("Examples:")
79
+ if scenario[index] =~ /^\|.*\|/
80
+ step = scenario[index - 1]
81
+ @inline_tables[step] = []
82
+ until index >= scenario.length or scenario[index] =~ /(#{STEP_REGEX}|^\s*Examples:)/
83
+ @inline_tables[step] << scenario[index]
84
+ index += 1
85
+ end
86
+ else
87
+ @steps << scenario[index] if scenario[index] =~ STEP_REGEX
88
+ index += 1
89
+ end
90
+ end
91
+
92
+ if index < scenario.length and scenario[index].include?("Examples:")
93
+ index += 1
94
+ until index >= scenario.length
95
+ index += 2 if scenario[index].include?("Examples:")
96
+ @examples_table << scenario[index] if scenario[index] =~ /#{COMMENT_REGEX}\|.*\|/
97
+ index += 1
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end