cuke_modeler 3.2.0 → 3.7.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +48 -3
- data/LICENSE.txt +1 -1
- data/README.md +7 -7
- data/cuke_modeler.gemspec +35 -16
- data/lib/cuke_modeler.rb +1 -2
- data/lib/cuke_modeler/adapters/gherkin_10_adapter.rb +2 -1
- data/lib/cuke_modeler/adapters/gherkin_11_adapter.rb +2 -1
- data/lib/cuke_modeler/adapters/gherkin_12_adapter.rb +2 -1
- data/lib/cuke_modeler/adapters/gherkin_13_adapter.rb +2 -1
- data/lib/cuke_modeler/adapters/gherkin_14_adapter.rb +2 -1
- data/lib/cuke_modeler/adapters/gherkin_15_adapter.rb +13 -0
- data/lib/cuke_modeler/adapters/gherkin_16_adapter.rb +13 -0
- data/lib/cuke_modeler/adapters/gherkin_17_adapter.rb +13 -0
- data/lib/cuke_modeler/adapters/gherkin_9_adapter.rb +261 -243
- data/lib/cuke_modeler/containing.rb +47 -90
- data/lib/cuke_modeler/described.rb +40 -2
- data/lib/cuke_modeler/models/background.rb +11 -12
- data/lib/cuke_modeler/models/cell.rb +13 -8
- data/lib/cuke_modeler/models/comment.rb +5 -6
- data/lib/cuke_modeler/models/directory.rb +13 -18
- data/lib/cuke_modeler/models/doc_string.rb +10 -8
- data/lib/cuke_modeler/models/example.rb +63 -46
- data/lib/cuke_modeler/models/feature.rb +23 -17
- data/lib/cuke_modeler/models/feature_file.rb +5 -8
- data/lib/cuke_modeler/models/model.rb +2 -3
- data/lib/cuke_modeler/models/outline.rb +19 -15
- data/lib/cuke_modeler/models/row.rb +15 -8
- data/lib/cuke_modeler/models/rule.rb +11 -10
- data/lib/cuke_modeler/models/scenario.rb +17 -13
- data/lib/cuke_modeler/models/step.rb +40 -19
- data/lib/cuke_modeler/models/table.rb +9 -7
- data/lib/cuke_modeler/models/tag.rb +9 -6
- data/lib/cuke_modeler/named.rb +5 -2
- data/lib/cuke_modeler/nested.rb +22 -19
- data/lib/cuke_modeler/parsed.rb +8 -1
- data/lib/cuke_modeler/parsing.rb +38 -29
- data/lib/cuke_modeler/sourceable.rb +8 -1
- data/lib/cuke_modeler/stepped.rb +8 -1
- data/lib/cuke_modeler/taggable.rb +9 -2
- data/lib/cuke_modeler/version.rb +1 -1
- metadata +91 -36
@@ -1,11 +1,28 @@
|
|
1
|
+
# It's the module that has functionality for every possible model type. So, yes, it's long.
|
2
|
+
# rubocop:disable Metrics/ModuleLength
|
3
|
+
|
1
4
|
module CukeModeler
|
2
5
|
|
3
6
|
# NOT A PART OF THE PUBLIC API
|
4
7
|
# A mix-in module containing methods used by models that contain other models.
|
5
|
-
|
6
8
|
module Containing
|
7
9
|
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
# Executes the given code block with this model and every model that is a child of this model. Exact
|
13
|
+
# order of model tree traversal is not guaranteed beyond the first model traversed, which will be the
|
14
|
+
# model that called this method. If no block is provided, an `Enumerator` is returned instead.
|
15
|
+
def each(&block)
|
16
|
+
if block
|
17
|
+
block.call(self)
|
18
|
+
children.each { |child| child.each(&block) }
|
19
|
+
else
|
20
|
+
to_enum(:each)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
8
24
|
# Executes the given code block with every model that is a child of this model.
|
25
|
+
# DEPRECATED: use `Enumerable` module methods instead
|
9
26
|
def each_descendant(&block)
|
10
27
|
children.each do |child_model|
|
11
28
|
block.call(child_model)
|
@@ -14,6 +31,7 @@ module CukeModeler
|
|
14
31
|
end
|
15
32
|
|
16
33
|
# Executes the given code block with this model and every model that is a child of this model.
|
34
|
+
# DEPRECATED: use `Enumerable` module methods instead
|
17
35
|
def each_model(&block)
|
18
36
|
block.call(self)
|
19
37
|
|
@@ -74,14 +92,11 @@ module CukeModeler
|
|
74
92
|
end
|
75
93
|
|
76
94
|
def populate_block(step_object, parsed_step_data)
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
else
|
83
|
-
step_object.block = nil
|
84
|
-
end
|
95
|
+
step_object.block = if parsed_step_data['table']
|
96
|
+
build_child_model(Table, parsed_step_data['table'])
|
97
|
+
elsif parsed_step_data['doc_string']
|
98
|
+
build_child_model(DocString, parsed_step_data['doc_string'])
|
99
|
+
end
|
85
100
|
end
|
86
101
|
|
87
102
|
def populate_table(table_object, parsed_table_data)
|
@@ -154,7 +169,9 @@ module CukeModeler
|
|
154
169
|
populate_parsing_data(feature_file_object, processed_feature_file_data)
|
155
170
|
feature_file_object.path = processed_feature_file_data['path']
|
156
171
|
|
157
|
-
|
172
|
+
if processed_feature_file_data['feature']
|
173
|
+
feature_file_object.feature = build_child_model(Feature, processed_feature_file_data['feature'])
|
174
|
+
end
|
158
175
|
|
159
176
|
processed_feature_file_data['comments'].each do |comment_data|
|
160
177
|
feature_file_object.comments << build_child_model(Comment, comment_data)
|
@@ -202,7 +219,7 @@ module CukeModeler
|
|
202
219
|
end
|
203
220
|
|
204
221
|
def populate_content_type(doc_string_model, parsed_doc_string_data)
|
205
|
-
doc_string_model.content_type = parsed_doc_string_data['content_type']
|
222
|
+
doc_string_model.content_type = parsed_doc_string_data['content_type']
|
206
223
|
end
|
207
224
|
|
208
225
|
def populate_content(doc_string_model, parsed_doc_string_data)
|
@@ -221,89 +238,29 @@ module CukeModeler
|
|
221
238
|
end
|
222
239
|
end
|
223
240
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
if elements
|
228
|
-
elements.each do |element|
|
229
|
-
case element['type']
|
230
|
-
when 'Scenario', 'scenario'
|
231
|
-
model.tests << build_child_model(Scenario, element)
|
232
|
-
when 'ScenarioOutline', 'scenario_outline'
|
233
|
-
model.tests << build_child_model(Outline, element)
|
234
|
-
when 'Background', 'background'
|
235
|
-
model.background = build_child_model(Background, element)
|
236
|
-
when 'Rule'
|
237
|
-
model.rules << build_child_model(Rule, element)
|
238
|
-
else
|
239
|
-
raise(ArgumentError, "Unknown element type: #{element['type']}")
|
240
|
-
end
|
241
|
-
end
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
def populate_parsing_data(model, parsed_model_data)
|
246
|
-
model.parsing_data = parsed_model_data['cuke_modeler_parsing_data']
|
247
|
-
end
|
248
|
-
|
249
|
-
def populate_source_line(model, parsed_model_data)
|
250
|
-
model.source_line = parsed_model_data['line']
|
251
|
-
end
|
252
|
-
|
253
|
-
def populate_name(model, parsed_model_data)
|
254
|
-
model.name = parsed_model_data['name']
|
255
|
-
end
|
256
|
-
|
257
|
-
def populate_description(model, parsed_model_data)
|
258
|
-
model.description = trimmed_description(parsed_model_data['description'])
|
259
|
-
end
|
260
|
-
|
261
|
-
def trimmed_description(description)
|
262
|
-
description = description.split("\n")
|
263
|
-
|
264
|
-
trim_leading_blank_lines(description)
|
265
|
-
trim_trailing_blank_lines(description)
|
266
|
-
trim_leading_spaces(description)
|
267
|
-
trim_trailing_spaces(description)
|
268
|
-
|
269
|
-
description.join("\n")
|
270
|
-
end
|
271
|
-
|
272
|
-
def trim_leading_blank_lines(description)
|
273
|
-
description.replace(description.drop_while { |line| line !~ /\S/ })
|
274
|
-
end
|
275
|
-
|
276
|
-
def trim_trailing_blank_lines(description)
|
277
|
-
# Nothing to do. Already done by the parser but leaving this here in case that changes in future versions.
|
278
|
-
end
|
279
|
-
|
280
|
-
def trim_leading_spaces(description)
|
281
|
-
non_blank_lines = description.select { |line| line =~ /\S/ }
|
282
|
-
|
283
|
-
fewest_spaces = non_blank_lines.collect { |line| line[/^\s*/].length }.min || 0
|
241
|
+
# It's not getting better any time soon
|
242
|
+
# rubocop:disable Metrics/AbcSize
|
284
243
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
def populate_steps(model, parsed_model_data)
|
301
|
-
if parsed_model_data['steps']
|
302
|
-
parsed_model_data['steps'].each do |step_data|
|
303
|
-
model.steps << build_child_model(Step, step_data)
|
244
|
+
def populate_children(model, parsed_feature_data)
|
245
|
+
return unless parsed_feature_data['elements']
|
246
|
+
|
247
|
+
parsed_feature_data['elements'].each do |element|
|
248
|
+
case element['type']
|
249
|
+
when 'Scenario', 'scenario'
|
250
|
+
model.tests << build_child_model(Scenario, element)
|
251
|
+
when 'ScenarioOutline', 'scenario_outline'
|
252
|
+
model.tests << build_child_model(Outline, element)
|
253
|
+
when 'Background', 'background'
|
254
|
+
model.background = build_child_model(Background, element)
|
255
|
+
when 'Rule'
|
256
|
+
model.rules << build_child_model(Rule, element)
|
257
|
+
else
|
258
|
+
raise(ArgumentError, "Unknown element type: #{element['type']}")
|
304
259
|
end
|
305
260
|
end
|
306
261
|
end
|
262
|
+
# rubocop:enable Metrics/AbcSize
|
307
263
|
|
308
264
|
end
|
309
265
|
end
|
266
|
+
# rubocop:enable Metrics/ModuleLength
|
@@ -2,7 +2,6 @@ module CukeModeler
|
|
2
2
|
|
3
3
|
# NOT A PART OF THE PUBLIC API
|
4
4
|
# A mix-in module containing methods used by models that represent an element that has a description.
|
5
|
-
|
6
5
|
module Described
|
7
6
|
|
8
7
|
# The description of the element
|
@@ -19,11 +18,50 @@ module CukeModeler
|
|
19
18
|
description_lines = description.split("\n")
|
20
19
|
|
21
20
|
text << "\n" if description_lines.first =~ /\S/
|
22
|
-
text << description_lines.
|
21
|
+
text << description_lines.join("\n")
|
23
22
|
end
|
24
23
|
|
25
24
|
text
|
26
25
|
end
|
27
26
|
|
27
|
+
def no_description_to_output?
|
28
|
+
description.nil? || description.empty?
|
29
|
+
end
|
30
|
+
|
31
|
+
def populate_description(model, parsed_model_data)
|
32
|
+
model.description = trimmed_description(parsed_model_data['description'])
|
33
|
+
end
|
34
|
+
|
35
|
+
def trimmed_description(description)
|
36
|
+
description = description.split("\n")
|
37
|
+
|
38
|
+
trim_leading_blank_lines(description)
|
39
|
+
trim_trailing_blank_lines(description)
|
40
|
+
trim_leading_spaces(description)
|
41
|
+
trim_trailing_spaces(description)
|
42
|
+
|
43
|
+
description.join("\n")
|
44
|
+
end
|
45
|
+
|
46
|
+
def trim_leading_blank_lines(description)
|
47
|
+
description.replace(description.drop_while { |line| line !~ /\S/ })
|
48
|
+
end
|
49
|
+
|
50
|
+
def trim_trailing_blank_lines(_description)
|
51
|
+
# Nothing to do. Already done by the parser but leaving this here in case that changes in future versions.
|
52
|
+
end
|
53
|
+
|
54
|
+
def trim_leading_spaces(description)
|
55
|
+
non_blank_lines = description.select { |line| line =~ /\S/ }
|
56
|
+
|
57
|
+
fewest_spaces = non_blank_lines.collect { |line| line[/^\s*/].length }.min || 0
|
58
|
+
|
59
|
+
description.each { |line| line.slice!(0..(fewest_spaces - 1)) } if fewest_spaces.positive?
|
60
|
+
end
|
61
|
+
|
62
|
+
def trim_trailing_spaces(description)
|
63
|
+
description.map!(&:rstrip)
|
64
|
+
end
|
65
|
+
|
28
66
|
end
|
29
67
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module CukeModeler
|
2
2
|
|
3
3
|
# A class modeling a feature's background.
|
4
|
-
|
5
4
|
class Background < Model
|
6
5
|
|
7
6
|
include Parsing
|
@@ -23,17 +22,17 @@ module CukeModeler
|
|
23
22
|
|
24
23
|
super(source_text)
|
25
24
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
return unless source_text
|
26
|
+
|
27
|
+
parsed_background_data = parse_source(source_text)
|
28
|
+
populate_background(self, parsed_background_data)
|
30
29
|
end
|
31
30
|
|
32
31
|
# Returns *true* if the two models have equivalent steps and *false* otherwise.
|
33
|
-
def ==(
|
34
|
-
return false unless
|
32
|
+
def ==(other)
|
33
|
+
return false unless other.respond_to?(:steps)
|
35
34
|
|
36
|
-
steps ==
|
35
|
+
steps == other.steps
|
37
36
|
end
|
38
37
|
|
39
38
|
# Returns the model objects that belong to this model.
|
@@ -47,9 +46,9 @@ module CukeModeler
|
|
47
46
|
text = ''
|
48
47
|
|
49
48
|
text << "#{@keyword}:#{name_output_string}"
|
50
|
-
text << "\n"
|
51
|
-
text << "\n" unless
|
52
|
-
text << "\n"
|
49
|
+
text << "\n#{description_output_string}" unless no_description_to_output?
|
50
|
+
text << "\n" unless steps.empty? || no_description_to_output?
|
51
|
+
text << "\n#{steps_output_string}" unless steps.empty?
|
53
52
|
|
54
53
|
text
|
55
54
|
end
|
@@ -62,7 +61,7 @@ module CukeModeler
|
|
62
61
|
base_file_string = "# language: #{Parsing.dialect}\n#{dialect_feature_keyword}: Fake feature to parse\n"
|
63
62
|
source_text = base_file_string + source_text
|
64
63
|
|
65
|
-
parsed_file = Parsing
|
64
|
+
parsed_file = Parsing.parse_text(source_text, 'cuke_modeler_stand_alone_background.feature')
|
66
65
|
|
67
66
|
parsed_file['feature']['elements'].first
|
68
67
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module CukeModeler
|
2
2
|
|
3
3
|
# A class modeling a single cell of a row.
|
4
|
-
|
5
4
|
class Cell < Model
|
6
5
|
|
7
6
|
include Sourceable
|
@@ -18,10 +17,10 @@ module CukeModeler
|
|
18
17
|
def initialize(source_text = nil)
|
19
18
|
super(source_text)
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
return unless source_text
|
21
|
+
|
22
|
+
parsed_cell_data = parse_source(source_text)
|
23
|
+
populate_cell(self, parsed_cell_data)
|
25
24
|
end
|
26
25
|
|
27
26
|
# Returns a string representation of this model. For a cell model,
|
@@ -35,14 +34,20 @@ module CukeModeler
|
|
35
34
|
private
|
36
35
|
|
37
36
|
|
37
|
+
# It's only considered complex because of how deeply nested cells are in the tree. It's not REALLY complex.
|
38
|
+
# rubocop:disable Metrics/AbcSize
|
38
39
|
def parse_source(source_text)
|
39
|
-
base_file_string = "# language: #{Parsing.dialect}
|
40
|
-
|
40
|
+
base_file_string = "# language: #{Parsing.dialect}
|
41
|
+
#{dialect_feature_keyword}: Fake feature to parse
|
42
|
+
#{dialect_scenario_keyword}:
|
43
|
+
#{dialect_step_keyword} fake step\n"
|
44
|
+
source_text = "#{base_file_string}|#{source_text}|"
|
41
45
|
|
42
|
-
parsed_file = Parsing
|
46
|
+
parsed_file = Parsing.parse_text(source_text, 'cuke_modeler_stand_alone_cell.feature')
|
43
47
|
|
44
48
|
parsed_file['feature']['elements'].first['steps'].first['table']['rows'].first['cells'].first
|
45
49
|
end
|
50
|
+
# rubocop:enable Metrics/AbcSize
|
46
51
|
|
47
52
|
end
|
48
53
|
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module CukeModeler
|
2
2
|
|
3
3
|
# A class modeling a comment in a feature file.
|
4
|
-
|
5
4
|
class Comment < Model
|
6
5
|
|
7
6
|
include Parsing
|
@@ -18,10 +17,10 @@ module CukeModeler
|
|
18
17
|
def initialize(source_text = nil)
|
19
18
|
super(source_text)
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
return unless source_text
|
21
|
+
|
22
|
+
parsed_comment_data = parse_source(source_text)
|
23
|
+
populate_comment(self, parsed_comment_data)
|
25
24
|
end
|
26
25
|
|
27
26
|
# Returns a string representation of this model. For a comment model,
|
@@ -38,7 +37,7 @@ module CukeModeler
|
|
38
37
|
base_file_string = "\n#{dialect_feature_keyword}: Fake feature to parse"
|
39
38
|
source_text = "# language: #{Parsing.dialect}\n" + source_text + base_file_string
|
40
39
|
|
41
|
-
parsed_file = Parsing
|
40
|
+
parsed_file = Parsing.parse_text(source_text, 'cuke_modeler_stand_alone_comment.feature')
|
42
41
|
|
43
42
|
parsed_file['comments'].last
|
44
43
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module CukeModeler
|
2
2
|
|
3
3
|
# A class modeling a directory in a Cucumber suite.
|
4
|
-
|
5
4
|
class Directory < Model
|
6
5
|
|
7
6
|
|
@@ -24,12 +23,11 @@ module CukeModeler
|
|
24
23
|
|
25
24
|
super(directory_path)
|
26
25
|
|
27
|
-
|
28
|
-
|
26
|
+
return unless directory_path
|
27
|
+
raise(ArgumentError, "Unknown directory: #{directory_path.inspect}") unless File.exist?(directory_path)
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
end
|
29
|
+
processed_directory_data = process_directory(directory_path)
|
30
|
+
populate_directory(self, processed_directory_data)
|
33
31
|
end
|
34
32
|
|
35
33
|
# Returns the name of the modeled directory.
|
@@ -53,10 +51,9 @@ module CukeModeler
|
|
53
51
|
|
54
52
|
|
55
53
|
def process_directory(directory_path)
|
56
|
-
directory_data = {'path'
|
57
|
-
|
58
|
-
|
59
|
-
}
|
54
|
+
directory_data = { 'path' => directory_path,
|
55
|
+
'directories' => [],
|
56
|
+
'feature_files' => [] }
|
60
57
|
|
61
58
|
entries = Dir.entries(directory_path)
|
62
59
|
entries.delete '.'
|
@@ -65,13 +62,11 @@ module CukeModeler
|
|
65
62
|
entries.each do |entry|
|
66
63
|
entry = "#{directory_path}/#{entry}"
|
67
64
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
else
|
74
|
-
# Ignore anything that isn't a directory or a feature file
|
65
|
+
# Ignore anything that isn't a directory or a feature file
|
66
|
+
if File.directory?(entry)
|
67
|
+
directory_data['directories'] << process_directory(entry)
|
68
|
+
elsif entry =~ /\.feature$/
|
69
|
+
directory_data['feature_files'] << process_feature_file(entry)
|
75
70
|
end
|
76
71
|
end
|
77
72
|
|
@@ -82,7 +77,7 @@ module CukeModeler
|
|
82
77
|
def process_feature_file(file_path)
|
83
78
|
source_text = IO.read(file_path)
|
84
79
|
|
85
|
-
feature_file_data = Parsing
|
80
|
+
feature_file_data = Parsing.parse_text(source_text, file_path)
|
86
81
|
feature_file_data = feature_file_data.merge({ 'path' => file_path })
|
87
82
|
|
88
83
|
feature_file_data
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module CukeModeler
|
2
2
|
|
3
3
|
# A class modeling a step's doc string.
|
4
|
-
|
5
4
|
class DocString < Model
|
6
5
|
|
7
6
|
include Parsing
|
@@ -21,10 +20,10 @@ module CukeModeler
|
|
21
20
|
def initialize(source_text = nil)
|
22
21
|
super(source_text)
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
return unless source_text
|
24
|
+
|
25
|
+
parsed_doc_string_data = parse_source(source_text)
|
26
|
+
populate_docstring(self, parsed_doc_string_data)
|
28
27
|
end
|
29
28
|
|
30
29
|
# Returns a string representation of this model. For a doc string model,
|
@@ -40,10 +39,13 @@ module CukeModeler
|
|
40
39
|
|
41
40
|
|
42
41
|
def parse_source(source_text)
|
43
|
-
base_file_string = "# language: #{Parsing.dialect}
|
42
|
+
base_file_string = "# language: #{Parsing.dialect}
|
43
|
+
#{dialect_feature_keyword}:
|
44
|
+
#{dialect_scenario_keyword}:
|
45
|
+
#{dialect_step_keyword} step\n"
|
44
46
|
source_text = base_file_string + source_text
|
45
47
|
|
46
|
-
parsed_file = Parsing
|
48
|
+
parsed_file = Parsing.parse_text(source_text, 'cuke_modeler_stand_alone_doc_string.feature')
|
47
49
|
|
48
50
|
parsed_file['feature']['elements'].first['steps'].first['doc_string']
|
49
51
|
end
|
@@ -53,7 +55,7 @@ module CukeModeler
|
|
53
55
|
end
|
54
56
|
|
55
57
|
def content_output_string
|
56
|
-
|
58
|
+
content.nil? || content.empty? ? '' : content.gsub('"""', '\"\"\"') + "\n"
|
57
59
|
end
|
58
60
|
|
59
61
|
end
|