cuke_modeler 3.3.0 → 3.4.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -1
  3. data/cuke_modeler.gemspec +23 -14
  4. data/lib/cuke_modeler.rb +1 -1
  5. data/lib/cuke_modeler/adapters/gherkin_10_adapter.rb +2 -1
  6. data/lib/cuke_modeler/adapters/gherkin_11_adapter.rb +2 -1
  7. data/lib/cuke_modeler/adapters/gherkin_12_adapter.rb +2 -1
  8. data/lib/cuke_modeler/adapters/gherkin_13_adapter.rb +2 -1
  9. data/lib/cuke_modeler/adapters/gherkin_14_adapter.rb +2 -1
  10. data/lib/cuke_modeler/adapters/gherkin_15_adapter.rb +2 -1
  11. data/lib/cuke_modeler/adapters/gherkin_9_adapter.rb +261 -242
  12. data/lib/cuke_modeler/containing.rb +31 -89
  13. data/lib/cuke_modeler/described.rb +40 -1
  14. data/lib/cuke_modeler/models/background.rb +11 -11
  15. data/lib/cuke_modeler/models/cell.rb +13 -7
  16. data/lib/cuke_modeler/models/comment.rb +5 -5
  17. data/lib/cuke_modeler/models/directory.rb +13 -17
  18. data/lib/cuke_modeler/models/doc_string.rb +10 -7
  19. data/lib/cuke_modeler/models/example.rb +63 -45
  20. data/lib/cuke_modeler/models/feature.rb +23 -16
  21. data/lib/cuke_modeler/models/feature_file.rb +5 -7
  22. data/lib/cuke_modeler/models/model.rb +2 -1
  23. data/lib/cuke_modeler/models/outline.rb +19 -14
  24. data/lib/cuke_modeler/models/row.rb +10 -7
  25. data/lib/cuke_modeler/models/rule.rb +11 -9
  26. data/lib/cuke_modeler/models/scenario.rb +17 -12
  27. data/lib/cuke_modeler/models/step.rb +40 -18
  28. data/lib/cuke_modeler/models/table.rb +9 -6
  29. data/lib/cuke_modeler/models/tag.rb +9 -5
  30. data/lib/cuke_modeler/named.rb +5 -1
  31. data/lib/cuke_modeler/nested.rb +22 -18
  32. data/lib/cuke_modeler/parsed.rb +8 -0
  33. data/lib/cuke_modeler/parsing.rb +36 -26
  34. data/lib/cuke_modeler/sourceable.rb +8 -0
  35. data/lib/cuke_modeler/stepped.rb +8 -0
  36. data/lib/cuke_modeler/taggable.rb +9 -1
  37. data/lib/cuke_modeler/version.rb +1 -1
  38. metadata +53 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c0d1e0901ca4b0af16683c5f92dac6ee799058c6adadf58a9323e4c59e70813
4
- data.tar.gz: 8303d94eb1f5d0acc163a08435f9dda0bcda9b07a23375985aedde9fc3fd3138
3
+ metadata.gz: ab8068e33b09b4e508b0cd53f77c954052796fbbd33686070cbf6e65cd616378
4
+ data.tar.gz: 57849a93b03a7d6d0cc56548e4e6482eb61557c4b42a68950c82e3b43542e482
5
5
  SHA512:
6
- metadata.gz: 7948e5a8cf13728afeb2820ff66baefb31d878c96fc41145effc109d1d6fd6adc8e4c288928273e0b04bdd909c85b120e777c11594ce5fd7720e801d5aaae530
7
- data.tar.gz: bd9cd348ed6f090f0231de118e5fe9d28ca9da014e0b2c1481deef4d604adbf1050d6350abf708555a6bd2651099d05f8134f1b074a5b02e1ae97fa38ccf872b
6
+ metadata.gz: 3077bf8a1a1defacc92d371db01044c2ea5c7ef44dc7a194079a1e8721b4e8dad598eb4705acc4882d04a5934c27a70103a7eca2795f45ea9f5bebad37d24e7d
7
+ data.tar.gz: 0a9e2fbae81285bad22f59ef8626fd020be7516a824292fd08387eb38f34d1bc185f41d71084c1be84f331574c7d65145fa3e37ae28d8585ab5369f25adfffa5
@@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
 
9
9
  Nothing yet...
10
10
 
11
+ ## [3.4.0] - 2020-09-02
12
+
13
+ ### Added
14
+ - `Feature#has_background?` and `Rule#has_background?` now both have a more conventional name via the alias `#background?`
15
+
11
16
  ## [3.3.0] - 2020-08-15
12
17
 
13
18
  ### Added
@@ -329,7 +334,8 @@ Nothing yet...
329
334
  - Initial release
330
335
 
331
336
 
332
- [Unreleased]: https://github.com/enkessler/cuke_modeler/compare/v3.3.0...HEAD
337
+ [Unreleased]: https://github.com/enkessler/cuke_modeler/compare/v3.4.0...HEAD
338
+ [3.4.0]: https://github.com/enkessler/cuke_modeler/compare/v3.3.0...v3.4.0
333
339
  [3.3.0]: https://github.com/enkessler/cuke_modeler/compare/v3.2.0...v3.3.0
