cuke_sniffer 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,49 +1,68 @@
1
- <div class="title" onclick="toggleById('step_definitions_data', this)">
2
- Step Definitions +
3
- </div>
4
- <div class="shrink_section" id="step_definitions_data">
5
- <% if cuke_sniffer.step_definitions.count == 0 %>
6
- <div class="empty_set_message">There were no Step Definitions to sniff in '<%= cuke_sniffer.step_definitions_location %>'!</div>
7
- <% elsif cuke_sniffer.step_definitions.count >= 1 && cuke_sniffer.summary[:step_definitions][:total_score] ==0 %>
8
- <div class="empty_set_message">Excellent! No smells found for Step Definitions!</div>
9
- <% end %>
10
- <% index = 0 %>
11
- <% cuke_sniffer.step_definitions.each do |step_definition| %>
12
- <% next if step_definition.score <= 0 %>
13
- <div style="cursor: pointer; padding-top: 3px" onclick="updateDisplayStatus(document.getElementById('step_definition_<%=index%>'))">
14
- <div class="red_number" style="float:left"><%= step_definition.score %></div>
15
- <% parameters = "" %>
16
- <% step_definition.parameters.each do |param| %>
17
- <% if param == step_definition.parameters.first %>
18
- <% parameters << "| " %>
19
- <% end %>
20
- <% parameters << param %>
21
-
22
- <% if param == step_definition.parameters.last %>
23
- <% parameters << " |" %>
24
- <% else %>
25
- <% parameters << ", " %>
26
- <% end %>
27
- <% end %>
28
- <div style="float: left" class="notes"><%= "/" + step_definition.regex.to_s.match(/\(\?\-mix\:(?<regex>.*)\)/)[:regex] + "/ do " + parameters %></div>
29
- <div style="float: right; padding-right:1%" class="notes"><%= step_definition.calls.keys.count %> Call(s)</div>
30
- </div>
31
- <div id="step_definition_<%= index %>" style="display:none;">
32
- <br style="clear:both">
33
-
34
- <div class="sub_section" style="display:block">
35
- <div style="text-indent: 4%; float: left; width: 45%; border-right: 2px solid #add8e6;">
36
- <% step_definition.rules_hash.each_key do |rule| %>
37
- <pre style="margin: 0;"><%= rule %></pre>
38
- <% end %>
39
- </div>
40
- <div style="text-indent: 1%">
41
- <p style="margin:0"><%= step_definition.location.gsub(cuke_sniffer.step_definitions_location, "") %></p>
42
- </div>
43
- </div>
44
- </div>
45
- <br style="clear:both">
46
- <% index += 1 %>
47
- <% end %>
48
- </div>
49
- <br style="clear:both">
1
+ <div class="title" onclick="toggleById('step_definitions_data', this);updateDisplayStatus(document.getElementById('step_definition_function_bar'));updateDivScroll('step_definitions_data', 'step_definitions_table');">
2
+ Step Definitions +
3
+ </div>
4
+
5
+ <div id="step_definition_function_bar" class="function_bar">
6
+ <input type="button" class="function_button" value="Collapse All" onclick="collapseAll('step_definition_detail')" />
7
+ <input type="button" class="function_button" value="Expand All" onclick="expandAllShiftingRows('step_definition_detail')" />
8
+ </div>
9
+
10
+ <div class="new_sub_section" id="step_definitions_data">
11
+ <% if cuke_sniffer.step_definitions.count == 0 %>
12
+ <div class="empty_set_message">There were no Step Definitions to sniff in '<%= cuke_sniffer.step_definitions_location %>'!</div>
13
+ <% elsif cuke_sniffer.step_definitions.count >= 1 && cuke_sniffer.summary[:step_definitions][:total_score] ==0 %>
14
+ <div class="empty_set_message">Excellent! No smells found for Step Definitions!</div>
15
+ <% end %>
16
+ <table id="step_definitions_table" style="width:100%;" border="0" cellspacing="0">
17
+ <% index = 0 %>
18
+ <% cuke_sniffer.step_definitions.each do |step_definition| %>
19
+ <% next if step_definition.score <= 0 %>
20
+ <% if index.odd? %>
21
+ <tr class="notes blue_title_row" onclick="updateDisplayStatus(document.getElementById('step_definition_detail_<%=index%>')); updateDivScroll('step_definitions_data', 'step_definitions_table');">
22
+ <% else %>
23
+ <tr class="notes white_title_row" onclick="updateDisplayStatus(document.getElementById('step_definition_detail_<%=index%>')); updateDivScroll('step_definitions_data', 'step_definitions_table');">
24
+ <% end %>
25
+ <td id="step_definition_score_<%= index %>" valign="top" style="width:1%; color: red">
26
+ <%= step_definition.score %>
27
+ </td>
28
+ <td id="step_definition_regex_<%= index %>" style="text-indent: 2px;">
29
+ /<%= step_definition.regex.to_s.gsub(/[(]\?-mix:/, "")[0...-1] %>/
30
+ </td>
31
+ <td id="step_definition_calls_<%= index %>" valign="top" style="text-align:right;width:8%;">
32
+ <% if cuke_sniffer.cataloged? %>
33
+ Call(s): <%= step_definition.calls.size %>
34
+ <% else %>
35
+ Not cataloged
36
+ <% end %>
37
+ </td>
38
+ </tr>
39
+
40
+ <% if index.odd? %>
41
+ <tr id="step_definition_detail_<%= index %>" class="blue_row step_definition_detail" style="display: none;">
42
+ <% else %>
43
+ <tr id="step_definition_detail_<%= index %>" class="white_row step_definition_detail" style="display: none;">
44
+ <% end %>
45
+ <td colspan="2">
46
+ <% SummaryHelper::sort_improvement_list(step_definition.rules_hash).each do |phrase, count| %>
47
+ <div style="text-indent: 10px;">
48
+ <div style="color:red; display:inline-block;">
49
+ <%= count %>
50
+ </div>
51
+ <div style="display:inline-block;">
52
+ <%= phrase %>
53
+ </div>
54
+ </div>
55
+ <% end %>
56
+ </td>
57
+ <td valign="top" style="text-align: right">
58
+ <a href="file:///<%= step_definition.location.gsub(/:\d+$/, "") %>" title="Note: Links to file on system this report was generated.">
59
+ <%= step_definition.location.gsub(cuke_sniffer.step_definitions_location, '') %>
60
+ </a>
61
+ </td>
62
+ </tr>
63
+
64
+ <% index += 1 %>
65
+ <% end %>
66
+ </table>
67
+ </div>
68
+ <br style="clear:both">
@@ -3,7 +3,7 @@
3
3
  </div>
4
4
  <div class="shrink_section" style="display: block;" id="summary_data">
5
5
  <% data_symbols = [:features, :scenarios, :step_definitions, :hooks] %>
6
- <table style="width: 100%;" class="notes">
6
+ <table style="padding-left:2px; width: 100%;" class="notes" border="0" cellspacing="0">
7
7
  <tr class="divide_row">
8
8
  <td class="table_top"></td>
9
9
  <td class="table_top">Features</td>
@@ -11,47 +11,69 @@
11
11
  <td class="table_top">Step Definitions</td>
12
12
  <td class="table_top">Hooks</td>
13
13
  </tr>
14
- <tr>
15
- <td>Total Score</td>
14
+ <tr class="blue_row">
15
+ <td>
16
+ Total Score
17
+ <a title="Total Points for a section." class="help">(?)</a>
18
+ </td>
16
19
  <% data_symbols.each do |symbol| %>
17
- <td><%= cuke_sniffer.summary[symbol][:total_score] %></td>
20
+ <td>
21
+ <%= cuke_sniffer.summary[symbol][:total_score] %>
22
+ </td>
18
23
  <% end %>
19
24
  </tr>
20
25
  <tr>
21
- <td>Count</td>
26
+ <td>
27
+ Count
28
+ <a title="Total Number of objects found in the project." class="help" class="help">(?)</a>
29
+ </td>
22
30
  <% data_symbols.each do |symbol| %>
23
31
  <td><%= cuke_sniffer.summary[symbol][:total].inspect %></td>
24
32
  <% end %>
25
33
  </tr>
26
34
  <tr/>
27
- <tr>
28
- <td>Lowest Score</td>
35
+ <tr class="blue_row">
36
+ <td>
37
+ Lowest Score
38
+ <a title="Score of the best object found." class="help">(?)</a>
39
+ </td>
29
40
  <% data_symbols.each do |symbol| %>
30
41
  <td><%= cuke_sniffer.summary[symbol][:min] %></td>
31
42
  <% end %>
32
43
  </tr>
33
44
  <tr>
34
- <td>Highest Score</td>
45
+ <td>
46
+ Highest Score
47
+ <a title="Score of the object with the most to improve on." class="help">(?)</a>
48
+ </td>
35
49
  <% data_symbols.each do |symbol| %>
36
50
  <td><%= cuke_sniffer.summary[symbol][:max] %></td>
37
51
  <% end %>
38
52
  </tr>
39
- <tr>
40
- <td>Average Score</td>
53
+ <tr class="blue_row">
54
+ <td>
55
+ Average Score
56
+ <a title="Average score of a section." class="help">(?)</a>
57
+ </td>
41
58
  <% data_symbols.each do |symbol| %>
42
59
  <td><%= cuke_sniffer.summary[symbol][:average] %></td>
43
60
  <% end %>
44
61
  </tr>
45
62
  <tr/>
46
-
47
63
  <tr>
48
- <td>Threshold</td>
64
+ <td>
65
+ Threshold
66
+ <a title="The maximum score an object can have to be considered good." class="help">(?)</a>
67
+ </td>
49
68
  <% data_symbols.each do |symbol| %>
50
69
  <td><%= cuke_sniffer.summary[symbol][:threshold] %></td>
51
70
  <% end %>
52
71
  </tr>
53
- <tr>
54
- <td>Good</td>
72
+ <tr class="blue_row">
73
+ <td>
74
+ Good
75
+ <a title="Percentage of the objects that can be considered good in the project." class="help">(?)</a>
76
+ </td>
55
77
  <% data_symbols.each do |symbol| %>
56
78
  <td><%= ((cuke_sniffer.summary[symbol][:good].to_f/cuke_sniffer.summary[symbol][:total].to_f) * 100).round(2) %>
57
79
  %
@@ -59,7 +81,10 @@
59
81
  <% end %>
60
82
  </tr>
61
83
  <tr>
62
- <td>Bad</td>
84
+ <td>
85
+ Bad
86
+ <a title="Percentage of the objects that can be considered bad in the project." class="help">(?)</a>
87
+ </td>
63
88
  <% data_symbols.each do |symbol| %>
64
89
  <td><%= ((cuke_sniffer.summary[symbol][:bad].to_f/cuke_sniffer.summary[symbol][:total].to_f) * 100).round(2) %>
65
90
  %
@@ -0,0 +1,9 @@
1
+ <div style="font-family: Arial; float: left; color: #509f50; font-size: 20px; font-weight: bold; padding-bottom: 1px;">
2
+ Cuke Sniffer
3
+ </div>
4
+ <br style="clear:both">
5
+ <div style="border-top: 2px solid #509f50; text-align: right;">
6
+ <div style="font-family: Arial;border-bottom-left-radius: 15px; border-bottom-right-radius: 15px;float: right; background-color:#509f50; color:white;padding-left:40px;font-size:11pt;font-weight:bold;padding-right:10px;">
7
+ Score: <%= cuke_sniffer.summary[:total_score] %></div>
8
+ </div>
9
+ <br style="clear:both">
@@ -1,483 +1,508 @@
1
- module CukeSniffer
2
-
3
- # Contains the rules and various scores used in evaluating objects
4
- module RuleConfig
5
-
6
- # Will prevent suite from executing properly
7
- FATAL = 100
8
-
9
- # Will cause problem with debugging
10
- ERROR = 25
11
-
12
- # Readability/misuse of cucumber
13
- WARNING = 10
14
-
15
- # Small improvements that can be made
16
- INFO = 1
17
-
18
- fatal_rules = {
19
- :no_examples => {
20
- :enabled => true,
21
- :phrase => "Scenario Outline with no examples.",
22
- :score => FATAL,
23
- :targets => ["Scenario"],
24
- :reason => "object.type == \"Scenario Outline\" and object.examples_table.size == 1"
25
- },
26
- :no_examples_table => {
27
- :enabled => true,
28
- :phrase => "Scenario Outline with no examples table.",
29
- :score => FATAL,
30
- :targets => ["Scenario"],
31
- :reason => "object.type == \"Scenario Outline\" and object.examples_table.empty?"
32
- },
33
- :recursive_nested_step => {
34
- :enabled => true,
35
- :phrase => "Recursive nested step call.",
36
- :score => FATAL,
37
- :targets => ["StepDefinition"],
38
- :reason => "object.nested_steps.each_value {|nested_step| store_rule(object, rule) if nested_step =~ object.regex}"
39
- },
40
- :background_with_tag => {
41
- :enabled => true,
42
- :phrase => "There is a background with a tag. This feature file cannot run!",
43
- :score => FATAL,
44
- :targets => ["Background"],
45
- :reason => "object.tags.size > 0"
46
- },
47
- :comment_after_tag => {
48
- :enabled => true,
49
- :phrase => "Comment comes between tag and properly executing line. This feature file cannot run!",
50
- :score => FATAL,
51
- :targets => ["Feature", "Scenario"],
52
- :reason => "flag = false
53
- unless object.tags.empty?
54
- object.tags[1..-1].each do | tags |
55
- if tags =~ /^\\s*\\#.*$/
56
- flag = true
57
- break
58
- end
59
- end
60
- end
61
- flag"
62
- }
63
- }
64
-
65
- error_rules = {
66
- :no_description => {
67
- :enabled => true,
68
- :phrase => "{class} has no description.",
69
- :score => ERROR,
70
- :targets => ["Feature", "Scenario"],
71
- :reason => "object.name.empty?"
72
- },
73
- :no_scenarios => {
74
- :enabled => true,
75
- :phrase => "Feature with no scenarios.",
76
- :score => ERROR,
77
- :targets => ["Feature"],
78
- :reason => "object.scenarios.empty?"
79
- },
80
- :commented_step => {
81
- :enabled => true,
82
- :phrase => "Commented step.",
83
- :score => ERROR,
84
- :targets => ["Scenario", "Background"],
85
- :reason => "object.steps.each do |step|
86
- store_rule(object, rule) if is_comment?(step)
87
- end"
88
- },
89
- :commented_example => {
90
- :enabled => true,
91
- :phrase => "Commented example.",
92
- :score => ERROR,
93
- :targets => ["Scenario"],
94
- :reason => "if object.type == 'Scenario Outline'
95
- object.examples_table.each {|example| store_rule(object, rule) if is_comment?(example)}
96
- end"
97
- },
98
- :no_steps => {
99
- :enabled => true,
100
- :phrase => "No steps in Scenario.",
101
- :score => ERROR,
102
- :targets => ["Scenario", "Background"],
103
- :reason => "object.steps.empty?"
104
- },
105
- :one_word_step => {
106
- :enabled => true,
107
- :phrase => "Step that is only one word long.",
108
- :score => ERROR,
109
- :targets => ["Scenario", "Background"],
110
- :reason => "object.steps.each {|step| store_rule(object, rule) if step.split.count == 2}"
111
- },
112
- :no_code => {
113
- :enabled => true,
114
- :phrase => "No code in Step Definition.",
115
- :score => ERROR,
116
- :targets => ["StepDefinition"],
117
- :reason => "object.code.empty?"
118
- },
119
- :around_hook_without_2_parameters => {
120
- :enabled => true,
121
- :phrase => "Around hook without 2 parameters for Scenario and Block.",
122
- :score => ERROR,
123
- :targets => ["Hook"],
124
- :reason => "object.type == \"Around\" and object.parameters.count != 2"
125
- },
126
- :around_hook_no_block_call => {
127
- :enabled => true,
128
- :phrase => "Around hook does not call its block.",
129
- :score => ERROR,
130
- :targets => ["Hook"],
131
- :reason => "flag = true
132
- flag = false if object.type != 'Around'
133
- block_call = \"\#{object.parameters[1]}.call\"
134
- object.code.each do |line|
135
- if line.include?(block_call)
136
- flag = false
137
- break
138
- end
139
- end
140
- flag"
141
- },
142
- :hook_no_debugging => {
143
- :enabled => true,
144
- :phrase => "Hook without a begin/rescue. Reduced visibility when debugging.",
145
- :score => ERROR,
146
- :targets => ["Hook"],
147
- :reason => "(object.code.empty? != true and object.code.join.match(/.*begin.*rescue.*/).nil?)"
148
-
149
- },
150
- :hook_conflicting_tags => {
151
- :enabled => true,
152
- :phrase => "Hook that both expects and ignores the same tag. This hook will not function as expected.",
153
- :score => ERROR,
154
- :targets => ["Hook"],
155
- :reason => "all_tags = []
156
- object.tags.each { |single_tag| all_tags << single_tag.split(',') }
157
- all_tags.flatten!
158
- flag = false
159
- all_tags.each do |single_tag|
160
- tag = single_tag.gsub(\"~\", \"\")
161
- if all_tags.include?(tag) and all_tags.include?(\"~\#{tag}\")
162
- flag = true
163
- break
164
- end
165
- end
166
- flag
167
- "
168
- },
169
- }
170
-
171
- warning_rules = {
172
- :numbers_in_description => {
173
- :enabled => true,
174
- :phrase => "{class} has numbers in the description.",
175
- :score => WARNING,
176
- :targets => ["Feature", "Scenario", "Background"],
177
- :reason => "!(object.name =~ /\\d+/).nil?"
178
- },
179
- :empty_feature => {
180
- :enabled => true,
181
- :phrase => "Feature file has no content.",
182
- :score => WARNING,
183
- :targets => ["Feature"],
184
- :reason => "object.feature_lines == []"
185
- },
186
- :background_with_no_scenarios => {
187
- :enabled => true,
188
- :phrase => "Feature has a background with no scenarios.",
189
- :score => WARNING,
190
- :targets => ["Feature"],
191
- :reason => "object.scenarios.empty? and !object.background.nil?"
192
- },
193
- :background_with_one_scenario => {
194
- :enabled => true,
195
- :phrase => "Feature has a background with one scenario.",
196
- :score => WARNING,
197
- :targets => ["Feature"],
198
- :reason => "object.scenarios.size == 1 and !object.background.nil?"
199
- },
200
- :too_many_steps => {
201
- :enabled => true,
202
- :phrase => "{class} with too many steps.",
203
- :score => WARNING,
204
- :max => 7,
205
- :targets => ["Scenario", "Background"],
206
- :reason => "object.steps.count > rule.conditions[:max]"
207
- },
208
- :out_of_order_steps => {
209
- :enabled => true,
210
- :phrase => "Scenario steps out of Given/When/Then order.",
211
- :score => WARNING,
212
- :targets => ["Scenario"],
213
- :reason => 'step_order = object.get_step_order
214
- ["But", "*", "And"].each { |type| step_order.delete(type) }
215
- if(step_order != %w(Given When Then) and step_order != %w(When Then))
216
- store_rule(object, rule)
217
- end'
218
-
219
- },
220
- :invalid_first_step => {
221
- :enabled => true,
222
- :phrase => "Invalid first step. Began with And/But.",
223
- :score => WARNING,
224
- :targets => ["Scenario", "Background"],
225
- :reason => "!(object.steps.first =~ /^\\s*(And|But).*$/).nil?"
226
- },
227
- :asterisk_step => {
228
- :enabled => true,
229
- :phrase => "Step includes a * instead of Given/When/Then/And/But.",
230
- :score => WARNING,
231
- :targets => ["Scenario", "Background"],
232
- :reason => "object.steps.each do | step |
233
- store_rule(object, rule) if( step =~ /^\\s*[*].*$/)
234
- end
235
- "
236
- },
237
- :one_example => {
238
- :enabled => true,
239
- :phrase => "Scenario Outline with only one example.",
240
- :score => WARNING,
241
- :targets => ["Scenario"],
242
- :reason => "object.type == 'Scenario Outline' and object.examples_table.size == 2 and !is_comment?(object.examples_table[1])"
243
- },
244
- :too_many_examples => {
245
- :enabled => true,
246
- :phrase => "Scenario Outline with too many examples.",
247
- :score => WARNING,
248
- :max => 10,
249
- :targets => ["Scenario"],
250
- :reason => "object.type == 'Scenario Outline' and (object.examples_table.size - 1) >= rule.conditions[:max]"
251
- },
252
- :multiple_given_when_then => {
253
- :enabled => true,
254
- :phrase => "Given/When/Then used multiple times in the same {class}.",
255
- :score => WARNING,
256
- :targets => ["Scenario", "Background"],
257
- :reason => "
258
- step_order = object.get_step_order
259
- phrase = rule.phrase.gsub('{class}', type)
260
- ['Given', 'When', 'Then'].each {|step_start| store_rule(object, rule, phrase) if step_order.count(step_start) > 1}"
261
- },
262
- :too_many_parameters => {
263
- :enabled => true,
264
- :phrase => "Too many parameters in Step Definition.",
265
- :score => WARNING,
266
- :max => 4,
267
- :targets => ["StepDefinition"],
268
- :reason => "object.parameters.size > rule.conditions[:max]"
269
-
270
- },
271
- :lazy_debugging => {
272
- :enabled => true,
273
- :phrase => "Lazy Debugging through puts, p, or print",
274
- :score => WARNING,
275
- :targets => ["StepDefinition"],
276
- :reason => "object.code.each {|line| store_rule(object, rule) if line.strip =~ /^(p|puts)( |\\()('|\\\"|%(q|Q)?\\{)/}"
277
- },
278
- :pending => {
279
- :enabled => true,
280
- :phrase => "Pending step definition. Implement or remove.",
281
- :score => WARNING,
282
- :targets => ["StepDefinition"],
283
- :reason => "object.code.each {|line|
284
- if line =~ /^\\s*pending(\\(.*\\))?(\\s*[#].*)?$/
285
- store_rule(object, rule)
286
- break
287
- end
288
- }"
289
- },
290
- :feature_same_tag => {
291
- :enabled => true,
292
- :phrase => "Same tag appears on Feature.",
293
- :score => WARNING,
294
- :targets => ["Feature"],
295
- :reason => 'if(object.scenarios.count >= 2)
296
- object.scenarios[1..-1].each do |scenario|
297
- object.scenarios.first.tags.each do |tag|
298
- store_rule(object, rule) if scenario.tags.include?(tag)
299
- end
300
- end
301
- end'
302
- },
303
- :scenario_same_tag => {
304
- :enabled => true,
305
- :phrase => "Tag appears on all scenarios.",
306
- :score => WARNING,
307
- :targets => ["Feature"],
308
- #TODO really hacky
309
- :reason => "unless object.scenarios.empty?
310
- base_tag_list = object.scenarios.first.tags.clone
311
- object.scenarios.each do |scenario|
312
- base_tag_list.each do |tag|
313
- base_tag_list.delete(tag) unless scenario.tags.include?(tag)
314
- end
315
- end
316
- base_tag_list.count.times { store_rule(object, rule) }
317
- end"
318
- },
319
- :commas_in_description => {
320
- :enabled => true,
321
- :phrase => "There are commas in the description, creating possible multirunning scenarios or features.",
322
- :score => WARNING,
323
- :targets => ["Feature", "Scenario"],
324
- :reason => 'object.name.include?(",")'
325
- },
326
- :commented_tag => {
327
- :enabled => true,
328
- :phrase => "{class} has a commented out tag",
329
- :score => WARNING,
330
- :targets => ["Feature", "Scenario"],
331
- :reason => 'object.tags.each do | tag |
332
- store_rule(object, rule, rule.phrase.gsub("{class}", type)) if is_comment?(tag)
333
- end'
334
- },
335
- :empty_hook => {
336
- :enabled => true,
337
- :phrase => "Hook with no content.",
338
- :score => WARNING,
339
- :targets => ["Hook"],
340
- :reason => "object.code == []"
341
- },
342
- :hook_all_comments => {
343
- :enabled => true,
344
- :phrase => "Hook is only comments.",
345
- :score => WARNING,
346
- :targets => ["Hook"],
347
- :reason => "flag = true
348
- object.code.each do |line|
349
- flag = false if line.match(/^\\s*\\#.*$/).nil?
350
- end
351
- flag"
352
- },
353
- :hook_duplicate_tags => {
354
- :enabled => true,
355
- :phrase => "Hook has duplicate tags.",
356
- :score => WARNING,
357
- :targets => ["Hook"],
358
- :reason => "all_tags = []
359
- object.tags.each { |single_tag| all_tags << single_tag.split(',') }
360
- all_tags.flatten!
361
- unique_tags = all_tags.uniq
362
- true unless all_tags == unique_tags"
363
- }
364
- }
365
-
366
- info_rules = {
367
- :too_many_tags => {
368
- :enabled => true,
369
- :phrase => "{class} has too many tags.",
370
- :score => INFO,
371
- :max => 8,
372
- :targets => ["Feature", "Scenario"],
373
- :reason => "object.tags.size >= rule.conditions[:max]"
374
- },
375
- :long_name => {
376
- :enabled => true,
377
- :phrase => "{class} has a long description.",
378
- :score => INFO,
379
- :max => 180,
380
- :targets => ["Feature", "Scenario", "Background"],
381
- :reason => "object.name.length >= rule.conditions[:max]"
382
- },
383
- :implementation_word => {
384
- :enabled => true,
385
- :phrase => "Implementation word used: {word}.",
386
- :score => INFO,
387
- :words => ["page", "site", "url", "button", "drop down", "dropdown", "select list", "click", "text box", "radio button", "check box", "xml", "window", "pop up", "pop-up", "screen"],
388
- :targets => ["Scenario", "Background"],
389
- :reason => "object.steps.each do |step|
390
- next if is_comment?(step)
391
- rule.conditions[:words].each do |word|
392
- new_phrase = rule.phrase.gsub(/{.*}/, word)
393
- store_rule(object, rule, new_phrase) if step.include?(word)
394
- end
395
- end"
396
-
397
- },
398
- :too_many_scenarios => {
399
- :enabled => true,
400
- :phrase => "Feature with too many scenarios.",
401
- :score => INFO,
402
- :max => 10,
403
- :targets => ["Feature"],
404
- :reason => "object.scenarios.size >= rule.conditions[:max]"
405
- },
406
- :date_used => {
407
- :enabled => true,
408
- :phrase => "Date used.",
409
- :score => INFO,
410
- :targets => ["Scenario", "Background"],
411
- :reason => "object.steps.each {|step| store_rule(object, rule) if step =~ DATE_REGEX}"
412
- },
413
- :nested_step => {
414
- :enabled => true,
415
- :phrase => "Nested step call.",
416
- :score => INFO,
417
- :targets => ["StepDefinition"],
418
- :reason => "!object.nested_steps.empty?"
419
- },
420
- :commented_code => {
421
- :enabled => true,
422
- :phrase => "Commented code in Step Definition.",
423
- :score => INFO,
424
- :targets => ["StepDefinition"],
425
- :reason => "object.code.each {|line| store_rule(object, rule) if is_comment?(line)}"
426
- },
427
- :small_sleep => {
428
- :enabled => true,
429
- :phrase => "Small sleeps used. Use a wait_until like method.",
430
- :score => INFO,
431
- :max => 2,
432
- :targets => ["StepDefinition"],
433
- :reason => "object.code.each do |line|
434
- match_data = line.match /^\\s*sleep(\\s|\\()(?<sleep_time>.*)\\)?/
435
- if match_data
436
- sleep_value = match_data[:sleep_time].to_f
437
- store_rule(object, rule) if sleep_value < rule.conditions[:max]
438
- end
439
- end"
440
- },
441
- :large_sleep => {
442
- :enabled => true,
443
- :phrase => "Large sleeps used. Use a wait_until like method.",
444
- :score => INFO,
445
- :min => 2,
446
- :targets => ["StepDefinition"],
447
- :reason => "object.code.each do |line|
448
- match_data = line.match /^\\s*sleep(\\s|\\()(?<sleep_time>.*)\\)?/
449
- if match_data
450
- sleep_value = match_data[:sleep_time].to_f
451
- store_rule(object, rule) if sleep_value > rule.conditions[:min]
452
- end
453
- end"
454
- },
455
- :todo => {
456
- :enabled => true,
457
- :phrase => "Todo found. Resolve it.",
458
- :score => INFO,
459
- :targets => ["StepDefinition"],
460
- :reason => "object.code.each {|line| store_rule(object, rule) if line =~ /\\#(TODO|todo)/}
461
- false"
462
- },
463
- :hook_not_in_hooks_file => {
464
- :enabled => true,
465
- :phrase => "Hook found outside of the designated hooks file",
466
- :score => INFO,
467
- :file => "hooks.rb",
468
- :targets => ["Hook"],
469
- :reason => "object.location.include?(rule.conditions[:file]) != true"
470
- },
471
- }
472
-
473
- # Master hash used for rule data
474
- # * +:enabled+
475
- # * +:phrase+
476
- # * +:score+
477
- # Optional:
478
- # * +:words+
479
- # * +:max+
480
- # * +:min+
481
- RULES = {}.merge fatal_rules.merge error_rules.merge warning_rules.merge info_rules
482
- end
483
- end
1
+ module CukeSniffer
2
+
3
+ # Contains the rules and various scores used in evaluating objects
4
+ module RuleConfig
5
+
6
+ # Will prevent suite from executing properly
7
+ FATAL = 100
8
+
9
+ # Will cause problem with debugging
10
+ ERROR = 25
11
+
12
+ # Readability/misuse of cucumber
13
+ WARNING = 10
14
+
15
+ # Small improvements that can be made
16
+ INFO = 1
17
+
18
+ fatal_rules = {
19
+ :no_examples => {
20
+ :enabled => true,
21
+ :phrase => "Scenario Outline with no examples.",
22
+ :score => FATAL,
23
+ :targets => ["Scenario"],
24
+ :reason => "object.type == \"Scenario Outline\" and object.examples_table.size == 1"
25
+ },
26
+ :no_examples_table => {
27
+ :enabled => true,
28
+ :phrase => "Scenario Outline with no examples table.",
29
+ :score => FATAL,
30
+ :targets => ["Scenario"],
31
+ :reason => "object.type == \"Scenario Outline\" and object.examples_table.empty?"
32
+ },
33
+ :recursive_nested_step => {
34
+ :enabled => true,
35
+ :phrase => "Recursive nested step call.",
36
+ :score => FATAL,
37
+ :targets => ["StepDefinition"],
38
+ :reason => "object.nested_steps.each_value {|nested_step| store_rule(object, rule) if nested_step =~ object.regex}"
39
+ },
40
+ :background_with_tag => {
41
+ :enabled => true,
42
+ :phrase => "There is a background with a tag. This feature file cannot run!",
43
+ :score => FATAL,
44
+ :targets => ["Background"],
45
+ :reason => "object.tags.size > 0"
46
+ },
47
+ :comment_after_tag => {
48
+ :enabled => true,
49
+ :phrase => "Comment comes between tag and properly executing line. This feature file cannot run!",
50
+ :score => FATAL,
51
+ :targets => ["Feature", "Scenario"],
52
+ :reason => "flag = false
53
+ unless object.tags.empty?
54
+ object.tags[1..-1].each do | tags |
55
+ if tags =~ /^\\s*\\#.*$/
56
+ flag = true
57
+ break
58
+ end
59
+ end
60
+ end
61
+ flag"
62
+ },
63
+ :universal_nested_step => {
64
+ :enabled => true,
65
+ :phrase => "A nested step should not universally match all step definitions. Dead steps cannot be correctly cataloged.",
66
+ :score => FATAL,
67
+ :targets => ["StepDefinition"],
68
+ :reason => "object.nested_steps.each_value do | step_value |
69
+ modified_step = step_value.gsub(/\\\#{[^}]*}/, '.*')
70
+ store_rule(object, rule) if modified_step == '.*'
71
+ end"
72
+ }
73
+ }
74
+
75
+ error_rules = {
76
+ :no_description => {
77
+ :enabled => true,
78
+ :phrase => "{class} has no description.",
79
+ :score => ERROR,
80
+ :targets => ["Feature", "Scenario"],
81
+ :reason => "object.name.empty?"
82
+ },
83
+ :no_scenarios => {
84
+ :enabled => true,
85
+ :phrase => "Feature with no scenarios.",
86
+ :score => ERROR,
87
+ :targets => ["Feature"],
88
+ :reason => "object.scenarios.empty?"
89
+ },
90
+ :commented_step => {
91
+ :enabled => true,
92
+ :phrase => "Commented step.",
93
+ :score => ERROR,
94
+ :targets => ["Scenario", "Background"],
95
+ :reason => "object.steps.each do |step|
96
+ store_rule(object, rule) if is_comment?(step)
97
+ end"
98
+ },
99
+ :commented_example => {
100
+ :enabled => true,
101
+ :phrase => "Commented example.",
102
+ :score => ERROR,
103
+ :targets => ["Scenario"],
104
+ :reason => "if object.type == 'Scenario Outline'
105
+ object.examples_table.each {|example| store_rule(object, rule) if is_comment?(example)}
106
+ end"
107
+ },
108
+ :no_steps => {
109
+ :enabled => true,
110
+ :phrase => "No steps in Scenario.",
111
+ :score => ERROR,
112
+ :targets => ["Scenario", "Background"],
113
+ :reason => "object.steps.empty?"
114
+ },
115
+ :one_word_step => {
116
+ :enabled => true,
117
+ :phrase => "Step that is only one word long.",
118
+ :score => ERROR,
119
+ :targets => ["Scenario", "Background"],
120
+ :reason => "object.steps.each {|step| store_rule(object, rule) if step.split.count == 2}"
121
+ },
122
+ :no_code => {
123
+ :enabled => true,
124
+ :phrase => "No code in Step Definition.",
125
+ :score => ERROR,
126
+ :targets => ["StepDefinition"],
127
+ :reason => "object.code.empty?"
128
+ },
129
+ :around_hook_without_2_parameters => {
130
+ :enabled => true,
131
+ :phrase => "Around hook without 2 parameters for Scenario and Block.",
132
+ :score => ERROR,
133
+ :targets => ["Hook"],
134
+ :reason => "object.type == \"Around\" and object.parameters.count != 2"
135
+ },
136
+ :around_hook_no_block_call => {
137
+ :enabled => true,
138
+ :phrase => "Around hook does not call its block.",
139
+ :score => ERROR,
140
+ :targets => ["Hook"],
141
+ :reason => "flag = true
142
+ flag = false if object.type != 'Around'
143
+ block_call = \"\#{object.parameters[1]}.call\"
144
+ object.code.each do |line|
145
+ if line.include?(block_call)
146
+ flag = false
147
+ break
148
+ end
149
+ end
150
+ flag"
151
+ },
152
+ :hook_no_debugging => {
153
+ :enabled => true,
154
+ :phrase => "Hook without a begin/rescue. Reduced visibility when debugging.",
155
+ :score => ERROR,
156
+ :targets => ["Hook"],
157
+ :reason => "(object.code.empty? != true and object.code.join.match(/.*begin.*rescue.*/).nil?)"
158
+
159
+ },
160
+ :hook_conflicting_tags => {
161
+ :enabled => true,
162
+ :phrase => "Hook that both expects and ignores the same tag. This hook will not function as expected.",
163
+ :score => ERROR,
164
+ :targets => ["Hook"],
165
+ :reason => "all_tags = []
166
+ object.tags.each { |single_tag| all_tags << single_tag.split(',') }
167
+ all_tags.flatten!
168
+ flag = false
169
+ all_tags.each do |single_tag|
170
+ tag = single_tag.gsub(\"~\", \"\")
171
+ if all_tags.include?(tag) and all_tags.include?(\"~\#{tag}\")
172
+ flag = true
173
+ break
174
+ end
175
+ end
176
+ flag
177
+ "
178
+ },
179
+ }
180
+
181
+ warning_rules = {
182
+ :numbers_in_description => {
183
+ :enabled => true,
184
+ :phrase => "{class} has numbers in the description.",
185
+ :score => WARNING,
186
+ :targets => ["Feature", "Scenario", "Background"],
187
+ :reason => "!(object.name =~ /\\d+/).nil?"
188
+ },
189
+ :empty_feature => {
190
+ :enabled => true,
191
+ :phrase => "Feature file has no content.",
192
+ :score => WARNING,
193
+ :targets => ["Feature"],
194
+ :reason => "object.feature_lines == []"
195
+ },
196
+ :background_with_no_scenarios => {
197
+ :enabled => true,
198
+ :phrase => "Feature has a background with no scenarios.",
199
+ :score => WARNING,
200
+ :targets => ["Feature"],
201
+ :reason => "object.scenarios.empty? and !object.background.nil?"
202
+ },
203
+ :background_with_one_scenario => {
204
+ :enabled => true,
205
+ :phrase => "Feature has a background with one scenario.",
206
+ :score => WARNING,
207
+ :targets => ["Feature"],
208
+ :reason => "object.scenarios.size == 1 and !object.background.nil?"
209
+ },
210
+ :too_many_steps => {
211
+ :enabled => true,
212
+ :phrase => "{class} with too many steps.",
213
+ :score => WARNING,
214
+ :max => 7,
215
+ :targets => ["Scenario", "Background"],
216
+ :reason => "object.steps.count > rule.conditions[:max]"
217
+ },
218
+ :out_of_order_steps => {
219
+ :enabled => true,
220
+ :phrase => "Scenario steps out of Given/When/Then order.",
221
+ :score => WARNING,
222
+ :targets => ["Scenario"],
223
+ :reason => 'step_order = object.get_step_order
224
+ ["But", "*", "And"].each { |type| step_order.delete(type) }
225
+ if(step_order != %w(Given When Then) and step_order != %w(When Then))
226
+ store_rule(object, rule)
227
+ end'
228
+
229
+ },
230
+ :invalid_first_step => {
231
+ :enabled => true,
232
+ :phrase => "Invalid first step. Began with And/But.",
233
+ :score => WARNING,
234
+ :targets => ["Scenario", "Background"],
235
+ :reason => "!(object.steps.first =~ /^\\s*(And|But).*$/).nil?"
236
+ },
237
+ :asterisk_step => {
238
+ :enabled => true,
239
+ :phrase => "Step includes a * instead of Given/When/Then/And/But.",
240
+ :score => WARNING,
241
+ :targets => ["Scenario", "Background"],
242
+ :reason => "object.steps.each do | step |
243
+ store_rule(object, rule) if( step =~ /^\\s*[*].*$/)
244
+ end
245
+ "
246
+ },
247
+ :one_example => {
248
+ :enabled => true,
249
+ :phrase => "Scenario Outline with only one example.",
250
+ :score => WARNING,
251
+ :targets => ["Scenario"],
252
+ :reason => "object.type == 'Scenario Outline' and object.examples_table.size == 2 and !is_comment?(object.examples_table[1])"
253
+ },
254
+ :too_many_examples => {
255
+ :enabled => true,
256
+ :phrase => "Scenario Outline with too many examples.",
257
+ :score => WARNING,
258
+ :max => 10,
259
+ :targets => ["Scenario"],
260
+ :reason => "object.type == 'Scenario Outline' and (object.examples_table.size - 1) >= rule.conditions[:max]"
261
+ },
262
+ :multiple_given_when_then => {
263
+ :enabled => true,
264
+ :phrase => "Given/When/Then used multiple times in the same {class}.",
265
+ :score => WARNING,
266
+ :targets => ["Scenario", "Background"],
267
+ :reason => "
268
+ step_order = object.get_step_order
269
+ phrase = rule.phrase.gsub('{class}', type)
270
+ ['Given', 'When', 'Then'].each {|step_start| store_rule(object, rule, phrase) if step_order.count(step_start) > 1}"
271
+ },
272
+ :too_many_parameters => {
273
+ :enabled => true,
274
+ :phrase => "Too many parameters in Step Definition.",
275
+ :score => WARNING,
276
+ :max => 4,
277
+ :targets => ["StepDefinition"],
278
+ :reason => "object.parameters.size > rule.conditions[:max]"
279
+
280
+ },
281
+ :lazy_debugging => {
282
+ :enabled => true,
283
+ :phrase => "Lazy Debugging through puts, p, or print",
284
+ :score => WARNING,
285
+ :targets => ["StepDefinition"],
286
+ :reason => "object.code.each {|line| store_rule(object, rule) if line.strip =~ /^(p|puts)( |\\()('|\\\"|%(q|Q)?\\{)/}"
287
+ },
288
+ :pending => {
289
+ :enabled => true,
290
+ :phrase => "Pending step definition. Implement or remove.",
291
+ :score => WARNING,
292
+ :targets => ["StepDefinition"],
293
+ :reason => "object.code.each {|line|
294
+ if line =~ /^\\s*pending(\\(.*\\))?(\\s*[#].*)?$/
295
+ store_rule(object, rule)
296
+ break
297
+ end
298
+ }"
299
+ },
300
+ :feature_same_tag => {
301
+ :enabled => true,
302
+ :phrase => "Same tag appears on Feature.",
303
+ :score => WARNING,
304
+ :targets => ["Feature"],
305
+ :reason => 'if(object.scenarios.count >= 2)
306
+ object.scenarios[1..-1].each do |scenario|
307
+ object.scenarios.first.tags.each do |tag|
308
+ store_rule(object, rule) if scenario.tags.include?(tag)
309
+ end
310
+ end
311
+ end'
312
+ },
313
+ :scenario_same_tag => {
314
+ :enabled => true,
315
+ :phrase => "Tag appears on all scenarios.",
316
+ :score => WARNING,
317
+ :targets => ["Feature"],
318
+ #TODO really hacky
319
+ :reason => "unless object.scenarios.empty?
320
+ base_tag_list = object.scenarios.first.tags.clone
321
+ object.scenarios.each do |scenario|
322
+ base_tag_list.each do |tag|
323
+ base_tag_list.delete(tag) unless scenario.tags.include?(tag)
324
+ end
325
+ end
326
+ base_tag_list.count.times { store_rule(object, rule) }
327
+ end"
328
+ },
329
+ :commas_in_description => {
330
+ :enabled => true,
331
+ :phrase => "There are commas in the description, creating possible multirunning scenarios or features.",
332
+ :score => WARNING,
333
+ :targets => ["Feature", "Scenario"],
334
+ :reason => 'object.name.include?(",")'
335
+ },
336
+ :commented_tag => {
337
+ :enabled => true,
338
+ :phrase => "{class} has a commented out tag",
339
+ :score => WARNING,
340
+ :targets => ["Feature", "Scenario"],
341
+ :reason => 'object.tags.each do | tag |
342
+ store_rule(object, rule, rule.phrase.gsub("{class}", type)) if is_comment?(tag)
343
+ end'
344
+ },
345
+ :empty_hook => {
346
+ :enabled => true,
347
+ :phrase => "Hook with no content.",
348
+ :score => WARNING,
349
+ :targets => ["Hook"],
350
+ :reason => "object.code == []"
351
+ },
352
+ :hook_all_comments => {
353
+ :enabled => true,
354
+ :phrase => "Hook is only comments.",
355
+ :score => WARNING,
356
+ :targets => ["Hook"],
357
+ :reason => "flag = true
358
+ object.code.each do |line|
359
+ flag = false if line.match(/^\\s*\\#.*$/).nil?
360
+ end
361
+ flag"
362
+ },
363
+ :hook_duplicate_tags => {
364
+ :enabled => true,
365
+ :phrase => "Hook has duplicate tags.",
366
+ :score => WARNING,
367
+ :targets => ["Hook"],
368
+ :reason => "all_tags = []
369
+ object.tags.each { |single_tag| all_tags << single_tag.split(',') }
370
+ all_tags.flatten!
371
+ unique_tags = all_tags.uniq
372
+ true unless all_tags == unique_tags"
373
+ }
374
+ }
375
+
376
+ info_rules = {
377
+ :too_many_tags => {
378
+ :enabled => true,
379
+ :phrase => "{class} has too many tags.",
380
+ :score => INFO,
381
+ :max => 8,
382
+ :targets => ["Feature", "Scenario"],
383
+ :reason => "object.tags.size >= rule.conditions[:max]"
384
+ },
385
+ :long_name => {
386
+ :enabled => true,
387
+ :phrase => "{class} has a long description.",
388
+ :score => INFO,
389
+ :max => 180,
390
+ :targets => ["Feature", "Scenario", "Background"],
391
+ :reason => "object.name.length >= rule.conditions[:max]"
392
+ },
393
+ :implementation_word => {
394
+ :enabled => true,
395
+ :phrase => "Implementation word used: {word}.",
396
+ :score => INFO,
397
+ :words => ["page", "site", "url", "drop down", "dropdown", "select list", "click", "text box", "radio button", "check box", "xml", "window", "pop up", "pop-up", "screen", "tab", "database", "DB"],
398
+ :targets => ["Scenario", "Background"],
399
+ :reason => "object.steps.each do |step|
400
+ next if is_comment?(step)
401
+ rule.conditions[:words].each do |word|
402
+ new_phrase = rule.phrase.gsub(/{.*}/, word)
403
+ store_rule(object, rule, new_phrase) if step.include?(word)
404
+ end
405
+ end"
406
+
407
+ },
408
+ :implementation_word_button => {
409
+ :enabled => true,
410
+ :phrase => "Implementation word used: button.",
411
+ :score => INFO,
412
+ :targets => ["Scenario"],
413
+ :reason => "object.steps.each do |step|
414
+ matches = step.match(/(?<prefix>\\w+)\\sbutton/i)
415
+ if(!matches.nil? and matches[:prefix].downcase != 'radio')
416
+ store_rule(object, rule)
417
+ end
418
+ end"
419
+
420
+ },:too_many_scenarios => {
421
+ :enabled => true,
422
+ :phrase => "Feature with too many scenarios.",
423
+ :score => INFO,
424
+ :max => 10,
425
+ :targets => ["Feature"],
426
+ :reason => "object.scenarios.size >= rule.conditions[:max]"
427
+ },
428
+ :date_used => {
429
+ :enabled => true,
430
+ :phrase => "Date used.",
431
+ :score => INFO,
432
+ :targets => ["Scenario", "Background"],
433
+ :reason => "object.steps.each {|step| store_rule(object, rule) if step =~ DATE_REGEX}"
434
+ },
435
+ :nested_step => {
436
+ :enabled => true,
437
+ :phrase => "Nested step call.",
438
+ :score => INFO,
439
+ :targets => ["StepDefinition"],
440
+ :reason => "!object.nested_steps.empty?"
441
+ },
442
+ :commented_code => {
443
+ :enabled => true,
444
+ :phrase => "Commented code in Step Definition.",
445
+ :score => INFO,
446
+ :targets => ["StepDefinition"],
447
+ :reason => "object.code.each {|line| store_rule(object, rule) if is_comment?(line)}"
448
+ },
449
+ :small_sleep => {
450
+ :enabled => true,
451
+ :phrase => "Small sleeps used. Use a wait_until like method.",
452
+ :score => INFO,
453
+ :max => 2,
454
+ :targets => ["StepDefinition"],
455
+ :reason => "object.code.each do |line|
456
+ match_data = line.match /^\\s*sleep(\\s|\\()(?<sleep_time>.*)\\)?/
457
+ if match_data
458
+ sleep_value = match_data[:sleep_time].to_f
459
+ store_rule(object, rule) if sleep_value < rule.conditions[:max]
460
+ end
461
+ end"
462
+ },
463
+ :large_sleep => {
464
+ :enabled => true,
465
+ :phrase => "Large sleeps used. Use a wait_until like method.",
466
+ :score => INFO,
467
+ :min => 2,
468
+ :targets => ["StepDefinition"],
469
+ :reason => "object.code.each do |line|
470
+ match_data = line.match /^\\s*sleep(\\s|\\()(?<sleep_time>.*)\\)?/
471
+ if match_data
472
+ sleep_value = match_data[:sleep_time].to_f
473
+ store_rule(object, rule) if sleep_value > rule.conditions[:min]
474
+ end
475
+ end"
476
+ },
477
+ :todo => {
478
+ :enabled => true,
479
+ :phrase => "Todo found. Resolve it.",
480
+ :score => INFO,
481
+ :targets => ["StepDefinition"],
482
+ :reason => "object.code.each {|line| store_rule(object, rule) if line =~ /\\#(TODO|todo)/}
483
+ false"
484
+ },
485
+ :hook_not_in_hooks_file => {
486
+ :enabled => true,
487
+ :phrase => "Hook found outside of the designated hooks file",
488
+ :score => INFO,
489
+ :file => "hooks.rb",
490
+ :targets => ["Hook"],
491
+ :reason => "object.location.include?(rule.conditions[:file]) != true"
492
+ },
493
+ }
494
+
495
+ # Master hash used for rule data
496
+ # * +:enabled+
497
+ # * +:phrase+
498
+ # * +:score+
499
+ # * +:targets+
500
+ # * +:reason+
501
+ # Optional:
502
+ # * +:words+
503
+ # * +:max+
504
+ # * +:min+
505
+ # * +:file+
506
+ RULES = {}.merge fatal_rules.merge error_rules.merge warning_rules.merge info_rules
507
+ end
508
+ end