cuke_modeler 2.0.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +59 -2
  3. data/README.md +14 -16
  4. data/cuke_modeler.gemspec +12 -9
  5. data/lib/cuke_modeler.rb +1 -0
  6. data/lib/cuke_modeler/adapters/gherkin_10_adapter.rb +12 -0
  7. data/lib/cuke_modeler/adapters/gherkin_11_adapter.rb +12 -0
  8. data/lib/cuke_modeler/adapters/gherkin_12_adapter.rb +12 -0
  9. data/lib/cuke_modeler/adapters/gherkin_13_adapter.rb +12 -0
  10. data/lib/cuke_modeler/adapters/gherkin_14_adapter.rb +12 -0
  11. data/lib/cuke_modeler/adapters/gherkin_15_adapter.rb +12 -0
  12. data/lib/cuke_modeler/adapters/{gherkin_6_adapter.rb → gherkin_9_adapter.rb} +104 -69
  13. data/lib/cuke_modeler/containing.rb +15 -5
  14. data/lib/cuke_modeler/models/background.rb +1 -1
  15. data/lib/cuke_modeler/models/cell.rb +1 -1
  16. data/lib/cuke_modeler/models/comment.rb +1 -1
  17. data/lib/cuke_modeler/models/directory.rb +2 -2
  18. data/lib/cuke_modeler/models/doc_string.rb +1 -1
  19. data/lib/cuke_modeler/models/example.rb +1 -1
  20. data/lib/cuke_modeler/models/feature.rb +16 -5
  21. data/lib/cuke_modeler/models/feature_file.rb +2 -2
  22. data/lib/cuke_modeler/models/outline.rb +1 -1
  23. data/lib/cuke_modeler/models/row.rb +1 -1
  24. data/lib/cuke_modeler/models/rule.rb +99 -0
  25. data/lib/cuke_modeler/models/scenario.rb +1 -1
  26. data/lib/cuke_modeler/models/step.rb +1 -1
  27. data/lib/cuke_modeler/models/table.rb +1 -1
  28. data/lib/cuke_modeler/models/tag.rb +1 -1
  29. data/lib/cuke_modeler/parsing.rb +89 -110
  30. data/lib/cuke_modeler/version.rb +1 -1
  31. data/testing/cucumber/features/modeling/feature_modeling.feature +28 -7
  32. data/testing/cucumber/features/modeling/feature_output.feature +45 -23
  33. data/testing/cucumber/features/modeling/rule_modeling.feature +108 -0
  34. data/testing/cucumber/features/modeling/rule_output.feature +111 -0
  35. metadata +39 -140
  36. data/.gitignore +0 -18
  37. data/.simplecov +0 -7
  38. data/.travis.yml +0 -63
  39. data/Gemfile +0 -37
  40. data/Rakefile +0 -73
  41. data/appveyor.yml +0 -61
  42. data/lib/cuke_modeler/adapters/gherkin_2_adapter.rb +0 -274
  43. data/lib/cuke_modeler/adapters/gherkin_3_adapter.rb +0 -297
  44. data/lib/cuke_modeler/adapters/gherkin_4_adapter.rb +0 -309
  45. data/testing/cucumber/step_definitions/action_steps.rb +0 -13
  46. data/testing/cucumber/step_definitions/background_steps.rb +0 -1
  47. data/testing/cucumber/step_definitions/directory_steps.rb +0 -6
  48. data/testing/cucumber/step_definitions/doc_string_steps.rb +0 -1
  49. data/testing/cucumber/step_definitions/feature_file_steps.rb +0 -16
  50. data/testing/cucumber/step_definitions/feature_steps.rb +0 -7
  51. data/testing/cucumber/step_definitions/modeling_steps.rb +0 -49
  52. data/testing/cucumber/step_definitions/setup_steps.rb +0 -32
  53. data/testing/cucumber/step_definitions/step_steps.rb +0 -3
  54. data/testing/cucumber/step_definitions/table_steps.rb +0 -1
  55. data/testing/cucumber/step_definitions/tag_steps.rb +0 -3
  56. data/testing/cucumber/step_definitions/verification_steps.rb +0 -181
  57. data/testing/cucumber/support/env.rb +0 -30
  58. data/testing/dialect_helper.rb +0 -48
  59. data/testing/file_helper.rb +0 -47
  60. data/testing/gemfiles/gherkin2.gemfile +0 -33
  61. data/testing/gemfiles/gherkin3.gemfile +0 -26
  62. data/testing/gemfiles/gherkin4.gemfile +0 -27
  63. data/testing/gemfiles/gherkin5.gemfile +0 -27
  64. data/testing/gemfiles/gherkin6.gemfile +0 -10
  65. data/testing/rspec/spec/integration/adapters/gherkin_2_adapter_spec.rb +0 -166
  66. data/testing/rspec/spec/integration/adapters/gherkin_3_adapter_spec.rb +0 -166
  67. data/testing/rspec/spec/integration/adapters/gherkin_4_adapter_spec.rb +0 -165
  68. data/testing/rspec/spec/integration/adapters/gherkin_6_adapter_spec.rb +0 -166
  69. data/testing/rspec/spec/integration/models/background_integration_spec.rb +0 -442
  70. data/testing/rspec/spec/integration/models/cell_integration_spec.rb +0 -335
  71. data/testing/rspec/spec/integration/models/comment_integration_spec.rb +0 -177
  72. data/testing/rspec/spec/integration/models/directory_integration_spec.rb +0 -218
  73. data/testing/rspec/spec/integration/models/doc_string_integration_spec.rb +0 -402
  74. data/testing/rspec/spec/integration/models/example_integration_spec.rb +0 -741
  75. data/testing/rspec/spec/integration/models/feature_file_integration_spec.rb +0 -272
  76. data/testing/rspec/spec/integration/models/feature_integration_spec.rb +0 -650
  77. data/testing/rspec/spec/integration/models/model_integration_spec.rb +0 -15
  78. data/testing/rspec/spec/integration/models/outline_integration_spec.rb +0 -624
  79. data/testing/rspec/spec/integration/models/row_integration_spec.rb +0 -291
  80. data/testing/rspec/spec/integration/models/scenario_integration_spec.rb +0 -479
  81. data/testing/rspec/spec/integration/models/step_integration_spec.rb +0 -569
  82. data/testing/rspec/spec/integration/models/table_integration_spec.rb +0 -337
  83. data/testing/rspec/spec/integration/models/tag_integration_spec.rb +0 -259
  84. data/testing/rspec/spec/integration/nested_integration_spec.rb +0 -91
  85. data/testing/rspec/spec/integration/parsing_integration_spec.rb +0 -122
  86. data/testing/rspec/spec/integration/shared/models_integration_specs.rb +0 -18
  87. data/testing/rspec/spec/spec_helper.rb +0 -136
  88. data/testing/rspec/spec/unit/cuke_modeler_unit_spec.rb +0 -25
  89. data/testing/rspec/spec/unit/described_unit_spec.rb +0 -23
  90. data/testing/rspec/spec/unit/models/background_unit_spec.rb +0 -83
  91. data/testing/rspec/spec/unit/models/cell_unit_spec.rb +0 -68
  92. data/testing/rspec/spec/unit/models/comment_unit_spec.rb +0 -68
  93. data/testing/rspec/spec/unit/models/directory_unit_spec.rb +0 -127
  94. data/testing/rspec/spec/unit/models/doc_string_unit_spec.rb +0 -100
  95. data/testing/rspec/spec/unit/models/example_unit_spec.rb +0 -133
  96. data/testing/rspec/spec/unit/models/feature_file_unit_spec.rb +0 -125
  97. data/testing/rspec/spec/unit/models/feature_unit_spec.rb +0 -157
  98. data/testing/rspec/spec/unit/models/model_unit_spec.rb +0 -15
  99. data/testing/rspec/spec/unit/models/outline_unit_spec.rb +0 -117
  100. data/testing/rspec/spec/unit/models/row_unit_spec.rb +0 -68
  101. data/testing/rspec/spec/unit/models/scenario_unit_spec.rb +0 -86
  102. data/testing/rspec/spec/unit/models/step_unit_spec.rb +0 -109
  103. data/testing/rspec/spec/unit/models/table_unit_spec.rb +0 -77
  104. data/testing/rspec/spec/unit/models/tag_unit_spec.rb +0 -68
  105. data/testing/rspec/spec/unit/named_unit_spec.rb +0 -23
  106. data/testing/rspec/spec/unit/nested_unit_spec.rb +0 -43
  107. data/testing/rspec/spec/unit/parsed_unit_spec.rb +0 -27
  108. data/testing/rspec/spec/unit/parsing_unit_spec.rb +0 -54
  109. data/testing/rspec/spec/unit/shared/bare_bones_models_unit_specs.rb +0 -14
  110. data/testing/rspec/spec/unit/shared/containing_models_unit_specs.rb +0 -127
  111. data/testing/rspec/spec/unit/shared/described_models_unit_specs.rb +0 -38
  112. data/testing/rspec/spec/unit/shared/keyworded_models_unit_specs.rb +0 -58
  113. data/testing/rspec/spec/unit/shared/models_unit_specs.rb +0 -15
  114. data/testing/rspec/spec/unit/shared/named_models_unit_specs.rb +0 -39
  115. data/testing/rspec/spec/unit/shared/nested_models_unit_specs.rb +0 -51
  116. data/testing/rspec/spec/unit/shared/parsed_models_unit_specs.rb +0 -39
  117. data/testing/rspec/spec/unit/shared/prepopulated_models_unit_specs.rb +0 -18
  118. data/testing/rspec/spec/unit/shared/sourced_models_unit_specs.rb +0 -39
  119. data/testing/rspec/spec/unit/shared/stepped_models_unit_specs.rb +0 -46
  120. data/testing/rspec/spec/unit/shared/stringifiable_models_unit_specs.rb +0 -18
  121. data/testing/rspec/spec/unit/shared/tagged_models_unit_specs.rb +0 -72
  122. data/testing/rspec/spec/unit/sourceable_unit_spec.rb +0 -27
  123. data/testing/rspec/spec/unit/stepped_unit_spec.rb +0 -23
  124. data/testing/rspec/spec/unit/taggable_unit_spec.rb +0 -69
  125. data/testing/test_languages.json +0 -45
  126. data/todo.txt +0 -25
