cuke_sniffer 0.0.6 → 0.0.7

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