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