cucumber_analytics 0.0.9 → 1.0.0

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 (94) hide show
  1. checksums.yaml +15 -0
  2. data/History.rdoc +16 -0
  3. data/README.rdoc +5 -3
  4. data/Rakefile +7 -2
  5. data/cucumber_analytics.gemspec +7 -5
  6. data/features/analysis/step_collection.feature +44 -45
  7. data/features/modeling/background_modeling.feature +14 -144
  8. data/features/modeling/directory_modeling.feature +3 -2
  9. data/features/modeling/doc_string_modeling.feature +46 -0
  10. data/features/modeling/example_modeling.feature +13 -34
  11. data/features/modeling/feature_file_modeling.feature +3 -2
  12. data/features/modeling/feature_modeling.feature +18 -80
  13. data/features/modeling/outline_modeling.feature +25 -164
  14. data/features/modeling/scenario_modeling.feature +17 -144
  15. data/features/modeling/step_modeling.feature +68 -0
  16. data/features/modeling/table_modeling.feature +41 -0
  17. data/features/step_definitions/background_steps.rb +12 -11
  18. data/features/step_definitions/directory_steps.rb +6 -3
  19. data/features/step_definitions/doc_string_steps.rb +50 -0
  20. data/features/step_definitions/{file_steps.rb → feature_file_steps.rb} +8 -2
  21. data/features/step_definitions/feature_steps.rb +8 -4
  22. data/features/step_definitions/outline_steps.rb +12 -6
  23. data/features/step_definitions/setup_steps.rb +2 -2
  24. data/features/step_definitions/spec_steps.rb +6 -3
  25. data/features/step_definitions/step_steps.rb +91 -0
  26. data/features/step_definitions/table_steps.rb +10 -0
  27. data/features/step_definitions/test_steps.rb +6 -10
  28. data/features/step_definitions/world_steps.rb +28 -19
  29. data/features/support/env.rb +0 -2
  30. data/lib/cucumber_analytics/background.rb +16 -0
  31. data/lib/cucumber_analytics/containing.rb +18 -0
  32. data/lib/cucumber_analytics/directory.rb +83 -0
  33. data/lib/cucumber_analytics/doc_string.rb +55 -0
  34. data/lib/cucumber_analytics/example.rb +100 -0
  35. data/lib/cucumber_analytics/feature.rb +120 -0
  36. data/lib/cucumber_analytics/feature_element.rb +22 -40
  37. data/lib/cucumber_analytics/feature_file.rb +74 -0
  38. data/lib/cucumber_analytics/outline.rb +49 -0
  39. data/lib/cucumber_analytics/parsing.rb +30 -0
  40. data/lib/cucumber_analytics/scenario.rb +31 -0
  41. data/lib/cucumber_analytics/step.rb +142 -32
  42. data/lib/cucumber_analytics/table.rb +51 -0
  43. data/lib/cucumber_analytics/taggable.rb +35 -0
  44. data/lib/cucumber_analytics/test_element.rb +36 -91
  45. data/lib/cucumber_analytics/version.rb +1 -1
  46. data/lib/cucumber_analytics/world.rb +109 -153
  47. data/lib/cucumber_analytics.rb +12 -8
  48. data/spec/integration/background_integration_spec.rb +18 -0
  49. data/spec/integration/directory_integration_spec.rb +24 -0
  50. data/spec/{feature_spec.rb → integration/feature_file_integration_spec.rb} +5 -5
  51. data/spec/integration/feature_integration_spec.rb +86 -0
  52. data/spec/integration/outline_integration_spec.rb +22 -0
  53. data/spec/integration/scenario_integration_spec.rb +18 -0
  54. data/spec/integration/step_integration_spec.rb +116 -0
  55. data/spec/integration/world_integration_spec.rb +40 -0
  56. data/spec/spec_helper.rb +7 -3
  57. data/spec/unit/background_unit_spec.rb +22 -0
  58. data/spec/unit/bare_bones_unit_specs.rb +13 -0
  59. data/spec/unit/containing_element_unit_specs.rb +17 -0
  60. data/spec/unit/directory_unit_spec.rb +91 -0
  61. data/spec/unit/doc_string_unit_spec.rb +65 -0
  62. data/spec/unit/example_unit_spec.rb +171 -0
  63. data/spec/unit/feature_element_unit_spec.rb +19 -0
  64. data/spec/unit/feature_element_unit_specs.rb +39 -0
  65. data/spec/unit/feature_file_unit_spec.rb +82 -0
  66. data/spec/unit/feature_unit_spec.rb +81 -0
  67. data/spec/unit/nested_element_unit_specs.rb +24 -0
  68. data/spec/unit/outline_unit_spec.rb +56 -0
  69. data/spec/unit/parsing_unit_spec.rb +21 -0
  70. data/spec/unit/prepopulated_unit_specs.rb +13 -0
  71. data/spec/unit/scenario_unit_spec.rb +36 -0
  72. data/spec/unit/step_unit_spec.rb +231 -0
  73. data/spec/unit/table_unit_spec.rb +52 -0
  74. data/spec/unit/taggable_unit_spec.rb +63 -0
  75. data/spec/unit/tagged_element_unit_specs.rb +48 -0
  76. data/spec/unit/test_element_unit_spec.rb +40 -0
  77. data/spec/unit/test_element_unit_specs.rb +31 -0
  78. data/spec/unit/world_unit_spec.rb +167 -0
  79. metadata +106 -41
  80. data/lib/cucumber_analytics/logging.rb +0 -28
  81. data/lib/cucumber_analytics/outline_example.rb +0 -110
  82. data/lib/cucumber_analytics/parsed_background.rb +0 -45
  83. data/lib/cucumber_analytics/parsed_directory.rb +0 -78
  84. data/lib/cucumber_analytics/parsed_feature.rb +0 -97
  85. data/lib/cucumber_analytics/parsed_file.rb +0 -199
  86. data/lib/cucumber_analytics/parsed_scenario.rb +0 -67
  87. data/lib/cucumber_analytics/parsed_scenario_outline.rb +0 -122
  88. data/spec/background_spec.rb +0 -23
  89. data/spec/directory_spec.rb +0 -18
  90. data/spec/example_spec.rb +0 -37
  91. data/spec/file_spec.rb +0 -20
  92. data/spec/outline_spec.rb +0 -32
  93. data/spec/scenario_spec.rb +0 -33
  94. data/spec/step_spec.rb +0 -24
