cuke_modeler 3.27.0 → 3.28.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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +579 -572
  3. data/LICENSE.txt +22 -22
  4. data/README.md +135 -135
  5. data/cuke_modeler.gemspec +59 -59
  6. data/lib/cuke_modeler/adapters/gherkin_10_adapter.rb +13 -13
  7. data/lib/cuke_modeler/adapters/gherkin_11_adapter.rb +13 -13
  8. data/lib/cuke_modeler/adapters/gherkin_12_adapter.rb +13 -13
  9. data/lib/cuke_modeler/adapters/gherkin_13_adapter.rb +13 -13
  10. data/lib/cuke_modeler/adapters/gherkin_14_adapter.rb +13 -13
  11. data/lib/cuke_modeler/adapters/gherkin_15_adapter.rb +13 -13
  12. data/lib/cuke_modeler/adapters/gherkin_16_adapter.rb +13 -13
  13. data/lib/cuke_modeler/adapters/gherkin_17_adapter.rb +13 -13
  14. data/lib/cuke_modeler/adapters/gherkin_18_adapter.rb +27 -27
  15. data/lib/cuke_modeler/adapters/gherkin_19_adapter.rb +64 -64
  16. data/lib/cuke_modeler/adapters/gherkin_20_adapter.rb +359 -359
  17. data/lib/cuke_modeler/adapters/gherkin_21_adapter.rb +13 -13
  18. data/lib/cuke_modeler/adapters/gherkin_22_adapter.rb +13 -13
  19. data/lib/cuke_modeler/adapters/gherkin_23_adapter.rb +13 -13
  20. data/lib/cuke_modeler/adapters/gherkin_24_adapter.rb +13 -13
  21. data/lib/cuke_modeler/adapters/gherkin_25_adapter.rb +13 -13
  22. data/lib/cuke_modeler/adapters/gherkin_26_adapter.rb +13 -13
  23. data/lib/cuke_modeler/adapters/gherkin_27_adapter.rb +13 -13
  24. data/lib/cuke_modeler/adapters/gherkin_28_adapter.rb +13 -13
  25. data/lib/cuke_modeler/adapters/gherkin_29_adapter.rb +13 -13
  26. data/lib/cuke_modeler/adapters/gherkin_30_adapter.rb +13 -13
  27. data/lib/cuke_modeler/adapters/gherkin_31_adapter.rb +13 -13
  28. data/lib/cuke_modeler/adapters/gherkin_32_adapter.rb +13 -13
  29. data/lib/cuke_modeler/adapters/gherkin_33_adapter.rb +13 -13
  30. data/lib/cuke_modeler/adapters/gherkin_34_adapter.rb +13 -13
  31. data/lib/cuke_modeler/adapters/gherkin_35_adapter.rb +13 -13
  32. data/lib/cuke_modeler/adapters/gherkin_36_adapter.rb +13 -13
  33. data/lib/cuke_modeler/adapters/gherkin_37_adapter.rb +13 -13
  34. data/lib/cuke_modeler/adapters/gherkin_38_adapter.rb +13 -0
  35. data/lib/cuke_modeler/adapters/gherkin_9_adapter.rb +375 -375
  36. data/lib/cuke_modeler/adapters/gherkin_base_adapter.rb +17 -17
  37. data/lib/cuke_modeler/containing.rb +105 -105
  38. data/lib/cuke_modeler/described.rb +71 -71
  39. data/lib/cuke_modeler/models/background.rb +122 -122
  40. data/lib/cuke_modeler/models/cell.rb +88 -88
  41. data/lib/cuke_modeler/models/comment.rb +82 -82
  42. data/lib/cuke_modeler/models/directory.rb +143 -143
  43. data/lib/cuke_modeler/models/doc_string.rb +104 -104
  44. data/lib/cuke_modeler/models/example.rb +274 -274
  45. data/lib/cuke_modeler/models/feature.rb +200 -200
  46. data/lib/cuke_modeler/models/feature_file.rb +116 -116
  47. data/lib/cuke_modeler/models/model.rb +87 -87
  48. data/lib/cuke_modeler/models/outline.rb +145 -145
  49. data/lib/cuke_modeler/models/row.rb +104 -104
  50. data/lib/cuke_modeler/models/rule.rb +162 -162
  51. data/lib/cuke_modeler/models/scenario.rb +128 -128
  52. data/lib/cuke_modeler/models/step.rb +178 -178
  53. data/lib/cuke_modeler/models/table.rb +117 -117
  54. data/lib/cuke_modeler/models/tag.rb +75 -75
  55. data/lib/cuke_modeler/named.rb +26 -26
  56. data/lib/cuke_modeler/nested.rb +61 -61
  57. data/lib/cuke_modeler/parsed.rb +24 -24
  58. data/lib/cuke_modeler/parsing.rb +208 -208
  59. data/lib/cuke_modeler/sourceable.rb +29 -29
  60. data/lib/cuke_modeler/stepped.rb +34 -34
  61. data/lib/cuke_modeler/taggable.rb +57 -57
  62. data/lib/cuke_modeler/version.rb +4 -4
  63. data/lib/cuke_modeler.rb +32 -32
  64. data/testing/cucumber/features/analysis/step_comparison.feature +25 -25
  65. data/testing/cucumber/features/analysis/test_comparison.feature +35 -35
  66. data/testing/cucumber/features/modeling/background_modeling.feature +105 -105
  67. data/testing/cucumber/features/modeling/background_output.feature +60 -60
  68. data/testing/cucumber/features/modeling/cell_modeling.feature +68 -68
  69. data/testing/cucumber/features/modeling/cell_output.feature +36 -36
  70. data/testing/cucumber/features/modeling/comment_modeling.feature +62 -62
  71. data/testing/cucumber/features/modeling/comment_output.feature +40 -40
  72. data/testing/cucumber/features/modeling/directory_modeling.feature +62 -62
  73. data/testing/cucumber/features/modeling/directory_output.feature +33 -33
  74. data/testing/cucumber/features/modeling/doc_string_modeling.feature +85 -85
  75. data/testing/cucumber/features/modeling/doc_string_output.feature +50 -50
  76. data/testing/cucumber/features/modeling/example_modeling.feature +155 -155
  77. data/testing/cucumber/features/modeling/example_output.feature +57 -57
  78. data/testing/cucumber/features/modeling/feature_file_modeling.feature +49 -49
  79. data/testing/cucumber/features/modeling/feature_file_output.feature +33 -33
  80. data/testing/cucumber/features/modeling/feature_modeling.feature +149 -149
  81. data/testing/cucumber/features/modeling/feature_output.feature +144 -144
  82. data/testing/cucumber/features/modeling/model_output.feature +61 -61
  83. data/testing/cucumber/features/modeling/model_structure.feature +42 -42
  84. data/testing/cucumber/features/modeling/outline_modeling.feature +175 -175
  85. data/testing/cucumber/features/modeling/outline_output.feature +87 -87
  86. data/testing/cucumber/features/modeling/row_modeling.feature +70 -70
  87. data/testing/cucumber/features/modeling/row_output.feature +40 -40
  88. data/testing/cucumber/features/modeling/rule_modeling.feature +171 -171
  89. data/testing/cucumber/features/modeling/rule_output.feature +136 -136
  90. data/testing/cucumber/features/modeling/scenario_modeling.feature +146 -146
  91. data/testing/cucumber/features/modeling/scenario_output.feature +63 -63
  92. data/testing/cucumber/features/modeling/step_modeling.feature +105 -105
  93. data/testing/cucumber/features/modeling/step_output.feature +47 -47
  94. data/testing/cucumber/features/modeling/table_modeling.feature +73 -73
  95. data/testing/cucumber/features/modeling/table_output.feature +42 -42
  96. data/testing/cucumber/features/modeling/tag_modeling.feature +62 -62
  97. data/testing/cucumber/features/modeling/tag_output.feature +40 -40
  98. metadata +6 -8
