cuke_sniffer 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|