cuke_modeler 3.3.0 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
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