@@ -32,7 +32,6 @@ module CukeModeler
32
32
 
33
33
  model.parent_model = self
34
34
 
35
-
36
35
  model
37
36
  end
38
37
 
@@ -130,6 +129,15 @@ module CukeModeler
130
129
  populate_children(feature_object, parsed_feature_data)
131
130
  end
132
131
 
132
+ def populate_rule(rule_object, parsed_rule_data)
133
+ populate_parsing_data(rule_object, parsed_rule_data)
134
+ populate_source_line(rule_object, parsed_rule_data)
135
+ populate_keyword(rule_object, parsed_rule_data)
136
+ populate_name(rule_object, parsed_rule_data)
137
+ populate_description(rule_object, parsed_rule_data)
138
+ populate_children(rule_object, parsed_rule_data)
139
+ end
140
+
133
141
  def populate_directory(directory_object, processed_directory_data)
134
142
  directory_object.path = processed_directory_data['path']
135
143
 
@@ -213,18 +221,20 @@ module CukeModeler
213
221
  end
214
222
  end
215
223
 
216
- def populate_children(feature_model, parsed_feature_data)
224
+ def populate_children(model, parsed_feature_data)
217
225
  elements = parsed_feature_data['elements']
218
226
 
219
227
  if elements
220
228
  elements.each do |element|
