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,375 +1,375 @@
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 9.x of the *cucumber-gherkin* gem into input that is consumable
11
- # by this gem. Internal helper class.
12
- class Gherkin9Adapter < 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],
21
- [:comments]])
22
-
23
- adapted_ast['comments'] = adapt_comments(ast)
24
- adapted_ast['feature'] = adapt_feature(ast[:feature])
25
-
26
- adapted_ast
27
- end
28
-
29
- # Adapts the AST sub-tree that is rooted at the given feature node.
30
- def adapt_feature(feature_ast)
31
- return nil unless feature_ast
32
-
33
- adapted_feature = {}
34
-
35
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
36
- save_original_data(adapted_feature, feature_ast)
37
- clear_child_elements(adapted_feature, [[:tags],
38
- [:children]])
39
-
40
- adapted_feature['language'] = feature_ast[:language]
41
- adapted_feature['keyword'] = feature_ast[:keyword]
42
- adapted_feature['name'] = feature_ast[:name]
43
- adapted_feature['description'] = feature_ast[:description] || ''
44
- adapted_feature['line'] = feature_ast[:location][:line]
45
- adapted_feature['column'] = feature_ast[:location][:column]
46
-
47
- adapted_feature['elements'] = adapt_child_elements(feature_ast)
48
- adapted_feature['tags'] = adapt_tags(feature_ast)
49
-
50
- adapted_feature
51
- end
52
-
53
- # Adapts the AST sub-tree that is rooted at the given background node.
54
- def adapt_background(background_ast)
55
- adapted_background = {}
56
-
57
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
58
- save_original_data(adapted_background, background_ast)
59
- clear_child_elements(adapted_background, [[:background, :steps]])
60
-
61
- adapted_background['type'] = 'Background'
62
- adapted_background['keyword'] = background_ast[:background][:keyword]
63
- adapted_background['name'] = background_ast[:background][:name]
64
- adapted_background['description'] = background_ast[:background][:description] || ''
65
- adapted_background['line'] = background_ast[:background][:location][:line]
66
- adapted_background['column'] = background_ast[:background][:location][:column]
67
-
68
- adapted_background['steps'] = adapt_steps(background_ast[:background])
69
-
70
- adapted_background
71
- end
72
-
73
- # Adapts the AST sub-tree that is rooted at the given rule node.
74
- def adapt_rule(rule_ast)
75
- adapted_rule = {}
76
-
77
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
78
- save_original_data(adapted_rule, rule_ast)
79
- clear_child_elements(adapted_rule, [[: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
-
90
- adapted_rule
91
- end
92
-
93
- # Adapts the AST sub-tree that is rooted at the given scenario node.
94
- def adapt_scenario(test_ast)
95
- adapted_scenario = {}
96
-
97
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
98
- save_original_data(adapted_scenario, test_ast)
99
- clear_child_elements(adapted_scenario, [[:scenario, :tags],
100
- [:scenario, :steps]])
101
-
102
- adapted_scenario['type'] = 'Scenario'
103
- adapted_scenario['keyword'] = test_ast[:scenario][:keyword]
104
- adapted_scenario['name'] = test_ast[:scenario][:name]
105
- adapted_scenario['description'] = test_ast[:scenario][:description] || ''
106
- adapted_scenario['line'] = test_ast[:scenario][:location][:line]
107
- adapted_scenario['column'] = test_ast[:scenario][:location][:column]
108
-
109
- adapted_scenario['tags'] = adapt_tags(test_ast[:scenario])
110
- adapted_scenario['steps'] = adapt_steps(test_ast[:scenario])
111
-
112
- adapted_scenario
113
- end
114
-
115
- # Adapts the AST sub-tree that is rooted at the given outline node.
116
- def adapt_outline(test_ast)
117
- adapted_outline = {}
118
-
119
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
120
- save_original_data(adapted_outline, test_ast)
121
- clear_child_elements(adapted_outline, [[:scenario, :tags],
122
- [:scenario, :steps],
123
- [:scenario, :examples]])
124
-
125
- adapted_outline['type'] = 'ScenarioOutline'
126
- adapted_outline['keyword'] = test_ast[:scenario][:keyword]
127
- adapted_outline['name'] = test_ast[:scenario][:name]
128
- adapted_outline['description'] = test_ast[:scenario][:description] || ''
129
- adapted_outline['line'] = test_ast[:scenario][:location][:line]
130
- adapted_outline['column'] = test_ast[:scenario][:location][:column]
131
-
132
- adapted_outline['tags'] = adapt_tags(test_ast[:scenario])
133
- adapted_outline['steps'] = adapt_steps(test_ast[:scenario])
134
- adapted_outline['examples'] = adapt_examples(test_ast[:scenario])
135
-
136
- adapted_outline
137
- end
138
-
139
- # Adapts the AST sub-tree that is rooted at the given example node.
140
- def adapt_example(example_ast)
141
- adapted_example = {}
142
-
143
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
144
- save_original_data(adapted_example, example_ast)
145
- clear_child_elements(adapted_example, [[:tags],
146
- [:table_header],
147
- [:table_body]])
148
-
149
- adapted_example['keyword'] = example_ast[:keyword]
150
- adapted_example['name'] = example_ast[:name]
151
- adapted_example['line'] = example_ast[:location][:line]
152
- adapted_example['column'] = example_ast[:location][:column]
153
- adapted_example['description'] = example_ast[:description] || ''
154
-
155
- adapted_example['rows'] = []
156
- adapted_example['rows'] << adapt_table_row(example_ast[:table_header]) if example_ast[:table_header]
157
-
158
- example_ast[:table_body]&.each do |row|
159
- adapted_example['rows'] << adapt_table_row(row)
160
- end
161
-
162
- adapted_example['tags'] = adapt_tags(example_ast)
163
-
164
- adapted_example
165
- end
166
-
167
- # Adapts the AST sub-tree that is rooted at the given tag node.
168
- def adapt_tag(tag_ast)
169
- adapted_tag = {}
170
-
171
- # Saving off the original data
172
- save_original_data(adapted_tag, tag_ast)
173
-
174
- adapted_tag['name'] = tag_ast[:name]
175
- adapted_tag['line'] = tag_ast[:location][:line]
176
- adapted_tag['column'] = tag_ast[:location][:column]
177
-
178
- adapted_tag
179
- end
180
-
181
- # Adapts the AST sub-tree that is rooted at the given comment node.
182
- def adapt_comment(comment_ast)
183
- adapted_comment = {}
184
-
185
- # Saving off the original data
186
- save_original_data(adapted_comment, comment_ast)
187
-
188
- adapted_comment['text'] = comment_ast[:text]
189
- adapted_comment['line'] = comment_ast[:location][:line]
190
- adapted_comment['column'] = comment_ast[:location][:column]
191
-
192
- adapted_comment
193
- end
194
-
195
- # Adapts the AST sub-tree that is rooted at the given step node.
196
- def adapt_step(step_ast)
197
- adapted_step = {}
198
-
199
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
200
- save_original_data(adapted_step, step_ast)
201
- clear_child_elements(adapted_step, [[:data_table],
202
- [:doc_string]])
203
-
204
- adapted_step['keyword'] = step_ast[:keyword]
205
- adapted_step['name'] = step_ast[:text]
206
- adapted_step['line'] = step_ast[:location][:line]
207
- adapted_step['column'] = step_ast[:location][:column]
208
-
209
- if step_ast[:doc_string]
210
- adapted_step['doc_string'] = adapt_doc_string(step_ast[:doc_string])
211
- elsif step_ast[:data_table]
212
- adapted_step['table'] = adapt_step_table(step_ast[:data_table])
213
- end
214
-
215
- adapted_step
216
- end
217
-
218
- # Adapts the AST sub-tree that is rooted at the given doc string node.
219
- def adapt_doc_string(doc_string_ast)
220
- adapted_doc_string = {}
221
-
222
- # Saving off the original data
223
- save_original_data(adapted_doc_string, doc_string_ast)
224
-
225
- adapted_doc_string['value'] = doc_string_ast[:content]
226
- adapted_doc_string['content_type'] = doc_string_ast[:media_type]
227
- adapted_doc_string['line'] = doc_string_ast[:location][:line]
228
- adapted_doc_string['column'] = doc_string_ast[:location][:column]
229
-
230
- adapted_doc_string
231
- end
232
-
233
- # Adapts the AST sub-tree that is rooted at the given table node.
234
- def adapt_step_table(step_table_ast)
235
- adapted_step_table = {}
236
-
237
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
238
- save_original_data(adapted_step_table, step_table_ast)
239
- clear_child_elements(adapted_step_table, [[:rows]])
240
-
241
- adapted_step_table['rows'] = []
242
- step_table_ast[:rows].each do |row|
243
- adapted_step_table['rows'] << adapt_table_row(row)
244
- end
245
- adapted_step_table['line'] = step_table_ast[:location][:line]
246
- adapted_step_table['column'] = step_table_ast[:location][:column]
247
-
248
- adapted_step_table
249
- end
250
-
251
- # Adapts the AST sub-tree that is rooted at the given row node.
252
- def adapt_table_row(table_row_ast)
253
- adapted_table_row = {}
254
-
255
- # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
256
- save_original_data(adapted_table_row, table_row_ast)
257
- clear_child_elements(adapted_table_row, [[:cells]])
258
-
259
- adapted_table_row['line'] = table_row_ast[:location][:line]
260
- adapted_table_row['column'] = table_row_ast[:location][:column]
261
-
262
- adapted_table_row['cells'] = []
263
- table_row_ast[:cells].each do |row|
264
- adapted_table_row['cells'] << adapt_table_cell(row)
265
- end
266
-
267
- adapted_table_row
268
- end
269
-
270
- # Adapts the AST sub-tree that is rooted at the given cell node.
271
- def adapt_table_cell(cell_ast)
272
- adapted_cell = {}
273
-
274
- # Saving off the original data
275
- save_original_data(adapted_cell, cell_ast)
276
-
277
- adapted_cell['value'] = cell_ast[:value]
278
- adapted_cell['line'] = cell_ast[:location][:line]
279
- adapted_cell['column'] = cell_ast[:location][:column]
280
-
281
- adapted_cell
282
- end
283
-
284
-
285
- private
286
-
287
-
288
- def adapt_comments(file_ast)
289
- return [] unless file_ast[:comments]
290
-
291
- file_ast[:comments].map { |comment| adapt_comment(comment) }
292
- end
293
-
294
- def adapt_tags(element_ast)
295
- return [] unless element_ast[:tags]
296
-
297
- element_ast[:tags].map { |tag| adapt_tag(tag) }
298
- end
299
-
300
- def adapt_steps(element_ast)
301
- return [] unless element_ast[:steps]
302
-
303
- element_ast[:steps].map { |step| adapt_step(step) }
304
- end
305
-
306
- def adapt_examples(element_ast)
307
- return [] unless element_ast[:examples]
308
-
309
- element_ast[:examples].map { |example| adapt_example(example) }
310
- end
311
-
312
- def adapt_child_elements(element_ast)
313
- return [] unless element_ast[:children]
314
-
315
- element_ast[:children].map do |child_element|
316
- if child_element[:background]
317
- adapt_background(child_element)
318
- elsif child_element[:rule]
319
- adapt_rule(child_element)
320
- else
321
- adapt_test(child_element)
322
- end
323
- end
324
- end
325
-
326
- def adapt_test(test_ast)
327
- if (test_node?(test_ast) && test_has_examples?(test_ast)) ||
328
- (test_node?(test_ast) && test_uses_outline_keyword?(test_ast))
329
-
330
- adapt_outline(test_ast)
331
- elsif test_node?(test_ast)
332
- adapt_scenario(test_ast)
333
- else
334
- raise(ArgumentError, "Unknown test type with keys: #{test_ast.keys}")
335
- end
336
- end
337
-
338
- def clear_child_elements(ast, child_paths)
339
- child_paths.each do |traversal_path|
340
- # Wipe the value if it's there but don't add any keys to the hash if it didn't already have them
341
- if ast['cuke_modeler_parsing_data'].dig(*traversal_path)
342
- bury(ast['cuke_modeler_parsing_data'], traversal_path, nil)
343
- end
344
- end
345
- end
346
-
347
- def bury(hash, traversal_path, value)
348
- keys = *traversal_path
349
-
350
- current = hash
351
- (keys.count - 1).times do |index|
352
- current = hash[keys[index]]
353
- end
354
-
355
- current[keys.last] = value
356
- end
357
-
358
- def test_node?(ast_node)
359
- !ast_node[:scenario].nil?
360
- end
361
-
362
- def test_has_examples?(ast_node)
363
- !ast_node[:scenario][:examples].nil?
364
- end
365
-
366
- def test_uses_outline_keyword?(test_ast)
367
- Parsing.dialects[Parsing.dialect]['scenarioOutline'].include?(test_ast[:scenario][:keyword])
368
- end
369
-
370
- end
371
-
372
- private_constant :Gherkin9Adapter
373
- end
374
-
375
- # 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 9.x of the *cucumber-gherkin* gem into input that is consumable
11
+ # by this gem. Internal helper class.
12
+ class Gherkin9Adapter < 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],
21
+ [:comments]])
22
+
23
+ adapted_ast['comments'] = adapt_comments(ast)
24
+ adapted_ast['feature'] = adapt_feature(ast[:feature])
25
+
26
+ adapted_ast
27
+ end
28
+
29
+ # Adapts the AST sub-tree that is rooted at the given feature node.
30
+ def adapt_feature(feature_ast)
31
+ return nil unless feature_ast
32
+
33
+ adapted_feature = {}
34
+
35
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
36
+ save_original_data(adapted_feature, feature_ast)
37
+ clear_child_elements(adapted_feature, [[:tags],
38
+ [:children]])
39
+
40
+ adapted_feature['language'] = feature_ast[:language]
41
+ adapted_feature['keyword'] = feature_ast[:keyword]
42
+ adapted_feature['name'] = feature_ast[:name]
43
+ adapted_feature['description'] = feature_ast[:description] || ''
44
+ adapted_feature['line'] = feature_ast[:location][:line]
45
+ adapted_feature['column'] = feature_ast[:location][:column]
46
+
47
+ adapted_feature['elements'] = adapt_child_elements(feature_ast)
48
+ adapted_feature['tags'] = adapt_tags(feature_ast)
49
+
50
+ adapted_feature
51
+ end
52
+
53
+ # Adapts the AST sub-tree that is rooted at the given background node.
54
+ def adapt_background(background_ast)
55
+ adapted_background = {}
56
+
57
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
58
+ save_original_data(adapted_background, background_ast)
59
+ clear_child_elements(adapted_background, [[:background, :steps]])
60
+
61
+ adapted_background['type'] = 'Background'
62
+ adapted_background['keyword'] = background_ast[:background][:keyword]
63
+ adapted_background['name'] = background_ast[:background][:name]
64
+ adapted_background['description'] = background_ast[:background][:description] || ''
65
+ adapted_background['line'] = background_ast[:background][:location][:line]
66
+ adapted_background['column'] = background_ast[:background][:location][:column]
67
+
68
+ adapted_background['steps'] = adapt_steps(background_ast[:background])
69
+
70
+ adapted_background
71
+ end
72
+
73
+ # Adapts the AST sub-tree that is rooted at the given rule node.
74
+ def adapt_rule(rule_ast)
75
+ adapted_rule = {}
76
+
77
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
78
+ save_original_data(adapted_rule, rule_ast)
79
+ clear_child_elements(adapted_rule, [[: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
+
90
+ adapted_rule
91
+ end
92
+
93
+ # Adapts the AST sub-tree that is rooted at the given scenario node.
94
+ def adapt_scenario(test_ast)
95
+ adapted_scenario = {}
96
+
97
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
98
+ save_original_data(adapted_scenario, test_ast)
99
+ clear_child_elements(adapted_scenario, [[:scenario, :tags],
100
+ [:scenario, :steps]])
101
+
102
+ adapted_scenario['type'] = 'Scenario'
103
+ adapted_scenario['keyword'] = test_ast[:scenario][:keyword]
104
+ adapted_scenario['name'] = test_ast[:scenario][:name]
105
+ adapted_scenario['description'] = test_ast[:scenario][:description] || ''
106
+ adapted_scenario['line'] = test_ast[:scenario][:location][:line]
107
+ adapted_scenario['column'] = test_ast[:scenario][:location][:column]
108
+
109
+ adapted_scenario['tags'] = adapt_tags(test_ast[:scenario])
110
+ adapted_scenario['steps'] = adapt_steps(test_ast[:scenario])
111
+
112
+ adapted_scenario
113
+ end
114
+
115
+ # Adapts the AST sub-tree that is rooted at the given outline node.
116
+ def adapt_outline(test_ast)
117
+ adapted_outline = {}
118
+
119
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
120
+ save_original_data(adapted_outline, test_ast)
121
+ clear_child_elements(adapted_outline, [[:scenario, :tags],
122
+ [:scenario, :steps],
123
+ [:scenario, :examples]])
124
+
125
+ adapted_outline['type'] = 'ScenarioOutline'
126
+ adapted_outline['keyword'] = test_ast[:scenario][:keyword]
127
+ adapted_outline['name'] = test_ast[:scenario][:name]
128
+ adapted_outline['description'] = test_ast[:scenario][:description] || ''
129
+ adapted_outline['line'] = test_ast[:scenario][:location][:line]
130
+ adapted_outline['column'] = test_ast[:scenario][:location][:column]
131
+
132
+ adapted_outline['tags'] = adapt_tags(test_ast[:scenario])
133
+ adapted_outline['steps'] = adapt_steps(test_ast[:scenario])
134
+ adapted_outline['examples'] = adapt_examples(test_ast[:scenario])
135
+
136
+ adapted_outline
137
+ end
138
+
139
+ # Adapts the AST sub-tree that is rooted at the given example node.
140
+ def adapt_example(example_ast)
141
+ adapted_example = {}
142
+
143
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
144
+ save_original_data(adapted_example, example_ast)
145
+ clear_child_elements(adapted_example, [[:tags],
146
+ [:table_header],
147
+ [:table_body]])
148
+
149
+ adapted_example['keyword'] = example_ast[:keyword]
150
+ adapted_example['name'] = example_ast[:name]
151
+ adapted_example['line'] = example_ast[:location][:line]
152
+ adapted_example['column'] = example_ast[:location][:column]
153
+ adapted_example['description'] = example_ast[:description] || ''
154
+
155
+ adapted_example['rows'] = []
156
+ adapted_example['rows'] << adapt_table_row(example_ast[:table_header]) if example_ast[:table_header]
157
+
158
+ example_ast[:table_body]&.each do |row|
159
+ adapted_example['rows'] << adapt_table_row(row)
160
+ end
161
+
162
+ adapted_example['tags'] = adapt_tags(example_ast)
163
+
164
+ adapted_example
165
+ end
166
+
167
+ # Adapts the AST sub-tree that is rooted at the given tag node.
168
+ def adapt_tag(tag_ast)
169
+ adapted_tag = {}
170
+
171
+ # Saving off the original data
172
+ save_original_data(adapted_tag, tag_ast)
173
+
174
+ adapted_tag['name'] = tag_ast[:name]
175
+ adapted_tag['line'] = tag_ast[:location][:line]
176
+ adapted_tag['column'] = tag_ast[:location][:column]
177
+
178
+ adapted_tag
179
+ end
180
+
181
+ # Adapts the AST sub-tree that is rooted at the given comment node.
182
+ def adapt_comment(comment_ast)
183
+ adapted_comment = {}
184
+
185
+ # Saving off the original data
186
+ save_original_data(adapted_comment, comment_ast)
187
+
188
+ adapted_comment['text'] = comment_ast[:text]
189
+ adapted_comment['line'] = comment_ast[:location][:line]
190
+ adapted_comment['column'] = comment_ast[:location][:column]
191
+
192
+ adapted_comment
193
+ end
194
+
195
+ # Adapts the AST sub-tree that is rooted at the given step node.
196
+ def adapt_step(step_ast)
197
+ adapted_step = {}
198
+
199
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
200
+ save_original_data(adapted_step, step_ast)
201
+ clear_child_elements(adapted_step, [[:data_table],
202
+ [:doc_string]])
203
+
204
+ adapted_step['keyword'] = step_ast[:keyword]
205
+ adapted_step['name'] = step_ast[:text]
206
+ adapted_step['line'] = step_ast[:location][:line]
207
+ adapted_step['column'] = step_ast[:location][:column]
208
+
209
+ if step_ast[:doc_string]
210
+ adapted_step['doc_string'] = adapt_doc_string(step_ast[:doc_string])
211
+ elsif step_ast[:data_table]
212
+ adapted_step['table'] = adapt_step_table(step_ast[:data_table])
213
+ end
214
+
215
+ adapted_step
216
+ end
217
+
218
+ # Adapts the AST sub-tree that is rooted at the given doc string node.
219
+ def adapt_doc_string(doc_string_ast)
220
+ adapted_doc_string = {}
221
+
222
+ # Saving off the original data
223
+ save_original_data(adapted_doc_string, doc_string_ast)
224
+
225
+ adapted_doc_string['value'] = doc_string_ast[:content]
226
+ adapted_doc_string['content_type'] = doc_string_ast[:media_type]
227
+ adapted_doc_string['line'] = doc_string_ast[:location][:line]
228
+ adapted_doc_string['column'] = doc_string_ast[:location][:column]
229
+
230
+ adapted_doc_string
231
+ end
232
+
233
+ # Adapts the AST sub-tree that is rooted at the given table node.
234
+ def adapt_step_table(step_table_ast)
235
+ adapted_step_table = {}
236
+
237
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
238
+ save_original_data(adapted_step_table, step_table_ast)
239
+ clear_child_elements(adapted_step_table, [[:rows]])
240
+
241
+ adapted_step_table['rows'] = []
242
+ step_table_ast[:rows].each do |row|
243
+ adapted_step_table['rows'] << adapt_table_row(row)
244
+ end
245
+ adapted_step_table['line'] = step_table_ast[:location][:line]
246
+ adapted_step_table['column'] = step_table_ast[:location][:column]
247
+
248
+ adapted_step_table
249
+ end
250
+
251
+ # Adapts the AST sub-tree that is rooted at the given row node.
252
+ def adapt_table_row(table_row_ast)
253
+ adapted_table_row = {}
254
+
255
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
256
+ save_original_data(adapted_table_row, table_row_ast)
257
+ clear_child_elements(adapted_table_row, [[:cells]])
258
+
259
+ adapted_table_row['line'] = table_row_ast[:location][:line]
260
+ adapted_table_row['column'] = table_row_ast[:location][:column]
261
+
262
+ adapted_table_row['cells'] = []
263
+ table_row_ast[:cells].each do |row|
264
+ adapted_table_row['cells'] << adapt_table_cell(row)
265
+ end
266
+
267
+ adapted_table_row
268
+ end
269
+
270
+ # Adapts the AST sub-tree that is rooted at the given cell node.
271
+ def adapt_table_cell(cell_ast)
272
+ adapted_cell = {}
273
+
274
+ # Saving off the original data
275
+ save_original_data(adapted_cell, cell_ast)
276
+
277
+ adapted_cell['value'] = cell_ast[:value]
278
+ adapted_cell['line'] = cell_ast[:location][:line]
279
+ adapted_cell['column'] = cell_ast[:location][:column]
280
+
281
+ adapted_cell
282
+ end
283
+
284
+
285
+ private
286
+
287
+
288
+ def adapt_comments(file_ast)
289
+ return [] unless file_ast[:comments]
290
+
291
+ file_ast[:comments].map { |comment| adapt_comment(comment) }
292
+ end
293
+
294
+ def adapt_tags(element_ast)
295
+ return [] unless element_ast[:tags]
296
+
297
+ element_ast[:tags].map { |tag| adapt_tag(tag) }
298
+ end
299
+
300
+ def adapt_steps(element_ast)
301
+ return [] unless element_ast[:steps]
302
+
303
+ element_ast[:steps].map { |step| adapt_step(step) }
304
+ end
305
+
306
+ def adapt_examples(element_ast)
307
+ return [] unless element_ast[:examples]
308
+
309
+ element_ast[:examples].map { |example| adapt_example(example) }
310
+ end
311
+
312
+ def adapt_child_elements(element_ast)
313
+ return [] unless element_ast[:children]
314
+
315
+ element_ast[:children].map do |child_element|
316
+ if child_element[:background]
317
+ adapt_background(child_element)
318
+ elsif child_element[:rule]
319
+ adapt_rule(child_element)
320
+ else
321
+ adapt_test(child_element)
322
+ end
323
+ end
324
+ end
325
+
326
+ def adapt_test(test_ast)
327
+ if (test_node?(test_ast) && test_has_examples?(test_ast)) ||
328
+ (test_node?(test_ast) && test_uses_outline_keyword?(test_ast))
329
+
330
+ adapt_outline(test_ast)
331
+ elsif test_node?(test_ast)
332
+ adapt_scenario(test_ast)
333
+ else
334
+ raise(ArgumentError, "Unknown test type with keys: #{test_ast.keys}")
335
+ end
336
+ end
337
+
338
+ def clear_child_elements(ast, child_paths)
339
+ child_paths.each do |traversal_path|
340
+ # Wipe the value if it's there but don't add any keys to the hash if it didn't already have them
341
+ if ast['cuke_modeler_parsing_data'].dig(*traversal_path)
342
+ bury(ast['cuke_modeler_parsing_data'], traversal_path, nil)
343
+ end
344
+ end
345
+ end
346
+
347
+ def bury(hash, traversal_path, value)
348
+ keys = *traversal_path
349
+
350
+ current = hash
351
+ (keys.count - 1).times do |index|
352
+ current = hash[keys[index]]
353
+ end
354
+
355
+ current[keys.last] = value
356
+ end
357
+
358
+ def test_node?(ast_node)
359
+ !ast_node[:scenario].nil?
360
+ end
361
+
362
+ def test_has_examples?(ast_node)
363
+ !ast_node[:scenario][:examples].nil?
364
+ end
365
+
366
+ def test_uses_outline_keyword?(test_ast)
367
+ Parsing.dialects[Parsing.dialect]['scenarioOutline'].include?(test_ast[:scenario][:keyword])
368
+ end
369
+
370
+ end
371
+
372
+ private_constant :Gherkin9Adapter
373
+ end
374
+
375
+ # rubocop:enable Metrics/ClassLength, Metrics/AbcSize, Metrics/MethodLength