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.
- data/bin/cuke_sniffer +6 -1
- data/lib/cuke_sniffer/cli.rb +35 -14
- data/lib/cuke_sniffer/cuke_sniffer_helper.rb +165 -164
- data/lib/cuke_sniffer/formatter.rb +2 -19
- data/lib/cuke_sniffer/report/css.html.erb +133 -0
- data/lib/cuke_sniffer/report/dead_steps.html.erb +62 -35
- data/lib/cuke_sniffer/report/features.html.erb +104 -50
- data/lib/cuke_sniffer/report/hooks.html.erb +78 -48
- data/lib/cuke_sniffer/report/improvement_list.html.erb +12 -6
- data/lib/cuke_sniffer/report/js.html.erb +65 -0
- data/lib/cuke_sniffer/report/min_template.html.erb +16 -125
- data/lib/cuke_sniffer/report/rules.html.erb +75 -8
- data/lib/cuke_sniffer/report/standard_template.html.erb +14 -127
- data/lib/cuke_sniffer/report/step_definitions.html.erb +68 -49
- data/lib/cuke_sniffer/report/summary.html.erb +40 -15
- data/lib/cuke_sniffer/report/title.html.erb +9 -0
- data/lib/cuke_sniffer/rule_config.rb +508 -483
- metadata +5 -3
- data/lib/cuke_sniffer/report/sub_rules.html.erb +0 -24
data/bin/cuke_sniffer
CHANGED
@@ -14,7 +14,9 @@ Other Options for Running include:
|
|
14
14
|
-p, --project <project_root> : Root directory of project.
|
15
15
|
-f, --features <feature_path> : Path to features directory.
|
16
16
|
-s, --step_definitions <step_def_path> : Path to step definitions directory.
|
17
|
-
-hk, --hooks <hooks_path> : Path to support directory.
|
17
|
+
-hk, --hooks <hooks_path> : Path to support directory.
|
18
|
+
-nc, --no_catalog : Disable cataloging of steps. Reduced runtime and no
|
19
|
+
"
|
18
20
|
|
19
21
|
def find_index_by_regex_match(list, regex)
|
20
22
|
list.each do |item|
|
@@ -29,6 +31,7 @@ def build_cli_hash(argv)
|
|
29
31
|
:features_location => /-f|--features/,
|
30
32
|
:step_definitions_location => /-s|--step_definitions/,
|
31
33
|
:project_location => /-p|--project/}
|
34
|
+
|
32
35
|
cli_hash = {}
|
33
36
|
cucumber_hash.each do |symbol, regex|
|
34
37
|
index_of_key = find_index_by_regex_match(argv, regex)
|
@@ -36,6 +39,8 @@ def build_cli_hash(argv)
|
|
36
39
|
cli_hash[symbol] = argv[index_of_key + 1]
|
37
40
|
end
|
38
41
|
end
|
42
|
+
|
43
|
+
cli_hash[:no_catalog] = true if find_index_by_regex_match(argv, /-nc|--no_catalog/)
|
39
44
|
cli_hash
|
40
45
|
end
|
41
46
|
|
data/lib/cuke_sniffer/cli.rb
CHANGED
@@ -22,6 +22,7 @@ module CukeSniffer
|
|
22
22
|
xml_accessor :features, :as => [CukeSniffer::Feature], :in => "features"
|
23
23
|
xml_accessor :step_definitions, :as => [CukeSniffer::StepDefinition], :in => "step_definitions"
|
24
24
|
xml_accessor :hooks, :as => [CukeSniffer::Hook], :in => "hooks"
|
25
|
+
xml_accessor :cataloged
|
25
26
|
|
26
27
|
|
27
28
|
# Feature array: All Features gathered from the specified folder
|
@@ -53,6 +54,9 @@ module CukeSniffer
|
|
53
54
|
# Rules hash: All the rules that exist at runtime and their corresponding data
|
54
55
|
attr_accessor :rules
|
55
56
|
|
57
|
+
# Boolean: Status of if the projects step definitions were cataloged for calls
|
58
|
+
attr_accessor :cataloged
|
59
|
+
|
56
60
|
|
57
61
|
# Does analysis against the passed features and step definition locations
|
58
62
|
#
|
@@ -73,6 +77,9 @@ module CukeSniffer
|
|
73
77
|
# Against folders
|
74
78
|
# cuke_sniffer = CukeSniffer::CLI.new({:features_location =>"my_features_directory\", :step_definitions_location =>"my_steps_directory\"})
|
75
79
|
#
|
80
|
+
# Disabling cataloging for improved runtime and no dead steps identified
|
81
|
+
# cuke_sniffer = CukeSniffer::CLI.new({:no_catalog => true})
|
82
|
+
#
|
76
83
|
# You can mix and match all of the above examples
|
77
84
|
#
|
78
85
|
# Displays the sequence and a . indicator for each new loop in that process.
|
@@ -82,7 +89,7 @@ module CukeSniffer
|
|
82
89
|
def initialize(parameters = {})
|
83
90
|
initialize_rule_targets(parameters)
|
84
91
|
evaluate_rules
|
85
|
-
catalog_step_calls
|
92
|
+
catalog_step_calls if @cataloged
|
86
93
|
assess_score
|
87
94
|
end
|
88
95
|
|
@@ -155,6 +162,21 @@ module CukeSniffer
|
|
155
162
|
CukeSniffer::CukeSnifferHelper.catalog_possible_dead_steps(@step_definitions, converted_steps)
|
156
163
|
end
|
157
164
|
|
165
|
+
def assess_score
|
166
|
+
puts "\nAssessing Score: "
|
167
|
+
initialize_summary
|
168
|
+
summarize(:features, @features, "Feature")
|
169
|
+
summarize(:scenarios, @scenarios, "Scenario")
|
170
|
+
summarize(:step_definitions, @step_definitions, "StepDefinition")
|
171
|
+
summarize(:hooks, @hooks, "Hook")
|
172
|
+
@summary[:improvement_list] = CukeSniffer::SummaryHelper.sort_improvement_list(@summary[:improvement_list])
|
173
|
+
@improvement_list = @summary[:improvement_list]
|
174
|
+
end
|
175
|
+
|
176
|
+
def cataloged?
|
177
|
+
@cataloged
|
178
|
+
end
|
179
|
+
|
158
180
|
private
|
159
181
|
|
160
182
|
def initialize_rule_targets(parameters)
|
@@ -166,6 +188,8 @@ module CukeSniffer
|
|
166
188
|
|
167
189
|
puts("\nHooks:")
|
168
190
|
@hooks = build_objects_for_extension_from_location(@hooks_location, "rb") { |location| build_hooks(location) }
|
191
|
+
|
192
|
+
initialize_catalog_status(parameters)
|
169
193
|
end
|
170
194
|
|
171
195
|
def initialize_locations(parameters)
|
@@ -180,6 +204,14 @@ module CukeSniffer
|
|
180
204
|
@scenarios = CukeSniffer::CukeSnifferHelper.get_all_scenarios(@features)
|
181
205
|
end
|
182
206
|
|
207
|
+
def initialize_catalog_status(parameters)
|
208
|
+
if parameters[:no_catalog] == true
|
209
|
+
@cataloged = false
|
210
|
+
else
|
211
|
+
@cataloged = true
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
183
215
|
def evaluate_rules
|
184
216
|
@rules = CukeSniffer::CukeSnifferHelper.build_rules(RULES)
|
185
217
|
CukeSniffer::RulesEvaluator.new(self, @rules)
|
@@ -192,20 +224,9 @@ module CukeSniffer
|
|
192
224
|
}
|
193
225
|
end
|
194
226
|
|
195
|
-
def
|
196
|
-
puts "\nAssessing Score: "
|
197
|
-
initialize_summary
|
198
|
-
summarize(:features, @features, "Feature", @features_summary)
|
199
|
-
summarize(:scenarios, @scenarios, "Scenario", @scenarios_summary)
|
200
|
-
summarize(:step_definitions, @step_definitions, "StepDefinition", @step_definitions_summary)
|
201
|
-
summarize(:hooks, @hooks, "Hook", @hooks_summary)
|
202
|
-
@summary[:improvement_list] = CukeSniffer::SummaryHelper.sort_improvement_list(@summary[:improvement_list])
|
203
|
-
@improvement_list = @summary[:improvement_list]
|
204
|
-
end
|
205
|
-
|
206
|
-
def summarize(symbol, list, name, summary_object)
|
227
|
+
def summarize(symbol, list, name)
|
207
228
|
@summary[symbol] = CukeSniffer::SummaryHelper.assess_rule_target_list(list, name)
|
208
|
-
@summary[:total_score]
|
229
|
+
@summary[:total_score] += @summary[symbol][:total_score]
|
209
230
|
@summary[symbol][:improvement_list].each do |phrase, count|
|
210
231
|
@summary[:improvement_list][phrase] ||= 0
|
211
232
|
@summary[:improvement_list][phrase] += @summary[symbol][:improvement_list][phrase]
|
@@ -1,165 +1,166 @@
|
|
1
|
-
module CukeSniffer
|
2
|
-
# Author:: Robert Cochran (mailto:cochrarj@miamioh.edu)
|
3
|
-
# Copyright:: Copyright (C) 2013 Robert Cochran
|
4
|
-
# License:: Distributes under the MIT License
|
5
|
-
# Static class used for aiding cuke_sniffer in various tasks
|
6
|
-
class CukeSnifferHelper
|
7
|
-
|
8
|
-
# Iterates over the passed features list and returns all steps found in scenarios and backgrounds.
|
9
|
-
def self.extract_steps_from_features(features)
|
10
|
-
steps = {}
|
11
|
-
features.each do |feature|
|
12
|
-
steps.merge! extract_scenario_steps(feature.background) unless feature.background.nil?
|
13
|
-
feature.scenarios.each do |scenario|
|
14
|
-
if scenario.type == "Scenario Outline"
|
15
|
-
steps.merge! extract_scenario_outline_steps(scenario)
|
16
|
-
else
|
17
|
-
steps.merge! extract_scenario_steps(scenario)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
steps
|
22
|
-
end
|
23
|
-
|
24
|
-
# Iterates over the passed features list and returns all scenarios and backgrounds found.
|
25
|
-
def self.get_all_scenarios(features)
|
26
|
-
scenarios = []
|
27
|
-
features.each do |feature|
|
28
|
-
scenarios << feature.background unless feature.background.nil?
|
29
|
-
scenarios << feature.scenarios
|
30
|
-
end
|
31
|
-
scenarios.flatten
|
32
|
-
end
|
33
|
-
|
34
|
-
# Grabs the values from an example without the bars
|
35
|
-
def self.extract_variables_from_example(example)
|
36
|
-
example = example[example.index('|')..example.length]
|
37
|
-
example.split(/\s*\|\s*/) - [""]
|
38
|
-
end
|
39
|
-
|
40
|
-
# Creates a step call from the details of an example table
|
41
|
-
def self.build_updated_step_from_example(step, variable_list, row_variables)
|
42
|
-
new_step = step.dup
|
43
|
-
variable_list.each do |variable|
|
44
|
-
if step.include? variable
|
45
|
-
table_variable_to_insert = row_variables[variable_list.index(variable)]
|
46
|
-
table_variable_to_insert ||= ""
|
47
|
-
new_step.gsub!("<#{variable}>", table_variable_to_insert)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
new_step
|
51
|
-
end
|
52
|
-
|
53
|
-
# Creates a hash of steps with the build up example step calls.
|
54
|
-
def self.extract_scenario_outline_steps(scenario)
|
55
|
-
steps = {}
|
56
|
-
examples = scenario.examples_table
|
57
|
-
return {} if examples.empty?
|
58
|
-
variable_list = extract_variables_from_example(examples.first)
|
59
|
-
(1...examples.size).each do |example_counter|
|
60
|
-
#TODO Abstraction needed for this regex matcher (constants?)
|
61
|
-
next if examples[example_counter] =~ /^\#.*$/
|
62
|
-
row_variables = extract_variables_from_example(examples[example_counter])
|
63
|
-
step_counter = 1
|
64
|
-
scenario.steps.each do |step|
|
65
|
-
step_line = scenario.start_line + step_counter
|
66
|
-
location = "#{scenario.location.gsub(/\d+$/, step_line.to_s)}(Example #{example_counter})"
|
67
|
-
steps[location] = build_updated_step_from_example(step, variable_list, row_variables)
|
68
|
-
step_counter += 1
|
69
|
-
end
|
70
|
-
end
|
71
|
-
steps
|
72
|
-
end
|
73
|
-
|
74
|
-
# Returns all steps found in a scenario
|
75
|
-
def self.extract_scenario_steps(scenario)
|
76
|
-
steps_hash = {}
|
77
|
-
counter = 1
|
78
|
-
scenario.steps.each do |step|
|
79
|
-
location = scenario.location.gsub(/:\d*$/, ":#{scenario.start_line + counter}")
|
80
|
-
steps_hash[location] = step
|
81
|
-
counter += 1
|
82
|
-
end
|
83
|
-
steps_hash
|
84
|
-
end
|
85
|
-
|
86
|
-
# Builds a list of rule objects out of a hash. See CukeSniffer::RulesConfig for hash example.
|
87
|
-
def self.build_rules(rules)
|
88
|
-
return [] if rules.nil?
|
89
|
-
rules.collect do |key, value|
|
90
|
-
CukeSniffer::CukeSnifferHelper.build_rule(value)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
# Builds rule object out of a hash. See CukeSniffer::RulesConfig for hash example.
|
95
|
-
def self.build_rule(rule_hash)
|
96
|
-
rule = CukeSniffer::Rule.new
|
97
|
-
rule.phrase = rule_hash[:phrase]
|
98
|
-
rule.score = rule_hash[:score]
|
99
|
-
rule.enabled = rule_hash[:enabled]
|
100
|
-
conditional_keys = rule_hash.keys - [:phrase, :score, :enabled, :targets, :reason]
|
101
|
-
conditions = {}
|
102
|
-
conditional_keys.each do |key|
|
103
|
-
conditions[key] = (rule_hash[key].kind_of? Array) ? Array.new(rule_hash[key]) : rule_hash[key]
|
104
|
-
end
|
105
|
-
rule.conditions = conditions
|
106
|
-
rule.reason = rule_hash[:reason]
|
107
|
-
rule.targets = rule_hash[:targets]
|
108
|
-
rule
|
109
|
-
end
|
110
|
-
|
111
|
-
# Returns a list of all nested step calls found in a step definition.
|
112
|
-
def self.extract_steps_from_step_definitions(step_definitions)
|
113
|
-
steps = {}
|
114
|
-
step_definitions.each do |definition|
|
115
|
-
definition.nested_steps.
|
116
|
-
steps[
|
117
|
-
end
|
118
|
-
end
|
119
|
-
steps
|
120
|
-
end
|
121
|
-
|
122
|
-
# Returns a fuzzy match for a step definition for cataloging steps.
|
123
|
-
def self.convert_steps_with_expressions(steps_with_expressions)
|
124
|
-
step_regex_hash = {}
|
125
|
-
steps_with_expressions.each do |step_location, step_value|
|
126
|
-
modified_step = step_value.gsub(/\#{[^}]*}/, '.*')
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
1
|
+
module CukeSniffer
|
2
|
+
# Author:: Robert Cochran (mailto:cochrarj@miamioh.edu)
|
3
|
+
# Copyright:: Copyright (C) 2013 Robert Cochran
|
4
|
+
# License:: Distributes under the MIT License
|
5
|
+
# Static class used for aiding cuke_sniffer in various tasks
|
6
|
+
class CukeSnifferHelper
|
7
|
+
|
8
|
+
# Iterates over the passed features list and returns all steps found in scenarios and backgrounds.
|
9
|
+
def self.extract_steps_from_features(features)
|
10
|
+
steps = {}
|
11
|
+
features.each do |feature|
|
12
|
+
steps.merge! extract_scenario_steps(feature.background) unless feature.background.nil?
|
13
|
+
feature.scenarios.each do |scenario|
|
14
|
+
if scenario.type == "Scenario Outline"
|
15
|
+
steps.merge! extract_scenario_outline_steps(scenario)
|
16
|
+
else
|
17
|
+
steps.merge! extract_scenario_steps(scenario)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
steps
|
22
|
+
end
|
23
|
+
|
24
|
+
# Iterates over the passed features list and returns all scenarios and backgrounds found.
|
25
|
+
def self.get_all_scenarios(features)
|
26
|
+
scenarios = []
|
27
|
+
features.each do |feature|
|
28
|
+
scenarios << feature.background unless feature.background.nil?
|
29
|
+
scenarios << feature.scenarios
|
30
|
+
end
|
31
|
+
scenarios.flatten
|
32
|
+
end
|
33
|
+
|
34
|
+
# Grabs the values from an example without the bars
|
35
|
+
def self.extract_variables_from_example(example)
|
36
|
+
example = example[example.index('|')..example.length]
|
37
|
+
example.split(/\s*\|\s*/) - [""]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Creates a step call from the details of an example table
|
41
|
+
def self.build_updated_step_from_example(step, variable_list, row_variables)
|
42
|
+
new_step = step.dup
|
43
|
+
variable_list.each do |variable|
|
44
|
+
if step.include? variable
|
45
|
+
table_variable_to_insert = row_variables[variable_list.index(variable)]
|
46
|
+
table_variable_to_insert ||= ""
|
47
|
+
new_step.gsub!("<#{variable}>", table_variable_to_insert)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
new_step
|
51
|
+
end
|
52
|
+
|
53
|
+
# Creates a hash of steps with the build up example step calls.
|
54
|
+
def self.extract_scenario_outline_steps(scenario)
|
55
|
+
steps = {}
|
56
|
+
examples = scenario.examples_table
|
57
|
+
return {} if examples.empty?
|
58
|
+
variable_list = extract_variables_from_example(examples.first)
|
59
|
+
(1...examples.size).each do |example_counter|
|
60
|
+
#TODO Abstraction needed for this regex matcher (constants?)
|
61
|
+
next if examples[example_counter] =~ /^\#.*$/
|
62
|
+
row_variables = extract_variables_from_example(examples[example_counter])
|
63
|
+
step_counter = 1
|
64
|
+
scenario.steps.each do |step|
|
65
|
+
step_line = scenario.start_line + step_counter
|
66
|
+
location = "#{scenario.location.gsub(/\d+$/, step_line.to_s)}(Example #{example_counter})"
|
67
|
+
steps[location] = build_updated_step_from_example(step, variable_list, row_variables)
|
68
|
+
step_counter += 1
|
69
|
+
end
|
70
|
+
end
|
71
|
+
steps
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns all steps found in a scenario
|
75
|
+
def self.extract_scenario_steps(scenario)
|
76
|
+
steps_hash = {}
|
77
|
+
counter = 1
|
78
|
+
scenario.steps.each do |step|
|
79
|
+
location = scenario.location.gsub(/:\d*$/, ":#{scenario.start_line + counter}")
|
80
|
+
steps_hash[location] = step
|
81
|
+
counter += 1
|
82
|
+
end
|
83
|
+
steps_hash
|
84
|
+
end
|
85
|
+
|
86
|
+
# Builds a list of rule objects out of a hash. See CukeSniffer::RulesConfig for hash example.
|
87
|
+
def self.build_rules(rules)
|
88
|
+
return [] if rules.nil?
|
89
|
+
rules.collect do |key, value|
|
90
|
+
CukeSniffer::CukeSnifferHelper.build_rule(value)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Builds rule object out of a hash. See CukeSniffer::RulesConfig for hash example.
|
95
|
+
def self.build_rule(rule_hash)
|
96
|
+
rule = CukeSniffer::Rule.new
|
97
|
+
rule.phrase = rule_hash[:phrase]
|
98
|
+
rule.score = rule_hash[:score]
|
99
|
+
rule.enabled = rule_hash[:enabled]
|
100
|
+
conditional_keys = rule_hash.keys - [:phrase, :score, :enabled, :targets, :reason]
|
101
|
+
conditions = {}
|
102
|
+
conditional_keys.each do |key|
|
103
|
+
conditions[key] = (rule_hash[key].kind_of? Array) ? Array.new(rule_hash[key]) : rule_hash[key]
|
104
|
+
end
|
105
|
+
rule.conditions = conditions
|
106
|
+
rule.reason = rule_hash[:reason]
|
107
|
+
rule.targets = rule_hash[:targets]
|
108
|
+
rule
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns a list of all nested step calls found in a step definition.
|
112
|
+
def self.extract_steps_from_step_definitions(step_definitions)
|
113
|
+
steps = {}
|
114
|
+
step_definitions.each do |definition|
|
115
|
+
definition.nested_steps.each do |location, step|
|
116
|
+
steps[location] = step
|
117
|
+
end
|
118
|
+
end
|
119
|
+
steps
|
120
|
+
end
|
121
|
+
|
122
|
+
# Returns a fuzzy match for a step definition for cataloging steps.
|
123
|
+
def self.convert_steps_with_expressions(steps_with_expressions)
|
124
|
+
step_regex_hash = {}
|
125
|
+
steps_with_expressions.each do |step_location, step_value|
|
126
|
+
modified_step = step_value.gsub(/\#{[^}]*}/, '.*')
|
127
|
+
next if modified_step == '.*'
|
128
|
+
step_regex_hash[step_location] = Regexp.new('^' + modified_step + '$')
|
129
|
+
end
|
130
|
+
step_regex_hash
|
131
|
+
end
|
132
|
+
|
133
|
+
# Extracts all possible step calls from the passed features and step definitions.
|
134
|
+
def self.get_all_steps(features, step_definitions)
|
135
|
+
feature_steps = CukeSniffer::CukeSnifferHelper.extract_steps_from_features(features)
|
136
|
+
step_definition_steps = CukeSniffer::CukeSnifferHelper.extract_steps_from_step_definitions(step_definitions)
|
137
|
+
feature_steps.merge step_definition_steps
|
138
|
+
end
|
139
|
+
|
140
|
+
# Applies all possible fuzzy calls to a step definition.
|
141
|
+
def self.catalog_possible_dead_steps(step_definitions, steps_with_expressions)
|
142
|
+
step_definitions.each do |step_definition|
|
143
|
+
next unless step_definition.calls.empty?
|
144
|
+
regex_as_string = step_definition.regex.to_s.gsub(/\(\?-mix:\^?/, "").gsub(/\$\)$/, "")
|
145
|
+
steps_with_expressions.each do |step_location, step_value|
|
146
|
+
if regex_as_string =~ step_value
|
147
|
+
step_definition.add_call(step_location, step_value)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
step_definitions
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns a list of all step definitions with a capture group
|
155
|
+
def self.get_steps_with_expressions(steps)
|
156
|
+
steps_with_expressions = {}
|
157
|
+
steps.each do |step_location, step_value|
|
158
|
+
if step_value =~ /\#{.*}/
|
159
|
+
steps_with_expressions[step_location] = step_value
|
160
|
+
end
|
161
|
+
end
|
162
|
+
steps_with_expressions
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
165
166
|
end
|