221
229
  case element['type']
222
230
  when 'Scenario', 'scenario'
223
- feature_model.tests << build_child_model(Scenario, element)
231
+ model.tests << build_child_model(Scenario, element)
224
232
  when 'ScenarioOutline', 'scenario_outline'
225
- feature_model.tests << build_child_model(Outline, element)
233
+ model.tests << build_child_model(Outline, element)
226
234
  when 'Background', 'background'
227
- feature_model.background = build_child_model(Background, element)
235
+ model.background = build_child_model(Background, element)
236
+ when 'Rule'
237
+ model.rules << build_child_model(Rule, element)
228
238
  else
229
239
  raise(ArgumentError, "Unknown element type: #{element['type']}")
230
240
  end
@@ -64,7 +64,7 @@ module CukeModeler
64
64
 
65
65
  parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_background.feature')
66
66
 
67
- parsed_file.first['feature']['elements'].first
67
+ parsed_file['feature']['elements'].first
68
68
  end
69
69
 
70
70
  end
@@ -41,7 +41,7 @@ module CukeModeler
41
41
 
42
42
  parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_cell.feature')
43
43
 
44
- parsed_file.first['feature']['elements'].first['steps'].first['table']['rows'].first['cells'].first
44
+ parsed_file['feature']['elements'].first['steps'].first['table']['rows'].first['cells'].first
45
45
  end