@@ -1,359 +1,359 @@
1
- require_relative 'gherkin_base_adapter'
2
-
3
- # Some things just aren't going to get better due to the inherent complexity of the AST
4
- # rubocop:disable Metrics/ClassLength, Metrics/AbcSize, Metrics/MethodLength
5
-
6
- module CukeModeler
7
-
8
- # @api private
9
- #
10
- # An adapter that can convert the output of version 20.x of the *cucumber-gherkin* gem into input that is consumable
11
- # by this gem. Internal helper class.
12
- class Gherkin20Adapter < GherkinBaseAdapter
13
-
14
- # Adapts the given AST into the shape that this gem expects
15
- def adapt(ast)
16
- adapted_ast = {}
17
-
18
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
19
- save_original_data(adapted_ast, ast)
20
- clear_child_elements(adapted_ast, [[:feature], [:comments]])
21
-
22
- adapted_ast['comments'] = adapt_comments(ast)
23
- adapted_ast['feature'] = adapt_feature(ast.feature)
24
-
25
- adapted_ast
26
- end
27
-
28
- # Adapts the AST sub-tree that is rooted at the given feature node.
29
- def adapt_feature(feature_ast)
30
- return nil unless feature_ast
31
-
32
- adapted_feature = {}
33
-
34
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
35
- save_original_data(adapted_feature, feature_ast)
36
- clear_child_elements(adapted_feature, [[:tags],
37
- [:children]])
38
-
39
- adapted_feature['language'] = feature_ast.language
40
- adapted_feature['keyword'] = feature_ast.keyword
41
- adapted_feature['name'] = feature_ast.name
42
- adapted_feature['description'] = feature_ast.description
43
- adapted_feature['line'] = feature_ast.location.line
44
- adapted_feature['column'] = feature_ast.location.column
45
-
46
- adapted_feature['elements'] = adapt_child_elements(feature_ast)
47
- adapted_feature['tags'] = adapt_tags(feature_ast)
48
-
49
- adapted_feature
50
- end
51
-
52
- # Adapts the AST sub-tree that is rooted at the given background node.
53
- def adapt_background(background_ast)
54
- adapted_background = {}
55
-
56
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
57
- save_original_data(adapted_background, background_ast)
58
- clear_child_elements(adapted_background, [[:background, :steps]])
59
-
60
- adapted_background['type'] = 'Background'
61
- adapted_background['keyword'] = background_ast.background.keyword
62
- adapted_background['name'] = background_ast.background.name
63
- adapted_background['description'] = background_ast.background.description
64
- adapted_background['line'] = background_ast.background.location.line
65
- adapted_background['column'] = background_ast.background.location.column
66
-
67
- adapted_background['steps'] = adapt_steps(background_ast.background)
68
-
69
- adapted_background
70
- end
71
-
72
- # Adapts the AST sub-tree that is rooted at the given rule node.
73
- def adapt_rule(rule_ast)
74
- adapted_rule = {}
75
-
76
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
77
- save_original_data(adapted_rule, rule_ast)
78
- clear_child_elements(adapted_rule, [[:rule, :tags],
79
- [:rule, :children]])
80
-
81
- adapted_rule['type'] = 'Rule'
82
- adapted_rule['keyword'] = rule_ast.rule.keyword
83
- adapted_rule['name'] = rule_ast.rule.name
84
- adapted_rule['description'] = rule_ast.rule.description
85
- adapted_rule['line'] = rule_ast.rule.location.line
86
- adapted_rule['column'] = rule_ast.rule.location.column
87
-
88
- adapted_rule['elements'] = adapt_child_elements(rule_ast.rule)
89
- adapted_rule['tags'] = adapt_tags(rule_ast.rule)
90
-
91
- adapted_rule
92
- end
93
-
94
- # Adapts the AST sub-tree that is rooted at the given scenario node.
95
- def adapt_scenario(test_ast)
96
- adapted_scenario = {}
97
-
98
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
99
- save_original_data(adapted_scenario, test_ast)
100
- clear_child_elements(adapted_scenario, [[:scenario, :tags],
101
- [:scenario, :steps]])
102
-
103
- adapted_scenario['type'] = 'Scenario'
104
- adapted_scenario['keyword'] = test_ast.scenario.keyword
105
- adapted_scenario['name'] = test_ast.scenario.name
106
- adapted_scenario['description'] = test_ast.scenario.description
107
- adapted_scenario['line'] = test_ast.scenario.location.line
108
- adapted_scenario['column'] = test_ast.scenario.location.column
109
-
110
- adapted_scenario['tags'] = adapt_tags(test_ast.scenario)
111
- adapted_scenario['steps'] = adapt_steps(test_ast.scenario)
112
-
113
- adapted_scenario
114
- end
115
-
116
- # Adapts the AST sub-tree that is rooted at the given outline node.
117
- def adapt_outline(test_ast)
118
- adapted_outline = {}
119
-
120
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
121
- save_original_data(adapted_outline, test_ast)
122
- clear_child_elements(adapted_outline, [[:scenario, :tags],
123
- [:scenario, :steps],
124
- [:scenario, :examples]])
125
-
126
- adapted_outline['type'] = 'ScenarioOutline'
127
- adapted_outline['keyword'] = test_ast.scenario.keyword
128
- adapted_outline['name'] = test_ast.scenario.name
129
- adapted_outline['description'] = test_ast.scenario.description
130
- adapted_outline['line'] = test_ast.scenario.location.line
131
- adapted_outline['column'] = test_ast.scenario.location.column
132
-
133
- adapted_outline['tags'] = adapt_tags(test_ast.scenario)
134
- adapted_outline['steps'] = adapt_steps(test_ast.scenario)
135
- adapted_outline['examples'] = adapt_examples(test_ast.scenario)
136
-
137
- adapted_outline
138
- end
139
-
140
- # Adapts the AST sub-tree that is rooted at the given example node.
141
- def adapt_example(example_ast)
142
- adapted_example = {}
143
-
144
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
145
- save_original_data(adapted_example, example_ast)
146
- clear_child_elements(adapted_example, [[:tags],
147
- [:table_header],
148
- [:table_body]])
149
-
150
- adapted_example['keyword'] = example_ast.keyword
151
- adapted_example['name'] = example_ast.name
152
- adapted_example['line'] = example_ast.location.line
153
- adapted_example['column'] = example_ast.location.column
154
- adapted_example['description'] = example_ast.description
155
-
156
- adapted_example['rows'] = []
157
- adapted_example['rows'] << adapt_table_row(example_ast.table_header) if example_ast.table_header
158
-
159
- example_ast.table_body&.each do |row|
160
- adapted_example['rows'] << adapt_table_row(row)
161
- end
162
-
163
- adapted_example['tags'] = adapt_tags(example_ast)
164
-
165
- adapted_example
166
- end
167
-
168
- # Adapts the AST sub-tree that is rooted at the given tag node.
169
- def adapt_tag(tag_ast)
170
- adapted_tag = {}
171
-
172
- # Saving off the original data
173
- save_original_data(adapted_tag, tag_ast)
174
-
175
- adapted_tag['name'] = tag_ast.name
176
- adapted_tag['line'] = tag_ast.location.line
177
- adapted_tag['column'] = tag_ast.location.column
178
-
179
- adapted_tag
180
- end
181
-
182
- # Adapts the AST sub-tree that is rooted at the given comment node.
183
- def adapt_comment(comment_ast)
184
- adapted_comment = {}
185
-
186
- # Saving off the original data
187
- save_original_data(adapted_comment, comment_ast)
188
-
189
- adapted_comment['text'] = comment_ast.text
190
- adapted_comment['line'] = comment_ast.location.line
191
- adapted_comment['column'] = comment_ast.location.column
192
-
193
- adapted_comment
194
- end
195
-
196
- # Adapts the AST sub-tree that is rooted at the given step node.
197
- def adapt_step(step_ast)
198
- adapted_step = {}
199
-
200
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
201
- save_original_data(adapted_step, step_ast)
202
- clear_child_elements(adapted_step, [[:data_table],
203
- [:doc_string]])
204
-
205
- adapted_step['keyword'] = step_ast.keyword
206
- adapted_step['name'] = step_ast.text
207
- adapted_step['line'] = step_ast.location.line
208
- adapted_step['column'] = step_ast.location.column
209
-
210
- if step_ast.doc_string
211
- adapted_step['doc_string'] = adapt_doc_string(step_ast.doc_string)
212
- elsif step_ast.data_table
213
- adapted_step['table'] = adapt_step_table(step_ast.data_table)
214
- end
215
-
216
- adapted_step
217
- end
218
-
219
- # Adapts the AST sub-tree that is rooted at the given doc string node.
220
- def adapt_doc_string(doc_string_ast)
221
- adapted_doc_string = {}
222
-
223
- # Saving off the original data
224
- save_original_data(adapted_doc_string, doc_string_ast)
225
-
226
- adapted_doc_string['value'] = doc_string_ast.content
227
- adapted_doc_string['content_type'] = doc_string_ast.media_type
228
- adapted_doc_string['line'] = doc_string_ast.location.line
229
- adapted_doc_string['column'] = doc_string_ast.location.column
230
-
231
- adapted_doc_string
232
- end
233
-
234
- # Adapts the AST sub-tree that is rooted at the given table node.
235
- def adapt_step_table(step_table_ast)
236
- adapted_step_table = {}
237
-
238
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
239
- save_original_data(adapted_step_table, step_table_ast)
240
- clear_child_elements(adapted_step_table, [[:rows]])
241
-
242
- adapted_step_table['rows'] = []
243
- step_table_ast.rows.each do |row|
244
- adapted_step_table['rows'] << adapt_table_row(row)
245
- end
246
- adapted_step_table['line'] = step_table_ast.location.line
247
- adapted_step_table['column'] = step_table_ast.location.column
248
-
249
- adapted_step_table
250
- end
251
-
252
- # Adapts the AST sub-tree that is rooted at the given row node.
253
- def adapt_table_row(table_row_ast)
254
- adapted_table_row = {}
255
-
256
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
257
- save_original_data(adapted_table_row, table_row_ast)
258
- clear_child_elements(adapted_table_row, [[:cells]])
259
-
260
- adapted_table_row['line'] = table_row_ast.location.line
261
- adapted_table_row['column'] = table_row_ast.location.column
262
-
263
- adapted_table_row['cells'] = []
264
- table_row_ast.cells.each do |row|
265
- adapted_table_row['cells'] << adapt_table_cell(row)
266
- end
267
-
268
- adapted_table_row
269
- end
270
-
271
- # Adapts the AST sub-tree that is rooted at the given cell node.
272
- def adapt_table_cell(cell_ast)
273
- adapted_cell = {}
274
-
275
- # Saving off the original data
276
- save_original_data(adapted_cell, cell_ast)
277
-
278
- adapted_cell['value'] = cell_ast.value
279
- adapted_cell['line'] = cell_ast.location.line
280
- adapted_cell['column'] = cell_ast.location.column
281
-
282
- adapted_cell
283
- end
284
-
285
-
286
- private
287
-
288
-
289
- def adapt_comments(file_ast)
290
- file_ast.comments.map { |comment| adapt_comment(comment) }
291
- end
292
-
293
- def adapt_tags(element_ast)
294
- element_ast.tags.map { |tag| adapt_tag(tag) }
295
- end
296
-
297
- def adapt_steps(element_ast)
298
- element_ast.steps.map { |step| adapt_step(step) }
299
- end
300
-
301
- def adapt_examples(element_ast)
302
- element_ast.examples.map { |example| adapt_example(example) }
303
- end
304
-
305
- def adapt_child_elements(element_ast)
306
- element_ast.children.map do |child_element|
307
- if child_element.background
308
- adapt_background(child_element)
309
- elsif child_element.respond_to?(:rule) && child_element.rule
310
- adapt_rule(child_element)
311
- else
312
- adapt_test(child_element)
313
- end
314
- end
315
- end
316
-
317
- def adapt_test(test_ast)
318
- if test_has_examples?(test_ast) || test_uses_outline_keyword?(test_ast)
319
- adapt_outline(test_ast)
320
- else
321
- adapt_scenario(test_ast)
322
- end
323
- end
324
-
325
- def clear_child_elements(ast, child_paths)
326
- # rubocop:disable Security/Eval -- This is not blind data
327
- # rubocop:disable Style/DocumentDynamicEvalDefinition -- Nice idea but bad detection ability
328
- child_paths.each do |traversal_path|
329
- # Don't add any properties to the object if it didn't already have them
330
- # e.g. ast['cuke_modeler_parsing_data'].background.steps
331
- # e.g. ast['cuke_modeler_parsing_data'].data_table
332
- next unless eval("ast['cuke_modeler_parsing_data'].#{traversal_path.join('.')}", binding, __FILE__, __LINE__)
333
-
334
-
335
- # Determine the path to the property and wipe it
336
- property_path = traversal_path[0..-2].join('.')
337
- property_path = property_path.empty? ? '' : ".#{property_path}"
338
-
339
- # e.g. ast['cuke_modeler_parsing_data'].scenario.instance_variable_set("@steps", nil)
340
- # e.g. ast['cuke_modeler_parsing_data'].instance_variable_set("@tags", nil)
341
- eval("ast['cuke_modeler_parsing_data']#{property_path}.instance_variable_set(\"@#{traversal_path.last}\", nil)", binding, __FILE__, __LINE__) # rubocop:disable Layout/LineLength
342
- end
343
- # rubocop:enable Security/Eval, Style/DocumentDynamicEvalDefinition
344
- end
345
-
346
- def test_has_examples?(ast_node)
347
- ast_node.scenario.examples.any?
348
- end
349
-
350
- def test_uses_outline_keyword?(test_ast)
351
- Parsing.dialects[Parsing.dialect]['scenarioOutline'].include?(test_ast.scenario.keyword)
352
- end
353
-
354
- end
355
-
356
- private_constant :Gherkin20Adapter
357
- end
358
-
359
- # rubocop:enable Metrics/ClassLength, Metrics/AbcSize, Metrics/MethodLength
1
+ require_relative 'gherkin_base_adapter'
2
+
3
+ # Some things just aren't going to get better due to the inherent complexity of the AST
4
+ # rubocop:disable Metrics/ClassLength, Metrics/AbcSize, Metrics/MethodLength
5
+
6
+ module CukeModeler
7
+
8
+ # @api private
9
+ #
10
+ # An adapter that can convert the output of version 20.x of the *cucumber-gherkin* gem into input that is consumable
11
+ # by this gem. Internal helper class.
12
+ class Gherkin20Adapter < GherkinBaseAdapter
13
+
14
+ # Adapts the given AST into the shape that this gem expects
15
+ def adapt(ast)
16
+ adapted_ast = {}
17
+
18
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
19
+ save_original_data(adapted_ast, ast)
20
+ clear_child_elements(adapted_ast, [[:feature], [:comments]])
21
+
22
+ adapted_ast['comments'] = adapt_comments(ast)
23
+ adapted_ast['feature'] = adapt_feature(ast.feature)
24
+
25
+ adapted_ast
26
+ end
27
+
28
+ # Adapts the AST sub-tree that is rooted at the given feature node.
29
+ def adapt_feature(feature_ast)
30
+ return nil unless feature_ast
31
+
32
+ adapted_feature = {}
33
+
34
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
35
+ save_original_data(adapted_feature, feature_ast)
36
+ clear_child_elements(adapted_feature, [[:tags],
37
+ [:children]])
38
+
39
+ adapted_feature['language'] = feature_ast.language
40
+ adapted_feature['keyword'] = feature_ast.keyword
41
+ adapted_feature['name'] = feature_ast.name
42
+ adapted_feature['description'] = feature_ast.description
43
+ adapted_feature['line'] = feature_ast.location.line
44
+ adapted_feature['column'] = feature_ast.location.column
45
+
46
+ adapted_feature['elements'] = adapt_child_elements(feature_ast)
47
+ adapted_feature['tags'] = adapt_tags(feature_ast)
48
+
49
+ adapted_feature
50
+ end
51
+
52
+ # Adapts the AST sub-tree that is rooted at the given background node.
53
+ def adapt_background(background_ast)
54
+ adapted_background = {}
55
+
56
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
57
+ save_original_data(adapted_background, background_ast)
58
+ clear_child_elements(adapted_background, [[:background, :steps]])
59
+
60
+ adapted_background['type'] = 'Background'
61
+ adapted_background['keyword'] = background_ast.background.keyword
62
+ adapted_background['name'] = background_ast.background.name
63
+ adapted_background['description'] = background_ast.background.description
64
+ adapted_background['line'] = background_ast.background.location.line
65
+ adapted_background['column'] = background_ast.background.location.column
66
+
67
+ adapted_background['steps'] = adapt_steps(background_ast.background)
68
+
69
+ adapted_background
70
+ end
71
+
72
+ # Adapts the AST sub-tree that is rooted at the given rule node.
73
+ def adapt_rule(rule_ast)
74
+ adapted_rule = {}
75
+
76
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
77
+ save_original_data(adapted_rule, rule_ast)
78
+ clear_child_elements(adapted_rule, [[:rule, :tags],
79
+ [:rule, :children]])
80
+
81
+ adapted_rule['type'] = 'Rule'
82
+ adapted_rule['keyword'] = rule_ast.rule.keyword
83
+ adapted_rule['name'] = rule_ast.rule.name
84
+ adapted_rule['description'] = rule_ast.rule.description
85
+ adapted_rule['line'] = rule_ast.rule.location.line
86
+ adapted_rule['column'] = rule_ast.rule.location.column
87
+
88
+ adapted_rule['elements'] = adapt_child_elements(rule_ast.rule)
89
+ adapted_rule['tags'] = adapt_tags(rule_ast.rule)
90
+
91
+ adapted_rule
92
+ end
93
+
94
+ # Adapts the AST sub-tree that is rooted at the given scenario node.
95
+ def adapt_scenario(test_ast)
96
+ adapted_scenario = {}
97
+
98
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
99
+ save_original_data(adapted_scenario, test_ast)
100
+ clear_child_elements(adapted_scenario, [[:scenario, :tags],
101
+ [:scenario, :steps]])
102
+
103
+ adapted_scenario['type'] = 'Scenario'
104
+ adapted_scenario['keyword'] = test_ast.scenario.keyword
105
+ adapted_scenario['name'] = test_ast.scenario.name
106
+ adapted_scenario['description'] = test_ast.scenario.description
107
+ adapted_scenario['line'] = test_ast.scenario.location.line
108
+ adapted_scenario['column'] = test_ast.scenario.location.column
109
+
110
+ adapted_scenario['tags'] = adapt_tags(test_ast.scenario)
111
+ adapted_scenario['steps'] = adapt_steps(test_ast.scenario)
112
+
113
+ adapted_scenario
114
+ end
115
+
116
+ # Adapts the AST sub-tree that is rooted at the given outline node.
117
+ def adapt_outline(test_ast)
118
+ adapted_outline = {}
119
+
120
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
121
+ save_original_data(adapted_outline, test_ast)
122
+ clear_child_elements(adapted_outline, [[:scenario, :tags],
123
+ [:scenario, :steps],
124
+ [:scenario, :examples]])
125
+
126
+ adapted_outline['type'] = 'ScenarioOutline'
127
+ adapted_outline['keyword'] = test_ast.scenario.keyword
128
+ adapted_outline['name'] = test_ast.scenario.name
129
+ adapted_outline['description'] = test_ast.scenario.description
130
+ adapted_outline['line'] = test_ast.scenario.location.line
131
+ adapted_outline['column'] = test_ast.scenario.location.column
132
+
133
+ adapted_outline['tags'] = adapt_tags(test_ast.scenario)
134
+ adapted_outline['steps'] = adapt_steps(test_ast.scenario)
135
+ adapted_outline['examples'] = adapt_examples(test_ast.scenario)
136
+
137
+ adapted_outline
138
+ end
139
+
140
+ # Adapts the AST sub-tree that is rooted at the given example node.
141
+ def adapt_example(example_ast)
142
+ adapted_example = {}
143
+
144
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
145
+ save_original_data(adapted_example, example_ast)
146
+ clear_child_elements(adapted_example, [[:tags],
147
+ [:table_header],
148
+ [:table_body]])
149
+
150
+ adapted_example['keyword'] = example_ast.keyword
151
+ adapted_example['name'] = example_ast.name
152
+ adapted_example['line'] = example_ast.location.line
153
+ adapted_example['column'] = example_ast.location.column
154
+ adapted_example['description'] = example_ast.description
155
+
156
+ adapted_example['rows'] = []
157
+ adapted_example['rows'] << adapt_table_row(example_ast.table_header) if example_ast.table_header
158
+
159
+ example_ast.table_body&.each do |row|
160
+ adapted_example['rows'] << adapt_table_row(row)
161
+ end
162
+
163
+ adapted_example['tags'] = adapt_tags(example_ast)
164
+
165
+ adapted_example
166
+ end
167
+
168
+ # Adapts the AST sub-tree that is rooted at the given tag node.
169
+ def adapt_tag(tag_ast)
170
+ adapted_tag = {}
171
+
172
+ # Saving off the original data
173
+ save_original_data(adapted_tag, tag_ast)
174
+
175
+ adapted_tag['name'] = tag_ast.name
176
+ adapted_tag['line'] = tag_ast.location.line
177
+ adapted_tag['column'] = tag_ast.location.column
178
+
179
+ adapted_tag
180
+ end
181
+
182
+ # Adapts the AST sub-tree that is rooted at the given comment node.
183
+ def adapt_comment(comment_ast)
184
+ adapted_comment = {}
185
+
186
+ # Saving off the original data
187
+ save_original_data(adapted_comment, comment_ast)
188
+
189
+ adapted_comment['text'] = comment_ast.text
190
+ adapted_comment['line'] = comment_ast.location.line
191
+ adapted_comment['column'] = comment_ast.location.column
192
+
193
+ adapted_comment
194
+ end
195
+
196
+ # Adapts the AST sub-tree that is rooted at the given step node.
197
+ def adapt_step(step_ast)
198
+ adapted_step = {}
199
+
200
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
201
+ save_original_data(adapted_step, step_ast)
202
+ clear_child_elements(adapted_step, [[:data_table],
203
+ [:doc_string]])
204
+
205
+ adapted_step['keyword'] = step_ast.keyword
206
+ adapted_step['name'] = step_ast.text
207
+ adapted_step['line'] = step_ast.location.line
208
+ adapted_step['column'] = step_ast.location.column
209
+
210
+ if step_ast.doc_string
211
+ adapted_step['doc_string'] = adapt_doc_string(step_ast.doc_string)
212
+ elsif step_ast.data_table
213
+ adapted_step['table'] = adapt_step_table(step_ast.data_table)
214
+ end
215
+
216
+ adapted_step
217
+ end
218
+
219
+ # Adapts the AST sub-tree that is rooted at the given doc string node.
220
+ def adapt_doc_string(doc_string_ast)
221
+ adapted_doc_string = {}
222
+
223
+ # Saving off the original data
224
+ save_original_data(adapted_doc_string, doc_string_ast)
225
+
226
+ adapted_doc_string['value'] = doc_string_ast.content
227
+ adapted_doc_string['content_type'] = doc_string_ast.media_type
228
+ adapted_doc_string['line'] = doc_string_ast.location.line
229
+ adapted_doc_string['column'] = doc_string_ast.location.column
230
+
231
+ adapted_doc_string
232
+ end
233
+
234
+ # Adapts the AST sub-tree that is rooted at the given table node.
235
+ def adapt_step_table(step_table_ast)
236
+ adapted_step_table = {}
237
+
238
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
239
+ save_original_data(adapted_step_table, step_table_ast)
240
+ clear_child_elements(adapted_step_table, [[:rows]])
241
+
242
+ adapted_step_table['rows'] = []
243
+ step_table_ast.rows.each do |row|
244
+ adapted_step_table['rows'] << adapt_table_row(row)
245
+ end
246
+ adapted_step_table['line'] = step_table_ast.location.line
247
+ adapted_step_table['column'] = step_table_ast.location.column
248
+
249
+ adapted_step_table
250
+ end
251
+
252
+ # Adapts the AST sub-tree that is rooted at the given row node.
253
+ def adapt_table_row(table_row_ast)
254
+ adapted_table_row = {}
255
+
256
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
257
+ save_original_data(adapted_table_row, table_row_ast)
258
+ clear_child_elements(adapted_table_row, [[:cells]])
259
+
260
+ adapted_table_row['line'] = table_row_ast.location.line
261
+ adapted_table_row['column'] = table_row_ast.location.column
262
+
263
+ adapted_table_row['cells'] = []
264
+ table_row_ast.cells.each do |row|
265
+ adapted_table_row['cells'] << adapt_table_cell(row)
266
+ end
267
+
268
+ adapted_table_row
269
+ end
270
+
271
+ # Adapts the AST sub-tree that is rooted at the given cell node.
272
+ def adapt_table_cell(cell_ast)
273
+ adapted_cell = {}
274
+
275
+ # Saving off the original data
276
+ save_original_data(adapted_cell, cell_ast)
277
+
278
+ adapted_cell['value'] = cell_ast.value
279
+ adapted_cell['line'] = cell_ast.location.line
280
+ adapted_cell['column'] = cell_ast.location.column
281
+
282
+ adapted_cell
283
+ end
284
+
285
+
286
+ private
287
+
288
+
289
+ def adapt_comments(file_ast)
290
+ file_ast.comments.map { |comment| adapt_comment(comment) }
291
+ end
292
+
293
+ def adapt_tags(element_ast)
294
+ element_ast.tags.map { |tag| adapt_tag(tag) }
295
+ end
296
+
297
+ def adapt_steps(element_ast)
298
+ element_ast.steps.map { |step| adapt_step(step) }
299
+ end
300
+
301
+ def adapt_examples(element_ast)
302
+ element_ast.examples.map { |example| adapt_example(example) }
303
+ end
304
+
305
+ def adapt_child_elements(element_ast)
306
+ element_ast.children.map do |child_element|
307
+ if child_element.background
308
+ adapt_background(child_element)
309
+ elsif child_element.respond_to?(:rule) && child_element.rule
310
+ adapt_rule(child_element)
311
+ else
312
+ adapt_test(child_element)
313
+ end
314
+ end
315
+ end
316
+
317
+ def adapt_test(test_ast)
318
+ if test_has_examples?(test_ast) || test_uses_outline_keyword?(test_ast)
319
+ adapt_outline(test_ast)
320
+ else
321
+ adapt_scenario(test_ast)
322
+ end
323
+ end
324
+
325
+ def clear_child_elements(ast, child_paths)
326
+ # rubocop:disable Security/Eval -- This is not blind data
327
+ # rubocop:disable Style/DocumentDynamicEvalDefinition -- Nice idea but bad detection ability
328
+ child_paths.each do |traversal_path|
329
+ # Don't add any properties to the object if it didn't already have them
330
+ # e.g. ast['cuke_modeler_parsing_data'].background.steps
331
+ # e.g. ast['cuke_modeler_parsing_data'].data_table
332
+ next unless eval("ast['cuke_modeler_parsing_data'].#{traversal_path.join('.')}", binding, __FILE__, __LINE__)
333
+
334
+
335
+ # Determine the path to the property and wipe it
336
+ property_path = traversal_path[0..-2].join('.')
337
+ property_path = property_path.empty? ? '' : ".#{property_path}"
338
+
339
+ # e.g. ast['cuke_modeler_parsing_data'].scenario.instance_variable_set("@steps", nil)
340
+ # e.g. ast['cuke_modeler_parsing_data'].instance_variable_set("@tags", nil)
341
+ eval("ast['cuke_modeler_parsing_data']#{property_path}.instance_variable_set(\"@#{traversal_path.last}\", nil)", binding, __FILE__, __LINE__) # rubocop:disable Layout/LineLength
342
+ end
343
+ # rubocop:enable Security/Eval, Style/DocumentDynamicEvalDefinition
344
+ end
345
+
346
+ def test_has_examples?(ast_node)
347
+ ast_node.scenario.examples.any?
348
+ end
349
+
350
+ def test_uses_outline_keyword?(test_ast)
351
+ Parsing.dialects[Parsing.dialect]['scenarioOutline'].include?(test_ast.scenario.keyword)
352
+ end
353
+
354
+ end
355
+
356
+ private_constant :Gherkin20Adapter
357
+ end
358
+
359
+ # rubocop:enable Metrics/ClassLength, Metrics/AbcSize, Metrics/MethodLength