@@ -1,22 +1,67 @@
1
1
  module CucumberAnalytics
2
+
3
+ # A class modeling a Cucumber Feature.
4
+
2
5
  class Step
3
6
 
7
+ include Containing
8
+
9
+
10
+ # The step's keyword
11
+ attr_accessor :keyword
12
+
13
+ # The base text of the step
14
+ attr_accessor :base
15
+
16
+ # The step's passed block
17
+ attr_accessor :block
4
18
 
5
- attr_reader :keyword
6
- attr_reader :base
7
- attr_reader :block
19
+ # The parent object that contains *self*
8
20
  attr_accessor :parent_element
9
21
 
22
+ # The step's arguments
23
+ attr_accessor :arguments
24
+
25
+
26
+ # Creates a new Step object and, if *source* is provided, populates the
27
+ # object.
28
+ def initialize(source = nil)
29
+ @arguments = []
30
+
31
+ parsed_step = process_source(source)
32
+
33
+ build_step(parsed_step) if parsed_step
34
+ end
35
+
36
+ # Sets the delimiter that will be used by default when determining the
37
+ # boundaries of step arguments.
38
+ def delimiter=(new_delimiter)
39
+ self.left_delimiter = new_delimiter
40
+ self.right_delimiter = new_delimiter
41
+ end
42
+
43
+ # Returns the delimiter that is used to mark the beginning of a step
44
+ # argument.
45
+ def left_delimiter
46
+ @left_delimiter || World.left_delimiter
47
+ end
10
48
 
11
- # Creates a new Step object based on the passed string. If the optional
12
- # string array is provided, it becomes the block for the step.
13
- def initialize(step, block = nil)
14
- CucumberAnalytics::Logging.logger.info('Step#initialize')
15
- CucumberAnalytics::Logging.logger.debug("step: #{step}")
49
+ # Sets the left delimiter that will be used by default when determining
50
+ # step arguments.
51
+ def left_delimiter=(new_delimiter)
52
+ @left_delimiter = new_delimiter
53
+ end
54
+
55
+ # Returns the delimiter that is used to mark the end of a step
56
+ # argument.
57
+ def right_delimiter
58
+ @right_delimiter || World.right_delimiter
59
+ end
16
60
 