46
46
 
47
47
  end
@@ -40,7 +40,7 @@ module CukeModeler
40
40
 
41
41
  parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_comment.feature')
42
42
 
43
- parsed_file.first['comments'].last
43
+ parsed_file['comments'].last
44
44
  end
45
45
 
46
46
  end
@@ -82,8 +82,8 @@ module CukeModeler
82
82
  def process_feature_file(file_path)
83
83
  source_text = IO.read(file_path)
84
84
 
85
- feature_file_data = Parsing::parse_text(source_text, file_path).first
86
- feature_file_data = feature_file_data.merge({'path' => file_path})
85
+ feature_file_data = Parsing::parse_text(source_text, file_path)
86
+ feature_file_data = feature_file_data.merge({ 'path' => file_path })
87
87
 
88
88
  feature_file_data
89
89
  end
@@ -45,7 +45,7 @@ module CukeModeler
45
45
 
46
46
  parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_doc_string.feature')
47
47
 
48
- parsed_file.first['feature']['elements'].first['steps'].first['doc_string']
48
+ parsed_file['feature']['elements'].first['steps'].first['doc_string']
49
49
  end
50
50
 
51
51
  def content_type_output_string
@@ -124,7 +124,7 @@ module CukeModeler
124
124
 
125
125
  parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_example.feature')
126
126
 
127
- parsed_file.first['feature']['elements'].first['examples'].first
127
+ parsed_file['feature']['elements'].first['examples'].first
128
128
  end
129
129
 
130
130
  def determine_buffer_size(index)
@@ -17,6 +17,9 @@ module CukeModeler
17
17
  # The Background object contained by the Feature
18
18
  attr_accessor :background
19
19
 
20
+ # The Rule objects contained by the Feature
21
+ attr_accessor :rules
22
+
20
23
  # The Scenario and Outline objects contained by the Feature
21
24
  attr_accessor :tests
22
25
 
@@ -25,6 +28,7 @@ module CukeModeler
25
28
  # object.
26
29
  def initialize(source_text = nil)
27
30
  @tags = []
31
+ @rules = []
28
32
  @tests = []
29
33
 
30
34
  super(source_text)
@@ -50,6 +54,8 @@ module CukeModeler
50
54
  @tests.select { |test| test.is_a? Outline }
51
55
  end
52
56
 
57
+ # TODO: Remove this method on next major version release
58
+ # DEPRECATED
53
59
  # Returns the number of test cases contained in the feature. A test case is a
54
60
  # single set of test values, such as an individual scenario or one example row
55
61
  # of an outline.
@@ -63,7 +69,7 @@ module CukeModeler
63
69
 
64
70
  # Returns the model objects that belong to this model.
65
71
  def children
66
- models = tests + tags
72
+ models = rules + tests + tags
67
73
  models << background if background
68
74
 
69
75
  models
@@ -79,6 +85,7 @@ module CukeModeler
79
85
  text << "\n" + description_output_string unless (description.nil? || description.empty?)
80
86
  text << "\n\n" + background_output_string if background
81
87
  text << "\n\n" + tests_output_string unless tests.empty?
