cuke_modeler 3.8.0 → 3.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +39 -2
- data/README.md +1 -2
- data/cuke_modeler.gemspec +3 -4
- data/lib/cuke_modeler/adapters/gherkin_18_adapter.rb +12 -1
- data/lib/cuke_modeler/adapters/gherkin_19_adapter.rb +61 -0
- data/lib/cuke_modeler/adapters/gherkin_20_adapter.rb +337 -0
- data/lib/cuke_modeler/adapters/gherkin_21_adapter.rb +13 -0
- data/lib/cuke_modeler/adapters/gherkin_9_adapter.rb +6 -7
- data/lib/cuke_modeler/adapters/gherkin_base_adapter.rb +14 -0
- data/lib/cuke_modeler/containing.rb +1 -0
- data/lib/cuke_modeler/models/rule.rb +8 -1
- data/lib/cuke_modeler/nested.rb +1 -0
- data/lib/cuke_modeler/parsing.rb +40 -2
- data/lib/cuke_modeler/version.rb +1 -1
- data/testing/cucumber/features/modeling/rule_modeling.feature +44 -2
- data/testing/cucumber/features/modeling/rule_output.feature +4 -0
- metadata +26 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 35f7177528b87ec447574a0549a13bf63515e9f99a2782fd8f9e784e83fd76e3
|
4
|
+
data.tar.gz: ed28248635db6207feeb4e290604832eabef6dde42ba30726c1b125dce64017d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fbc9729e61a869d98331f7046208aad43d9377079e1411abcaf02561fa03dde35286887ac90edd41e6c9b801e56ece769bb9041b4d28c356581ccd71da5298ea
|
7
|
+
data.tar.gz: e13737018ae92f43e9474b5c1612beb95a3939008aa30f41e1b3563f6f8a7e39af10446f5913314e53d944650f7443279f0e952d568e15d13b9b2d352a59042b
|
data/CHANGELOG.md
CHANGED
@@ -6,7 +6,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
-
|
9
|
+
Nothing yet...
|
10
|
+
|
11
|
+
## [3.12.0] - 2021-09-09
|
12
|
+
|
13
|
+
### Added
|
14
|
+
- Support added for more versions of the `cucumber-gherkin` gem
|
15
|
+
- 21.x
|
16
|
+
|
17
|
+
## [3.11.0] - 2021-07-31
|
18
|
+
|
19
|
+
### Added
|
20
|
+
- Support added for more versions of the `cucumber-gherkin` gem
|
21
|
+
- 20.x
|
22
|
+
|
23
|
+
### Fixed
|
24
|
+
- `Rule` models are now returnable from `Model#get_ancestor()`. Updating that method was missed when `Rule` models
|
25
|
+
were first added.
|
26
|
+
|
27
|
+
## [3.10.0] - 2021-05-28
|
28
|
+
|
29
|
+
### Added
|
30
|
+
- Support added for more versions of the `cucumber-gherkin` gem
|
31
|
+
- 19.x
|
32
|
+
|
33
|
+
### Fixed
|
34
|
+
- `Rule` models now clear out parsing data for their tags. This data was an unintentional duplication of the
|
35
|
+
parsing data that the `Tag` models already contained.
|
36
|
+
|
37
|
+
|
38
|
+
## [3.9.0] - 2021-04-23
|
39
|
+
|
40
|
+
### Added
|
41
|
+
- `Rule` models are now taggable elements
|
42
|
+
|
10
43
|
|
11
44
|
## [3.8.0] - 2021-04-18
|
12
45
|
|
@@ -369,7 +402,11 @@ Noting yet...
|
|
369
402
|
- Initial release
|
370
403
|
|
371
404
|
|
372
|
-
[Unreleased]: https://github.com/enkessler/cuke_modeler/compare/v3.
|
405
|
+
[Unreleased]: https://github.com/enkessler/cuke_modeler/compare/v3.12.0...HEAD
|
406
|
+
[3.12.0]: https://github.com/enkessler/cuke_modeler/compare/v3.11.0...v3.12.0
|
407
|
+
[3.11.0]: https://github.com/enkessler/cuke_modeler/compare/v3.10.0...v3.11.0
|
408
|
+
[3.10.0]: https://github.com/enkessler/cuke_modeler/compare/v3.9.0...v3.10.0
|
409
|
+
[3.9.0]: https://github.com/enkessler/cuke_modeler/compare/v3.8.0...v3.9.0
|
373
410
|
[3.8.0]: https://github.com/enkessler/cuke_modeler/compare/v3.7.0...v3.8.0
|
374
411
|
[3.7.0]: https://github.com/enkessler/cuke_modeler/compare/v3.6.0...v3.7.0
|
375
412
|
[3.6.0]: https://github.com/enkessler/cuke_modeler/compare/v3.5.0...v3.6.0
|
data/README.md
CHANGED
@@ -8,8 +8,7 @@ User stuff:
|
|
8
8
|
[![Yard Docs](http://img.shields.io/badge/Documentation-API-blue.svg)](https://www.rubydoc.info/gems/cuke_modeler)
|
9
9
|
|
10
10
|
Developer stuff:
|
11
|
-
[![Build Status](https://
|
12
|
-
[![Build status](https://ci.appveyor.com/api/projects/status/is8xqvoqn3pjh9l0/branch/master?svg=true)](https://ci.appveyor.com/project/enkessler/cuke-modeler)
|
11
|
+
[![Build Status](https://github.com/enkessler/cuke_modeler/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/enkessler/cuke_modeler/actions/workflows/ci.yml?query=branch%3Amaster)
|
13
12
|
[![Coverage Status](https://coveralls.io/repos/github/enkessler/cuke_modeler/badge.svg?branch=master)](https://coveralls.io/github/enkessler/cuke_modeler?branch=master)
|
14
13
|
[![Maintainability](https://api.codeclimate.com/v1/badges/83986d8f7a918fed9707/maintainability)](https://codeclimate.com/github/enkessler/cuke_modeler/maintainability)
|
15
14
|
[![Inline docs](http://inch-ci.org/github/enkessler/cuke_modeler.svg?branch=master)](https://inch-ci.org/github/enkessler/cuke_modeler)
|
data/cuke_modeler.gemspec
CHANGED
@@ -36,11 +36,10 @@ Gem::Specification.new do |spec|
|
|
36
36
|
|
37
37
|
spec.required_ruby_version = '>= 2.3', '< 4.0'
|
38
38
|
|
39
|
-
spec.add_runtime_dependency 'cucumber-gherkin', '<
|
39
|
+
spec.add_runtime_dependency 'cucumber-gherkin', '< 22.0'
|
40
40
|
|
41
41
|
spec.add_development_dependency 'bundler', '< 3.0'
|
42
42
|
spec.add_development_dependency 'childprocess', '< 5.0'
|
43
|
-
spec.add_development_dependency 'coveralls', '< 1.0.0'
|
44
43
|
spec.add_development_dependency 'ffi', '< 2.0' # This is an invisible dependency for the `childprocess` gem on Windows
|
45
44
|
# Cucumber 4.x is the earliest version to use cucumber-gherkin
|
46
45
|
spec.add_development_dependency 'cucumber', '>= 4.0.0', '< 7.0.0'
|
@@ -50,8 +49,8 @@ Gem::Specification.new do |spec|
|
|
50
49
|
# RuboCop drops Ruby 2.3 support after this version and we need to maintain Ruby 2.3 compatibility when writing code
|
51
50
|
# for this gem
|
52
51
|
spec.add_development_dependency 'rubocop', '< 0.82.0'
|
53
|
-
|
54
|
-
spec.add_development_dependency 'simplecov', '
|
52
|
+
spec.add_development_dependency 'simplecov', '< 1.0'
|
53
|
+
spec.add_development_dependency 'simplecov-lcov', '< 1.0'
|
55
54
|
spec.add_development_dependency 'test-unit', '< 4.0.0'
|
56
55
|
spec.add_development_dependency 'yard', '< 1.0'
|
57
56
|
end
|
@@ -6,8 +6,19 @@ module CukeModeler
|
|
6
6
|
# NOT A PART OF THE PUBLIC API
|
7
7
|
# An adapter that can convert the output of version 18.x of the *cucumber-gherkin* gem into input that is consumable
|
8
8
|
# by this gem.
|
9
|
-
|
10
9
|
class Gherkin18Adapter < Gherkin9Adapter
|
11
10
|
|
11
|
+
# Adapts the AST sub-tree that is rooted at the given rule node.
|
12
|
+
def adapt_rule(rule_ast)
|
13
|
+
adapted_rule = super(rule_ast)
|
14
|
+
|
15
|
+
clear_child_elements(adapted_rule, [[:rule, :tags]])
|
16
|
+
|
17
|
+
# Tagging of Rules was added in Gherkin 18
|
18
|
+
adapted_rule['tags'] = adapt_tags(rule_ast[:rule])
|
19
|
+
|
20
|
+
adapted_rule
|
21
|
+
end
|
22
|
+
|
12
23
|
end
|
13
24
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require_relative 'gherkin_18_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 19.x of the *cucumber-gherkin* gem into input that is consumable
|
8
|
+
# by this gem.
|
9
|
+
class Gherkin19Adapter < Gherkin18Adapter
|
10
|
+
|
11
|
+
# Adapts the AST sub-tree that is rooted at the given step node.
|
12
|
+
def adapt_step(step_ast)
|
13
|
+
adapted_step = super(step_ast)
|
14
|
+
|
15
|
+
clear_child_elements(adapted_step, [[:dataTable],
|
16
|
+
[:docString]])
|
17
|
+
|
18
|
+
if step_ast[:docString]
|
19
|
+
adapted_step['doc_string'] = adapt_doc_string(step_ast[:docString])
|
20
|
+
elsif step_ast[:dataTable]
|
21
|
+
adapted_step['table'] = adapt_step_table(step_ast[:dataTable])
|
22
|
+
end
|
23
|
+
|
24
|
+
adapted_step
|
25
|
+
end
|
26
|
+
|
27
|
+
# Adapts the AST sub-tree that is rooted at the given doc string node.
|
28
|
+
def adapt_doc_string(doc_string_ast)
|
29
|
+
adapted_doc_string = super(doc_string_ast)
|
30
|
+
|
31
|
+
adapted_doc_string['content_type'] = doc_string_ast[:mediaType]
|
32
|
+
|
33
|
+
adapted_doc_string
|
34
|
+
end
|
35
|
+
|
36
|
+
# Adapts the AST sub-tree that is rooted at the given example node.
|
37
|
+
def adapt_example(example_ast)
|
38
|
+
adapted_example = super(example_ast)
|
39
|
+
|
40
|
+
clear_child_elements(adapted_example, [[:tableHeader],
|
41
|
+
[:tableBody]])
|
42
|
+
|
43
|
+
adapted_example['rows'] << adapt_table_row(example_ast[:tableHeader]) if example_ast[:tableHeader]
|
44
|
+
|
45
|
+
example_ast[:tableBody].each do |row|
|
46
|
+
adapted_example['rows'] << adapt_table_row(row)
|
47
|
+
end
|
48
|
+
|
49
|
+
adapted_example
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
|
56
|
+
def test_has_examples?(ast_node)
|
57
|
+
ast_node[:scenario][:examples].any?
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,337 @@
|
|
1
|
+
require_relative 'gherkin_base_adapter'
|
2
|
+
|
3
|
+
# Some things just aren't going to get better due to the inherent complexity of the AST
|
4
|
+
# rubocop:disable Metrics/ClassLength, Metrics/AbcSize, Metrics/MethodLength
|
5
|
+
|
6
|
+
module CukeModeler
|
7
|
+
|
8
|
+
# NOT A PART OF THE PUBLIC API
|
9
|
+
# An adapter that can convert the output of version 20.x of the *cucumber-gherkin* gem into input that is consumable
|
10
|
+
# by this gem.
|
11
|
+
class Gherkin20Adapter < GherkinBaseAdapter
|
12
|
+
|
13
|
+
# Adapts the given AST into the shape that this gem expects
|
14
|
+
def adapt(ast)
|
15
|
+
adapted_ast = {}
|
16
|
+
|
17
|
+
# Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
|
18
|
+
save_original_data(adapted_ast, ast)
|
19
|
+
clear_child_elements(adapted_ast, [[:feature], [:comments]])
|
20
|
+
|
21
|
+
adapted_ast['comments'] = adapt_comments(ast)
|
22
|
+
adapted_ast['feature'] = adapt_feature(ast.feature)
|
23
|
+
|
24
|
+
adapted_ast
|
25
|
+
end
|
26
|
+
|
27
|
+
# Adapts the AST sub-tree that is rooted at the given feature node.
|
28
|
+
def adapt_feature(feature_ast)
|
29
|
+
return nil unless feature_ast
|
30
|
+
|
31
|
+
adapted_feature = {}
|
32
|
+
|
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]])
|
37
|
+
|
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
|
42
|
+
|
43
|
+
adapted_feature['elements'] = adapt_child_elements(feature_ast)
|
44
|
+
adapted_feature['tags'] = adapt_tags(feature_ast)
|
45
|
+
|
46
|
+
adapted_feature
|
47
|
+
end
|
48
|
+
|
49
|
+
# Adapts the AST sub-tree that is rooted at the given background node.
|
50
|
+
def adapt_background(background_ast)
|
51
|
+
adapted_background = {}
|
52
|
+
|
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]])
|
56
|
+
|
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
|
62
|
+
|
63
|
+
adapted_background['steps'] = adapt_steps(background_ast.background)
|
64
|
+
|
65
|
+
adapted_background
|
66
|
+
end
|
67
|
+
|
68
|
+
# Adapts the AST sub-tree that is rooted at the given rule node.
|
69
|
+
def adapt_rule(rule_ast)
|
70
|
+
adapted_rule = {}
|
71
|
+
|
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, :tags],
|
75
|
+
[:rule, :children]])
|
76
|
+
|
77
|
+
adapted_rule['type'] = 'Rule'
|
78
|
+
adapted_rule['keyword'] = rule_ast.rule.keyword
|
79
|
+
adapted_rule['name'] = rule_ast.rule.name
|
80
|
+
adapted_rule['description'] = rule_ast.rule.description
|
81
|
+
adapted_rule['line'] = rule_ast.rule.location.line
|
82
|
+
|
83
|
+
adapted_rule['elements'] = adapt_child_elements(rule_ast.rule)
|
84
|
+
adapted_rule['tags'] = adapt_tags(rule_ast.rule)
|
85
|
+
|
86
|
+
adapted_rule
|
87
|
+
end
|
88
|
+
|
89
|
+
# Adapts the AST sub-tree that is rooted at the given scenario node.
|
90
|
+
def adapt_scenario(test_ast)
|
91
|
+
adapted_scenario = {}
|
92
|
+
|
93
|
+
# Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
|
94
|
+
save_original_data(adapted_scenario, test_ast)
|
95
|
+
clear_child_elements(adapted_scenario, [[:scenario, :tags],
|
96
|
+
[:scenario, :steps]])
|
97
|
+
|
98
|
+
adapted_scenario['type'] = 'Scenario'
|
99
|
+
adapted_scenario['keyword'] = test_ast.scenario.keyword
|
100
|
+
adapted_scenario['name'] = test_ast.scenario.name
|
101
|
+
adapted_scenario['description'] = test_ast.scenario.description
|
102
|
+
adapted_scenario['line'] = test_ast.scenario.location.line
|
103
|
+
|
104
|
+
adapted_scenario['tags'] = adapt_tags(test_ast.scenario)
|
105
|
+
adapted_scenario['steps'] = adapt_steps(test_ast.scenario)
|
106
|
+
|
107
|
+
adapted_scenario
|
108
|
+
end
|
109
|
+
|
110
|
+
# Adapts the AST sub-tree that is rooted at the given outline node.
|
111
|
+
def adapt_outline(test_ast)
|
112
|
+
adapted_outline = {}
|
113
|
+
|
114
|
+
# Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
|
115
|
+
save_original_data(adapted_outline, test_ast)
|
116
|
+
clear_child_elements(adapted_outline, [[:scenario, :tags],
|
117
|
+
[:scenario, :steps],
|
118
|
+
[:scenario, :examples]])
|
119
|
+
|
120
|
+
adapted_outline['type'] = 'ScenarioOutline'
|
121
|
+
adapted_outline['keyword'] = test_ast.scenario.keyword
|
122
|
+
adapted_outline['name'] = test_ast.scenario.name
|
123
|
+
adapted_outline['description'] = test_ast.scenario.description
|
124
|
+
adapted_outline['line'] = test_ast.scenario.location.line
|
125
|
+
|
126
|
+
adapted_outline['tags'] = adapt_tags(test_ast.scenario)
|
127
|
+
adapted_outline['steps'] = adapt_steps(test_ast.scenario)
|
128
|
+
adapted_outline['examples'] = adapt_examples(test_ast.scenario)
|
129
|
+
|
130
|
+
adapted_outline
|
131
|
+
end
|
132
|
+
|
133
|
+
# Adapts the AST sub-tree that is rooted at the given example node.
|
134
|
+
def adapt_example(example_ast)
|
135
|
+
adapted_example = {}
|
136
|
+
|
137
|
+
# Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
|
138
|
+
save_original_data(adapted_example, example_ast)
|
139
|
+
clear_child_elements(adapted_example, [[:tags],
|
140
|
+
[:table_header],
|
141
|
+
[:table_body]])
|
142
|
+
|
143
|
+
adapted_example['keyword'] = example_ast.keyword
|
144
|
+
adapted_example['name'] = example_ast.name
|
145
|
+
adapted_example['line'] = example_ast.location.line
|
146
|
+
adapted_example['description'] = example_ast.description
|
147
|
+
|
148
|
+
adapted_example['rows'] = []
|
149
|
+
adapted_example['rows'] << adapt_table_row(example_ast.table_header) if example_ast.table_header
|
150
|
+
|
151
|
+
example_ast.table_body&.each do |row|
|
152
|
+
adapted_example['rows'] << adapt_table_row(row)
|
153
|
+
end
|
154
|
+
|
155
|
+
adapted_example['tags'] = adapt_tags(example_ast)
|
156
|
+
|
157
|
+
adapted_example
|
158
|
+
end
|
159
|
+
|
160
|
+
# Adapts the AST sub-tree that is rooted at the given tag node.
|
161
|
+
def adapt_tag(tag_ast)
|
162
|
+
adapted_tag = {}
|
163
|
+
|
164
|
+
# Saving off the original data
|
165
|
+
save_original_data(adapted_tag, tag_ast)
|
166
|
+
|
167
|
+
adapted_tag['name'] = tag_ast.name
|
168
|
+
adapted_tag['line'] = tag_ast.location.line
|
169
|
+
|
170
|
+
adapted_tag
|
171
|
+
end
|
172
|
+
|
173
|
+
# Adapts the AST sub-tree that is rooted at the given comment node.
|
174
|
+
def adapt_comment(comment_ast)
|
175
|
+
adapted_comment = {}
|
176
|
+
|
177
|
+
# Saving off the original data
|
178
|
+
save_original_data(adapted_comment, comment_ast)
|
179
|
+
|
180
|
+
adapted_comment['text'] = comment_ast.text
|
181
|
+
adapted_comment['line'] = comment_ast.location.line
|
182
|
+
|
183
|
+
adapted_comment
|
184
|
+
end
|
185
|
+
|
186
|
+
# Adapts the AST sub-tree that is rooted at the given step node.
|
187
|
+
def adapt_step(step_ast)
|
188
|
+
adapted_step = {}
|
189
|
+
|
190
|
+
# Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
|
191
|
+
save_original_data(adapted_step, step_ast)
|
192
|
+
clear_child_elements(adapted_step, [[:data_table],
|
193
|
+
[:doc_string]])
|
194
|
+
|
195
|
+
adapted_step['keyword'] = step_ast.keyword
|
196
|
+
adapted_step['name'] = step_ast.text
|
197
|
+
adapted_step['line'] = step_ast.location.line
|
198
|
+
|
199
|
+
if step_ast.doc_string
|
200
|
+
adapted_step['doc_string'] = adapt_doc_string(step_ast.doc_string)
|
201
|
+
elsif step_ast.data_table
|
202
|
+
adapted_step['table'] = adapt_step_table(step_ast.data_table)
|
203
|
+
end
|
204
|
+
|
205
|
+
adapted_step
|
206
|
+
end
|
207
|
+
|
208
|
+
# Adapts the AST sub-tree that is rooted at the given doc string node.
|
209
|
+
def adapt_doc_string(doc_string_ast)
|
210
|
+
adapted_doc_string = {}
|
211
|
+
|
212
|
+
# Saving off the original data
|
213
|
+
save_original_data(adapted_doc_string, doc_string_ast)
|
214
|
+
|
215
|
+
adapted_doc_string['value'] = doc_string_ast.content
|
216
|
+
adapted_doc_string['content_type'] = doc_string_ast.media_type
|
217
|
+
adapted_doc_string['line'] = doc_string_ast.location.line
|
218
|
+
|
219
|
+
adapted_doc_string
|
220
|
+
end
|
221
|
+
|
222
|
+
# Adapts the AST sub-tree that is rooted at the given table node.
|
223
|
+
def adapt_step_table(step_table_ast)
|
224
|
+
adapted_step_table = {}
|
225
|
+
|
226
|
+
# Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
|
227
|
+
save_original_data(adapted_step_table, step_table_ast)
|
228
|
+
clear_child_elements(adapted_step_table, [[:rows]])
|
229
|
+
|
230
|
+
adapted_step_table['rows'] = []
|
231
|
+
step_table_ast.rows.each do |row|
|
232
|
+
adapted_step_table['rows'] << adapt_table_row(row)
|
233
|
+
end
|
234
|
+
adapted_step_table['line'] = step_table_ast.location.line
|
235
|
+
|
236
|
+
adapted_step_table
|
237
|
+
end
|
238
|
+
|
239
|
+
# Adapts the AST sub-tree that is rooted at the given row node.
|
240
|
+
def adapt_table_row(table_row_ast)
|
241
|
+
adapted_table_row = {}
|
242
|
+
|
243
|
+
# Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
|
244
|
+
save_original_data(adapted_table_row, table_row_ast)
|
245
|
+
clear_child_elements(adapted_table_row, [[:cells]])
|
246
|
+
|
247
|
+
adapted_table_row['line'] = table_row_ast.location.line
|
248
|
+
|
249
|
+
adapted_table_row['cells'] = []
|
250
|
+
table_row_ast.cells.each do |row|
|
251
|
+
adapted_table_row['cells'] << adapt_table_cell(row)
|
252
|
+
end
|
253
|
+
|
254
|
+
adapted_table_row
|
255
|
+
end
|
256
|
+
|
257
|
+
# Adapts the AST sub-tree that is rooted at the given cell node.
|
258
|
+
def adapt_table_cell(cell_ast)
|
259
|
+
adapted_cell = {}
|
260
|
+
|
261
|
+
# Saving off the original data
|
262
|
+
save_original_data(adapted_cell, cell_ast)
|
263
|
+
|
264
|
+
adapted_cell['value'] = cell_ast.value
|
265
|
+
adapted_cell['line'] = cell_ast.location.line
|
266
|
+
|
267
|
+
adapted_cell
|
268
|
+
end
|
269
|
+
|
270
|
+
|
271
|
+
private
|
272
|
+
|
273
|
+
|
274
|
+
def adapt_comments(file_ast)
|
275
|
+
file_ast.comments.map { |comment| adapt_comment(comment) }
|
276
|
+
end
|
277
|
+
|
278
|
+
def adapt_tags(element_ast)
|
279
|
+
element_ast.tags.map { |tag| adapt_tag(tag) }
|
280
|
+
end
|
281
|
+
|
282
|
+
def adapt_steps(element_ast)
|
283
|
+
element_ast.steps.map { |step| adapt_step(step) }
|
284
|
+
end
|
285
|
+
|
286
|
+
def adapt_examples(element_ast)
|
287
|
+
element_ast.examples.map { |example| adapt_example(example) }
|
288
|
+
end
|
289
|
+
|
290
|
+
def adapt_child_elements(element_ast)
|
291
|
+
adapted_children = []
|
292
|
+
|
293
|
+
element_ast.children.each do |child_element|
|
294
|
+
adapted_children << if child_element.background
|
295
|
+
adapt_background(child_element)
|
296
|
+
elsif child_element.respond_to?(:rule) && child_element.rule
|
297
|
+
adapt_rule(child_element)
|
298
|
+
else
|
299
|
+
adapt_test(child_element)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
adapted_children
|
304
|
+
end
|
305
|
+
|
306
|
+
def adapt_test(test_ast)
|
307
|
+
if test_has_examples?(test_ast) || test_uses_outline_keyword?(test_ast)
|
308
|
+
adapt_outline(test_ast)
|
309
|
+
else
|
310
|
+
adapt_scenario(test_ast)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def clear_child_elements(ast, child_paths)
|
315
|
+
# rubocop:disable Security/Eval - This is not blind data
|
316
|
+
child_paths.each do |traversal_path|
|
317
|
+
# Wipe the value if it's there but don't add any attributes to the object if it didn't already have them
|
318
|
+
if eval("ast['cuke_modeler_parsing_data'].#{traversal_path.join('.')}", binding, __FILE__, __LINE__)
|
319
|
+
property_path = traversal_path[0..-2].join('.')
|
320
|
+
eval("ast['cuke_modeler_parsing_data']#{property_path.empty? ? '' : '.' + property_path}.instance_variable_set(\"@#{traversal_path.last}\", nil)", binding, __FILE__, __LINE__) # rubocop:disable Layout/LineLength
|
321
|
+
end
|
322
|
+
end
|
323
|
+
# rubocop:enable Security/Eval
|
324
|
+
end
|
325
|
+
|
326
|
+
def test_has_examples?(ast_node)
|
327
|
+
ast_node.scenario.examples.any?
|
328
|
+
end
|
329
|
+
|
330
|
+
def test_uses_outline_keyword?(test_ast)
|
331
|
+
Parsing.dialects[Parsing.dialect]['scenarioOutline'].include?(test_ast.scenario.keyword)
|
332
|
+
end
|
333
|
+
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
# rubocop:enable Metrics/ClassLength, Metrics/AbcSize, Metrics/MethodLength
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'gherkin_20_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 21.x of the *cucumber-gherkin* gem into input that is consumable
|
8
|
+
# by this gem.
|
9
|
+
|
10
|
+
class Gherkin21Adapter < Gherkin20Adapter
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative 'gherkin_base_adapter'
|
2
|
+
|
1
3
|
# Some things just aren't going to get better due to the inherent complexity of the AST
|
2
4
|
# rubocop:disable Metrics/ClassLength, Metrics/AbcSize, Metrics/MethodLength
|
3
5
|
|
@@ -6,7 +8,7 @@ module CukeModeler
|
|
6
8
|
# NOT A PART OF THE PUBLIC API
|
7
9
|
# An adapter that can convert the output of version 9.x of the *cucumber-gherkin* gem into input that is consumable
|
8
10
|
# by this gem.
|
9
|
-
class Gherkin9Adapter
|
11
|
+
class Gherkin9Adapter < GherkinBaseAdapter
|
10
12
|
|
11
13
|
# Adapts the given AST into the shape that this gem expects
|
12
14
|
def adapt(ast)
|
@@ -322,12 +324,9 @@ module CukeModeler
|
|
322
324
|
end
|
323
325
|
end
|
324
326
|
|
325
|
-
def save_original_data(adapted_ast, raw_ast)
|
326
|
-
adapted_ast['cuke_modeler_parsing_data'] = Marshal.load(Marshal.dump(raw_ast))
|
327
|
-
end
|
328
|
-
|
329
327
|
def clear_child_elements(ast, child_paths)
|
330
328
|
child_paths.each do |traversal_path|
|
329
|
+
# Wipe the value if it's there but don't add any keys to the hash if it didn't already have them
|
331
330
|
if ast['cuke_modeler_parsing_data'].dig(*traversal_path)
|
332
331
|
bury(ast['cuke_modeler_parsing_data'], traversal_path, nil)
|
333
332
|
end
|
@@ -346,11 +345,11 @@ module CukeModeler
|
|
346
345
|
end
|
347
346
|
|
348
347
|
def test_node?(ast_node)
|
349
|
-
ast_node[:scenario]
|
348
|
+
!ast_node[:scenario].nil?
|
350
349
|
end
|
351
350
|
|
352
351
|
def test_has_examples?(ast_node)
|
353
|
-
ast_node[:scenario][:examples]
|
352
|
+
!ast_node[:scenario][:examples].nil?
|
354
353
|
end
|
355
354
|
|
356
355
|
def test_uses_outline_keyword?(test_ast)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module CukeModeler
|
2
|
+
|
3
|
+
# NOT A PART OF THE PUBLIC API
|
4
|
+
# A class providing some basic and common adapter functionality.
|
5
|
+
class GherkinBaseAdapter
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def save_original_data(adapted_ast, raw_ast)
|
10
|
+
adapted_ast['cuke_modeler_parsing_data'] = Marshal.load(Marshal.dump(raw_ast))
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -150,6 +150,7 @@ module CukeModeler
|
|
150
150
|
populate_keyword(rule_object, parsed_rule_data)
|
151
151
|
populate_name(rule_object, parsed_rule_data)
|
152
152
|
populate_description(rule_object, parsed_rule_data)
|
153
|
+
populate_tags(rule_object, parsed_rule_data)
|
153
154
|
populate_children(rule_object, parsed_rule_data)
|
154
155
|
end
|
155
156
|
|
@@ -7,6 +7,7 @@ module CukeModeler
|
|
7
7
|
include Parsed
|
8
8
|
include Named
|
9
9
|
include Described
|
10
|
+
include Taggable
|
10
11
|
include Sourceable
|
11
12
|
|
12
13
|
|
@@ -23,6 +24,7 @@ module CukeModeler
|
|
23
24
|
# Creates a new Rule object and, if *source_text* is provided, populates the
|
24
25
|
# object.
|
25
26
|
def initialize(source_text = nil)
|
27
|
+
@tags = []
|
26
28
|
@tests = []
|
27
29
|
|
28
30
|
super(source_text)
|
@@ -52,17 +54,21 @@ module CukeModeler
|
|
52
54
|
|
53
55
|
# Returns the model objects that belong to this model.
|
54
56
|
def children
|
55
|
-
models = tests
|
57
|
+
models = tests + tags
|
56
58
|
models << background if background
|
57
59
|
|
58
60
|
models
|
59
61
|
end
|
60
62
|
|
63
|
+
# Building strings just isn't pretty
|
64
|
+
# rubocop:disable Metrics/AbcSize
|
65
|
+
|
61
66
|
# Returns a string representation of this model. For a rule model,
|
62
67
|
# this will be Gherkin text that is equivalent to the rule being modeled.
|
63
68
|
def to_s
|
64
69
|
text = ''
|
65
70
|
|
71
|
+
text << "#{tag_output_string}\n" unless tags.empty?
|
66
72
|
text << "#{@keyword}:#{name_output_string}"
|
67
73
|
text << "\n#{description_output_string}" unless no_description_to_output?
|
68
74
|
text << "\n\n#{background_output_string}" if background
|
@@ -70,6 +76,7 @@ module CukeModeler
|
|
70
76
|
|
71
77
|
text
|
72
78
|
end
|
79
|
+
# rubocop:enable Metrics/AbcSize
|
73
80
|
|
74
81
|
|
75
82
|
private
|
data/lib/cuke_modeler/nested.rb
CHANGED
data/lib/cuke_modeler/parsing.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# rubocop:disable Metrics/ModuleLength - Just not going to worry about this
|
2
|
+
|
1
3
|
# Have to at least load some version of the gem before which version of the gem has been loaded can
|
2
4
|
# be determined and the rest of the needed files can be loaded.
|
3
5
|
require 'gherkin'
|
@@ -7,7 +9,7 @@ require 'gherkin'
|
|
7
9
|
# an 'adapter' appropriate to the version of the *cucumber-gherkin* gem that has been activated.
|
8
10
|
gherkin_version = Gem.loaded_specs['cucumber-gherkin'].version.version
|
9
11
|
gherkin_major_version = gherkin_version.match(/^(\d+)\./)[1].to_i
|
10
|
-
supported_gherkin_versions =
|
12
|
+
supported_gherkin_versions = (9..21)
|
11
13
|
|
12
14
|
raise("Unknown Gherkin version: '#{gherkin_version}'") unless supported_gherkin_versions.include?(gherkin_major_version)
|
13
15
|
|
@@ -32,7 +34,7 @@ module CukeModeler
|
|
32
34
|
|
33
35
|
# The dialects currently known by the gherkin gem
|
34
36
|
def dialects
|
35
|
-
|
37
|
+
Gherkin::DIALECTS
|
36
38
|
end
|
37
39
|
|
38
40
|
# Parses the Cucumber feature given in *source_text* and returns a hash representation of
|
@@ -61,6 +63,40 @@ module CukeModeler
|
|
61
63
|
# inside of it, so I'm leaving this here in case it changes again
|
62
64
|
# rubocop:disable Lint/DuplicateMethods
|
63
65
|
case gherkin_major_version
|
66
|
+
when 20, 21
|
67
|
+
# TODO: make these methods private?
|
68
|
+
# NOT A PART OF THE PUBLIC API
|
69
|
+
# The method to use for parsing Gherkin text
|
70
|
+
def parsing_method(source_text, filename)
|
71
|
+
messages = Gherkin.from_source(filename,
|
72
|
+
source_text,
|
73
|
+
{ include_gherkin_document: true })
|
74
|
+
.to_a
|
75
|
+
|
76
|
+
error_message = messages.find(&:parse_error)
|
77
|
+
gherkin_ast_message = messages.find(&:gherkin_document)
|
78
|
+
|
79
|
+
raise error_message.parse_error.message if error_message
|
80
|
+
|
81
|
+
gherkin_ast_message.gherkin_document
|
82
|
+
end
|
83
|
+
when 19
|
84
|
+
# TODO: make these methods private?
|
85
|
+
# NOT A PART OF THE PUBLIC API
|
86
|
+
# The method to use for parsing Gherkin text
|
87
|
+
def parsing_method(source_text, filename)
|
88
|
+
messages = Gherkin.from_source(filename,
|
89
|
+
source_text,
|
90
|
+
{ include_gherkin_document: true })
|
91
|
+
.to_a.map(&:to_hash)
|
92
|
+
|
93
|
+
error_message = messages.find { |message| message[:parseError] }
|
94
|
+
gherkin_ast_message = messages.find { |message| message[:gherkinDocument] }
|
95
|
+
|
96
|
+
raise error_message[:parseError][:message] if error_message
|
97
|
+
|
98
|
+
gherkin_ast_message[:gherkinDocument]
|
99
|
+
end
|
64
100
|
when 13, 14, 15, 16, 17, 18
|
65
101
|
# TODO: make these methods private?
|
66
102
|
# NOT A PART OF THE PUBLIC API
|
@@ -158,3 +194,5 @@ module CukeModeler
|
|
158
194
|
|
159
195
|
end
|
160
196
|
end
|
197
|
+
|
198
|
+
# rubocop:enable Metrics/ModuleLength
|
data/lib/cuke_modeler/version.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
Feature: Rule modeling
|
2
2
|
|
3
|
-
Rule models
|
4
|
-
containing models for any background, scenarios,
|
3
|
+
Rule models represent a Rule portion of a feature. They expose several attributes of the rule that they
|
4
|
+
represent, as well as containing models for any background, scenarios, outlines, and tags that are present
|
5
|
+
in that rule.
|
5
6
|
|
6
7
|
|
7
8
|
Background:
|
@@ -86,6 +87,47 @@ containing models for any background, scenarios, or outlines that are present in
|
|
86
87
|
Then the model returns models for the following outlines:
|
87
88
|
| Outline 1 |
|
88
89
|
|
90
|
+
@gherkin_min_version_18
|
91
|
+
Scenario: Modeling a rules's tags
|
92
|
+
Given the following gherkin:
|
93
|
+
"""
|
94
|
+
@feature_tag
|
95
|
+
Feature:
|
96
|
+
|
97
|
+
@rule_tag_1
|
98
|
+
@rule_tag_2
|
99
|
+
Rule:
|
100
|
+
"""
|
101
|
+
And a feature model based on that gherkin
|
102
|
+
"""
|
103
|
+
@model = CukeModeler::Feature.new(<source_text>)
|
104
|
+
"""
|
105
|
+
And the rule model of that feature model
|
106
|
+
"""
|
107
|
+
@model = @model.rules.first
|
108
|
+
"""
|
109
|
+
When the rules's tags are requested
|
110
|
+
"""
|
111
|
+
@model.tags
|
112
|
+
"""
|
113
|
+
Then the model returns models for the following tags:
|
114
|
+
| @rule_tag_1 |
|
115
|
+
| @rule_tag_2 |
|
116
|
+
When the rule's inherited tags are requested
|
117
|
+
"""
|
118
|
+
@model.applied_tags
|
119
|
+
"""
|
120
|
+
Then the model returns models for the following tags:
|
121
|
+
| @feature_tag |
|
122
|
+
When all of the rule's tags are requested
|
123
|
+
"""
|
124
|
+
@model.all_tags
|
125
|
+
"""
|
126
|
+
Then the model returns models for the following tags:
|
127
|
+
| @feature_tag |
|
128
|
+
| @rule_tag_1 |
|
129
|
+
| @rule_tag_2 |
|
130
|
+
|
89
131
|
Scenario: Modeling a rule's source line
|
90
132
|
Given the following gherkin:
|
91
133
|
"""
|
@@ -4,9 +4,12 @@ Feature: Rule output
|
|
4
4
|
input for the same kind of model.
|
5
5
|
|
6
6
|
|
7
|
+
@gherkin_min_version_18
|
7
8
|
Scenario: Outputting a rule model
|
8
9
|
Given the following gherkin:
|
9
10
|
"""
|
11
|
+
@tag1@tag2
|
12
|
+
@tag3
|
10
13
|
Rule: A rule with everything it could have
|
11
14
|
Including a description
|
12
15
|
and then some.
|
@@ -54,6 +57,7 @@ Feature: Rule output
|
|
54
57
|
"""
|
55
58
|
Then the following text is provided:
|
56
59
|
"""
|
60
|
+
@tag1 @tag2 @tag3
|
57
61
|
Rule: A rule with everything it could have
|
58
62
|
|
59
63
|
Including a description
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cuke_modeler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Kessler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-09-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cucumber-gherkin
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "<"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '22.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "<"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '22.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,20 +52,6 @@ dependencies:
|
|
52
52
|
- - "<"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '5.0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: coveralls
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "<"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 1.0.0
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "<"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: 1.0.0
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
56
|
name: ffi
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -160,16 +146,30 @@ dependencies:
|
|
160
146
|
name: simplecov
|
161
147
|
requirement: !ruby/object:Gem::Requirement
|
162
148
|
requirements:
|
163
|
-
- - "
|
149
|
+
- - "<"
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '1.0'
|
152
|
+
type: :development
|
153
|
+
prerelease: false
|
154
|
+
version_requirements: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - "<"
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '1.0'
|
159
|
+
- !ruby/object:Gem::Dependency
|
160
|
+
name: simplecov-lcov
|
161
|
+
requirement: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - "<"
|
164
164
|
- !ruby/object:Gem::Version
|
165
|
-
version: 0
|
165
|
+
version: '1.0'
|
166
166
|
type: :development
|
167
167
|
prerelease: false
|
168
168
|
version_requirements: !ruby/object:Gem::Requirement
|
169
169
|
requirements:
|
170
|
-
- - "
|
170
|
+
- - "<"
|
171
171
|
- !ruby/object:Gem::Version
|
172
|
-
version: 0
|
172
|
+
version: '1.0'
|
173
173
|
- !ruby/object:Gem::Dependency
|
174
174
|
name: test-unit
|
175
175
|
requirement: !ruby/object:Gem::Requirement
|
@@ -225,7 +225,11 @@ files:
|
|
225
225
|
- lib/cuke_modeler/adapters/gherkin_16_adapter.rb
|
226
226
|
- lib/cuke_modeler/adapters/gherkin_17_adapter.rb
|
227
227
|
- lib/cuke_modeler/adapters/gherkin_18_adapter.rb
|
228
|
+
- lib/cuke_modeler/adapters/gherkin_19_adapter.rb
|
229
|
+
- lib/cuke_modeler/adapters/gherkin_20_adapter.rb
|
230
|
+
- lib/cuke_modeler/adapters/gherkin_21_adapter.rb
|
228
231
|
- lib/cuke_modeler/adapters/gherkin_9_adapter.rb
|
232
|
+
- lib/cuke_modeler/adapters/gherkin_base_adapter.rb
|
229
233
|
- lib/cuke_modeler/containing.rb
|
230
234
|
- lib/cuke_modeler/described.rb
|
231
235
|
- lib/cuke_modeler/models/background.rb
|