17
- @base = step.sub(/#{World::STEP_KEYWORD_PATTERN}/, '')
18
- @block = block
19
- @keyword = step.slice(/#{World::STEP_KEYWORD_PATTERN}/).strip
61
+ # Sets the right delimiter that will be used by default when determining
62
+ # step arguments.
63
+ def right_delimiter=(new_delimiter)
64
+ @right_delimiter = new_delimiter
20
65
  end
21
66
 
22
67
  # Returns true if the two steps have the same text, minus any keywords
@@ -28,16 +73,18 @@ module CucumberAnalytics
28
73
  left_step == right_step
29
74
  end
30
75
 
31
- # Returns the text of the step. Options can be set to selectively exclude
32
- # certain portions of the text. *left_delimiter* and *right_delimiter* are
33
- # used to determine which parts of the step are arguments.
76
+ # Deprecated
34
77
  #
35
- # a_step = CucumberAnalytics.new('Given *some* step with a block:', ['block line 1', 'block line 2'])
78
+ # Returns the entire text of the step. Options can be set to selectively
79
+ # exclude certain portions of the text. *left_delimiter* and *right_delimiter*
80
+ # are used to determine which parts of the step are arguments.
81
+ #
82
+ # a_step = CucumberAnalytics.new("Given *some* step with a block:\n|block line 1|\n|block line 2|")
36
83
  #
37
84
  # a_step.step_text
38
- # #=> ['Given *some* step with a block:', 'block line 1', 'block line 2']
85
+ # #=> ['Given *some* step with a block:', '|block line 1|', '|block line 2|']
39
86
  # a_step.step_text(:with_keywords => false)
40
- # #=> ['*some* step with a block:', 'block line 1', 'block line 2']
87
+ # #=> ['*some* step with a block:', '|block line 1|', '|block line 2|']
41
88
  # a_step.step_text(:with_arguments => false, :left_delimiter => '*', :right_delimiter => '*')
42
89
  # #=> ['Given ** step with a block:']
43
90
  # a_step.step_text(:with_keywords => false, :with_arguments => false, :left_delimiter => '-', :right_delimiter => '-'))
@@ -46,8 +93,8 @@ module CucumberAnalytics
46
93
  def step_text(options = {})
47
94
  options = {:with_keywords => true,
48
95
  :with_arguments => true,
49
- :left_delimiter => World.left_delimiter,
50
- :right_delimiter => World.right_delimiter}.merge(options)
96
+ :left_delimiter => self.left_delimiter,
97
+ :right_delimiter => self.right_delimiter}.merge(options)
51
98
 
52
99
  final_step = []
53
100
  step_text = ''
@@ -57,7 +104,7 @@ module CucumberAnalytics
57
104
  if options[:with_arguments]
58
105
  step_text += @base
59
106
  final_step << step_text
60
- final_step.concat @block if @block
107
+ final_step.concat(rebuild_block_text(@block)) if @block
61
108
  else
62
109
  step_text += stripped_step(@base, options[:left_delimiter], options[:right_delimiter])
63
110
  final_step << step_text
@@ -66,28 +113,91 @@ module CucumberAnalytics
66
113
  final_step
67
114
  end
68
115
 
116
+ # Populates the step's arguments based on the step's text and some method of
117
+ # determining which parts of the text are arguments. Methods include using
118
+ # a regular expression and using the step's delimiters.
119
+ def scan_arguments(*how)
120
+ if how.count == 1
121
+ pattern = how.first
122
+ else
123
+ left_delimiter = how[0] || self.left_delimiter
124
+ right_delimiter = how[1] || self.right_delimiter
125
+
126
+ return [] unless left_delimiter && right_delimiter
127
+
128
+ pattern = Regexp.new(Regexp.escape(left_delimiter) + '(.*?)' + Regexp.escape(right_delimiter))
129
+ end
130
+
131
+ @arguments = @base.scan(pattern).flatten
132
+ end
133
+
69
134
 
70
135
  private
71
136
 
72
137
 
138
+ def process_source(source)
139
+ case
140
+ when source.is_a?(String)
141
+ parse_step(source)
142
+ else
143
+ source
144
+ end
145
+ end
146
+
147
+ def parse_step(source_text)
148
+ base_file_string = "Feature: Fake feature to parse\nScenario:\n"
149
+ source_text = base_file_string + source_text
150
+
151
+ parsed_file = Parsing::parse_text(source_text)
152
+
153
+ parsed_file.first['elements'].first['steps'].first
154
+ end
155
+
156
+ def build_step(step)
157
+ populate_base(step)
158
+ populate_block(step)
159
+ populate_keyword(step)
160
+
161
+ scan_arguments
162
+ end
163
+
164
+ def populate_base(step)
165
+ @base = step['name']
166
+ end
167
+
168
+ def populate_block(step)
169
+ @block = build_block(step)
170
+ end
171
+
172
+ def populate_keyword(step)
173
+ @keyword = step['keyword'].strip
174
+ end
175
+
73
176
  # Returns the step string minus any arguments based on the given delimiters.
74
177
  def stripped_step(step, left_delimiter, right_delimiter)
75
- original_left = left_delimiter
76
- original_right = right_delimiter
77
-
78
- begin
79
- Regexp.new(left_delimiter)
80
- rescue RegexpError
81
- left_delimiter = '\\' + left_delimiter
178
+ unless left_delimiter.nil? || right_delimiter.nil?
179
+ pattern = Regexp.new(Regexp.escape(left_delimiter) + '.*?' + Regexp.escape(right_delimiter))
180
+ step = step.gsub(pattern, left_delimiter + right_delimiter)
82
181
  end
83
182
 
84
- begin
85
- Regexp.new(right_delimiter)
86
- rescue RegexpError
87
- right_delimiter = '\\' + right_delimiter
183
+ step
184
+ end
185
+
186
+ def build_block(step)
187
+ case
188
+ when step['rows']
189
+ @block = build_child_element(Table, step['rows'])
190
+ when step['doc_string']
191
+ @block = build_child_element(DocString, step['doc_string'])
192
+ else
193
+ @block = nil
88
194
  end
89
195
 
90
- step.gsub(Regexp.new("#{left_delimiter}.*?#{right_delimiter}"), original_left + original_right)
196
+ @block
197
+ end
198
+
199
+ def rebuild_block_text(blok)
200
+ blok.contents.collect { |row| "|#{row.join('|')}|" }
91
201
  end
92
202
 
93
203
  end
@@ -0,0 +1,51 @@
1
+ module CucumberAnalytics
2
+
3
+ # A class modeling the table of a Step.
4
+
5
+ class Table
6
+
7
+ # The parent object that contains *self*
8
+ attr_accessor :parent_element
9
+
10
+ # The contents of the table
11
+ attr_accessor :contents
12
+
13
+
14
+ # Creates a new Table object and, if *source* is provided, populates
15
+ # the object.
16
+ def initialize(source = nil)
17
+ @contents = []
18
+
19
+ parsed_table = process_source(source)
20
+
21
+ build_table(parsed_table) if parsed_table
22
+ end
23
+
24
+
25
+ private
26
+
27
+
28
+ def process_source(source)
29
+ case
30
+ when source.is_a?(String)
31
+ parse_table(source)
32
+ else
33
+ source
34
+ end
35
+ end
36
+
37
+ def parse_table(source_text)
38
+ base_file_string = "Feature:\nScenario:\n* step\n"
39
+ source_text = base_file_string + source_text
40
+
41
+ parsed_file = Parsing::parse_text(source_text)
42
+
43
+ parsed_file.first['elements'].first['steps'].first['rows']
44
+ end
45
+
46
+ def build_table(table)
47
+ @contents = table.collect { |row| row['cells'] }
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,35 @@
1
+ module CucumberAnalytics
2
+
3
+ # A mix-in module containing methods used by elements that can be tagged.
4
+
5
+ module Taggable
6
+
7
+ # The tags which are directly assigned to the element
8
+ attr_accessor :tags
9
+
10
+
11
+ # Returns the tags which are indirectly assigned to the element (i.e. they
12
+ # have been inherited from a parent element).
13
+ def applied_tags
14
+ @parent_element.respond_to?(:all_tags) ? @parent_element.all_tags : []
15
+ end
16
+
17
+ # Returns all of the tags which are applicable to the element.
18
+ def all_tags
19
+ applied_tags + @tags
20
+ end
21
+
22
+
23
+ private
24
+
25
+
26
+ def populate_element_tags(parsed_element)
27
+ if parsed_element['tags']
28
+ parsed_element['tags'].each do |tag|
29
+ @tags << tag['name']
30
+ end
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -1,123 +1,68 @@
1
1
  module CucumberAnalytics
2
+
3
+ # A class modeling an element that contains steps.
4
+
2
5
  class TestElement < FeatureElement
3
6
 
7
+ include Containing
4
8
 
5
- attr_reader :steps
6
9
 
10
+ # The steps contained by the TestElement
11
+ attr_accessor :steps
7
12
 
8
- # Creates a new TestElement object.
9
- def initialize(source_lines = nil)
10
- CucumberAnalytics::Logging.logger.info('TestElement#initialize')
11
13
 
14
+ # Creates a new TestElement object and, if *parsed_test_element* is provided,
15
+ # populates the object.
16
+ def initialize(parsed_test_element = nil)
12
17
  super
13
18
 
14
19
  @steps = []
20
+
21
+ build_test_element(parsed_test_element) if parsed_test_element
15
22
  end
16
23
 
17
- # Returns true if the two elements have the same steps, minus any keywords
18
- # and arguments, and false otherwise.
24
+ # Returns true if the two elements have equivalent steps and false otherwise.
19
25
  def ==(other_element)
20
26
  steps == other_element.steps
21
27
  end
22
28
 
23
-
24
- private
25
-
26
-
27
- def parse_test_element_steps(source_lines)
28
- CucumberAnalytics::Logging.logger.info('TestElement#parse_test_element_steps')
29
- CucumberAnalytics::Logging.logger.debug('source lines')
30
- source_lines.each do |line|
31
- CucumberAnalytics::Logging.logger.debug(line.chomp)
32
- end
33
-
34
- until source_lines.empty? or source_lines.first =~ /^\s*(?:@|Examples:)/
35
- line = source_lines.first
36
- block = nil
37
-
38
- case
39
- when line =~ /^\s*"""/
40
- block = extract_doc_block(source_lines)
41
-
42
- found_step = Step.new(@steps.last.keyword + ' ' + @steps.last.base, block)
43
- found_step.parent_element = self
44
-
45
- @steps[@steps.size - 1] = found_step
46
- when line =~ /^\s*\|/
47
- block = extract_table_block(source_lines)
48
-
49
- found_step = Step.new(@steps.last.keyword + ' ' + @steps.last.base, block)
50
- found_step.parent_element = self
51
-
52
- @steps[@steps.size - 1] = found_step
53
- else
54
- unless World.ignored_line?(line)
55
- found_step = Step.new(line.strip)
56
- found_step.parent_element = self
57
-
58
- @steps << found_step
59
- end
60
-
61
- source_lines.shift
62
- end
63
- end
29
+ # Returns the immediate child elements of the element.
30
+ def contains
31
+ @steps
64
32
  end
65
33
 
66
- def extract_doc_block(source_lines)
67
- CucumberAnalytics::Logging.logger.info('TestElement#extract_doc_block')
68
- CucumberAnalytics::Logging.logger.debug('source lines')
69
- source_lines.each do |line|
70
- CucumberAnalytics::Logging.logger.debug(line.chomp)
71
- end
72
-
73
- step_block = []
74
34
 
75
- line = source_lines.first
76
- leading_whitespace = line[/^\s*/]
77
-
78
- step_block << line.strip
79
- source_lines.shift
80
-
81
- line = source_lines.first
82
- until line =~ /^\s*"""/
35
+ private
83
36
 
84
- leading_whitespace.length.times do
85
- line.slice!(0, 1) if line[0] =~ /\s/
86
- end
87
37
 
88
- step_block << line.chomp
89
- source_lines.shift
90
- line = source_lines.first
38
+ def process_source(source)
39
+ case
40
+ when source.is_a?(String)
41
+ parse_test_element(source)
42
+ else
43
+ source
91
44
  end
92
-
93
- step_block << line.strip
94
- source_lines.shift
95
-
96
- step_block
97
45
  end
98
46
 
99
- def extract_table_block(source_lines)
100
- CucumberAnalytics::Logging.logger.info('TestElement#extract_table_block')
101
- CucumberAnalytics::Logging.logger.debug('source lines')
102
- source_lines.each do |line|
103
- CucumberAnalytics::Logging.logger.debug(line.chomp)
104
- end
47
+ def parse_test_element(source_text)
48
+ base_file_string = "Feature: Fake feature to parse\n"
49
+ source_text = base_file_string + source_text
105
50
 
106
- step_block = []
51
+ parsed_file = Parsing::parse_text(source_text)
107
52
 
108
- line = source_lines.first
53
+ parsed_file.first['elements'].first
54
+ end
109
55
 
110
- step_block << line.strip
111
- source_lines.shift
56
+ def build_test_element(parsed_test_element)
57
+ populate_test_element_steps(parsed_test_element)
58
+ end
112
59
 
113
- line = source_lines.first
114
- while line =~ /^\s*\|/
115
- step_block << line.strip
116
- source_lines.shift
117
- line = source_lines.first
60
+ def populate_test_element_steps(parsed_test_element)
61
+ if parsed_test_element['steps']
62
+ parsed_test_element['steps'].each do |step|
63
+ @steps << build_child_element(Step, step)
64
+ end
118
65
  end
119
-
120
- step_block
121
66
  end
122
67
 
123
68
  end
@@ -1,3 +1,3 @@
1
1
  module CucumberAnalytics
2
- VERSION = "0.0.9"
2
+ VERSION = '1.0.0'
3
3
  end