88
+ text << "\n\n" + rules_output_string unless rules.empty?
82
89
 
83
90
  text
84
91
  end
@@ -90,18 +97,22 @@ module CukeModeler
90
97
  def parse_source(source_text)
91
98
  parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_feature.feature')
92
99
 
93
- parsed_file.first['feature']
100
+ parsed_file['feature']
94
101
  end
95
102
 
96
103
  def background_output_string
97
- test_output_string(background)
104
+ child_element_output_string(background)
98
105
  end
99
106
 
100
107
  def tests_output_string
101
- tests.collect { |test| test_output_string(test) }.join("\n\n")
108
+ tests.collect { |test| child_element_output_string(test) }.join("\n\n")
109
+ end
110
+
111
+ def rules_output_string
112
+ rules.collect { |rule| child_element_output_string(rule) }.join("\n\n")
102
113
  end
103
114
 
104
- def test_output_string(model)
115
+ def child_element_output_string(model)
105
116
  model.to_s.split("\n").collect { |line| line.empty? ? '' : " #{line}" }.join("\n")
106
117
  end
107
118
 
@@ -57,8 +57,8 @@ module CukeModeler
57
57
  def process_feature_file(file_path)
58
58
  source_text = IO.read(file_path)
59
59
 
60
- feature_file_data = Parsing::parse_text(source_text, file_path).first
61
- feature_file_data = feature_file_data.merge({'path' => file_path})
60
+ feature_file_data = Parsing::parse_text(source_text, file_path)
61
+ feature_file_data = feature_file_data.merge({ 'path' => file_path })
62
62
 
63
63
  feature_file_data
64
64
  end
@@ -72,7 +72,7 @@ module CukeModeler
72
72
 
73
73
  parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_outline.feature')
74
74
 
75
- parsed_file.first['feature']['elements'].first
75
+ parsed_file['feature']['elements'].first
76
76
  end
77
77
 
78
78
  def examples_output_string
@@ -43,7 +43,7 @@ module CukeModeler
43
43
 
44
44
  parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_row.feature')
45
45
 
46
- parsed_file.first['feature']['elements'].first['steps'].first['table']['rows'].first
46
+ parsed_file['feature']['elements'].first['steps'].first['table']['rows'].first
47
47
  end
48
48
 
49
49
  end
@@ -0,0 +1,99 @@
1
+ module CukeModeler
2
+
3
+ # A class modeling a rule in a Cucumber suite.
4
+
5
+ class Rule < Model
6
+
7
+ include Parsing
8
+ include Parsed
9
+ include Named
10
+ include Described
11
+ include Sourceable
12
+
13
+
14
+ # The keyword for the rule
15
+ attr_accessor :keyword
16
+
17
+ # The Background object contained by the Rule
18
+ attr_accessor :background
19
+
20
+ # The Scenario and Outline objects contained by the Rule
21
+ attr_accessor :tests
22
+
23
+
24
+ # Creates a new Rule object and, if *source_text* is provided, populates the
25
+ # object.
26
+ def initialize(source_text = nil)
27
+ @tests = []
28
+
29
+ super(source_text)
30
+
31
+ if source_text
32
+ parsed_rule_data = parse_source(source_text)
33
+ populate_rule(self, parsed_rule_data)
34
+ end
35
+ end
36
+
37
+ # Returns *true* if the rule contains a background, *false* otherwise.
38
+ def has_background?
39
+ !@background.nil?
40
+ end
41
+
42
+ # Returns the scenario models contained in the rule.
43
+ def scenarios
44
+ @tests.select { |test| test.is_a? Scenario }
45
+ end
46
+
47
+ # Returns the outline models contained in the rule.
48
+ def outlines
49
+ @tests.select { |test| test.is_a? Outline }
50
+ end
51
+
52
+ # Returns the model objects that belong to this model.
53
+ def children
54
+ models = tests
55
+ models << background if background
56
+
57
+ models
58
+ end
59
+
60
+ # Returns a string representation of this model. For a rule model,
61
+ # this will be Gherkin text that is equivalent to the rule being modeled.
62
+ def to_s
63
+ text = ''
64
+
65
+ text << "#{@keyword}:#{name_output_string}"
66
+ text << "\n" + description_output_string unless (description.nil? || description.empty?)
67
+ text << "\n\n" + background_output_string if background
68
+ text << "\n\n" + tests_output_string unless tests.empty?
69
+
70
+ text
71
+ end
72
+
73
+
74
+ private
75
+
76
+
77
+ def parse_source(source_text)
78
+ base_file_string = "# language: #{Parsing.dialect}\n#{dialect_feature_keyword}: Fake feature to parse\n"
79
+ source_text = base_file_string + source_text
80
+
81
+ parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_rule.feature')
82
+
83
+ parsed_file['feature']['elements'].first
84
+ end
85
+
86
+ def background_output_string
87
+ test_output_string(background)
88
+ end
89
+
90
+ def tests_output_string
91
+ tests.collect { |test| test_output_string(test) }.join("\n\n")
92
+ end
93
+
94
+ def test_output_string(model)
95
+ model.to_s.split("\n").collect { |line| line.empty? ? '' : " #{line}" }.join("\n")
96
+ end
97
+
98
+ end
99
+ end
@@ -67,7 +67,7 @@ module CukeModeler
67
67
 