334
340
  [3.2.0]: https://github.com/enkessler/cuke_modeler/compare/v3.1.0...v3.2.0
335
341
  [3.1.0]: https://github.com/enkessler/cuke_modeler/compare/v3.0.0...v3.1.0
@@ -1,17 +1,21 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'cuke_modeler/version'
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "cuke_modeler"
6
+ spec.name = 'cuke_modeler'
8
7
  spec.version = CukeModeler::VERSION
9
- spec.authors = ["Eric Kessler"]
10
- spec.email = ["morrow748@gmail.com"]
11
- spec.summary = %q{A gem providing functionality to model Gherkin based test suites.}
12
- spec.description = %q{This gem facilitates modeling a test suite that is written in Gherkin (e.g. Cucumber, SpecFlow, Lettuce, etc.). It does this by providing an abstraction layer on top of the Abstract Syntax Tree that the 'cucumber-gherkin' gem generates when parsing features, as well as providing models for feature files and directories in order to be able to have a fully traversable model tree of a test suite's structure. These models can then be analyzed or manipulated more easily than the underlying AST layer.}
8
+ spec.authors = ['Eric Kessler']
9
+ spec.email = ['morrow748@gmail.com']
10
+ spec.summary = 'A gem providing functionality to model Gherkin based test suites.'
11
+ spec.description = ['This gem facilitates modeling a test suite that is written in Gherkin (e.g. Cucumber, ',
12
+ 'SpecFlow, Lettuce, etc.). It does this by providing an abstraction layer on top of the ',
13
+ "Abstract Syntax Tree that the 'cucumber-gherkin' gem generates when parsing features, ",
14
+ 'as well as providing models for feature files and directories in order to be able to ',
15
+ "have a fully traversable model tree of a test suite's structure. These models can then ",
16
+ 'be analyzed or manipulated more easily than the underlying AST layer.'].join("\n")
13
17
  spec.homepage = 'https://github.com/enkessler/cuke_modeler'
14
- spec.license = "MIT"
18
+ spec.license = 'MIT'
15
19
 
16
20
  # Specify which files should be added to the gem when it is released.
17
21
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -20,19 +24,24 @@ Gem::Specification.new do |spec|
20
24
  source_controlled_files.keep_if { |file| file =~ %r{^(lib|testing/cucumber/features)} }
21
25
  source_controlled_files + ['README.md', 'LICENSE.txt', 'CHANGELOG.md', 'cuke_modeler.gemspec']
22
26
  end
23
- spec.require_paths = ["lib"]
27
+ spec.require_paths = ['lib']
24
28
 
25
29
  spec.required_ruby_version = '>= 2.3', '< 3.0'
26
30
 
27
31
  spec.add_runtime_dependency 'cucumber-gherkin', '< 16.0'
28
32
 
29
33
  spec.add_development_dependency 'bundler', '< 3.0'
30
- spec.add_development_dependency "rake", '< 13.0.0'
31
- spec.add_development_dependency 'cucumber', '< 5.0.0'
32
- spec.add_development_dependency 'rspec', '~> 3.0'
33
- spec.add_development_dependency 'simplecov', '<= 0.16.1'
34
- spec.add_development_dependency 'racatt', '~> 1.0'
35
34
  spec.add_development_dependency 'coveralls', '< 1.0.0'
35
+ # Cucumber 4.x is the earliest version to use cucumber-gherkin
36
+ spec.add_development_dependency 'cucumber', '>= 4.0.0', '< 6.0.0'
37
+ spec.add_development_dependency 'racatt', '~> 1.0'
36
38
  spec.add_development_dependency 'rainbow', '< 4.0.0'
39
+ spec.add_development_dependency 'rake', '< 14.0.0'
40
+ spec.add_development_dependency 'rspec', '~> 3.0'
41
+ # RuboCop drops Ruby 2.3 support after this version and we need to maintain Ruby 2.3 compatibility when writing code
42
+ # for this gem
43
+ spec.add_development_dependency 'rubocop', '< 0.82.0'
44
+ # Coveralls gem does not support any newer version than this
45
+ spec.add_development_dependency 'simplecov', '<= 0.16.1'
37
46
  spec.add_development_dependency 'test-unit', '< 4.0.0'
38
47
  end
@@ -4,7 +4,7 @@ module CukeModeler
4
4
  end
5
5
 
6
6
 
7
- require "cuke_modeler/version"
7
+ require 'cuke_modeler/version'
8
8
 
9
9
  require 'cuke_modeler/parsing'
10
10
  require 'cuke_modeler/containing'
@@ -4,7 +4,8 @@ require_relative 'gherkin_9_adapter'
4
4
  module CukeModeler
5
5
 
6
6
  # NOT A PART OF THE PUBLIC API
7
- # An adapter that can convert the output of version 10.x of the *cucumber-gherkin* gem into input that is consumable by this gem.
7
+ # An adapter that can convert the output of version 10.x of the *cucumber-gherkin* gem into input that is consumable
8
+ # by this gem.
8
9
 
9
10
  class Gherkin10Adapter < Gherkin9Adapter
10
11
 
@@ -4,7 +4,8 @@ require_relative 'gherkin_9_adapter'
4
4
  module CukeModeler
5
5
 
6
6
  # NOT A PART OF THE PUBLIC API
7
- # An adapter that can convert the output of version 11.x of the *cucumber-gherkin* gem into input that is consumable by this gem.
7
+ # An adapter that can convert the output of version 11.x of the *cucumber-gherkin* gem into input that is consumable
8
+ # by this gem.
8
9
 
9
10
  class Gherkin11Adapter < Gherkin9Adapter
10
11
 
@@ -4,7 +4,8 @@ require_relative 'gherkin_9_adapter'
4
4
  module CukeModeler
5
5
 
6
6
  # NOT A PART OF THE PUBLIC API
7
- # An adapter that can convert the output of version 12.x of the *cucumber-gherkin* gem into input that is consumable by this gem.
7
+ # An adapter that can convert the output of version 12.x of the *cucumber-gherkin* gem into input that is consumable
8
+ # by this gem.
8
9
 
9
10
  class Gherkin12Adapter < Gherkin9Adapter
10
11
 
@@ -4,7 +4,8 @@ require_relative 'gherkin_9_adapter'
4
4
  module CukeModeler
5
5
 
6
6
  # NOT A PART OF THE PUBLIC API
7
- # An adapter that can convert the output of version 13.x of the *cucumber-gherkin* gem into input that is consumable by this gem.
7
+ # An adapter that can convert the output of version 13.x of the *cucumber-gherkin* gem into input that is consumable
8
+ # by this gem.
8
9
 
9
10
  class Gherkin13Adapter < Gherkin9Adapter
10
11
 
@@ -4,7 +4,8 @@ require_relative 'gherkin_9_adapter'
4
4
  module CukeModeler
5
5
 
6
6
  # NOT A PART OF THE PUBLIC API
7
- # An adapter that can convert the output of version 14.x of the *cucumber-gherkin* gem into input that is consumable by this gem.
7
+ # An adapter that can convert the output of version 14.x of the *cucumber-gherkin* gem into input that is consumable
8
+ # by this gem.
8
9
 
9
10
  class Gherkin14Adapter < Gherkin9Adapter
10
11
 
@@ -4,7 +4,8 @@ require_relative 'gherkin_9_adapter'
4
4
  module CukeModeler
5
5
 
6
6
  # NOT A PART OF THE PUBLIC API
7
- # An adapter that can convert the output of version 15.x of the *cucumber-gherkin* gem into input that is consumable by this gem.
7
+ # An adapter that can convert the output of version 15.x of the *cucumber-gherkin* gem into input that is consumable
8
+ # by this gem.
8
9
 
9
10
  class Gherkin15Adapter < Gherkin9Adapter
10
11
 
@@ -1,345 +1,364 @@
1
+ # Some things just aren't going to get better due to the inherent complexity of the AST
2
+ # rubocop:disable Metrics/ClassLength, Metrics/AbcSize, Metrics/MethodLength
3
+
1
4
  module CukeModeler
2
5
 
3
6
  # NOT A PART OF THE PUBLIC API
4
- # An adapter that can convert the output of version 9.x of the *cucumber-gherkin* gem into input that is consumable by this gem.
7
+ # An adapter that can convert the output of version 9.x of the *cucumber-gherkin* gem into input that is consumable
8
+ # by this gem.
5
9
 
6
10
  class Gherkin9Adapter
7
11
 
8
12
  # Adapts the given AST into the shape that this gem expects
9
- def adapt(parsed_ast)
10
- # Saving off the original data
11
- parsed_ast['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_ast))
12
-
13
- # Removing parsed data for child elements in order to avoid duplicating data
14
- parsed_ast['cuke_modeler_parsing_data'][:feature] = nil if parsed_ast['cuke_modeler_parsing_data'][:feature]
15
- parsed_ast['cuke_modeler_parsing_data'][:comments] = nil if parsed_ast['cuke_modeler_parsing_data'][:comments]
13
+ def adapt(ast)
14
+ adapted_ast = {}
16
15
 
17
- parsed_ast['comments'] = []
18
- if parsed_ast[:comments]
19
- parsed_ast[:comments].each do |comment|
20
- adapt_comment!(comment)
21
- end
22
- parsed_ast['comments'].concat(parsed_ast.delete(:comments))
23
- end
16
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
17
+ save_original_data(adapted_ast, ast)
18
+ clear_child_elements(adapted_ast, [[:feature],
19
+ [:comments]])
24
20
 
25
- adapt_feature!(parsed_ast[:feature]) if parsed_ast[:feature]
26
- parsed_ast['feature'] = parsed_ast.delete(:feature)
21
+ adapted_ast['comments'] = adapt_comments(ast)
22
+ adapted_ast['feature'] = adapt_feature(ast[:feature])
27
23
 
28
- parsed_ast
24
+ adapted_ast
29
25
  end
30
26
 
31
27
  # Adapts the AST sub-tree that is rooted at the given feature node.
32
- def adapt_feature!(parsed_feature)
33
- # Saving off the original data
34
- parsed_feature['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_feature))
28
+ def adapt_feature(feature_ast)
29
+ return nil unless feature_ast
35
30
 
36
- # Removing parsed data for child elements in order to avoid duplicating data
37
- parsed_feature['cuke_modeler_parsing_data'][:tags] = nil if parsed_feature['cuke_modeler_parsing_data'][:tags]
38
- parsed_feature['cuke_modeler_parsing_data'][:children] = nil if parsed_feature['cuke_modeler_parsing_data'][:children]
31
+ adapted_feature = {}
39
32
 
40
- parsed_feature['keyword'] = parsed_feature.delete(:keyword)
41
- parsed_feature['name'] = parsed_feature.delete(:name)
42
- parsed_feature['description'] = parsed_feature.delete(:description) || ''
43
- parsed_feature['line'] = parsed_feature.delete(:location)[:line]
33
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
34
+ save_original_data(adapted_feature, feature_ast)
35
+ clear_child_elements(adapted_feature, [[:tags],
36
+ [:children]])
44
37
 
45
- parsed_feature['elements'] = []
46
- if parsed_feature[:children]
47
- adapt_child_elements!(parsed_feature[:children])
48
- parsed_feature['elements'].concat(parsed_feature.delete(:children))
49
- end
38
+ adapted_feature['keyword'] = feature_ast[:keyword]
39
+ adapted_feature['name'] = feature_ast[:name]
40
+ adapted_feature['description'] = feature_ast[:description] || ''
41
+ adapted_feature['line'] = feature_ast[:location][:line]
50
42
 
51
- parsed_feature['tags'] = []
52
- if parsed_feature[:tags]
53
- parsed_feature[:tags].each do |tag|
54
- adapt_tag!(tag)
55
- end
56
- parsed_feature['tags'].concat(parsed_feature.delete(:tags))
57
- end
43
+ adapted_feature['elements'] = adapt_child_elements(feature_ast)
44
+ adapted_feature['tags'] = adapt_tags(feature_ast)
45
+
46
+ adapted_feature
58
47
  end
59
48
 
60
49
  # Adapts the AST sub-tree that is rooted at the given background node.
61
- def adapt_background!(parsed_background)
62
- # Saving off the original data
63
- parsed_background['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_background))
50
+ def adapt_background(background_ast)
51
+ adapted_background = {}
64
52
 
65
- # Removing parsed data for child elements in order to avoid duplicating data
66
- parsed_background['cuke_modeler_parsing_data'][:background][:steps] = nil if parsed_background['cuke_modeler_parsing_data'][:background][:steps]
53
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
54
+ save_original_data(adapted_background, background_ast)
55
+ clear_child_elements(adapted_background, [[:background, :steps]])
67
56
 
68
- parsed_background['type'] = 'Background'
69
- parsed_background['keyword'] = parsed_background[:background].delete(:keyword)
70
- parsed_background['name'] = parsed_background[:background].delete(:name)
71
- parsed_background['description'] = parsed_background[:background].delete(:description) || ''
72
- parsed_background['line'] = parsed_background[:background].delete(:location)[:line]
57
+ adapted_background['type'] = 'Background'
58
+ adapted_background['keyword'] = background_ast[:background][:keyword]
59
+ adapted_background['name'] = background_ast[:background][:name]
60
+ adapted_background['description'] = background_ast[:background][:description] || ''
61
+ adapted_background['line'] = background_ast[:background][:location][:line]
73
62
 
74
- parsed_background['steps'] = []
75
- if parsed_background[:background][:steps]
76
- parsed_background[:background][:steps].each do |step|
77
- adapt_step!(step)
78
- end
79
- parsed_background['steps'].concat(parsed_background[:background].delete(:steps))
80
- end
63
+ adapted_background['steps'] = adapt_steps(background_ast[:background])
64
+
65
+ adapted_background
81
66
  end
82
67
 
83
68
  # Adapts the AST sub-tree that is rooted at the given rule node.
84
- def adapt_rule!(parsed_rule)
85
- # Saving off the original data
86
- parsed_rule['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_rule))
69
+ def adapt_rule(rule_ast)
70
+ adapted_rule = {}
87
71
 
88
- # Removing parsed data for child elements in order to avoid duplicating data
89
- parsed_rule['cuke_modeler_parsing_data'][:rule][:children] = nil if parsed_rule['cuke_modeler_parsing_data'][:rule][:children]
72
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
73
+ save_original_data(adapted_rule, rule_ast)
74
+ clear_child_elements(adapted_rule, [[:rule, :children]])
90
75
 
91
- parsed_rule['type'] = 'Rule'
92
- parsed_rule['keyword'] = parsed_rule[:rule].delete(:keyword)
93
- parsed_rule['name'] = parsed_rule[:rule].delete(:name)
94
- parsed_rule['description'] = parsed_rule[:rule].delete(:description) || ''
95
- parsed_rule['line'] = parsed_rule[:rule].delete(:location)[:line]
76
+ adapted_rule['type'] = 'Rule'
77
+ adapted_rule['keyword'] = rule_ast[:rule][:keyword]
78
+ adapted_rule['name'] = rule_ast[:rule][:name]
79
+ adapted_rule['description'] = rule_ast[:rule][:description] || ''
80
+ adapted_rule['line'] = rule_ast[:rule][:location][:line]
96
81
 
97
- parsed_rule['elements'] = []
98
- if parsed_rule[:rule][:children]
99
- adapt_child_elements!(parsed_rule[:rule][:children])
100
- parsed_rule['elements'].concat(parsed_rule[:rule].delete(:children))
101
- end
82
+ adapted_rule['elements'] = adapt_child_elements(rule_ast[:rule])
83
+
84
+ adapted_rule
102
85
  end
103
86
 
104
87
  # Adapts the AST sub-tree that is rooted at the given scenario node.
105
- def adapt_scenario!(parsed_test)
106
- # Removing parsed data for child elements in order to avoid duplicating data
107
- parsed_test['cuke_modeler_parsing_data'][:scenario][:tags] = nil if parsed_test['cuke_modeler_parsing_data'][:scenario][:tags]
108
- parsed_test['cuke_modeler_parsing_data'][:scenario][:steps] = nil if parsed_test['cuke_modeler_parsing_data'][:scenario][:steps]
109
-
110
- parsed_test['type'] = 'Scenario'
111
- parsed_test['keyword'] = parsed_test[:scenario].delete(:keyword)
112
- parsed_test['name'] = parsed_test[:scenario].delete(:name)
113
- parsed_test['description'] = parsed_test[:scenario].delete(:description) || ''
114
- parsed_test['line'] = parsed_test[:scenario].delete(:location)[:line]
115
-
116
- parsed_test['tags'] = []
117
- if parsed_test[:scenario][:tags]
118
- parsed_test[:scenario][:tags].each do |tag|
119
- adapt_tag!(tag)
120
- end
121
- parsed_test['tags'].concat(parsed_test[:scenario].delete(:tags))
122
- end
88
+ def adapt_scenario(test_ast)
89
+ adapted_scenario = {}
123
90
 
124
- parsed_test['steps'] = []
125
- if parsed_test[:scenario][:steps]
126
- parsed_test[:scenario][:steps].each do |step|
127
- adapt_step!(step)
128
- end
129
- parsed_test['steps'].concat(parsed_test[:scenario].delete(:steps))
130
- end
131
- end
91
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
92
+ save_original_data(adapted_scenario, test_ast)
93
+ clear_child_elements(adapted_scenario, [[:scenario, :tags],
94
+ [:scenario, :steps]])
132
95
 
133
- # Adapts the AST sub-tree that is rooted at the given outline node.
134
- def adapt_outline!(parsed_test)
135
- # Removing parsed data for child elements in order to avoid duplicating data
136
- parsed_test['cuke_modeler_parsing_data'][:scenario][:tags] = nil if parsed_test['cuke_modeler_parsing_data'][:scenario][:tags]
137
- parsed_test['cuke_modeler_parsing_data'][:scenario][:steps] = nil if parsed_test['cuke_modeler_parsing_data'][:scenario][:steps]
138
- parsed_test['cuke_modeler_parsing_data'][:scenario][:examples] = nil if parsed_test['cuke_modeler_parsing_data'][:scenario][:examples]
139
-
140
- parsed_test['type'] = 'ScenarioOutline'
141
- parsed_test['keyword'] = parsed_test[:scenario].delete(:keyword)
142
- parsed_test['name'] = parsed_test[:scenario].delete(:name)
143
- parsed_test['description'] = parsed_test[:scenario].delete(:description) || ''
144
- parsed_test['line'] = parsed_test[:scenario].delete(:location)[:line]
145
-
146
- parsed_test['tags'] = []
147
- if parsed_test[:scenario][:tags]
148
- parsed_test[:scenario][:tags].each do |tag|
149
- adapt_tag!(tag)
150
- end
151
- parsed_test['tags'].concat(parsed_test[:scenario].delete(:tags))
152
- end
96
+ adapted_scenario['type'] = 'Scenario'
97
+ adapted_scenario['keyword'] = test_ast[:scenario][:keyword]
98
+ adapted_scenario['name'] = test_ast[:scenario][:name]
99
+ adapted_scenario['description'] = test_ast[:scenario][:description] || ''
100
+ adapted_scenario['line'] = test_ast[:scenario][:location][:line]
153
101
 
154
- parsed_test['steps'] = []
155
- if parsed_test[:scenario][:steps]
156
- parsed_test[:scenario][:steps].each do |step|
157
- adapt_step!(step)
158
- end
159
- parsed_test['steps'].concat(parsed_test[:scenario].delete(:steps))
160
- end
102
+ adapted_scenario['tags'] = adapt_tags(test_ast[:scenario])
103
+ adapted_scenario['steps'] = adapt_steps(test_ast[:scenario])
161
104
 
162
- parsed_test['examples'] = []
163
- if parsed_test[:scenario][:examples]
164
- parsed_test[:scenario][:examples].each do |step|
165
- adapt_example!(step)
166
- end
167
- parsed_test['examples'].concat(parsed_test[:scenario].delete(:examples))
168
- end
105
+ adapted_scenario
106
+ end
107
+
108
+ # Adapts the AST sub-tree that is rooted at the given outline node.
109
+ def adapt_outline(test_ast)
110
+ adapted_outline = {}
111
+
112
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
113
+ save_original_data(adapted_outline, test_ast)
114
+ clear_child_elements(adapted_outline, [[:scenario, :tags],
115
+ [:scenario, :steps],
116
+ [:scenario, :examples]])
117
+
118
+ adapted_outline['type'] = 'ScenarioOutline'
119
+ adapted_outline['keyword'] = test_ast[:scenario][:keyword]
120
+ adapted_outline['name'] = test_ast[:scenario][:name]
121
+ adapted_outline['description'] = test_ast[:scenario][:description] || ''
122
+ adapted_outline['line'] = test_ast[:scenario][:location][:line]
123
+
124
+ adapted_outline['tags'] = adapt_tags(test_ast[:scenario])
125
+ adapted_outline['steps'] = adapt_steps(test_ast[:scenario])
126
+ adapted_outline['examples'] = adapt_examples(test_ast[:scenario])
127
+
128
+ adapted_outline
169
129
  end
170
130
 
171
131
  # Adapts the AST sub-tree that is rooted at the given example node.
172
- def adapt_example!(parsed_example)
173
- # Saving off the original data
174
- parsed_example['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_example))
132
+ def adapt_example(example_ast)
133
+ adapted_example = {}
175
134
 
176
- # Removing parsed data for child elements in order to avoid duplicating data
177
- parsed_example['cuke_modeler_parsing_data'][:tags] = nil if parsed_example['cuke_modeler_parsing_data'][:tags]
178
- parsed_example['cuke_modeler_parsing_data'][:table_header] = nil if parsed_example['cuke_modeler_parsing_data'][:table_header]
179
- parsed_example['cuke_modeler_parsing_data'][:table_body] = nil if parsed_example['cuke_modeler_parsing_data'][:table_body]
135
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
136
+ save_original_data(adapted_example, example_ast)
137
+ clear_child_elements(adapted_example, [[:tags],
138
+ [:table_header],
139
+ [:table_body]])
180
140
 
181
- parsed_example['keyword'] = parsed_example.delete(:keyword)
182
- parsed_example['name'] = parsed_example.delete(:name)
183
- parsed_example['line'] = parsed_example.delete(:location)[:line]
184
- parsed_example['description'] = parsed_example.delete(:description) || ''
141
+ adapted_example['keyword'] = example_ast[:keyword]
142
+ adapted_example['name'] = example_ast[:name]
143
+ adapted_example['line'] = example_ast[:location][:line]
144
+ adapted_example['description'] = example_ast[:description] || ''
185
145
 
186
- parsed_example['rows'] = []
146
+ adapted_example['rows'] = []
147
+ adapted_example['rows'] << adapt_table_row(example_ast[:table_header]) if example_ast[:table_header]
187
148
 
188
- if parsed_example[:table_header]
189
- adapt_table_row!(parsed_example[:table_header])
190
- parsed_example['rows'] << parsed_example.delete(:table_header)
149
+ example_ast[:table_body]&.each do |row|
150
+ adapted_example['rows'] << adapt_table_row(row)
191
151
  end
192
152
 
193
- if parsed_example[:table_body]
194
- parsed_example[:table_body].each do |row|
195
- adapt_table_row!(row)
196
- end
197
- parsed_example['rows'].concat(parsed_example.delete(:table_body))
198
- end
153
+ adapted_example['tags'] = adapt_tags(example_ast)
199
154
 
200
- parsed_example['tags'] = []
201
- if parsed_example[:tags]
202
- parsed_example[:tags].each do |tag|
203
- adapt_tag!(tag)
204
- end
205
- parsed_example['tags'].concat(parsed_example.delete(:tags))
206
- end
155
+ adapted_example
207
156
  end
208
157
 
209
158
  # Adapts the AST sub-tree that is rooted at the given tag node.
210
- def adapt_tag!(parsed_tag)
159
+ def adapt_tag(tag_ast)
160
+ adapted_tag = {}
161
+
211
162
  # Saving off the original data
212
- parsed_tag['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_tag))
163
+ save_original_data(adapted_tag, tag_ast)
213
164
 
214
- parsed_tag['name'] = parsed_tag.delete(:name)
215
- parsed_tag['line'] = parsed_tag.delete(:location)[:line]
165
+ adapted_tag['name'] = tag_ast[:name]
166
+ adapted_tag['line'] = tag_ast[:location][:line]
167
+
168
+ adapted_tag
216
169
  end
217
170
 
218
171
  # Adapts the AST sub-tree that is rooted at the given comment node.
219
- def adapt_comment!(parsed_comment)
172
+ def adapt_comment(comment_ast)
173
+ adapted_comment = {}
174
+
220
175
  # Saving off the original data
221
- parsed_comment['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_comment))
176
+ save_original_data(adapted_comment, comment_ast)
222
177
 
223
- parsed_comment['text'] = parsed_comment.delete(:text)
224
- parsed_comment['line'] = parsed_comment.delete(:location)[:line]
178
+ adapted_comment['text'] = comment_ast[:text]
179
+ adapted_comment['line'] = comment_ast[:location][:line]
180
+
181
+ adapted_comment
225
182
  end
226
183
 
227
184
  # Adapts the AST sub-tree that is rooted at the given step node.
228
- def adapt_step!(parsed_step)
229
- # Saving off the original data
230
- parsed_step['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_step))
231
-
232
- # Removing parsed data for child elements in order to avoid duplicating data
233
- parsed_step['cuke_modeler_parsing_data'][:data_table] = nil if parsed_step['cuke_modeler_parsing_data'][:data_table]
234
- parsed_step['cuke_modeler_parsing_data'][:doc_string] = nil if parsed_step['cuke_modeler_parsing_data'][:doc_string]
235
-
236
- parsed_step['keyword'] = parsed_step.delete(:keyword)
237
- parsed_step['name'] = parsed_step.delete(:text)
238
- parsed_step['line'] = parsed_step.delete(:location)[:line]
239
-
240
- case
241
- when parsed_step[:doc_string]
242
- adapt_doc_string!(parsed_step[:doc_string])
243
- parsed_step['doc_string'] = parsed_step.delete(:doc_string)
244
- when parsed_step[:data_table]
245
- adapt_step_table!(parsed_step[:data_table])
246
- parsed_step['table'] = parsed_step.delete(:data_table)
247
- else
248
- # Step has no extra argument
185
+ def adapt_step(step_ast)
186
+ adapted_step = {}
187
+
188
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
189
+ save_original_data(adapted_step, step_ast)
190
+ clear_child_elements(adapted_step, [[:data_table],
191
+ [:doc_string]])
192
+
193
+ adapted_step['keyword'] = step_ast[:keyword]
194
+ adapted_step['name'] = step_ast[:text]
195
+ adapted_step['line'] = step_ast[:location][:line]
196
+
197
+ if step_ast[:doc_string]
198
+ adapted_step['doc_string'] = adapt_doc_string(step_ast[:doc_string])
199
+ elsif step_ast[:data_table]
200
+ adapted_step['table'] = adapt_step_table(step_ast[:data_table])
249
201
  end
202
+
203
+ adapted_step
250
204
  end
251
205
 
252
206
  # Adapts the AST sub-tree that is rooted at the given doc string node.
253
- def adapt_doc_string!(parsed_doc_string)
207
+ def adapt_doc_string(doc_string_ast)
208
+ adapted_doc_string = {}
209
+
254
210
  # Saving off the original data
255
- parsed_doc_string['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_doc_string))
211
+ save_original_data(adapted_doc_string, doc_string_ast)
212
+
213
+ adapted_doc_string['value'] = doc_string_ast[:content]
214
+ adapted_doc_string['content_type'] = doc_string_ast[:media_type]
215
+ adapted_doc_string['line'] = doc_string_ast[:location][:line]
256
216
 
257
- parsed_doc_string['value'] = parsed_doc_string.delete(:content)
258
- parsed_doc_string['content_type'] = parsed_doc_string.delete(:media_type)
259
- parsed_doc_string['line'] = parsed_doc_string.delete(:location)[:line]
217
+ adapted_doc_string
260
218
  end
261
219
 
262
220
  # Adapts the AST sub-tree that is rooted at the given table node.
263
- def adapt_step_table!(parsed_step_table)
264
- # Saving off the original data
265
- parsed_step_table['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_step_table))
221
+ def adapt_step_table(step_table_ast)
222
+ adapted_step_table = {}
266
223
 
267
- # Removing parsed data for child elements in order to avoid duplicating data
268
- parsed_step_table['cuke_modeler_parsing_data'][:rows] = nil
224
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
225
+ save_original_data(adapted_step_table, step_table_ast)
226
+ clear_child_elements(adapted_step_table, [[:rows]])
269
227
 
270
- parsed_step_table['rows'] = []
271
- parsed_step_table[:rows].each do |row|
272
- adapt_table_row!(row)
228
+ adapted_step_table['rows'] = []
229
+ step_table_ast[:rows].each do |row|
230
+ adapted_step_table['rows'] << adapt_table_row(row)
273
231
  end
274
- parsed_step_table['rows'].concat(parsed_step_table.delete(:rows))
275
- parsed_step_table['line'] = parsed_step_table.delete(:location)[:line]
232
+ adapted_step_table['line'] = step_table_ast[:location][:line]
233
+
234
+ adapted_step_table
276
235
  end
277
236
 
278
237
  # Adapts the AST sub-tree that is rooted at the given row node.
279
- def adapt_table_row!(parsed_table_row)
280
- # Saving off the original data
281
- parsed_table_row['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_table_row))
238
+ def adapt_table_row(table_row_ast)
239
+ adapted_table_row = {}
282
240
 
283
- # Removing parsed data for child elements in order to avoid duplicating data which the child elements will themselves include
284
- parsed_table_row['cuke_modeler_parsing_data'][:cells] = nil
241
+ # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
242
+ save_original_data(adapted_table_row, table_row_ast)
243
+ clear_child_elements(adapted_table_row, [[:cells]])
285
244
 
286
- parsed_table_row['line'] = parsed_table_row.delete(:location)[:line]
245
+ adapted_table_row['line'] = table_row_ast[:location][:line]
287
246
 
288
- parsed_table_row['cells'] = []
289
- parsed_table_row[:cells].each do |row|
290
- adapt_table_cell!(row)
247
+ adapted_table_row['cells'] = []
248
+ table_row_ast[:cells].each do |row|
249
+ adapted_table_row['cells'] << adapt_table_cell(row)
291
250
  end
292
- parsed_table_row['cells'].concat(parsed_table_row.delete(:cells))
251
+
252
+ adapted_table_row
293
253
  end
294
254
 
295
255
  # Adapts the AST sub-tree that is rooted at the given cell node.
296
- def adapt_table_cell!(parsed_cell)
256
+ def adapt_table_cell(cell_ast)
257
+ adapted_cell = {}
258
+
297
259
  # Saving off the original data
298
- parsed_cell['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_cell))
260
+ save_original_data(adapted_cell, cell_ast)
299
261
 
300
- parsed_cell['value'] = parsed_cell.delete(:value)
301
- parsed_cell['line'] = parsed_cell.delete(:location)[:line]
262
+ adapted_cell['value'] = cell_ast[:value]
263
+ adapted_cell['line'] = cell_ast[:location][:line]
264
+
265
+ adapted_cell
302
266
  end
303
267
 
304
268
 
305
269
  private
306
270
 
307
271
 
308
- def adapt_child_elements!(parsed_children)
309
- background_child = parsed_children.find { |child| child[:background] }
310
- rule_children = parsed_children.select { |child| child[:rule] }
311
- remaining_children = parsed_children.reject { |child| child[:background] || child[:rule] }
272
+ def adapt_comments(file_ast)
273
+ return [] unless file_ast[:comments]
274
+
275
+ file_ast[:comments].map { |comment| adapt_comment(comment) }
276
+ end
277
+
278
+ def adapt_tags(element_ast)
279
+ return [] unless element_ast[:tags]
280
+
281
+ element_ast[:tags].map { |tag| adapt_tag(tag) }
282
+ end
283
+
284
+ def adapt_steps(element_ast)
285
+ return [] unless element_ast[:steps]
286
+
287
+ element_ast[:steps].map { |step| adapt_step(step) }
288
+ end
289
+
290
+ def adapt_examples(element_ast)
291
+ return [] unless element_ast[:examples]
292
+
293
+ element_ast[:examples].map { |example| adapt_example(example) }
294
+ end
295
+
296
+ def adapt_child_elements(element_ast)
297
+ return [] unless element_ast[:children]
298
+
299
+ adapted_children = []
300
+
301
+ element_ast[:children].each do |child_element|
302
+ adapted_children << if child_element[:background]
303
+ adapt_background(child_element)
304
+ elsif child_element[:rule]
305
+ adapt_rule(child_element)
306
+ else
307
+ adapt_test(child_element)
308
+ end
309
+ end
312
310
 
313
- adapt_background!(background_child) if background_child
314
- adapt_rules!(rule_children)
315
- adapt_tests!(remaining_children)
311
+ adapted_children
316
312
  end
317
313
 
318
- def adapt_rules!(parsed_rules)
319
- parsed_rules.each do |rule|
320
- adapt_rule!(rule)
314
+ def adapt_test(test_ast)
315
+ if (test_node?(test_ast) && test_has_examples?(test_ast)) ||
316
+ (test_node?(test_ast) && test_uses_outline_keyword?(test_ast))
317
+
318
+ adapt_outline(test_ast)
319
+ elsif test_node?(test_ast)
320
+ adapt_scenario(test_ast)
321
+ else
322
+ raise(ArgumentError, "Unknown test type with keys: #{test_ast.keys}")
321
323
  end
322
324
  end
323
325
 
324
- def adapt_tests!(parsed_tests)
325
- parsed_tests.each do |test|
326
- adapt_test!(test)
326
+ def save_original_data(adapted_ast, raw_ast)
327
+ adapted_ast['cuke_modeler_parsing_data'] = Marshal.load(Marshal.dump(raw_ast))
328
+ end
329
+
330
+ def clear_child_elements(ast, child_paths)
331
+ child_paths.each do |traversal_path|
332
+ if ast['cuke_modeler_parsing_data'].dig(*traversal_path)
333
+ bury(ast['cuke_modeler_parsing_data'], traversal_path, nil)
334
+ end
327
335
  end
328
336
  end
329
337
 
330
- def adapt_test!(parsed_test)
331
- # Saving off the original data
332
- parsed_test['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_test))
333
-
334
- case
335
- when (parsed_test[:scenario] && parsed_test[:scenario][:examples]) || (parsed_test[:scenario] && Parsing.dialects[Parsing.dialect]['scenarioOutline'].include?(parsed_test[:scenario][:keyword]))
336
- adapt_outline!(parsed_test)
337
- when parsed_test[:scenario]
338
- adapt_scenario!(parsed_test)
339
- else
340
- raise(ArgumentError, "Unknown test type with keys: #{parsed_test.keys}")
338
+ def bury(hash, traversal_path, value)
339
+ keys = *traversal_path
340
+
341
+ current = hash
342
+ (keys.count - 1).times do |index|
343
+ current = hash[keys[index]]
341
344
  end
345
+
346
+ current[keys.last] = value
347
+ end
348
+
349
+ def test_node?(ast_node)
350
+ ast_node[:scenario]
351
+ end
352
+
353
+ def test_has_examples?(ast_node)
354
+ ast_node[:scenario][:examples]
355
+ end
356
+
357
+ def test_uses_outline_keyword?(test_ast)
358
+ Parsing.dialects[Parsing.dialect]['scenarioOutline'].include?(test_ast[:scenario][:keyword])
342
359
  end
343
360
 
344
361
  end
345
362
  end
363
+
364
+ # rubocop:enable Metrics/ClassLength, Metrics/AbcSize, Metrics/MethodLength