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,142 @@
1
+ require 'erb'
2
+
3
+ module CukeSniffer
4
+ # Author:: Robert Cochran (mailto:cochrarj@miamioh.edu)
5
+ # Copyright:: Copyright (C) 2013 Robert Cochran
6
+ # License:: Distributes under the MIT License
7
+ # Mixins: CukeSniffer::Constants
8
+ # Static class used to generate output for the CukeSniffer::CLI object.
9
+ class Formatter
10
+ include CukeSniffer::Constants
11
+
12
+ # Prints out a summary of the results and the list of improvements to be made
13
+ def self.output_console(cuke_sniffer)
14
+ summary = cuke_sniffer.summary
15
+ output = "Suite Summary" +
16
+ " Total Score: #{summary[:total_score]}\n" +
17
+ get_output_summary_nodes(cuke_sniffer) +
18
+ console_improvement_list(summary[:improvement_list])
19
+
20
+ puts output
21
+ end
22
+
23
+ # Returns a string of formatted output for all the object sections of summary
24
+ def self.get_output_summary_nodes(cuke_sniffer)
25
+ output = ""
26
+ [:features, :scenarios, :step_definitions, :hooks].each do |summary_section|
27
+ output += console_summary(summary_section.to_s.gsub("_", " ").capitalize, cuke_sniffer.summary[summary_section])
28
+ end
29
+ output
30
+ end
31
+
32
+ # Formats the section data for a summary object
33
+ def self.console_summary(name, summary)
34
+ " #{name}\n" +
35
+ " Min: #{summary[:min]} (#{summary[:min_file]})\n" +
36
+ " Max: #{summary[:max]} (#{summary[:max_file]})\n" +
37
+ " Average: #{summary[:average]}\n"
38
+ end
39
+
40
+ # Formats the improvement list data for summary
41
+ def self.console_improvement_list(improvement_list)
42
+ output = " Improvements to make:\n"
43
+ improvement_list.each do |improvement, count|
44
+ output << " (#{count}) #{improvement}\n"
45
+ end
46
+ output
47
+ end
48
+
49
+ # Creates a html file with the collected project details
50
+ # file_name defaults to "cuke_sniffer_results.html" unless specified
51
+ # Second parameter used for passing into the markup.
52
+ # cuke_sniffer.output_html
53
+ # Or
54
+ # cuke_sniffer.output_html("results01-01-0001.html")
55
+ def self.output_html(cuke_sniffer, file_name = DEFAULT_OUTPUT_FILE_NAME, template_name = "standard_template")
56
+ cuke_sniffer = sort_cuke_sniffer_lists(cuke_sniffer)
57
+ output = ERB.new(extract_markup("#{template_name}.html.erb")).result(binding)
58
+ File.open(format_html_file_name(file_name), 'w') do |f| f.write(output) end
59
+ end
60
+
61
+ # Returns an ERB page built up for the passed file name
62
+ def self.build_page(cuke_sniffer, erb_file)
63
+ ERB.new(extract_markup(erb_file)).result(binding)
64
+ end
65
+
66
+ # Assigns an html extension if one is not provided for the passed file name
67
+ def self.format_html_file_name(file_name)
68
+ if file_name =~ /\.html$/
69
+ file_name
70
+ else
71
+ file_name + ".html"
72
+ end
73
+ end
74
+
75
+ # Creates a html file with minimum information: Summary, Rules, Improvement List.
76
+ # file_name defaults to "cuke_sniffer_results.html" unless specified
77
+ # Second parameter used for passing into the markup.
78
+ # cuke_sniffer.output_min_html
79
+ # Or
80
+ # cuke_sniffer.output_min_html("results01-01-0001.html")
81
+ def self.output_min_html(cuke_sniffer, file_name = DEFAULT_OUTPUT_FILE_NAME)
82
+ output_html(cuke_sniffer, file_name, "min_template")
83
+ end
84
+
85
+ # Returns the Rules erb page that utilizes sub page sections of enabled and disabled rules
86
+ def self.rules_template(cuke_sniffer)
87
+ enabled_rules = sub_rules_template(cuke_sniffer, true, "Enabled Rules")
88
+ disabled_rules = sub_rules_template(cuke_sniffer, false, "Disabled Rules")
89
+ ERB.new(extract_markup("rules.html.erb")).result(binding)
90
+ end
91
+
92
+ # Returns the sub rules erb page for enabled status of a rule
93
+ def self.sub_rules_template(cuke_sniffer, state, heading)
94
+ ERB.new(extract_markup("sub_rules.html.erb")).result(binding)
95
+ end
96
+
97
+ # Creates a xml file with the collected project details
98
+ # file_name defaults to "cuke_sniffer.xml" unless specified
99
+ # cuke_sniffer.output_xml
100
+ # Or
101
+ # cuke_sniffer.output_xml("cuke_sniffer01-01-0001.xml")
102
+ def self.output_xml(cuke_sniffer, file_name = DEFAULT_OUTPUT_FILE_NAME)
103
+ file_name = file_name + ".xml" unless file_name =~ /\.xml$/
104
+
105
+ doc = Nokogiri::XML::Document.new
106
+ doc.root = cuke_sniffer.to_xml
107
+ open(file_name, "w") do |file|
108
+ file << doc.serialize
109
+ end
110
+ end
111
+
112
+ # Formats a list of rule conditions into a compact string of 5 or less elements per row.
113
+ def self.convert_array_condition_into_list_of_strings(condition_list)
114
+ result = []
115
+ while condition_list.size > 0
116
+ five_words = condition_list.slice!(0, 5)
117
+ result << five_words.join(", ")
118
+ end
119
+ result
120
+ end
121
+
122
+ # Sorts all of the lists on a cuke_sniffer object to be in descending order for each objects score.
123
+ def self.sort_cuke_sniffer_lists(cuke_sniffer)
124
+ cuke_sniffer.features = cuke_sniffer.features.sort_by { |feature| feature.total_score }.reverse
125
+ cuke_sniffer.step_definitions = cuke_sniffer.step_definitions.sort_by { |step_definition| step_definition.score }.reverse
126
+ cuke_sniffer.hooks = cuke_sniffer.hooks.sort_by { |hook| hook.score }.reverse
127
+ cuke_sniffer.rules = cuke_sniffer.rules.sort_by { |rule| rule.score }.reverse
128
+ cuke_sniffer
129
+ end
130
+
131
+ # Returns the markup for a desired erb file.
132
+ def self.extract_markup(template_name = "markup.html.erb", markup_source = MARKUP_SOURCE)
133
+ markup_location = "#{markup_source}/#{template_name}"
134
+ markup = ""
135
+ File.open(markup_location).lines.each do |line|
136
+ markup << line
137
+ end
138
+ markup
139
+ end
140
+
141
+ end
142
+ end
@@ -1,160 +1,77 @@
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
- # Cucumber Hook class used for evaluating rules
8
- # Extends CukeSniffer::RulesEvaluator
9
- class Hook < RulesEvaluator
10
-
11
- xml_accessor :start_line
12
- xml_accessor :type
13
- xml_accessor :tags, :as => [], :in => "tags"
14
- xml_accessor :parameters, :as => [], :in => "parameters"
15
- xml_accessor :code, :as => [], :in => "code"
16
-
17
- # The type of the hook: AfterConfiguration, After, AfterStep, Around, Before, at_exit
18
- attr_accessor :type
19
-
20
- # The list of tags used as a filter for the hook
21
- attr_accessor :tags
22
-
23
- # The parameters that are declared on the hook
24
- attr_accessor :parameters
25
-
26
- # Integer of the line in which the hook was found
27
- attr_accessor :start_line
28
-
29
- # Array of strings that contain the code kept in the hook
30
- attr_accessor :code
31
-
32
-
33
- # location must be in the format of "file_path\file_name.rb:line_number"
34
- # raw_code is an array of strings that represents the step definition
35
- # must contain the hook declaration line and the pairing end
36
- def initialize(location, raw_code)
37
- super(location)
38
-
39
- @start_line = location.match(/:(?<line>\d*)$/)[:line].to_i
40
- @type = nil
41
- @tags = []
42
- @parameters = []
43
-
44
- end_match_index = (raw_code.size - 1) - raw_code.reverse.index("end")
45
- @code = raw_code[1...end_match_index]
46
-
47
- raw_code.each do |line|
48
- if line =~ HOOK_REGEX
49
- matches = HOOK_REGEX.match(line)
50
- @type = matches[:type]
51
- hook_tag_regexp = /["']([^"']*)["']/
52
- matches[:tags].scan(hook_tag_regexp).each { |tag| @tags << tag[0] } if matches[:tags]
53
- @parameters = matches[:parameters].split(/,\s*/) if matches[:parameters]
54
- end
55
- end
56
- evaluate_score
57
- end
58
-
59
- def ==(comparison_object) # :nodoc:
60
- super(comparison_object) &&
61
- comparison_object.type == type &&
62
- comparison_object.tags == tags &&
63
- comparison_object.parameters == parameters &&
64
- comparison_object.code == code
65
- end
66
-
67
- private
68
-
69
- def evaluate_score
70
- rule_empty_hook
71
- rule_hook_not_in_hooks_file
72
- rule_no_debugging
73
- rule_all_comments
74
- rule_conflicting_tags
75
- rule_duplicate_tags
76
- if @type == "Around"
77
- rule_around_hook_without_2_parameters
78
- rule_around_hook_no_block_call
79
- end
80
- end
81
-
82
- def rule_empty_hook
83
- rule = RULES[:empty_hook]
84
- store_rule(rule) if @code == []
85
- end
86
-
87
- def rule_hook_not_in_hooks_file
88
- rule = RULES[:hook_not_in_hooks_file]
89
- store_rule(rule) unless @location.include?(rule[:file])
90
- end
91
-
92
- def rule_around_hook_without_2_parameters
93
- rule = RULES[:around_hook_without_2_parameters]
94
- store_rule(rule) unless @parameters.count == 2
95
- end
96
-
97
- def rule_around_hook_no_block_call
98
- return if rule_stored?(:around_hook_without_2_parameters)
99
- rule = RULES[:around_hook_no_block_call]
100
- block_call = "#{@parameters[1]}.call"
101
- @code.each do |line|
102
- return if line.include?(block_call)
103
- end
104
- store_rule(rule)
105
- end
106
-
107
- def rule_stored?(rule_symbol)
108
- @rules_hash.keys.include?(RULES[rule_symbol][:phrase])
109
- end
110
-
111
- def rule_no_debugging
112
- return if rule_stored?(:empty_hook)
113
- rule = RULES[:hook_no_debugging]
114
- begin_found = false
115
- rescue_found = false
116
- @code.each do |line|
117
- begin_found = true if line.include?("begin")
118
- rescue_found = true if line.include?("rescue")
119
- break if begin_found and rescue_found
120
- end
121
- store_rule(rule) unless begin_found and rescue_found
122
- end
123
-
124
- def rule_all_comments
125
- rule = RULES[:hook_all_comments]
126
- @code.each do |line|
127
- return unless is_comment?(line)
128
- end
129
- store_rule(rule)
130
- end
131
-
132
- def rule_conflicting_tags
133
- rule = RULES[:hook_conflicting_tags]
134
- all_tags = flatten_tags
135
-
136
- all_tags.each do |single_tag|
137
- tag = single_tag.gsub("~", "")
138
- if all_tags.include?(tag) and all_tags.include?("~#{tag}")
139
- store_rule(rule)
140
- return
141
- end
142
- end
143
- end
144
-
145
- def flatten_tags
146
- all_tags = []
147
- @tags.each { |single_tag| all_tags << single_tag.split(',') }
148
- all_tags.flatten
149
- end
150
-
151
- def rule_duplicate_tags
152
- rule = RULES[:hook_duplicate_tags]
153
- all_tags = flatten_tags
154
- unique_tags = all_tags.uniq
155
-
156
- store_rule(rule) unless all_tags == unique_tags
157
- end
158
-
159
- end
160
- end
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
+ # Cucumber Hook class used for evaluating rules
8
+ # Extends CukeSniffer::RulesEvaluator
9
+ class Hook < RuleTarget
10
+
11
+ xml_accessor :start_line
12
+ xml_accessor :type
13
+ xml_accessor :tags, :as => [], :in => "tags"
14
+ xml_accessor :parameters, :as => [], :in => "parameters"
15
+ xml_accessor :code, :as => [], :in => "code"
16
+
17
+ # The type of the hook: AfterConfiguration, After, AfterStep, Around, Before, at_exit
18
+ attr_accessor :type
19
+
20
+ # The list of tags used as a filter for the hook
21
+ attr_accessor :tags
22
+
23
+ # The parameters that are declared on the hook
24
+ attr_accessor :parameters
25
+
26
+ # Integer of the line in which the hook was found
27
+ attr_accessor :start_line
28
+
29
+ # Array of strings that contain the code kept in the hook
30
+ attr_accessor :code
31
+
32
+
33
+ # location must be in the format of "file_path\file_name.rb:line_number"
34
+ # raw_code is an array of strings that represents the step definition
35
+ # must contain the hook declaration line and the pairing end
36
+ def initialize(location, hook_block)
37
+ super(location)
38
+ @start_line = location.match(/:(?<line>\d*)$/)[:line].to_i
39
+ end_match_index = (hook_block.size - 1) - hook_block.reverse.index("end")
40
+ @code = hook_block[1...end_match_index]
41
+ initialize_hook_signature(hook_block)
42
+ end
43
+
44
+ def ==(comparison_object) # :nodoc:
45
+ super(comparison_object) &&
46
+ comparison_object.type == type &&
47
+ comparison_object.tags == tags &&
48
+ comparison_object.parameters == parameters &&
49
+ comparison_object.code == code
50
+ end
51
+
52
+ private
53
+
54
+ def initialize_hook_signature(hook_block)
55
+ @type = nil
56
+ @tags = []
57
+ @parameters = []
58
+
59
+ hook_signature = extract_hook_signature(hook_block)
60
+ matches = HOOK_REGEX.match(hook_signature)
61
+ @type = matches[:type]
62
+ initialize_tags(matches[:tags]) if matches[:tags]
63
+ @parameters = matches[:parameters].split(/,\s*/) if matches[:parameters]
64
+ end
65
+
66
+ def extract_hook_signature(hook_block)
67
+ hook_block.each do |line|
68
+ return line if line =~ HOOK_REGEX
69
+ end
70
+ end
71
+
72
+ def initialize_tags(tag_list)
73
+ hook_tag_regexp = /["']([^"']*)["']/
74
+ tag_list.scan(hook_tag_regexp).each { |tag| @tags << tag[0] }
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,36 @@
1
+ <div class="title" onclick="toggleById('dead_steps_data', this)">
2
+ Dead Steps +
3
+ </div>
4
+ <div class="shrink_section" id="dead_steps_data">
5
+ <% dead_steps = cuke_sniffer.get_dead_steps %>
6
+ <% dead_steps.delete(:total) %>
7
+ <% dead_step_count = dead_steps.values.flatten.count %>
8
+ <% if dead_step_count == 0 %>
9
+ <div class="empty_set_message">No dead steps found!</div>
10
+ <% elsif dead_step_count == 1 %>
11
+ <div class="empty_set_message" style="color: red"><%= dead_step_count %> Dead Step found.</div>
12
+ <% else %>
13
+ <div class="empty_set_message" style="color: red"><%= dead_step_count %> Dead Steps found.</div>
14
+ <% end %>
15
+
16
+ <table style="width: 100%">
17
+ <% dead_steps.each_key do |file_name| %>
18
+ <tr>
19
+ <td>
20
+ <div style="text-indent: 2%;font-weight: bold;"><%= file_name.gsub(cuke_sniffer.step_definitions_location, "") %>
21
+ .rb
22
+ </div>
23
+ <table style="width: 100%; padding-left: 3%;">
24
+ <% dead_steps[file_name].each do |regex| %>
25
+ <tr>
26
+ <td style="width: 3%"><%= regex.match(/(?<line>\d+):/)[:line] %></td>
27
+ <td><%= regex.match(/:(?<regex>.*)/)[:regex] %></td>
28
+ </tr>
29
+ <% end %>
30
+ </table>
31
+ </td>
32
+ </tr>
33
+ <% end %>
34
+ </table>
35
+ </div>
36
+ <br style="clear:both">
@@ -0,0 +1,60 @@
1
+ <div class="title" onclick="toggleById('features_data', this)">
2
+ Features +
3
+ </div>
4
+ <div class="shrink_section" id="features_data">
5
+ <% if cuke_sniffer.features.count == 0 %>
6
+ <div class="empty_set_message">There were no Features to sniff in '<%= cuke_sniffer.features_location %>'!</div>
7
+ <% elsif cuke_sniffer.features.count >= 1 && cuke_sniffer.scenarios.count >=1 && cuke_sniffer.summary[:features][:total_score] ==0 && cuke_sniffer.summary[:scenarios][:total_score] ==0 %>
8
+ <div class="empty_set_message">Excellent! No smells found for Features and Scenarios!</div>
9
+ <% end %>
10
+ <% index = 0 %>
11
+ <% cuke_sniffer.features.each do |feature| %>
12
+ <% next if feature.total_score <= 0 %>
13
+ <div style="cursor: pointer; padding-top: 3px" onclick="updateDisplayStatus(document.getElementById('feature_<%=index%>'))">
14
+ <div class="red_number" style="float:left"><%= feature.total_score + feature.scenarios_score %></div>
15
+ <div class="notes"><%= feature.name %></div>
16
+ </div>
17
+ <div id="feature_<%= index %>" class="sub_section">
18
+ <div style="text-indent: 4%; float: left; width: 45%; border-right: 2px solid #add8e6; padding-bottom: 4px">
19
+ <% if feature.rules_hash.empty? %>
20
+ <pre style="margin: 0;"> </pre>
21
+ <% end %>
22
+ <% feature.rules_hash.each_key do |rule| %>
23
+ <pre style="margin: 0;"><%= rule %></pre>
24
+ <% end %>
25
+ </div>
26
+ <div style="text-indent: 1%; padding-bottom: 4px">
27
+ <%= feature.location.gsub(cuke_sniffer.features_location, "") %>
28
+ </div>
29
+ <br style="clear:both">
30
+
31
+ <div>
32
+ <% unless feature.background.nil? or feature.background.score > 0 %>
33
+ <div class="red_number" style="float:left; padding-left: 1%"><%= feature.background.score %></div>
34
+ <div style="float: left; width: 45%;"><%= feature.background.type + ": " + feature.background.name %></div>
35
+ <div style="margin-left:1%;float: left; border-left: 2px solid #90ee90; border-top: 2px solid #90ee90;text-indent: 1%;">
36
+ <% feature.background.rules_hash.each_key do |rule| %>
37
+ <pre style="margin: 0; text_indent: 3%;"><%= rule %></pre>
38
+ <% end %>
39
+ </div>
40
+ <br style="clear:both">
41
+ <% end %>
42
+ </div>
43
+ <div>
44
+ <% feature.scenarios.each do |scenario| %>
45
+ <% next if scenario.score <= 0 %>
46
+ <div class="red_number" style="float:left; padding-left: 1%"><%= scenario.score %></div>
47
+ <div style="float: left; width: 45%;"><%= scenario.type + ": " + scenario.name %></div>
48
+ <div style="margin-left:1%;float: left; border-left: 2px solid #90ee90; border-top: 2px solid #90ee90; text-indent: 1%">
49
+ <% scenario.rules_hash.each_key do |rule| %>
50
+ <pre style="margin: 0; text_indent: 3%;"><%= rule %></pre>
51
+ <% end %>
52
+ </div>
53
+ <br style="clear:both">
54
+ <% end %>
55
+ </div>
56
+ </div>
57
+ <% index += 1 %>
58
+ <% end %>
59
+ </div>
60
+ <br style="clear:both">