68
68
  parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_scenario.feature')
69
69
 
70
- parsed_file.first['feature']['elements'].first
70
+ parsed_file['feature']['elements'].first
71
71
  end
72
72
 
73
73
  end
@@ -64,7 +64,7 @@ module CukeModeler
64
64
 
65
65
  parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_step.feature')
66
66
 
67
- parsed_file.first['feature']['elements'].first['steps'].first
67
+ parsed_file['feature']['elements'].first['steps'].first
68
68
  end
69
69
 
70
70
  def text_matches?(other_step)
@@ -47,7 +47,7 @@ module CukeModeler
47
47
 
48
48
  parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_table.feature')
49
49
 
50
- parsed_file.first['feature']['elements'].first['steps'].first['table']
50
+ parsed_file['feature']['elements'].first['steps'].first['table']
51
51
  end
52
52
 
53
53
  def row_output_string(row)
@@ -40,7 +40,7 @@ module CukeModeler
40
40
 
41
41
  parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_tag.feature')
42
42
 
43
- parsed_file.first['feature']['tags'].first
43
+ parsed_file['feature']['tags'].first
44
44
  end
45
45
 
46
46
  end
@@ -1,115 +1,36 @@
1
- module CukeModeler
2
-
3
- # A module providing source text parsing functionality.
4
-
5
- module Parsing
6
-
7
-
8
- # Have to at least load some version of the gem before which version of the gem has been loaded can
9
- # be determined and the rest of the needed files can be loaded. Try the old one first and then the
10
- # new one.
11
- begin
12
- require 'gherkin'
13
- rescue LoadError
14
- begin
15
- require 'gherkin/parser'
16
- rescue LoadError
17
- # Gherkin 6.x
18
- require 'gherkin/gherkin'
19
- end
20
- end
21
-
22
-
23
- # The *gherkin* gem loads differently and has different grammar rules across major versions. Parsing
24
- # will be done with an 'adapter' appropriate to the version of the *gherkin* gem that has been activated.
25
-
26
- gherkin_version = Gem.loaded_specs['gherkin'].version.version
27
-
28
- case gherkin_version
29
- when /^6\./
30
- require 'gherkin/gherkin'
31
- require 'cuke_modeler/adapters/gherkin_6_adapter'
32
-
33
- # NOT A PART OF THE PUBLIC API
34
- # The method to use for parsing Gherkin text
35
- def self.parsing_method(source_text, filename)
36
- messages = Gherkin::Gherkin.from_source(filename, source_text, {:default_dialect => CukeModeler::Parsing.dialect}).to_a
37
-
38
- messages.map(&:to_hash).find { |message| message[:gherkinDocument] }[:gherkinDocument]
39
- end
40
-
41
- # NOT A PART OF THE PUBLIC API
42
- # The adapter to use when converting an AST to a standard internal shape
43
- def self.adapter_class
44
- CukeModeler::Gherkin6Adapter
45
- end
46
-
47
- when /^[54]\./
48
- require 'gherkin/parser'
49
- require 'cuke_modeler/adapters/gherkin_4_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 9.x
6
+ require 'gherkin'
7
+ rescue LoadError => e
8
+ # Add other entry points again if things change again
9
+ raise e
10
+ end
50
11
 
