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.
- data/bin/cuke_sniffer +85 -63
- data/lib/cuke_sniffer.rb +23 -16
- data/lib/cuke_sniffer/cli.rb +269 -523
- data/lib/cuke_sniffer/constants.rb +5 -2
- data/lib/cuke_sniffer/cuke_sniffer_helper.rb +165 -0
- data/lib/cuke_sniffer/dead_steps_helper.rb +54 -0
- data/lib/cuke_sniffer/feature.rb +13 -81
- data/lib/cuke_sniffer/feature_rules_evaluator.rb +56 -115
- data/lib/cuke_sniffer/formatter.rb +142 -0
- data/lib/cuke_sniffer/hook.rb +77 -160
- data/lib/cuke_sniffer/report/dead_steps.html.erb +36 -0
- data/lib/cuke_sniffer/report/features.html.erb +60 -0
- data/lib/cuke_sniffer/report/hooks.html.erb +49 -0
- data/lib/cuke_sniffer/report/improvement_list.html.erb +18 -0
- data/lib/cuke_sniffer/report/legend.html.erb +167 -0
- data/lib/cuke_sniffer/report/min_template.html.erb +125 -0
- data/lib/cuke_sniffer/report/rules.html.erb +9 -0
- data/lib/cuke_sniffer/report/standard_template.html.erb +137 -0
- data/lib/cuke_sniffer/report/step_definitions.html.erb +49 -0
- data/lib/cuke_sniffer/report/sub_rules.html.erb +24 -0
- data/lib/cuke_sniffer/report/summary.html.erb +71 -0
- data/lib/cuke_sniffer/rule.rb +28 -0
- data/lib/cuke_sniffer/rule_config.rb +241 -48
- data/lib/cuke_sniffer/rule_target.rb +62 -0
- data/lib/cuke_sniffer/rules_evaluator.rb +65 -70
- data/lib/cuke_sniffer/scenario.rb +102 -238
- data/lib/cuke_sniffer/step_definition.rb +163 -239
- data/lib/cuke_sniffer/summary_helper.rb +91 -0
- data/lib/cuke_sniffer/summary_node.rb +19 -0
- metadata +23 -5
- data/lib/cuke_sniffer/report/markup.rhtml +0 -353
@@ -10,12 +10,15 @@ module CukeSniffer
|
|
10
10
|
COMMENT_REGEX = /#?\s*/ # :nodoc:
|
11
11
|
TAG_REGEX = /(^|\s+)(?<tag>@\S*)/ # :nodoc:
|
12
12
|
SCENARIO_TITLE_STYLES = /^\s*\#*\s*(?<type>Background|Scenario|Scenario Outline|Scenario Template):\s*/ # :nodoc:
|
13
|
-
STEP_STYLES = /(?<style>Given|When|Then|And|Or|But|Transform|\*)\s
|
13
|
+
STEP_STYLES = /(?<style>Given|When|Then|And|Or|But|Transform|\*)\s*/ # :nodoc:
|
14
14
|
STEP_REGEX = /^#{COMMENT_REGEX}#{STEP_STYLES}(?<step_string>.*)/ # :nodoc:
|
15
|
-
STEP_DEFINITION_REGEX = /^#{STEP_STYLES}
|
15
|
+
STEP_DEFINITION_REGEX = /^#{STEP_STYLES}[(]?\/(?<step>.+)\/[)]?\sdo\s?(\|(?<parameters>.*)\|)?$/ # :nodoc:
|
16
16
|
HOOK_STYLES = /(?<type>Before|After|AfterConfiguration|at_exit|Around|AfterStep)/ # :nodoc:
|
17
17
|
HOOK_REGEX = /^#{HOOK_STYLES}(\((?<tags>.*)\)\sdo|\s+do)(\s\|(?<parameters>.*)\|)?/
|
18
18
|
|
19
|
+
MARKUP_SOURCE = File.join(File.dirname(__FILE__), 'report')
|
20
|
+
DEFAULT_OUTPUT_FILE_NAME = "cuke_sniffer_result"
|
21
|
+
|
19
22
|
# hash: Stores scores to compare against for determining if an object is good
|
20
23
|
# * Key: String of the object name
|
21
24
|
# Project, Feature, Scenario, StepDefinition
|
@@ -0,0 +1,165 @@
|
|
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_key do |key|
|
116
|
+
steps[key] = definition.nested_steps[key]
|
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
|
+
step_regex_hash[step_location] = Regexp.new('^' + modified_step + '$')
|
128
|
+
end
|
129
|
+
step_regex_hash
|
130
|
+
end
|
131
|
+
|
132
|
+
# Extracts all possible step calls from the passed features and step definitions.
|
133
|
+
def self.get_all_steps(features, step_definitions)
|
134
|
+
feature_steps = CukeSniffer::CukeSnifferHelper.extract_steps_from_features(features)
|
135
|
+
step_definition_steps = CukeSniffer::CukeSnifferHelper.extract_steps_from_step_definitions(step_definitions)
|
136
|
+
feature_steps.merge step_definition_steps
|
137
|
+
end
|
138
|
+
|
139
|
+
# Applies all possible fuzzy calls to a step definition.
|
140
|
+
def self.catalog_possible_dead_steps(step_definitions, steps_with_expressions)
|
141
|
+
step_definitions.each do |step_definition|
|
142
|
+
next unless step_definition.calls.empty?
|
143
|
+
regex_as_string = step_definition.regex.to_s.gsub(/\(\?-mix:\^?/, "").gsub(/\$\)$/, "")
|
144
|
+
steps_with_expressions.each do |step_location, step_value|
|
145
|
+
if regex_as_string =~ step_value
|
146
|
+
step_definition.add_call(step_location, step_value)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
step_definitions
|
151
|
+
end
|
152
|
+
|
153
|
+
# Returns a list of all step definitions with a capture group
|
154
|
+
def self.get_steps_with_expressions(steps)
|
155
|
+
steps_with_expressions = {}
|
156
|
+
steps.each do |step_location, step_value|
|
157
|
+
if step_value =~ /\#{.*}/
|
158
|
+
steps_with_expressions[step_location] = step_value
|
159
|
+
end
|
160
|
+
end
|
161
|
+
steps_with_expressions
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,54 @@
|
|
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
|
+
# Mixins: CukeSniffer::Constants
|
6
|
+
# A static class to aid in the identification of dead steps.
|
7
|
+
class DeadStepsHelper
|
8
|
+
include CukeSniffer::Constants
|
9
|
+
|
10
|
+
# Returns a hash of dead steps for displaying in the html.
|
11
|
+
def self.build_dead_steps_hash(step_definitions)
|
12
|
+
dead_steps_hash = gather_all_dead_steps_by_file(step_definitions)
|
13
|
+
sort_dead_steps_in_file!(dead_steps_hash)
|
14
|
+
dead_steps_hash[:total] = count_dead_steps(dead_steps_hash)
|
15
|
+
dead_steps_hash
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns all dead step definitions in a file
|
19
|
+
def self.gather_all_dead_steps_by_file(step_definitions)
|
20
|
+
dead_steps_hash = {}
|
21
|
+
step_definitions.each do |step_definition|
|
22
|
+
location_match = step_definition.location.match(/(?<file>.*).rb:(?<line>\d+)/)
|
23
|
+
file_name = location_match[:file]
|
24
|
+
regex = format_step_definition_regex(step_definition.regex)
|
25
|
+
if step_definition.calls.empty?
|
26
|
+
dead_steps_hash[file_name] ||= []
|
27
|
+
dead_steps_hash[file_name] << "#{location_match[:line]}: /#{regex}/"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
dead_steps_hash
|
31
|
+
end
|
32
|
+
|
33
|
+
# Formats the regex of a step definition to remove the (?-mix) part of the to_s
|
34
|
+
def self.format_step_definition_regex(regex)
|
35
|
+
regex.to_s.match(/\(\?\-mix\:(?<regex>.*)\)/)[:regex]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Sorts the dead steps found in a hash by the line number in the file
|
39
|
+
def self.sort_dead_steps_in_file!(dead_steps_hash)
|
40
|
+
dead_steps_hash.each_key do |file|
|
41
|
+
dead_steps_hash[file].sort_by! { |row| row[/^\d+/].to_i }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the count of all possible dead steps in each file
|
46
|
+
def self.count_dead_steps(dead_steps_hash)
|
47
|
+
count = 0
|
48
|
+
dead_steps_hash.each_value do |dead_steps_in_file_list|
|
49
|
+
count += dead_steps_in_file_list.size
|
50
|
+
end
|
51
|
+
count
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/cuke_sniffer/feature.rb
CHANGED
@@ -6,9 +6,9 @@ module CukeSniffer
|
|
6
6
|
# Handles feature files and disassembles and evaluates
|
7
7
|
# its components.
|
8
8
|
# Extends CukeSniffer::FeatureRulesEvaluator
|
9
|
-
class Feature <
|
9
|
+
class Feature < FeatureRuleTarget
|
10
10
|
|
11
|
-
xml_accessor :scenarios, :as => [CukeSniffer::
|
11
|
+
xml_accessor :scenarios, :as => [CukeSniffer::FeatureRuleTarget], :in => "scenarios"
|
12
12
|
|
13
13
|
SCENARIO_TITLE_REGEX = /#{COMMENT_REGEX}#{SCENARIO_TITLE_STYLES}(?<name>.*)/ # :nodoc:
|
14
14
|
|
@@ -24,20 +24,17 @@ module CukeSniffer
|
|
24
24
|
# int: Total score of the feature and its scenarios
|
25
25
|
attr_accessor :total_score
|
26
26
|
|
27
|
+
# String array: A list of all the lines in a feature file
|
28
|
+
attr_accessor :feature_lines
|
29
|
+
|
27
30
|
# file_name must be in the format of "file_path\file_name.feature"
|
28
31
|
def initialize(file_name)
|
29
32
|
super(file_name)
|
30
33
|
@scenarios = []
|
31
34
|
@scenarios_score = 0
|
32
35
|
@total_score = 0
|
33
|
-
feature_lines =
|
34
|
-
|
35
|
-
rule = RULES[:empty_feature]
|
36
|
-
store_rule(rule)
|
37
|
-
else
|
38
|
-
split_feature(file_name, feature_lines)
|
39
|
-
evaluate_score
|
40
|
-
end
|
36
|
+
@feature_lines = IO.readlines(file_name)
|
37
|
+
split_feature(file_name, feature_lines) unless @feature_lines == []
|
41
38
|
end
|
42
39
|
|
43
40
|
def ==(comparison_object) # :nodoc:
|
@@ -45,16 +42,14 @@ module CukeSniffer
|
|
45
42
|
comparison_object.scenarios == scenarios
|
46
43
|
end
|
47
44
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
feature_file = File.open(file_name)
|
53
|
-
feature_file.each_line { |line| feature_lines << line }
|
54
|
-
feature_file.close
|
55
|
-
feature_lines
|
45
|
+
def update_score
|
46
|
+
@scenarios_score += @background.score unless @background.nil?
|
47
|
+
@scenarios.each { |scenario| @scenarios_score += scenario.score }
|
48
|
+
@total_score = @scenarios_score + @score
|
56
49
|
end
|
57
50
|
|
51
|
+
private
|
52
|
+
|
58
53
|
def split_feature(file_name, feature_lines)
|
59
54
|
index = 0
|
60
55
|
until feature_lines[index].match /Feature:\s*(?<name>.*)/
|
@@ -99,74 +94,11 @@ module CukeSniffer
|
|
99
94
|
|
100
95
|
def add_scenario_to_feature(code_block, index_of_title)
|
101
96
|
scenario = CukeSniffer::Scenario.new(index_of_title, code_block)
|
102
|
-
feature_applied_scenario_rules(scenario)
|
103
97
|
if scenario.type == "Background"
|
104
98
|
@background = scenario
|
105
99
|
else
|
106
100
|
@scenarios << scenario
|
107
101
|
end
|
108
102
|
end
|
109
|
-
|
110
|
-
def evaluate_score
|
111
|
-
super
|
112
|
-
rule_no_scenarios
|
113
|
-
rule_too_many_scenarios
|
114
|
-
rule_background_with_no_scenarios
|
115
|
-
rule_background_with_one_scenario
|
116
|
-
rule_scenario_same_tag
|
117
|
-
get_scenarios_score
|
118
|
-
@total_score = score + @scenarios_score
|
119
|
-
end
|
120
|
-
|
121
|
-
def feature_applied_scenario_rules(scenario)
|
122
|
-
rule_feature_same_tags(scenario)
|
123
|
-
end
|
124
|
-
|
125
|
-
def rule_feature_same_tags(scenario)
|
126
|
-
rule = RULES[:feature_same_tag]
|
127
|
-
tags.each do |tag|
|
128
|
-
scenario.store_rule(rule, rule[:phrase]) if scenario.tags.include?(tag)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
def rule_scenario_same_tag
|
133
|
-
rule = RULES[:scenario_same_tag]
|
134
|
-
unless scenarios.empty?
|
135
|
-
base_tag_list = scenarios.first.tags.clone
|
136
|
-
scenarios.each do |scenario|
|
137
|
-
base_tag_list.each do |tag|
|
138
|
-
base_tag_list.delete(tag) unless scenario.tags.include?(tag)
|
139
|
-
end
|
140
|
-
end
|
141
|
-
base_tag_list.count.times do
|
142
|
-
store_rule(rule, rule[:phrase])
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
def get_scenarios_score
|
148
|
-
@scenarios_score += @background.score unless @background.nil?
|
149
|
-
@scenarios.each { |scenario| @scenarios_score += scenario.score }
|
150
|
-
end
|
151
|
-
|
152
|
-
def rule_no_scenarios
|
153
|
-
rule = RULES[:no_scenarios]
|
154
|
-
store_rule(rule) if @scenarios.empty?
|
155
|
-
end
|
156
|
-
|
157
|
-
def rule_too_many_scenarios
|
158
|
-
rule = RULES[:too_many_scenarios]
|
159
|
-
store_rule(rule) if @scenarios.size >= rule[:max]
|
160
|
-
end
|
161
|
-
|
162
|
-
def rule_background_with_no_scenarios
|
163
|
-
rule = RULES[:background_with_no_scenarios]
|
164
|
-
store_rule(rule) if @scenarios.empty? and !@background.nil?
|
165
|
-
end
|
166
|
-
|
167
|
-
def rule_background_with_one_scenario
|
168
|
-
rule = RULES[:background_with_one_scenario]
|
169
|
-
store_rule(rule) if @scenarios.size == 1 and !@background.nil?
|
170
|
-
end
|
171
103
|
end
|
172
104
|
end
|
@@ -1,115 +1,56 @@
|
|
1
|
-
require 'cuke_sniffer/constants'
|
2
|
-
require 'cuke_sniffer/rule_config'
|
3
|
-
require 'cuke_sniffer/
|
4
|
-
|
5
|
-
module CukeSniffer
|
6
|
-
|
7
|
-
# Author:: Robert Cochran (mailto:cochrarj@miamioh.edu)
|
8
|
-
# Copyright:: Copyright (C) 2013 Robert Cochran
|
9
|
-
# License:: Distributes under the MIT License
|
10
|
-
# Parent class for Feature and Scenario objects
|
11
|
-
# holds shared attributes and rules.
|
12
|
-
# Extends CukeSniffer::
|
13
|
-
class
|
14
|
-
|
15
|
-
# string array: Contains all tags attached to a Feature or Scenario
|
16
|
-
attr_accessor :tags
|
17
|
-
|
18
|
-
# string: Name of the Feature or Scenario
|
19
|
-
attr_accessor :name
|
20
|
-
|
21
|
-
# Location must be in the format of "file_path\file_name.rb:line_number"
|
22
|
-
def initialize(location)
|
23
|
-
@name = ""
|
24
|
-
@tags = []
|
25
|
-
super(location)
|
26
|
-
end
|
27
|
-
|
28
|
-
def == (comparison_object) # :nodoc:
|
29
|
-
super(comparison_object) &&
|
30
|
-
comparison_object.name == name &&
|
31
|
-
comparison_object.tags == tags
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
def create_name(line, filter)
|
37
|
-
line.gsub!(/#{COMMENT_REGEX}#{filter}/, "")
|
38
|
-
line.strip!
|
39
|
-
@name += " " unless @name.empty? or line.empty?
|
40
|
-
@name += line
|
41
|
-
end
|
42
|
-
|
43
|
-
def update_tag_list(line)
|
44
|
-
comment_start = (line =~ /([^@\w]#)|(^#)/)
|
45
|
-
|
46
|
-
if comment_start
|
47
|
-
line[0...comment_start].split.each { |single_tag| @tags << single_tag }
|
48
|
-
@tags << line[comment_start..line.length].strip
|
49
|
-
else
|
50
|
-
line.split.each { |single_tag| @tags << single_tag }
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
rule_no_description(cls_name)
|
58
|
-
rule_numbers_in_name(cls_name)
|
59
|
-
rule_long_name(cls_name)
|
60
|
-
rule_commas_in_description(cls_name)
|
61
|
-
rule_comment_after_tag(cls_name)
|
62
|
-
rule_commented_tag(cls_name)
|
63
|
-
end
|
64
|
-
|
65
|
-
def rule_too_many_tags(type)
|
66
|
-
rule = RULES[:too_many_tags]
|
67
|
-
rule_phrase = rule[:phrase].gsub(/{.*}/, type)
|
68
|
-
store_rule(rule, rule_phrase) if tags.size >= rule[:max]
|
69
|
-
end
|
70
|
-
|
71
|
-
def rule_no_description(type)
|
72
|
-
rule = RULES[:no_description]
|
73
|
-
rule_phrase = rule[:phrase].gsub(/{.*}/, type)
|
74
|
-
store_rule(rule, rule_phrase) if name.empty?
|
75
|
-
end
|
76
|
-
|
77
|
-
def rule_numbers_in_name(type)
|
78
|
-
rule = RULES[:numbers_in_description]
|
79
|
-
rule_phrase = rule[:phrase].gsub(/{.*}/, type)
|
80
|
-
store_rule(rule, rule_phrase) if name =~ /\d/
|
81
|
-
end
|
82
|
-
|
83
|
-
def rule_long_name(type)
|
84
|
-
rule = RULES[:long_name]
|
85
|
-
rule_phrase = rule[:phrase].gsub(/{.*}/, type)
|
86
|
-
store_rule(rule, rule_phrase) if name.size >= rule[:max]
|
87
|
-
end
|
88
|
-
|
89
|
-
def rule_commas_in_description(type)
|
90
|
-
rule = RULES[:commas_in_description]
|
91
|
-
rule_phrase = rule[:phrase].gsub(/{.*}/, type)
|
92
|
-
store_rule(rule, rule_phrase) if name.include?(',')
|
93
|
-
end
|
94
|
-
|
95
|
-
def rule_comment_after_tag(type)
|
96
|
-
rule = RULES[:comment_after_tag]
|
97
|
-
|
98
|
-
last_comment_index = tags.rindex { |single_tag| is_comment?(single_tag) }
|
99
|
-
if last_comment_index
|
100
|
-
comment_after_tag = tags[0...last_comment_index].any? { |single_tag| !is_comment?(single_tag) }
|
101
|
-
rule_phrase = rule[:phrase].gsub(/{.*}/, type)
|
102
|
-
store_rule(rule, rule_phrase) if comment_after_tag
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def rule_commented_tag(type)
|
107
|
-
rule = RULES[:commented_tag]
|
108
|
-
tags.each do |tag|
|
109
|
-
store_rule(rule, rule[:phrase].gsub(/{.*}/, type)) if is_comment?(tag) && tag.match(/@\S*/)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
1
|
+
require 'cuke_sniffer/constants'
|
2
|
+
require 'cuke_sniffer/rule_config'
|
3
|
+
require 'cuke_sniffer/rule_target'
|
4
|
+
|
5
|
+
module CukeSniffer
|
6
|
+
|
7
|
+
# Author:: Robert Cochran (mailto:cochrarj@miamioh.edu)
|
8
|
+
# Copyright:: Copyright (C) 2013 Robert Cochran
|
9
|
+
# License:: Distributes under the MIT License
|
10
|
+
# Parent class for Feature and Scenario objects
|
11
|
+
# holds shared attributes and rules.
|
12
|
+
# Extends CukeSniffer::RuleTarget
|
13
|
+
class FeatureRuleTarget < RuleTarget
|
14
|
+
|
15
|
+
# string array: Contains all tags attached to a Feature or Scenario
|
16
|
+
attr_accessor :tags
|
17
|
+
|
18
|
+
# string: Name of the Feature or Scenario
|
19
|
+
attr_accessor :name
|
20
|
+
|
21
|
+
# Location must be in the format of "file_path\file_name.rb:line_number"
|
22
|
+
def initialize(location)
|
23
|
+
@name = ""
|
24
|
+
@tags = []
|
25
|
+
super(location)
|
26
|
+
end
|
27
|
+
|
28
|
+
def == (comparison_object) # :nodoc:
|
29
|
+
super(comparison_object) &&
|
30
|
+
comparison_object.name == name &&
|
31
|
+
comparison_object.tags == tags
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def create_name(line, filter)
|
37
|
+
line.gsub!(/#{COMMENT_REGEX}#{filter}/, "")
|
38
|
+
line.strip!
|
39
|
+
@name += " " unless @name.empty? or line.empty?
|
40
|
+
@name += line
|
41
|
+
end
|
42
|
+
|
43
|
+
def update_tag_list(line)
|
44
|
+
comment_start = (line =~ /([^@\w]#)|(^#)/)
|
45
|
+
|
46
|
+
if comment_start
|
47
|
+
line[0...comment_start].split.each { |single_tag| @tags << single_tag }
|
48
|
+
@tags << line[comment_start..line.length].strip
|
49
|
+
else
|
50
|
+
line.split.each { |single_tag| @tags << single_tag }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|