cuke_modeler 1.3.0 → 2.1.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 +5 -5
- data/.travis.yml +60 -17
- data/CHANGELOG.md +312 -0
- data/Gemfile +19 -3
- data/LICENSE.txt +1 -1
- data/README.md +17 -7
- data/Rakefile +45 -28
- data/appveyor.yml +57 -17
- data/cuke_modeler.gemspec +6 -3
- data/lib/cuke_modeler/adapters/gherkin_2_adapter.rb +1 -0
- data/lib/cuke_modeler/adapters/gherkin_3_adapter.rb +1 -0
- data/lib/cuke_modeler/adapters/gherkin_4_adapter.rb +2 -1
- data/lib/cuke_modeler/adapters/gherkin_5_adapter.rb +12 -0
- data/lib/cuke_modeler/adapters/gherkin_6_adapter.rb +310 -0
- data/lib/cuke_modeler/adapters/gherkin_7_adapter.rb +307 -0
- data/lib/cuke_modeler/adapters/gherkin_8_adapter.rb +12 -0
- data/lib/cuke_modeler/adapters/gherkin_9_adapter.rb +12 -0
- data/lib/cuke_modeler/containing.rb +16 -0
- data/lib/cuke_modeler/described.rb +1 -0
- data/lib/cuke_modeler/models/step.rb +31 -2
- data/lib/cuke_modeler/named.rb +1 -0
- data/lib/cuke_modeler/nested.rb +1 -0
- data/lib/cuke_modeler/parsed.rb +1 -0
- data/lib/cuke_modeler/parsing.rb +116 -68
- data/lib/cuke_modeler/sourceable.rb +1 -0
- data/lib/cuke_modeler/stepped.rb +1 -0
- data/lib/cuke_modeler/taggable.rb +1 -0
- data/lib/cuke_modeler/version.rb +1 -1
- data/testing/cucumber/features/analysis/step_comparison.feature +25 -0
- data/testing/cucumber/features/analysis/test_comparison.feature +1 -1
- data/testing/cucumber/step_definitions/feature_file_steps.rb +1 -1
- data/testing/cucumber/step_definitions/modeling_steps.rb +7 -2
- data/testing/cucumber/step_definitions/verification_steps.rb +11 -2
- data/testing/file_helper.rb +3 -0
- data/testing/gemfiles/gherkin2.gemfile +8 -0
- data/testing/gemfiles/gherkin3.gemfile +6 -0
- data/testing/gemfiles/gherkin4.gemfile +7 -0
- data/testing/gemfiles/gherkin5.gemfile +7 -0
- data/testing/gemfiles/gherkin6.gemfile +10 -0
- data/testing/gemfiles/gherkin7.gemfile +9 -0
- data/testing/gemfiles/gherkin8.gemfile +9 -0
- data/testing/gemfiles/gherkin9.gemfile +9 -0
- data/testing/helper_methods.rb +23 -0
- data/testing/rspec/spec/integration/{gherkin_2_adapter_spec.rb → adapters/gherkin_2_adapter_spec.rb} +13 -13
- data/testing/rspec/spec/integration/{gherkin_3_adapter_spec.rb → adapters/gherkin_3_adapter_spec.rb} +13 -13
- data/testing/rspec/spec/integration/{gherkin_4_adapter_spec.rb → adapters/gherkin_4_adapter_spec.rb} +13 -13
- data/testing/rspec/spec/integration/adapters/gherkin_5_adapter_spec.rb +165 -0
- data/testing/rspec/spec/integration/adapters/gherkin_6_adapter_spec.rb +159 -0
- data/testing/rspec/spec/integration/adapters/gherkin_7_adapter_spec.rb +162 -0
- data/testing/rspec/spec/integration/adapters/gherkin_8_adapter_spec.rb +162 -0
- data/testing/rspec/spec/integration/adapters/gherkin_9_adapter_spec.rb +162 -0
- data/testing/rspec/spec/integration/{background_integration_spec.rb → models/background_integration_spec.rb} +90 -86
- data/testing/rspec/spec/integration/{cell_integration_spec.rb → models/cell_integration_spec.rb} +49 -38
- data/testing/rspec/spec/integration/{comment_integration_spec.rb → models/comment_integration_spec.rb} +31 -20
- data/testing/rspec/spec/integration/{directory_integration_spec.rb → models/directory_integration_spec.rb} +3 -3
- data/testing/rspec/spec/integration/{doc_string_integration_spec.rb → models/doc_string_integration_spec.rb} +39 -35
- data/testing/rspec/spec/integration/{example_integration_spec.rb → models/example_integration_spec.rb} +109 -83
- data/testing/rspec/spec/integration/{feature_file_integration_spec.rb → models/feature_file_integration_spec.rb} +52 -38
- data/testing/rspec/spec/integration/{feature_integration_spec.rb → models/feature_integration_spec.rb} +125 -112
- data/testing/rspec/spec/integration/{model_integration_spec.rb → models/model_integration_spec.rb} +1 -1
- data/testing/rspec/spec/integration/{outline_integration_spec.rb → models/outline_integration_spec.rb} +138 -129
- data/testing/rspec/spec/integration/{row_integration_spec.rb → models/row_integration_spec.rb} +55 -35
- data/testing/rspec/spec/integration/{scenario_integration_spec.rb → models/scenario_integration_spec.rb} +92 -88
- data/testing/rspec/spec/integration/models/step_integration_spec.rb +573 -0
- data/testing/rspec/spec/integration/{table_integration_spec.rb → models/table_integration_spec.rb} +38 -34
- data/testing/rspec/spec/integration/{tag_integration_spec.rb → models/tag_integration_spec.rb} +56 -36
- data/testing/rspec/spec/integration/parsing_integration_spec.rb +45 -7
- data/testing/rspec/spec/spec_helper.rb +79 -43
- data/testing/rspec/spec/unit/cuke_modeler_unit_spec.rb +25 -0
- data/testing/rspec/spec/unit/{background_unit_spec.rb → models/background_unit_spec.rb} +1 -1
- data/testing/rspec/spec/unit/{cell_unit_spec.rb → models/cell_unit_spec.rb} +1 -1
- data/testing/rspec/spec/unit/{comment_unit_spec.rb → models/comment_unit_spec.rb} +1 -1
- data/testing/rspec/spec/unit/{directory_unit_spec.rb → models/directory_unit_spec.rb} +1 -1
- data/testing/rspec/spec/unit/{doc_string_unit_spec.rb → models/doc_string_unit_spec.rb} +1 -1
- data/testing/rspec/spec/unit/{example_unit_spec.rb → models/example_unit_spec.rb} +1 -1
- data/testing/rspec/spec/unit/{feature_file_unit_spec.rb → models/feature_file_unit_spec.rb} +1 -1
- data/testing/rspec/spec/unit/{feature_unit_spec.rb → models/feature_unit_spec.rb} +1 -1
- data/testing/rspec/spec/unit/{model_unit_spec.rb → models/model_unit_spec.rb} +1 -1
- data/testing/rspec/spec/unit/{outline_unit_spec.rb → models/outline_unit_spec.rb} +1 -1
- data/testing/rspec/spec/unit/{row_unit_spec.rb → models/row_unit_spec.rb} +1 -1
- data/testing/rspec/spec/unit/{scenario_unit_spec.rb → models/scenario_unit_spec.rb} +1 -1
- data/testing/rspec/spec/unit/{step_unit_spec.rb → models/step_unit_spec.rb} +2 -2
- data/testing/rspec/spec/unit/{table_unit_spec.rb → models/table_unit_spec.rb} +1 -1
- data/testing/rspec/spec/unit/{tag_unit_spec.rb → models/tag_unit_spec.rb} +1 -1
- data/testing/rspec/spec/unit/shared/containing_models_unit_specs.rb +102 -0
- data/todo.txt +5 -2
- metadata +80 -47
- data/History.md +0 -186
- data/testing/cucumber/support/transforms.rb +0 -3
- data/testing/rspec/spec/integration/step_integration_spec.rb +0 -459
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
module CukeModeler
|
|
2
|
+
|
|
3
|
+
# NOT A PART OF THE PUBLIC API
|
|
4
|
+
# An adapter that can convert the output of version 7.x of the *gherkin* gem into input that is consumable by this gem.
|
|
5
|
+
|
|
6
|
+
class Gherkin7Adapter
|
|
7
|
+
|
|
8
|
+
# 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
|
|
15
|
+
parsed_ast['cuke_modeler_parsing_data'][:comments] = nil
|
|
16
|
+
|
|
17
|
+
parsed_ast['comments'] = []
|
|
18
|
+
parsed_ast[:comments].each do |comment|
|
|
19
|
+
adapt_comment!(comment)
|
|
20
|
+
end
|
|
21
|
+
parsed_ast['comments'].concat(parsed_ast.delete(:comments))
|
|
22
|
+
|
|
23
|
+
adapt_feature!(parsed_ast[:feature]) if parsed_ast[:feature]
|
|
24
|
+
parsed_ast['feature'] = parsed_ast.delete(:feature)
|
|
25
|
+
|
|
26
|
+
[parsed_ast]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Adapts the AST sub-tree that is rooted at the given feature node.
|
|
30
|
+
def adapt_feature!(parsed_feature)
|
|
31
|
+
# Saving off the original data
|
|
32
|
+
parsed_feature['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_feature))
|
|
33
|
+
|
|
34
|
+
# Removing parsed data for child elements in order to avoid duplicating data
|
|
35
|
+
parsed_feature['cuke_modeler_parsing_data'][:tags] = nil
|
|
36
|
+
parsed_feature['cuke_modeler_parsing_data'][:children] = nil
|
|
37
|
+
|
|
38
|
+
parsed_feature['keyword'] = parsed_feature.delete(:keyword)
|
|
39
|
+
parsed_feature['name'] = parsed_feature.delete(:name)
|
|
40
|
+
parsed_feature['description'] = parsed_feature.delete(:description)
|
|
41
|
+
parsed_feature['line'] = parsed_feature.delete(:location)[:line]
|
|
42
|
+
|
|
43
|
+
parsed_feature['elements'] = []
|
|
44
|
+
adapt_child_elements!(parsed_feature[:children])
|
|
45
|
+
parsed_feature['elements'].concat(parsed_feature.delete(:children))
|
|
46
|
+
|
|
47
|
+
parsed_feature['tags'] = []
|
|
48
|
+
parsed_feature[:tags].each do |tag|
|
|
49
|
+
adapt_tag!(tag)
|
|
50
|
+
end
|
|
51
|
+
parsed_feature['tags'].concat(parsed_feature.delete(:tags))
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Adapts the AST sub-tree that is rooted at the given background node.
|
|
55
|
+
def adapt_background!(parsed_background)
|
|
56
|
+
# Saving off the original data
|
|
57
|
+
parsed_background['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_background))
|
|
58
|
+
|
|
59
|
+
# Removing parsed data for child elements in order to avoid duplicating data
|
|
60
|
+
parsed_background['cuke_modeler_parsing_data'][:background][:steps] = nil
|
|
61
|
+
|
|
62
|
+
parsed_background['type'] = 'Background'
|
|
63
|
+
parsed_background['keyword'] = parsed_background[:background].delete(:keyword)
|
|
64
|
+
parsed_background['name'] = parsed_background[:background].delete(:name)
|
|
65
|
+
parsed_background['description'] = parsed_background[:background].delete(:description)
|
|
66
|
+
parsed_background['line'] = parsed_background[:background].delete(:location)[:line]
|
|
67
|
+
|
|
68
|
+
parsed_background['steps'] = []
|
|
69
|
+
parsed_background[:background][:steps].each do |step|
|
|
70
|
+
adapt_step!(step)
|
|
71
|
+
end
|
|
72
|
+
parsed_background['steps'].concat(parsed_background[:background].delete(:steps))
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Adapts the AST sub-tree that is rooted at the given scenario node.
|
|
76
|
+
def adapt_scenario!(parsed_test)
|
|
77
|
+
# Removing parsed data for child elements in order to avoid duplicating data
|
|
78
|
+
parsed_test['cuke_modeler_parsing_data'][:scenario][:tags] = nil
|
|
79
|
+
parsed_test['cuke_modeler_parsing_data'][:scenario][:steps] = nil
|
|
80
|
+
|
|
81
|
+
parsed_test['type'] = 'Scenario'
|
|
82
|
+
parsed_test['keyword'] = parsed_test[:scenario].delete(:keyword)
|
|
83
|
+
parsed_test['name'] = parsed_test[:scenario].delete(:name)
|
|
84
|
+
parsed_test['description'] = parsed_test[:scenario].delete(:description)
|
|
85
|
+
parsed_test['line'] = parsed_test[:scenario].delete(:location)[:line]
|
|
86
|
+
|
|
87
|
+
parsed_test['tags'] = []
|
|
88
|
+
parsed_test[:scenario][:tags].each do |tag|
|
|
89
|
+
adapt_tag!(tag)
|
|
90
|
+
end
|
|
91
|
+
parsed_test['tags'].concat(parsed_test[:scenario].delete(:tags))
|
|
92
|
+
|
|
93
|
+
parsed_test['steps'] = []
|
|
94
|
+
parsed_test[:scenario][:steps].each do |step|
|
|
95
|
+
adapt_step!(step)
|
|
96
|
+
end
|
|
97
|
+
parsed_test['steps'].concat(parsed_test[:scenario].delete(:steps))
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Adapts the AST sub-tree that is rooted at the given outline node.
|
|
101
|
+
def adapt_outline!(parsed_test)
|
|
102
|
+
# Removing parsed data for child elements in order to avoid duplicating data
|
|
103
|
+
parsed_test['cuke_modeler_parsing_data'][:scenario][:tags] = nil
|
|
104
|
+
parsed_test['cuke_modeler_parsing_data'][:scenario][:steps] = nil
|
|
105
|
+
parsed_test['cuke_modeler_parsing_data'][:scenario][:examples] = nil
|
|
106
|
+
|
|
107
|
+
parsed_test['type'] = 'ScenarioOutline'
|
|
108
|
+
parsed_test['keyword'] = parsed_test[:scenario].delete(:keyword)
|
|
109
|
+
parsed_test['name'] = parsed_test[:scenario].delete(:name)
|
|
110
|
+
parsed_test['description'] = parsed_test[:scenario].delete(:description)
|
|
111
|
+
parsed_test['line'] = parsed_test[:scenario].delete(:location)[:line]
|
|
112
|
+
|
|
113
|
+
parsed_test['tags'] = []
|
|
114
|
+
parsed_test[:scenario][:tags].each do |tag|
|
|
115
|
+
adapt_tag!(tag)
|
|
116
|
+
end
|
|
117
|
+
parsed_test['tags'].concat(parsed_test[:scenario].delete(:tags))
|
|
118
|
+
|
|
119
|
+
parsed_test['steps'] = []
|
|
120
|
+
parsed_test[:scenario][:steps].each do |step|
|
|
121
|
+
adapt_step!(step)
|
|
122
|
+
end
|
|
123
|
+
parsed_test['steps'].concat(parsed_test[:scenario].delete(:steps))
|
|
124
|
+
|
|
125
|
+
parsed_test['examples'] = []
|
|
126
|
+
parsed_test[:scenario][:examples].each do |step|
|
|
127
|
+
adapt_example!(step)
|
|
128
|
+
end
|
|
129
|
+
parsed_test['examples'].concat(parsed_test[:scenario].delete(:examples))
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Adapts the AST sub-tree that is rooted at the given example node.
|
|
133
|
+
def adapt_example!(parsed_example)
|
|
134
|
+
# Saving off the original data
|
|
135
|
+
parsed_example['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_example))
|
|
136
|
+
|
|
137
|
+
# Removing parsed data for child elements in order to avoid duplicating data
|
|
138
|
+
parsed_example['cuke_modeler_parsing_data'][:tags] = nil
|
|
139
|
+
parsed_example['cuke_modeler_parsing_data'][:table_header] = nil
|
|
140
|
+
parsed_example['cuke_modeler_parsing_data'][:table_body] = nil
|
|
141
|
+
|
|
142
|
+
parsed_example['keyword'] = parsed_example.delete(:keyword)
|
|
143
|
+
parsed_example['name'] = parsed_example.delete(:name)
|
|
144
|
+
parsed_example['line'] = parsed_example.delete(:location)[:line]
|
|
145
|
+
parsed_example['description'] = parsed_example.delete(:description)
|
|
146
|
+
|
|
147
|
+
parsed_example['rows'] = []
|
|
148
|
+
|
|
149
|
+
if parsed_example[:table_header]
|
|
150
|
+
adapt_table_row!(parsed_example[:table_header])
|
|
151
|
+
parsed_example['rows'] << parsed_example.delete(:table_header)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
if parsed_example[:table_body]
|
|
155
|
+
parsed_example[:table_body].each do |row|
|
|
156
|
+
adapt_table_row!(row)
|
|
157
|
+
end
|
|
158
|
+
parsed_example['rows'].concat(parsed_example.delete(:table_body))
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
parsed_example['tags'] = []
|
|
162
|
+
parsed_example[:tags].each do |tag|
|
|
163
|
+
adapt_tag!(tag)
|
|
164
|
+
end
|
|
165
|
+
parsed_example['tags'].concat(parsed_example.delete(:tags))
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Adapts the AST sub-tree that is rooted at the given tag node.
|
|
169
|
+
def adapt_tag!(parsed_tag)
|
|
170
|
+
# Saving off the original data
|
|
171
|
+
parsed_tag['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_tag))
|
|
172
|
+
|
|
173
|
+
parsed_tag['name'] = parsed_tag.delete(:name)
|
|
174
|
+
parsed_tag['line'] = parsed_tag.delete(:location)[:line]
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Adapts the AST sub-tree that is rooted at the given comment node.
|
|
178
|
+
def adapt_comment!(parsed_comment)
|
|
179
|
+
# Saving off the original data
|
|
180
|
+
parsed_comment['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_comment))
|
|
181
|
+
|
|
182
|
+
parsed_comment['text'] = parsed_comment.delete(:text)
|
|
183
|
+
parsed_comment['line'] = parsed_comment.delete(:location)[:line]
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Adapts the AST sub-tree that is rooted at the given step node.
|
|
187
|
+
def adapt_step!(parsed_step)
|
|
188
|
+
# Saving off the original data
|
|
189
|
+
parsed_step['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_step))
|
|
190
|
+
|
|
191
|
+
# Removing parsed data for child elements in order to avoid duplicating data
|
|
192
|
+
parsed_step['cuke_modeler_parsing_data'][:data_table] = nil
|
|
193
|
+
parsed_step['cuke_modeler_parsing_data'][:doc_string] = nil
|
|
194
|
+
|
|
195
|
+
parsed_step['keyword'] = parsed_step.delete(:keyword)
|
|
196
|
+
parsed_step['name'] = parsed_step.delete(:text)
|
|
197
|
+
parsed_step['line'] = parsed_step.delete(:location)[:line]
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
case
|
|
201
|
+
when parsed_step[:doc_string]
|
|
202
|
+
adapt_doc_string!(parsed_step[:doc_string])
|
|
203
|
+
parsed_step['doc_string'] = parsed_step.delete(:doc_string)
|
|
204
|
+
when parsed_step[:data_table]
|
|
205
|
+
adapt_step_table!(parsed_step[:data_table])
|
|
206
|
+
parsed_step['table'] = parsed_step.delete(:data_table)
|
|
207
|
+
else
|
|
208
|
+
# Step has no extra argument
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Adapts the AST sub-tree that is rooted at the given doc string node.
|
|
213
|
+
def adapt_doc_string!(parsed_doc_string)
|
|
214
|
+
# Saving off the original data
|
|
215
|
+
parsed_doc_string['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_doc_string))
|
|
216
|
+
|
|
217
|
+
parsed_doc_string['value'] = parsed_doc_string.delete(:content)
|
|
218
|
+
parsed_doc_string['content_type'] = parsed_doc_string.delete(:content_type).strip # TODO: fix bug in Gherkin so that this whitespace is already trimmed off
|
|
219
|
+
parsed_doc_string['line'] = parsed_doc_string.delete(:location)[:line]
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Adapts the AST sub-tree that is rooted at the given table node.
|
|
223
|
+
def adapt_step_table!(parsed_step_table)
|
|
224
|
+
# Saving off the original data
|
|
225
|
+
parsed_step_table['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_step_table))
|
|
226
|
+
|
|
227
|
+
# Removing parsed data for child elements in order to avoid duplicating data
|
|
228
|
+
parsed_step_table['cuke_modeler_parsing_data'][:rows] = nil
|
|
229
|
+
|
|
230
|
+
parsed_step_table['rows'] = []
|
|
231
|
+
parsed_step_table[:rows].each do |row|
|
|
232
|
+
adapt_table_row!(row)
|
|
233
|
+
end
|
|
234
|
+
parsed_step_table['rows'].concat(parsed_step_table.delete(:rows))
|
|
235
|
+
parsed_step_table['line'] = parsed_step_table.delete(:location)[:line]
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Adapts the AST sub-tree that is rooted at the given row node.
|
|
239
|
+
def adapt_table_row!(parsed_table_row)
|
|
240
|
+
# Saving off the original data
|
|
241
|
+
parsed_table_row['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_table_row))
|
|
242
|
+
|
|
243
|
+
# Removing parsed data for child elements in order to avoid duplicating data which the child elements will themselves include
|
|
244
|
+
parsed_table_row['cuke_modeler_parsing_data'][:cells] = nil
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
parsed_table_row['line'] = parsed_table_row.delete(:location)[:line]
|
|
248
|
+
|
|
249
|
+
parsed_table_row['cells'] = []
|
|
250
|
+
parsed_table_row[:cells].each do |row|
|
|
251
|
+
adapt_table_cell!(row)
|
|
252
|
+
end
|
|
253
|
+
parsed_table_row['cells'].concat(parsed_table_row.delete(:cells))
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Adapts the AST sub-tree that is rooted at the given cell node.
|
|
257
|
+
def adapt_table_cell!(parsed_cell)
|
|
258
|
+
# Saving off the original data
|
|
259
|
+
parsed_cell['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_cell))
|
|
260
|
+
|
|
261
|
+
parsed_cell['value'] = parsed_cell.delete(:value)
|
|
262
|
+
parsed_cell['line'] = parsed_cell.delete(:location)[:line]
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
private
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def adapt_child_elements!(parsed_children)
|
|
270
|
+
return if parsed_children.empty?
|
|
271
|
+
|
|
272
|
+
background_child = parsed_children.find { |child| child[:background] }
|
|
273
|
+
|
|
274
|
+
if background_child
|
|
275
|
+
adapt_background!(background_child)
|
|
276
|
+
|
|
277
|
+
remaining_children = parsed_children.reject { |child| child[:background] }
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
adapt_tests!(remaining_children || parsed_children)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def adapt_tests!(parsed_tests)
|
|
284
|
+
return unless parsed_tests
|
|
285
|
+
|
|
286
|
+
parsed_tests.each do |test|
|
|
287
|
+
adapt_test!(test)
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def adapt_test!(parsed_test)
|
|
292
|
+
# Saving off the original data
|
|
293
|
+
parsed_test['cuke_modeler_parsing_data'] = Marshal::load(Marshal.dump(parsed_test))
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
case
|
|
297
|
+
when parsed_test[:scenario] && parsed_test[:scenario][:examples].any?
|
|
298
|
+
adapt_outline!(parsed_test)
|
|
299
|
+
when parsed_test[:scenario]
|
|
300
|
+
adapt_scenario!(parsed_test)
|
|
301
|
+
else
|
|
302
|
+
raise(ArgumentError, "Unknown test type with keys: #{parsed_test.keys}")
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
end
|
|
307
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require_relative 'gherkin_7_adapter'
|
|
2
|
+
|
|
3
|
+
module CukeModeler
|
|
4
|
+
|
|
5
|
+
# NOT A PART OF THE PUBLIC API
|
|
6
|
+
# An adapter that can convert the output of version 8.x of the *gherkin* gem into input that is consumable by this gem.
|
|
7
|
+
|
|
8
|
+
class Gherkin8Adapter < Gherkin7Adapter
|
|
9
|
+
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require_relative 'gherkin_7_adapter'
|
|
2
|
+
|
|
3
|
+
module CukeModeler
|
|
4
|
+
|
|
5
|
+
# NOT A PART OF THE PUBLIC API
|
|
6
|
+
# An adapter that can convert the output of version 9.x of the *gherkin* gem into input that is consumable by this gem.
|
|
7
|
+
|
|
8
|
+
class Gherkin9Adapter < Gherkin7Adapter
|
|
9
|
+
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
end
|
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
module CukeModeler
|
|
2
2
|
|
|
3
|
+
# NOT A PART OF THE PUBLIC API
|
|
3
4
|
# A mix-in module containing methods used by models that contain other models.
|
|
4
5
|
|
|
5
6
|
module Containing
|
|
6
7
|
|
|
8
|
+
# Executes the given code block with every model that is a child of this model.
|
|
9
|
+
def each_descendant(&block)
|
|
10
|
+
children.each do |child_model|
|
|
11
|
+
block.call(child_model)
|
|
12
|
+
child_model.each_descendant(&block) if child_model.respond_to?(:each_descendant)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Executes the given code block with this model and every model that is a child of this model.
|
|
17
|
+
def each_model(&block)
|
|
18
|
+
block.call(self)
|
|
19
|
+
|
|
20
|
+
each_descendant(&block)
|
|
21
|
+
end
|
|
22
|
+
|
|
7
23
|
|
|
8
24
|
private
|
|
9
25
|
|
|
@@ -33,9 +33,11 @@ module CukeModeler
|
|
|
33
33
|
# Returns *true* if the two steps have the same base text (i.e. minus any keyword,
|
|
34
34
|
# table, or doc string and *false* otherwise.
|
|
35
35
|
def ==(other_step)
|
|
36
|
-
return false unless other_step.
|
|
36
|
+
return false unless other_step.is_a?(CukeModeler::Step)
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
text_matches?(other_step) &&
|
|
39
|
+
table_matches?(other_step) &&
|
|
40
|
+
doc_string_matches?(other_step)
|
|
39
41
|
end
|
|
40
42
|
|
|
41
43
|
# Returns the model objects that belong to this model.
|
|
@@ -65,5 +67,32 @@ module CukeModeler
|
|
|
65
67
|
parsed_file.first['feature']['elements'].first['steps'].first
|
|
66
68
|
end
|
|
67
69
|
|
|
70
|
+
def text_matches?(other_step)
|
|
71
|
+
text == other_step.text
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def table_matches?(other_step)
|
|
75
|
+
return false if (!block.is_a?(CukeModeler::Table) || !other_step.block.is_a?(CukeModeler::Table)) && (block.is_a?(CukeModeler::Table) || other_step.block.is_a?(CukeModeler::Table))
|
|
76
|
+
return true unless block.is_a?(CukeModeler::Table) && other_step.block.is_a?(CukeModeler::Table)
|
|
77
|
+
|
|
78
|
+
first_step_values = block.rows.collect { |table_row| table_row.cells.map(&:value) }
|
|
79
|
+
second_step_values = other_step.block.rows.collect { |table_row| table_row.cells.map(&:value) }
|
|
80
|
+
|
|
81
|
+
first_step_values == second_step_values
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def doc_string_matches?(other_step)
|
|
85
|
+
return false if (!block.is_a?(CukeModeler::DocString) || !other_step.block.is_a?(CukeModeler::DocString)) && (block.is_a?(CukeModeler::DocString) || other_step.block.is_a?(CukeModeler::DocString))
|
|
86
|
+
return true unless block.is_a?(CukeModeler::DocString) && other_step.block.is_a?(CukeModeler::DocString)
|
|
87
|
+
|
|
88
|
+
first_content = block.content
|
|
89
|
+
first_content_type = block.content_type
|
|
90
|
+
second_content = other_step.block.content
|
|
91
|
+
second_content_type = other_step.block.content_type
|
|
92
|
+
|
|
93
|
+
(first_content == second_content) &&
|
|
94
|
+
(first_content_type == second_content_type)
|
|
95
|
+
end
|
|
96
|
+
|
|
68
97
|
end
|
|
69
98
|
end
|
data/lib/cuke_modeler/named.rb
CHANGED
data/lib/cuke_modeler/nested.rb
CHANGED
data/lib/cuke_modeler/parsed.rb
CHANGED
data/lib/cuke_modeler/parsing.rb
CHANGED
|
@@ -1,75 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
# The *gherkin* gem loads differently and has different grammar rules across major versions. Parsing
|
|
19
|
-
# will be done with an 'adapter' appropriate to the version of the *gherkin* gem that has been activated.
|
|
20
|
-
|
|
21
|
-
case Gem.loaded_specs['gherkin'].version.version
|
|
22
|
-
when /^[54]\./
|
|
23
|
-
require 'gherkin/parser'
|
|
24
|
-
require 'cuke_modeler/adapters/gherkin_4_adapter'
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
# todo - make these methods private?
|
|
28
|
-
def self.parsing_method(source_text, _filename)
|
|
29
|
-
Gherkin::Parser.new.parse(source_text)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def self.adapter_class
|
|
33
|
-
CukeModeler::Gherkin4Adapter
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
when /^3\./
|
|
37
|
-
require 'gherkin/parser'
|
|
38
|
-
require 'cuke_modeler/adapters/gherkin_3_adapter'
|
|
1
|
+
# Have to at least load some version of the gem before which version of the gem has been loaded can
|
|
2
|
+
# be determined and the rest of the needed files can be loaded. The entry points vary across versions,
|
|
3
|
+
# so try them all until one of them works.
|
|
4
|
+
begin
|
|
5
|
+
# Gherkin 2.x, 8.x, 9.x
|
|
6
|
+
require 'gherkin'
|
|
7
|
+
rescue LoadError
|
|
8
|
+
begin
|
|
9
|
+
require 'gherkin/parser'
|
|
10
|
+
rescue LoadError
|
|
11
|
+
# Gherkin 6.x, 7.x
|
|
12
|
+
require 'gherkin/gherkin'
|
|
13
|
+
end
|
|
14
|
+
end
|
|
39
15
|
|
|
40
16
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
17
|
+
# The *gherkin* gem loads differently and has different grammar rules across major versions. Parsing
|
|
18
|
+
# will be done with an 'adapter' appropriate to the version of the *gherkin* gem that has been activated.
|
|
19
|
+
|
|
20
|
+
gherkin_version = Gem.loaded_specs['gherkin'].version.version
|
|
21
|
+
gherkin_major_version = gherkin_version.match(/^(\d+)\./)[1].to_i
|
|
22
|
+
|
|
23
|
+
case gherkin_major_version
|
|
24
|
+
when 6, 7, 8, 9
|
|
25
|
+
require 'gherkin/dialect'
|
|
26
|
+
when 3, 4, 5
|
|
27
|
+
require 'gherkin/parser'
|
|
28
|
+
when 2
|
|
29
|
+
require 'stringio'
|
|
30
|
+
require 'gherkin/formatter/json_formatter'
|
|
31
|
+
require 'gherkin'
|
|
32
|
+
require 'json'
|
|
33
|
+
require 'multi_json'
|
|
34
|
+
else
|
|
35
|
+
raise("Unknown Gherkin version: '#{gherkin_version}'")
|
|
36
|
+
end
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
CukeModeler::Gherkin3Adapter
|
|
47
|
-
end
|
|
38
|
+
require "cuke_modeler/adapters/gherkin_#{gherkin_major_version}_adapter"
|
|
48
39
|
|
|
49
|
-
else # Assume version 2.x
|
|
50
|
-
require 'stringio'
|
|
51
|
-
require 'gherkin/formatter/json_formatter'
|
|
52
|
-
require 'gherkin'
|
|
53
|
-
require 'json'
|
|
54
|
-
require 'multi_json'
|
|
55
|
-
require 'cuke_modeler/adapters/gherkin_2_adapter'
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def self.parsing_method(source_text, filename)
|
|
59
|
-
io = StringIO.new
|
|
60
|
-
formatter = Gherkin::Formatter::JSONFormatter.new(io)
|
|
61
|
-
parser = Gherkin::Parser::Parser.new(formatter)
|
|
62
|
-
parser.parse(source_text, filename, 0)
|
|
63
|
-
formatter.done
|
|
64
|
-
MultiJson.load(io.string)
|
|
65
|
-
end
|
|
66
40
|
|
|
67
|
-
|
|
68
|
-
CukeModeler::Gherkin2Adapter
|
|
69
|
-
end
|
|
41
|
+
module CukeModeler
|
|
70
42
|
|
|
71
|
-
|
|
43
|
+
# A module providing source text parsing functionality.
|
|
72
44
|
|
|
45
|
+
module Parsing
|
|
73
46
|
|
|
74
47
|
class << self
|
|
75
48
|
|
|
@@ -96,17 +69,92 @@ module CukeModeler
|
|
|
96
69
|
def parse_text(source_text, filename = 'cuke_modeler_fake_file.feature')
|
|
97
70
|
raise(ArgumentError, "Text to parse must be a String but got #{source_text.class}") unless source_text.is_a?(String)
|
|
98
71
|
|
|
99
|
-
|
|
100
72
|
begin
|
|
101
73
|
parsed_result = parsing_method(source_text, filename)
|
|
102
74
|
rescue => e
|
|
103
75
|
raise(ArgumentError, "Error encountered while parsing '#{filename}'\n#{e.class} - #{e.message}")
|
|
104
76
|
end
|
|
105
77
|
|
|
106
|
-
|
|
78
|
+
adapter_class.new.adapt(parsed_result)
|
|
79
|
+
end
|
|
80
|
+
|
|
107
81
|
|
|
82
|
+
gherkin_version = Gem.loaded_specs['gherkin'].version.version
|
|
83
|
+
gherkin_major_version = gherkin_version.match(/^(\d+)\./)[1].to_i
|
|
84
|
+
|
|
85
|
+
case gherkin_major_version
|
|
86
|
+
when 9
|
|
87
|
+
# NOT A PART OF THE PUBLIC API
|
|
88
|
+
# The method to use for parsing Gherkin text
|
|
89
|
+
def parsing_method(source_text, filename)
|
|
90
|
+
messages = Gherkin.from_source(filename, source_text, { :include_gherkin_document => true }).to_a.map(&:to_hash)
|
|
91
|
+
|
|
92
|
+
potential_error_message = messages.find { |message| message[:attachment] }
|
|
93
|
+
gherkin_ast_message = messages.find { |message| message[:gherkin_document] }
|
|
94
|
+
|
|
95
|
+
if potential_error_message
|
|
96
|
+
raise potential_error_message[:attachment][:data] if potential_error_message[:attachment][:data] =~ /expected.*got/
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
gherkin_ast_message[:gherkin_document]
|
|
100
|
+
end
|
|
101
|
+
when 8
|
|
102
|
+
# NOT A PART OF THE PUBLIC API
|
|
103
|
+
# The method to use for parsing Gherkin text
|
|
104
|
+
def parsing_method(source_text, filename)
|
|
105
|
+
messages = Gherkin.from_source(filename, source_text, { :include_gherkin_document => true }).to_a.map(&:to_hash)
|
|
106
|
+
|
|
107
|
+
potential_error_message = messages.find { |message| message[:attachment] }
|
|
108
|
+
gherkin_ast_message = messages.find { |message| message[:gherkinDocument] }
|
|
109
|
+
|
|
110
|
+
if potential_error_message
|
|
111
|
+
raise potential_error_message[:attachment][:data] if potential_error_message[:attachment][:data] =~ /expected.*got/
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
gherkin_ast_message[:gherkinDocument]
|
|
115
|
+
end
|
|
116
|
+
when 6, 7
|
|
117
|
+
# NOT A PART OF THE PUBLIC API
|
|
118
|
+
# The method to use for parsing Gherkin text
|
|
119
|
+
def parsing_method(source_text, filename)
|
|
120
|
+
messages = Gherkin::Gherkin.from_source(filename, source_text).to_a.map(&:to_hash)
|
|
121
|
+
|
|
122
|
+
potential_error_message = messages.find { |message| message[:attachment] }
|
|
123
|
+
gherkin_ast_message = messages.find { |message| message[:gherkinDocument] }
|
|
124
|
+
|
|
125
|
+
if potential_error_message
|
|
126
|
+
raise potential_error_message[:attachment][:data] if potential_error_message[:attachment][:data] =~ /expected.*got/
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
gherkin_ast_message[:gherkinDocument]
|
|
130
|
+
end
|
|
131
|
+
when 3, 4, 5
|
|
132
|
+
# todo - make these methods private?
|
|
133
|
+
# NOT A PART OF THE PUBLIC API
|
|
134
|
+
# The method to use for parsing Gherkin text
|
|
135
|
+
# Filename isn't used by this version of Gherkin but keeping the parameter so that the calling method only has to know one method signature
|
|
136
|
+
def parsing_method(source_text, _filename)
|
|
137
|
+
Gherkin::Parser.new.parse(source_text)
|
|
138
|
+
end
|
|
139
|
+
when 2
|
|
140
|
+
# NOT A PART OF THE PUBLIC API
|
|
141
|
+
# The method to use for parsing Gherkin text
|
|
142
|
+
def parsing_method(source_text, filename)
|
|
143
|
+
io = StringIO.new
|
|
144
|
+
formatter = Gherkin::Formatter::JSONFormatter.new(io)
|
|
145
|
+
parser = Gherkin::Parser::Parser.new(formatter)
|
|
146
|
+
parser.parse(source_text, filename, 0)
|
|
147
|
+
formatter.done
|
|
148
|
+
MultiJson.load(io.string)
|
|
149
|
+
end
|
|
150
|
+
else
|
|
151
|
+
raise("Unknown Gherkin version: '#{gherkin_version}'")
|
|
152
|
+
end
|
|
108
153
|
|
|
109
|
-
|
|
154
|
+
# NOT A PART OF THE PUBLIC API
|
|
155
|
+
# The adapter to use when converting an AST to a standard internal shape
|
|
156
|
+
define_method('adapter_class') do
|
|
157
|
+
CukeModeler.const_get("Gherkin#{gherkin_major_version}Adapter")
|
|
110
158
|
end
|
|
111
159
|
|
|
112
160
|
end
|