51
12
 
52
- # todo - make these methods private?
53
- # NOT A PART OF THE PUBLIC API
54
- # The method to use for parsing Gherkin text
55
- # 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
56
- def self.parsing_method(source_text, _filename)
57
- Gherkin::Parser.new.parse(source_text)
58
- end
59
-
60
- # NOT A PART OF THE PUBLIC API
61
- # The adapter to use when converting an AST to a standard internal shape
62
- def self.adapter_class
63
- CukeModeler::Gherkin4Adapter
64
- end
13
+ # The *cucumber-gherkin* gem loads differently and has different grammar rules across major versions. Parsing
14
+ # will be done with an 'adapter' appropriate to the version of the *cucumber-gherkin* gem that has been activated.
65
15
 
66
- when /^3\./
67
- require 'gherkin/parser'
68
- require 'cuke_modeler/adapters/gherkin_3_adapter'
16
+ gherkin_version = Gem.loaded_specs['cucumber-gherkin'].version.version
17
+ gherkin_major_version = gherkin_version.match(/^(\d+)\./)[1].to_i
69
18
 
19
+ case gherkin_major_version
20
+ when 9, 10, 11, 12, 13, 14, 15
21
+ # Currently nothing else to load beyond the entry point to the gem
22
+ else
23
+ raise("Unknown Gherkin version: '#{gherkin_version}'")
24
+ end
70
25
 
71
- # NOT A PART OF THE PUBLIC API
72
- # The method to use for parsing Gherkin text
73
- # 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
74
- def self.parsing_method(source_text, _filename)
75
- Gherkin::Parser.new.parse(source_text)
76
- end
26
+ require "cuke_modeler/adapters/gherkin_#{gherkin_major_version}_adapter"
77
27
 
78
- # NOT A PART OF THE PUBLIC API
79
- # The adapter to use when converting an AST to a standard internal shape
80
- def self.adapter_class
81
- CukeModeler::Gherkin3Adapter
82
- end
83
- when /^2\./
84
- require 'stringio'
85
- require 'gherkin/formatter/json_formatter'
86
- require 'gherkin'
87
- require 'json'
88
- require 'multi_json'
89
- require 'cuke_modeler/adapters/gherkin_2_adapter'
90
-
91
-
92
- # NOT A PART OF THE PUBLIC API
93
- # The method to use for parsing Gherkin text
94
- def self.parsing_method(source_text, filename)
95
- io = StringIO.new
96
- formatter = Gherkin::Formatter::JSONFormatter.new(io)
97
- parser = Gherkin::Parser::Parser.new(formatter)
98
- parser.parse(source_text, filename, 0)
99
- formatter.done
100
- MultiJson.load(io.string)
101
- end
102
28
 
103
- # NOT A PART OF THE PUBLIC API
104
- # The adapter to use when converting an AST to a standard internal shape
105
- def self.adapter_class
106
- CukeModeler::Gherkin2Adapter
107
- end
29
+ module CukeModeler
108
30
 
109
- else
110
- raise("Unknown Gherkin version: '#{gherkin_version}'")
111
- end
31
+ # A module providing source text parsing functionality.
112
32
 
33
+ module Parsing
113
34
 
114
35
  class << self
115
36
 
@@ -125,28 +46,86 @@ module CukeModeler
125
46
  # The dialects currently known by the gherkin gem
126
47
  def dialects
127
48
  unless @dialects
128
- @dialects = Gem.loaded_specs['gherkin'].version.version[/^2\./] ? Gherkin::I18n::LANGUAGES : Gherkin::DIALECTS
49
+ @dialects = Gherkin::DIALECTS
129
50
  end
