cuke_modeler 3.0.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +43 -1
- data/README.md +3 -2
- data/cuke_modeler.gemspec +24 -15
- data/lib/cuke_modeler.rb +2 -1
- data/lib/cuke_modeler/adapters/gherkin_10_adapter.rb +2 -1
- data/lib/cuke_modeler/adapters/gherkin_11_adapter.rb +2 -1
- data/lib/cuke_modeler/adapters/gherkin_12_adapter.rb +2 -1
- data/lib/cuke_modeler/adapters/gherkin_13_adapter.rb +2 -1
- data/lib/cuke_modeler/adapters/gherkin_14_adapter.rb +13 -0
- data/lib/cuke_modeler/adapters/gherkin_15_adapter.rb +13 -0
- data/lib/cuke_modeler/adapters/gherkin_16_adapter.rb +13 -0
- data/lib/cuke_modeler/adapters/gherkin_9_adapter.rb +267 -223
- data/lib/cuke_modeler/containing.rb +41 -89
- data/lib/cuke_modeler/described.rb +40 -1
- data/lib/cuke_modeler/models/background.rb +11 -11
- data/lib/cuke_modeler/models/cell.rb +13 -7
- data/lib/cuke_modeler/models/comment.rb +5 -5
- data/lib/cuke_modeler/models/directory.rb +13 -17
- data/lib/cuke_modeler/models/doc_string.rb +10 -7
- data/lib/cuke_modeler/models/example.rb +63 -45
- data/lib/cuke_modeler/models/feature.rb +37 -19
- data/lib/cuke_modeler/models/feature_file.rb +5 -7
- data/lib/cuke_modeler/models/model.rb +2 -1
- data/lib/cuke_modeler/models/outline.rb +19 -14
- data/lib/cuke_modeler/models/row.rb +10 -7
- data/lib/cuke_modeler/models/rule.rb +101 -0
- data/lib/cuke_modeler/models/scenario.rb +17 -12
- data/lib/cuke_modeler/models/step.rb +40 -18
- data/lib/cuke_modeler/models/table.rb +9 -6
- data/lib/cuke_modeler/models/tag.rb +9 -5
- data/lib/cuke_modeler/named.rb +5 -1
- data/lib/cuke_modeler/nested.rb +22 -18
- data/lib/cuke_modeler/parsed.rb +8 -0
- data/lib/cuke_modeler/parsing.rb +39 -29
- data/lib/cuke_modeler/sourceable.rb +8 -0
- data/lib/cuke_modeler/stepped.rb +8 -0
- data/lib/cuke_modeler/taggable.rb +9 -1
- data/lib/cuke_modeler/version.rb +1 -1
- data/testing/cucumber/features/modeling/feature_modeling.feature +28 -7
- data/testing/cucumber/features/modeling/feature_output.feature +45 -23
- data/testing/cucumber/features/modeling/rule_modeling.feature +108 -0
- data/testing/cucumber/features/modeling/rule_output.feature +111 -0
- metadata +61 -35
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ba3e400ea68f67944c0c5bf170179fb50a60237e507d3113a921c844010f7d91
|
|
4
|
+
data.tar.gz: 6224ca8ff2505acfb0415c82e9b1963ba5cf21573e2a9cac8857cb63171b288f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a447530c96482e6464ad31151262e5a0fbfd11527445ae8965c834e6ece15ee33f89381eb2276d7ee043823ab491eaed932bb35f492bfdb2fe8a43defd605e37
|
|
7
|
+
data.tar.gz: e8b05b052cad1f7395dec1e8da58bd84145c1699dce19b9bb261eb76bf29ceb1ebf3e7fa2cfe13773ff9f29f8c86d93543f9a5a34f79b1fc2fe402257618bbe7
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,43 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
|
|
9
9
|
Nothing yet...
|
|
10
10
|
|
|
11
|
+
## [3.5.0] - 2020-12-19
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- Support added for more versions of the `cucumber-gherkin` gem
|
|
15
|
+
- 16.x
|
|
16
|
+
|
|
17
|
+
## [3.4.0] - 2020-09-02
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- `Feature#has_background?` and `Rule#has_background?` now both have a more conventional name via the alias `#background?`
|
|
21
|
+
|
|
22
|
+
## [3.3.0] - 2020-08-15
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- Support added for more versions of the `cucumber-gherkin` gem
|
|
26
|
+
- 15.x
|
|
27
|
+
|
|
28
|
+
## [3.2.0] - 2020-07-27
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
- The `Rule` keyword is now a modeled element.
|
|
32
|
+
|
|
33
|
+
### Deprecated
|
|
34
|
+
- `Feature#test_case_count` will be removed on the next major release. It's a random analysis method in what is
|
|
35
|
+
otherwise a purely abstraction layer library. The [CQL](https://github.com/enkessler/cql) gem is better suited to such tasks.
|
|
36
|
+
|
|
37
|
+
## [3.1.0] - 2020-06-28
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
- Support added for more versions of the `cucumber-gherkin` gem
|
|
41
|
+
- 14.x
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
- Text is converted to UTF-8 encoding before being passed to the underlying Gherkin gem. This is due to UTF-8 being
|
|
45
|
+
the only encoding supported by Gherkin. The `gherkin` gem did the conversion automatically and so this conversion
|
|
46
|
+
was not necessary previously but the `cucumber-gherkin` gem does not do any automatic conversion.
|
|
47
|
+
|
|
11
48
|
## [3.0.0] - 2020-06-08
|
|
12
49
|
|
|
13
50
|
### Changed
|
|
@@ -303,7 +340,12 @@ Nothing yet...
|
|
|
303
340
|
- Initial release
|
|
304
341
|
|
|
305
342
|
|
|
306
|
-
[Unreleased]: https://github.com/enkessler/cuke_modeler/compare/v3.
|
|
343
|
+
[Unreleased]: https://github.com/enkessler/cuke_modeler/compare/v3.5.0...HEAD
|
|
344
|
+
[3.5.0]: https://github.com/enkessler/cuke_modeler/compare/v3.4.0...v3.5.0
|
|
345
|
+
[3.4.0]: https://github.com/enkessler/cuke_modeler/compare/v3.3.0...v3.4.0
|
|
346
|
+
[3.3.0]: https://github.com/enkessler/cuke_modeler/compare/v3.2.0...v3.3.0
|
|
347
|
+
[3.2.0]: https://github.com/enkessler/cuke_modeler/compare/v3.1.0...v3.2.0
|
|
348
|
+
[3.1.0]: https://github.com/enkessler/cuke_modeler/compare/v3.0.0...v3.1.0
|
|
307
349
|
[3.0.0]: https://github.com/enkessler/cuke_modeler/compare/v2.1.0...v3.0.0
|
|
308
350
|
[2.1.0]: https://github.com/enkessler/cuke_modeler/compare/v2.0.0...v2.1.0
|
|
309
351
|
[2.0.0]: https://github.com/enkessler/cuke_modeler/compare/v1.5.1...v2.0.0
|
data/README.md
CHANGED
|
@@ -82,13 +82,14 @@ and setting their attributes afterward.
|
|
|
82
82
|
One could, if so inclined, use this method to dynamically edit or even create
|
|
83
83
|
an entire test suite!
|
|
84
84
|
|
|
85
|
-
For more information on the different models
|
|
85
|
+
For more information on the different models (which more or less have the same relation
|
|
86
|
+
to each other as described in the AST [here](https://github.com/cucumber/cucumber/tree/master/gherkin#ast)) and how to use them, see the
|
|
86
87
|
[documentation](https://app.cucumber.pro/projects/cuke_modeler).
|
|
87
88
|
|
|
88
89
|
## Modeling dialects other than English
|
|
89
90
|
|
|
90
91
|
The modeling functionality provided by this gem will work with any dialect that
|
|
91
|
-
is supported by the **gherkin** gem. For modeling at the feature level or higher,
|
|
92
|
+
is supported by the **cucumber-gherkin** gem. For modeling at the feature level or higher,
|
|
92
93
|
no additional effort is needed because the `# language` header at the top of a
|
|
93
94
|
feature already indicates that a non-default dialect is being used.
|
|
94
95
|
|
data/cuke_modeler.gemspec
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
|
|
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 =
|
|
6
|
+
spec.name = 'cuke_modeler'
|
|
8
7
|
spec.version = CukeModeler::VERSION
|
|
9
|
-
spec.authors = [
|
|
10
|
-
spec.email = [
|
|
11
|
-
spec.summary =
|
|
12
|
-
spec.description =
|
|
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 =
|
|
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 = [
|
|
27
|
+
spec.require_paths = ['lib']
|
|
24
28
|
|
|
25
29
|
spec.required_ruby_version = '>= 2.3', '< 3.0'
|
|
26
30
|
|
|
27
|
-
spec.add_runtime_dependency 'cucumber-gherkin', '<
|
|
31
|
+
spec.add_runtime_dependency 'cucumber-gherkin', '< 17.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
|
data/lib/cuke_modeler.rb
CHANGED
|
@@ -4,7 +4,7 @@ module CukeModeler
|
|
|
4
4
|
end
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
require
|
|
7
|
+
require 'cuke_modeler/version'
|
|
8
8
|
|
|
9
9
|
require 'cuke_modeler/parsing'
|
|
10
10
|
require 'cuke_modeler/containing'
|
|
@@ -19,6 +19,7 @@ require 'cuke_modeler/models/model'
|
|
|
19
19
|
require 'cuke_modeler/models/feature_file'
|
|
20
20
|
require 'cuke_modeler/models/directory'
|
|
21
21
|
require 'cuke_modeler/models/feature'
|
|
22
|
+
require 'cuke_modeler/models/rule'
|
|
22
23
|
require 'cuke_modeler/models/background'
|
|
23
24
|
require 'cuke_modeler/models/scenario'
|
|
24
25
|
require 'cuke_modeler/models/outline'
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require_relative 'gherkin_9_adapter'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
module CukeModeler
|
|
5
|
+
|
|
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
|
|
8
|
+
# by this gem.
|
|
9
|
+
|
|
10
|
+
class Gherkin14Adapter < Gherkin9Adapter
|
|
11
|
+
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require_relative 'gherkin_9_adapter'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
module CukeModeler
|
|
5
|
+
|
|
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
|
|
8
|
+
# by this gem.
|
|
9
|
+
|
|
10
|
+
class Gherkin15Adapter < Gherkin9Adapter
|
|
11
|
+
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require_relative 'gherkin_9_adapter'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
module CukeModeler
|
|
5
|
+
|
|
6
|
+
# NOT A PART OF THE PUBLIC API
|
|
7
|
+
# An adapter that can convert the output of version 16.x of the *cucumber-gherkin* gem into input that is consumable
|
|
8
|
+
# by this gem.
|
|
9
|
+
|
|
10
|
+
class Gherkin16Adapter < Gherkin9Adapter
|
|
11
|
+
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -1,320 +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
|
|
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(
|
|
10
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
26
|
-
|
|
21
|
+
adapted_ast['comments'] = adapt_comments(ast)
|
|
22
|
+
adapted_ast['feature'] = adapt_feature(ast[:feature])
|
|
27
23
|
|
|
28
|
-
|
|
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
|
|
33
|
-
|
|
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
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
|
62
|
-
|
|
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
|
-
#
|
|
66
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
# Adapts the AST sub-tree that is rooted at the given
|
|
84
|
-
def
|
|
85
|
-
|
|
86
|
-
parsed_test['cuke_modeler_parsing_data'][:scenario][:tags] = nil if parsed_test['cuke_modeler_parsing_data'][:scenario][:tags]
|
|
87
|
-
parsed_test['cuke_modeler_parsing_data'][:scenario][:steps] = nil if parsed_test['cuke_modeler_parsing_data'][:scenario][:steps]
|
|
88
|
-
|
|
89
|
-
parsed_test['type'] = 'Scenario'
|
|
90
|
-
parsed_test['keyword'] = parsed_test[:scenario].delete(:keyword)
|
|
91
|
-
parsed_test['name'] = parsed_test[:scenario].delete(:name)
|
|
92
|
-
parsed_test['description'] = parsed_test[:scenario].delete(:description) || ''
|
|
93
|
-
parsed_test['line'] = parsed_test[:scenario].delete(:location)[:line]
|
|
94
|
-
|
|
95
|
-
parsed_test['tags'] = []
|
|
96
|
-
if parsed_test[:scenario][:tags]
|
|
97
|
-
parsed_test[:scenario][:tags].each do |tag|
|
|
98
|
-
adapt_tag!(tag)
|
|
99
|
-
end
|
|
100
|
-
parsed_test['tags'].concat(parsed_test[:scenario].delete(:tags))
|
|
101
|
-
end
|
|
68
|
+
# Adapts the AST sub-tree that is rooted at the given rule node.
|
|
69
|
+
def adapt_rule(rule_ast)
|
|
70
|
+
adapted_rule = {}
|
|
102
71
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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]])
|
|
75
|
+
|
|
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]
|
|
81
|
+
|
|
82
|
+
adapted_rule['elements'] = adapt_child_elements(rule_ast[:rule])
|
|
83
|
+
|
|
84
|
+
adapted_rule
|
|
110
85
|
end
|
|
111
86
|
|
|
112
|
-
# Adapts the AST sub-tree that is rooted at the given
|
|
113
|
-
def
|
|
114
|
-
|
|
115
|
-
parsed_test['cuke_modeler_parsing_data'][:scenario][:tags] = nil if parsed_test['cuke_modeler_parsing_data'][:scenario][:tags]
|
|
116
|
-
parsed_test['cuke_modeler_parsing_data'][:scenario][:steps] = nil if parsed_test['cuke_modeler_parsing_data'][:scenario][:steps]
|
|
117
|
-
parsed_test['cuke_modeler_parsing_data'][:scenario][:examples] = nil if parsed_test['cuke_modeler_parsing_data'][:scenario][:examples]
|
|
118
|
-
|
|
119
|
-
parsed_test['type'] = 'ScenarioOutline'
|
|
120
|
-
parsed_test['keyword'] = parsed_test[:scenario].delete(:keyword)
|
|
121
|
-
parsed_test['name'] = parsed_test[:scenario].delete(:name)
|
|
122
|
-
parsed_test['description'] = parsed_test[:scenario].delete(:description) || ''
|
|
123
|
-
parsed_test['line'] = parsed_test[:scenario].delete(:location)[:line]
|
|
124
|
-
|
|
125
|
-
parsed_test['tags'] = []
|
|
126
|
-
if parsed_test[:scenario][:tags]
|
|
127
|
-
parsed_test[:scenario][:tags].each do |tag|
|
|
128
|
-
adapt_tag!(tag)
|
|
129
|
-
end
|
|
130
|
-
parsed_test['tags'].concat(parsed_test[:scenario].delete(:tags))
|
|
131
|
-
end
|
|
87
|
+
# Adapts the AST sub-tree that is rooted at the given scenario node.
|
|
88
|
+
def adapt_scenario(test_ast)
|
|
89
|
+
adapted_scenario = {}
|
|
132
90
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
end
|
|
138
|
-
parsed_test['steps'].concat(parsed_test[:scenario].delete(:steps))
|
|
139
|
-
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]])
|
|
140
95
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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]
|
|
101
|
+
|
|
102
|
+
adapted_scenario['tags'] = adapt_tags(test_ast[:scenario])
|
|
103
|
+
adapted_scenario['steps'] = adapt_steps(test_ast[:scenario])
|
|
104
|
+
|
|
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
|
|
148
129
|
end
|
|
149
130
|
|
|
150
131
|
# Adapts the AST sub-tree that is rooted at the given example node.
|
|
151
|
-
def adapt_example
|
|
152
|
-
|
|
153
|
-
parsed_example['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_example))
|
|
132
|
+
def adapt_example(example_ast)
|
|
133
|
+
adapted_example = {}
|
|
154
134
|
|
|
155
|
-
#
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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]])
|
|
159
140
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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] || ''
|
|
164
145
|
|
|
165
|
-
|
|
146
|
+
adapted_example['rows'] = []
|
|
147
|
+
adapted_example['rows'] << adapt_table_row(example_ast[:table_header]) if example_ast[:table_header]
|
|
166
148
|
|
|
167
|
-
|
|
168
|
-
adapt_table_row
|
|
169
|
-
parsed_example['rows'] << parsed_example.delete(:table_header)
|
|
149
|
+
example_ast[:table_body]&.each do |row|
|
|
150
|
+
adapted_example['rows'] << adapt_table_row(row)
|
|
170
151
|
end
|
|
171
152
|
|
|
172
|
-
|
|
173
|
-
parsed_example[:table_body].each do |row|
|
|
174
|
-
adapt_table_row!(row)
|
|
175
|
-
end
|
|
176
|
-
parsed_example['rows'].concat(parsed_example.delete(:table_body))
|
|
177
|
-
end
|
|
153
|
+
adapted_example['tags'] = adapt_tags(example_ast)
|
|
178
154
|
|
|
179
|
-
|
|
180
|
-
if parsed_example[:tags]
|
|
181
|
-
parsed_example[:tags].each do |tag|
|
|
182
|
-
adapt_tag!(tag)
|
|
183
|
-
end
|
|
184
|
-
parsed_example['tags'].concat(parsed_example.delete(:tags))
|
|
185
|
-
end
|
|
155
|
+
adapted_example
|
|
186
156
|
end
|
|
187
157
|
|
|
188
158
|
# Adapts the AST sub-tree that is rooted at the given tag node.
|
|
189
|
-
def adapt_tag
|
|
159
|
+
def adapt_tag(tag_ast)
|
|
160
|
+
adapted_tag = {}
|
|
161
|
+
|
|
190
162
|
# Saving off the original data
|
|
191
|
-
|
|
163
|
+
save_original_data(adapted_tag, tag_ast)
|
|
164
|
+
|
|
165
|
+
adapted_tag['name'] = tag_ast[:name]
|
|
166
|
+
adapted_tag['line'] = tag_ast[:location][:line]
|
|
192
167
|
|
|
193
|
-
|
|
194
|
-
parsed_tag['line'] = parsed_tag.delete(:location)[:line]
|
|
168
|
+
adapted_tag
|
|
195
169
|
end
|
|
196
170
|
|
|
197
171
|
# Adapts the AST sub-tree that is rooted at the given comment node.
|
|
198
|
-
def adapt_comment
|
|
172
|
+
def adapt_comment(comment_ast)
|
|
173
|
+
adapted_comment = {}
|
|
174
|
+
|
|
199
175
|
# Saving off the original data
|
|
200
|
-
|
|
176
|
+
save_original_data(adapted_comment, comment_ast)
|
|
177
|
+
|
|
178
|
+
adapted_comment['text'] = comment_ast[:text]
|
|
179
|
+
adapted_comment['line'] = comment_ast[:location][:line]
|
|
201
180
|
|
|
202
|
-
|
|
203
|
-
parsed_comment['line'] = parsed_comment.delete(:location)[:line]
|
|
181
|
+
adapted_comment
|
|
204
182
|
end
|
|
205
183
|
|
|
206
184
|
# Adapts the AST sub-tree that is rooted at the given step node.
|
|
207
|
-
def adapt_step
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
when parsed_step[:data_table]
|
|
224
|
-
adapt_step_table!(parsed_step[:data_table])
|
|
225
|
-
parsed_step['table'] = parsed_step.delete(:data_table)
|
|
226
|
-
else
|
|
227
|
-
# 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])
|
|
228
201
|
end
|
|
202
|
+
|
|
203
|
+
adapted_step
|
|
229
204
|
end
|
|
230
205
|
|
|
231
206
|
# Adapts the AST sub-tree that is rooted at the given doc string node.
|
|
232
|
-
def adapt_doc_string
|
|
207
|
+
def adapt_doc_string(doc_string_ast)
|
|
208
|
+
adapted_doc_string = {}
|
|
209
|
+
|
|
233
210
|
# Saving off the original data
|
|
234
|
-
|
|
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]
|
|
235
216
|
|
|
236
|
-
|
|
237
|
-
parsed_doc_string['content_type'] = parsed_doc_string.delete(:media_type)
|
|
238
|
-
parsed_doc_string['line'] = parsed_doc_string.delete(:location)[:line]
|
|
217
|
+
adapted_doc_string
|
|
239
218
|
end
|
|
240
219
|
|
|
241
220
|
# Adapts the AST sub-tree that is rooted at the given table node.
|
|
242
|
-
def adapt_step_table
|
|
243
|
-
|
|
244
|
-
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 = {}
|
|
245
223
|
|
|
246
|
-
#
|
|
247
|
-
|
|
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]])
|
|
248
227
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
adapt_table_row
|
|
228
|
+
adapted_step_table['rows'] = []
|
|
229
|
+
step_table_ast[:rows].each do |row|
|
|
230
|
+
adapted_step_table['rows'] << adapt_table_row(row)
|
|
252
231
|
end
|
|
253
|
-
|
|
254
|
-
|
|
232
|
+
adapted_step_table['line'] = step_table_ast[:location][:line]
|
|
233
|
+
|
|
234
|
+
adapted_step_table
|
|
255
235
|
end
|
|
256
236
|
|
|
257
237
|
# Adapts the AST sub-tree that is rooted at the given row node.
|
|
258
|
-
def adapt_table_row
|
|
259
|
-
|
|
260
|
-
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 = {}
|
|
261
240
|
|
|
262
|
-
#
|
|
263
|
-
|
|
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]])
|
|
264
244
|
|
|
265
|
-
|
|
245
|
+
adapted_table_row['line'] = table_row_ast[:location][:line]
|
|
266
246
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
adapt_table_cell
|
|
247
|
+
adapted_table_row['cells'] = []
|
|
248
|
+
table_row_ast[:cells].each do |row|
|
|
249
|
+
adapted_table_row['cells'] << adapt_table_cell(row)
|
|
270
250
|
end
|
|
271
|
-
|
|
251
|
+
|
|
252
|
+
adapted_table_row
|
|
272
253
|
end
|
|
273
254
|
|
|
274
255
|
# Adapts the AST sub-tree that is rooted at the given cell node.
|
|
275
|
-
def adapt_table_cell
|
|
256
|
+
def adapt_table_cell(cell_ast)
|
|
257
|
+
adapted_cell = {}
|
|
258
|
+
|
|
276
259
|
# Saving off the original data
|
|
277
|
-
|
|
260
|
+
save_original_data(adapted_cell, cell_ast)
|
|
278
261
|
|
|
279
|
-
|
|
280
|
-
|
|
262
|
+
adapted_cell['value'] = cell_ast[:value]
|
|
263
|
+
adapted_cell['line'] = cell_ast[:location][:line]
|
|
264
|
+
|
|
265
|
+
adapted_cell
|
|
281
266
|
end
|
|
282
267
|
|
|
283
268
|
|
|
284
269
|
private
|
|
285
270
|
|
|
286
271
|
|
|
287
|
-
def
|
|
288
|
-
|
|
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]
|
|
289
286
|
|
|
290
|
-
|
|
291
|
-
|
|
287
|
+
element_ast[:steps].map { |step| adapt_step(step) }
|
|
288
|
+
end
|
|
292
289
|
|
|
293
|
-
|
|
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
|
|
294
309
|
end
|
|
295
310
|
|
|
296
|
-
|
|
311
|
+
adapted_children
|
|
297
312
|
end
|
|
298
313
|
|
|
299
|
-
def
|
|
300
|
-
|
|
301
|
-
|
|
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}")
|
|
302
323
|
end
|
|
303
324
|
end
|
|
304
325
|
|
|
305
|
-
def
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
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]]
|
|
316
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])
|
|
317
359
|
end
|
|
318
360
|
|
|
319
361
|
end
|
|
320
362
|
end
|
|
363
|
+
|
|
364
|
+
# rubocop:enable Metrics/ClassLength, Metrics/AbcSize, Metrics/MethodLength
|