130
51
 
131
52
  @dialects
132
53
  end
133
54
 
134
- # Parses the Cucumber feature given in *source_text* and returns an array
135
- # containing the hash representation of its logical structure.
55
+ # Parses the Cucumber feature given in *source_text* and returns a hash representation of
56
+ # its logical structure. This is a standardized AST that should remain consistent across
57
+ # different versions of `cucumber-gherkin`
136
58
  def parse_text(source_text, filename = 'cuke_modeler_fake_file.feature')
137
59
  raise(ArgumentError, "Text to parse must be a String but got #{source_text.class}") unless source_text.is_a?(String)
138
60
 
139
-
140
61
  begin
141
- parsed_result = parsing_method(source_text, filename)
62
+ parsed_result = parsing_method(source_text.encode('UTF-8'), filename)
142
63
  rescue => e
143
64
  raise(ArgumentError, "Error encountered while parsing '#{filename}'\n#{e.class} - #{e.message}")
144
65
  end
145
66
 
146
- adapted_result = adapter_class.new.adapt(parsed_result)
67
+ adapter_class.new.adapt(parsed_result)
68
+ end
69
+
147
70
 
71
+ gherkin_version = Gem.loaded_specs['cucumber-gherkin'].version.version
72
+ gherkin_major_version = gherkin_version.match(/^(\d+)\./)[1].to_i
73
+
74
+ case gherkin_major_version
75
+ when 13, 14, 15
76
+ # todo - make these methods private?
77
+ # NOT A PART OF THE PUBLIC API
78
+ # The method to use for parsing Gherkin text
79
+ def parsing_method(source_text, filename)
80
+ messages = Gherkin.from_source(filename, source_text, { :include_gherkin_document => true }).to_a.map(&:to_hash)
81
+
82
+ error_message = messages.find { |message| message[:parse_error] }
83
+ gherkin_ast_message = messages.find { |message| message[:gherkin_document] }
84
+
85
+ raise error_message[:parse_error][:message] if error_message
86
+
87
+ gherkin_ast_message[:gherkin_document]
88
+ end
89
+ when 12
90
+ # todo - make these methods private?
91
+ # NOT A PART OF THE PUBLIC API
92
+ # The method to use for parsing Gherkin text
93
+ def parsing_method(source_text, filename)
94
+ messages = Gherkin.from_source(filename, source_text, { :include_gherkin_document => true }).to_a.map(&:to_hash)
95
+
96
+ potential_error_message = messages.find { |message| message[:attachment] }
97
+ gherkin_ast_message = messages.find { |message| message[:gherkin_document] }
98
+
99
+ if potential_error_message
100
+ raise potential_error_message[:attachment][:body] if potential_error_message[:attachment][:body] =~ /expected.*got/
101
+ end
102
+
103
+ gherkin_ast_message[:gherkin_document]
104
+ end
105
+ when 9, 10, 11
106
+ # todo - make these methods private?
107
+ # NOT A PART OF THE PUBLIC API
108
+ # The method to use for parsing Gherkin text
109
+ def parsing_method(source_text, filename)
110
+ messages = Gherkin.from_source(filename, source_text, { :include_gherkin_document => true }).to_a.map(&:to_hash)
111
+
112
+ potential_error_message = messages.find { |message| message[:attachment] }
113
+ gherkin_ast_message = messages.find { |message| message[:gherkin_document] }
114
+
115
+ if potential_error_message
116
+ raise potential_error_message[:attachment][:text] if potential_error_message[:attachment][:text] =~ /expected.*got/
117
+ end
118
+
119
+ gherkin_ast_message[:gherkin_document]
120
+ end
121
+ else
122
+ raise("Unknown Gherkin version: '#{gherkin_version}'")
123
+ end
148
124
 
149
- adapted_result
125
+ # NOT A PART OF THE PUBLIC API
126
+ # The adapter to use when converting an AST to a standard internal shape
127
+ define_method('adapter_class') do
128
+ CukeModeler.const_get("Gherkin#{gherkin_major_version}Adapter")
150
129
  end
151
130
 
152
131
  end