cuke_modeler 1.5.1 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -1
  3. data/LICENSE.txt +1 -1
  4. data/README.md +13 -15
  5. data/cuke_modeler.gemspec +12 -9
  6. data/lib/cuke_modeler.rb +1 -0
  7. data/lib/cuke_modeler/adapters/gherkin_10_adapter.rb +12 -0
  8. data/lib/cuke_modeler/adapters/gherkin_11_adapter.rb +12 -0
  9. data/lib/cuke_modeler/adapters/gherkin_12_adapter.rb +12 -0
  10. data/lib/cuke_modeler/adapters/gherkin_13_adapter.rb +12 -0
  11. data/lib/cuke_modeler/adapters/gherkin_14_adapter.rb +12 -0
  12. data/lib/cuke_modeler/adapters/{gherkin_6_adapter.rb → gherkin_9_adapter.rb} +105 -69
  13. data/lib/cuke_modeler/containing.rb +16 -5
  14. data/lib/cuke_modeler/described.rb +1 -0
  15. data/lib/cuke_modeler/models/background.rb +1 -1
  16. data/lib/cuke_modeler/models/cell.rb +1 -1
  17. data/lib/cuke_modeler/models/comment.rb +1 -1
  18. data/lib/cuke_modeler/models/directory.rb +2 -2
  19. data/lib/cuke_modeler/models/doc_string.rb +1 -1
  20. data/lib/cuke_modeler/models/example.rb +1 -1
  21. data/lib/cuke_modeler/models/feature.rb +16 -5
  22. data/lib/cuke_modeler/models/feature_file.rb +2 -2
  23. data/lib/cuke_modeler/models/outline.rb +1 -1
  24. data/lib/cuke_modeler/models/row.rb +1 -1
  25. data/lib/cuke_modeler/models/rule.rb +99 -0
  26. data/lib/cuke_modeler/models/scenario.rb +1 -1
  27. data/lib/cuke_modeler/models/step.rb +32 -3
  28. data/lib/cuke_modeler/models/table.rb +1 -1
  29. data/lib/cuke_modeler/models/tag.rb +1 -1
  30. data/lib/cuke_modeler/named.rb +1 -0
  31. data/lib/cuke_modeler/nested.rb +1 -0
  32. data/lib/cuke_modeler/parsed.rb +1 -0
  33. data/lib/cuke_modeler/parsing.rb +89 -102
  34. data/lib/cuke_modeler/sourceable.rb +1 -0
  35. data/lib/cuke_modeler/stepped.rb +1 -0
  36. data/lib/cuke_modeler/taggable.rb +1 -0
  37. data/lib/cuke_modeler/version.rb +1 -1
  38. data/testing/cucumber/features/analysis/step_comparison.feature +25 -0
  39. data/testing/cucumber/features/analysis/test_comparison.feature +1 -1
  40. data/testing/cucumber/features/modeling/feature_modeling.feature +28 -7
  41. data/testing/cucumber/features/modeling/feature_output.feature +45 -23
  42. data/testing/cucumber/features/modeling/rule_modeling.feature +108 -0
  43. data/testing/cucumber/features/modeling/rule_output.feature +111 -0
  44. metadata +39 -140
  45. data/.gitignore +0 -18
  46. data/.simplecov +0 -7
  47. data/.travis.yml +0 -60
  48. data/Gemfile +0 -37
  49. data/Rakefile +0 -73
  50. data/appveyor.yml +0 -61
  51. data/lib/cuke_modeler/adapters/gherkin_2_adapter.rb +0 -273
  52. data/lib/cuke_modeler/adapters/gherkin_3_adapter.rb +0 -296
  53. data/lib/cuke_modeler/adapters/gherkin_4_adapter.rb +0 -308
  54. data/testing/cucumber/step_definitions/action_steps.rb +0 -13
  55. data/testing/cucumber/step_definitions/background_steps.rb +0 -1
  56. data/testing/cucumber/step_definitions/directory_steps.rb +0 -6
  57. data/testing/cucumber/step_definitions/doc_string_steps.rb +0 -1
  58. data/testing/cucumber/step_definitions/feature_file_steps.rb +0 -16
  59. data/testing/cucumber/step_definitions/feature_steps.rb +0 -7
  60. data/testing/cucumber/step_definitions/modeling_steps.rb +0 -44
  61. data/testing/cucumber/step_definitions/setup_steps.rb +0 -32
  62. data/testing/cucumber/step_definitions/step_steps.rb +0 -3
  63. data/testing/cucumber/step_definitions/table_steps.rb +0 -1
  64. data/testing/cucumber/step_definitions/tag_steps.rb +0 -3
  65. data/testing/cucumber/step_definitions/verification_steps.rb +0 -173
  66. data/testing/cucumber/support/env.rb +0 -30
  67. data/testing/dialect_helper.rb +0 -48
  68. data/testing/file_helper.rb +0 -47
  69. data/testing/gemfiles/gherkin2.gemfile +0 -33
  70. data/testing/gemfiles/gherkin3.gemfile +0 -26
  71. data/testing/gemfiles/gherkin4.gemfile +0 -27
  72. data/testing/gemfiles/gherkin5.gemfile +0 -27
  73. data/testing/gemfiles/gherkin6.gemfile +0 -10
  74. data/testing/rspec/spec/integration/adapters/gherkin_2_adapter_spec.rb +0 -166
  75. data/testing/rspec/spec/integration/adapters/gherkin_3_adapter_spec.rb +0 -166
  76. data/testing/rspec/spec/integration/adapters/gherkin_4_adapter_spec.rb +0 -165
  77. data/testing/rspec/spec/integration/adapters/gherkin_6_adapter_spec.rb +0 -166
  78. data/testing/rspec/spec/integration/models/background_integration_spec.rb +0 -442
  79. data/testing/rspec/spec/integration/models/cell_integration_spec.rb +0 -335
  80. data/testing/rspec/spec/integration/models/comment_integration_spec.rb +0 -177
  81. data/testing/rspec/spec/integration/models/directory_integration_spec.rb +0 -218
  82. data/testing/rspec/spec/integration/models/doc_string_integration_spec.rb +0 -402
  83. data/testing/rspec/spec/integration/models/example_integration_spec.rb +0 -741
  84. data/testing/rspec/spec/integration/models/feature_file_integration_spec.rb +0 -272
  85. data/testing/rspec/spec/integration/models/feature_integration_spec.rb +0 -650
  86. data/testing/rspec/spec/integration/models/model_integration_spec.rb +0 -15
  87. data/testing/rspec/spec/integration/models/outline_integration_spec.rb +0 -624
  88. data/testing/rspec/spec/integration/models/row_integration_spec.rb +0 -291
  89. data/testing/rspec/spec/integration/models/scenario_integration_spec.rb +0 -479
  90. data/testing/rspec/spec/integration/models/step_integration_spec.rb +0 -475
  91. data/testing/rspec/spec/integration/models/table_integration_spec.rb +0 -337
  92. data/testing/rspec/spec/integration/models/tag_integration_spec.rb +0 -259
  93. data/testing/rspec/spec/integration/nested_integration_spec.rb +0 -91
  94. data/testing/rspec/spec/integration/parsing_integration_spec.rb +0 -122
  95. data/testing/rspec/spec/integration/shared/models_integration_specs.rb +0 -18
  96. data/testing/rspec/spec/spec_helper.rb +0 -125
  97. data/testing/rspec/spec/unit/cuke_modeler_unit_spec.rb +0 -25
  98. data/testing/rspec/spec/unit/described_unit_spec.rb +0 -23
  99. data/testing/rspec/spec/unit/models/background_unit_spec.rb +0 -83
  100. data/testing/rspec/spec/unit/models/cell_unit_spec.rb +0 -68
  101. data/testing/rspec/spec/unit/models/comment_unit_spec.rb +0 -68
  102. data/testing/rspec/spec/unit/models/directory_unit_spec.rb +0 -127
  103. data/testing/rspec/spec/unit/models/doc_string_unit_spec.rb +0 -100
  104. data/testing/rspec/spec/unit/models/example_unit_spec.rb +0 -133
  105. data/testing/rspec/spec/unit/models/feature_file_unit_spec.rb +0 -125
  106. data/testing/rspec/spec/unit/models/feature_unit_spec.rb +0 -157
  107. data/testing/rspec/spec/unit/models/model_unit_spec.rb +0 -15
  108. data/testing/rspec/spec/unit/models/outline_unit_spec.rb +0 -117
  109. data/testing/rspec/spec/unit/models/row_unit_spec.rb +0 -68
  110. data/testing/rspec/spec/unit/models/scenario_unit_spec.rb +0 -86
  111. data/testing/rspec/spec/unit/models/step_unit_spec.rb +0 -109
  112. data/testing/rspec/spec/unit/models/table_unit_spec.rb +0 -77
  113. data/testing/rspec/spec/unit/models/tag_unit_spec.rb +0 -68
  114. data/testing/rspec/spec/unit/named_unit_spec.rb +0 -23
  115. data/testing/rspec/spec/unit/nested_unit_spec.rb +0 -43
  116. data/testing/rspec/spec/unit/parsed_unit_spec.rb +0 -27
  117. data/testing/rspec/spec/unit/parsing_unit_spec.rb +0 -54
  118. data/testing/rspec/spec/unit/shared/bare_bones_models_unit_specs.rb +0 -14
  119. data/testing/rspec/spec/unit/shared/containing_models_unit_specs.rb +0 -127
  120. data/testing/rspec/spec/unit/shared/described_models_unit_specs.rb +0 -38
  121. data/testing/rspec/spec/unit/shared/keyworded_models_unit_specs.rb +0 -58
  122. data/testing/rspec/spec/unit/shared/models_unit_specs.rb +0 -15
  123. data/testing/rspec/spec/unit/shared/named_models_unit_specs.rb +0 -39
  124. data/testing/rspec/spec/unit/shared/nested_models_unit_specs.rb +0 -51
  125. data/testing/rspec/spec/unit/shared/parsed_models_unit_specs.rb +0 -39
  126. data/testing/rspec/spec/unit/shared/prepopulated_models_unit_specs.rb +0 -18
  127. data/testing/rspec/spec/unit/shared/sourced_models_unit_specs.rb +0 -39
  128. data/testing/rspec/spec/unit/shared/stepped_models_unit_specs.rb +0 -46
  129. data/testing/rspec/spec/unit/shared/stringifiable_models_unit_specs.rb +0 -18
  130. data/testing/rspec/spec/unit/shared/tagged_models_unit_specs.rb +0 -72
  131. data/testing/rspec/spec/unit/sourceable_unit_spec.rb +0 -27
  132. data/testing/rspec/spec/unit/stepped_unit_spec.rb +0 -23
  133. data/testing/rspec/spec/unit/taggable_unit_spec.rb +0 -69
  134. data/testing/test_languages.json +0 -45
  135. data/todo.txt +0 -24
@@ -1,5 +1,6 @@
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
@@ -31,7 +32,6 @@ module CukeModeler
31
32
 
32
33
  model.parent_model = self
33
34
 
34
-
35
35
  model
36
36
  end
37
37
 
@@ -129,6 +129,15 @@ module CukeModeler
129
129
  populate_children(feature_object, parsed_feature_data)
130
130
  end
131
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
+
132
141
  def populate_directory(directory_object, processed_directory_data)
133
142
  directory_object.path = processed_directory_data['path']
134
143
 
@@ -212,18 +221,20 @@ module CukeModeler
212
221
  end
213
222
  end
214
223
 
215
- def populate_children(feature_model, parsed_feature_data)
224
+ def populate_children(model, parsed_feature_data)
216
225
  elements = parsed_feature_data['elements']
217
226
 
218
227
  if elements
219
228
  elements.each do |element|
220
229
  case element['type']
221
230
  when 'Scenario', 'scenario'
222
- feature_model.tests << build_child_model(Scenario, element)
231
+ model.tests << build_child_model(Scenario, element)
223
232
  when 'ScenarioOutline', 'scenario_outline'
224
- feature_model.tests << build_child_model(Outline, element)
233
+ model.tests << build_child_model(Outline, element)
225
234
  when 'Background', 'background'
226
- 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)
227
238
  else
228
239
  raise(ArgumentError, "Unknown element type: #{element['type']}")
229
240
  end
@@ -1,5 +1,6 @@
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 represent an element that has a description.
4
5
 
5
6
  module Described
@@ -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
@@ -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.respond_to?(:text)
36
+ return false unless other_step.is_a?(CukeModeler::Step)
37
37
 
38
- text == other_step.text
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.
@@ -62,7 +64,34 @@ module CukeModeler
62
64
 
63
65
  parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_step.feature')
64
66
 
65
- parsed_file.first['feature']['elements'].first['steps'].first
67
+ parsed_file['feature']['elements'].first['steps'].first
68
+ end
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)
66
95
  end
67
96
 
68
97
  end
@@ -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,5 +1,6 @@
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 represent an element that has a name.
4
5
 
5
6
  module Named
@@ -1,5 +1,6 @@
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 are nested inside
4
5
  # of other models.
5
6
 
@@ -1,5 +1,6 @@
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 are parsed from source text.
4
5
 
5
6
  module Parsed
@@ -1,107 +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
- # The method to use for parsing Gherkin text
34
- def self.parsing_method(source_text, filename)
35
- messages = Gherkin::Gherkin.from_source(filename, source_text, {:default_dialect => CukeModeler::Parsing.dialect}).to_a
36
-
37
- messages.map(&:to_hash).find { |message| message[:gherkinDocument] }[:gherkinDocument]
38
- end
39
-
40
- # The adapter to use when converting an AST to a standard internal shape
41
- def self.adapter_class
42
- CukeModeler::Gherkin6Adapter
43
- end
44
-
45
- when /^[54]\./
46
- require 'gherkin/parser'
47
- 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
48
11
 
49
12
 
50
- # todo - make these methods private?
51
- # The method to use for parsing Gherkin text
52
- # 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
53
- def self.parsing_method(source_text, _filename)
54
- Gherkin::Parser.new.parse(source_text)
55
- end
56
-
57
- # The adapter to use when converting an AST to a standard internal shape
58
- def self.adapter_class
59
- CukeModeler::Gherkin4Adapter
60
- 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.
61
15
 
62
- when /^3\./
63
- require 'gherkin/parser'
64
- 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
65
18
 
19
+ case gherkin_major_version
20
+ when 9, 10, 11, 12, 13, 14
21
+ # Currently nothing else to load beyond the entry point to the gem
22
+ else
23
+ raise("Unknown Gherkin version: '#{gherkin_version}'")
24
+ end
66
25
 
67
- # The method to use for parsing Gherkin text
68
- # 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
69
- def self.parsing_method(source_text, _filename)
70
- Gherkin::Parser.new.parse(source_text)
71
- end
26
+ require "cuke_modeler/adapters/gherkin_#{gherkin_major_version}_adapter"
72
27
 
73
- # The adapter to use when converting an AST to a standard internal shape
74
- def self.adapter_class
75
- CukeModeler::Gherkin3Adapter
76
- end
77
- when /^2\./
78
- require 'stringio'
79
- require 'gherkin/formatter/json_formatter'
80
- require 'gherkin'
81
- require 'json'
82
- require 'multi_json'
83
- require 'cuke_modeler/adapters/gherkin_2_adapter'
84
-
85
-
86
- # The method to use for parsing Gherkin text
87
- def self.parsing_method(source_text, filename)
88
- io = StringIO.new
89
- formatter = Gherkin::Formatter::JSONFormatter.new(io)
90
- parser = Gherkin::Parser::Parser.new(formatter)
91
- parser.parse(source_text, filename, 0)
92
- formatter.done
93
- MultiJson.load(io.string)
94
- end
95
28
 
96
- # The adapter to use when converting an AST to a standard internal shape
97
- def self.adapter_class
98
- CukeModeler::Gherkin2Adapter
99
- end
29
+ module CukeModeler
100
30
 
101
- else
102
- raise("Unknown Gherkin version: '#{gherkin_version}'")
103
- end
31
+ # A module providing source text parsing functionality.
104
32
 
33
+ module Parsing
105
34
 
106
35
  class << self
107
36
 
@@ -117,28 +46,86 @@ module CukeModeler
117
46
  # The dialects currently known by the gherkin gem
118
47
  def dialects
119
48
  unless @dialects
120
- @dialects = Gem.loaded_specs['gherkin'].version.version[/^2\./] ? Gherkin::I18n::LANGUAGES : Gherkin::DIALECTS
49
+ @dialects = Gherkin::DIALECTS
121
50
  end
122
51
 
123
52
  @dialects
124
53
  end
125
54
 
126
- # Parses the Cucumber feature given in *source_text* and returns an array
127
- # 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`
128
58
  def parse_text(source_text, filename = 'cuke_modeler_fake_file.feature')
129
59
  raise(ArgumentError, "Text to parse must be a String but got #{source_text.class}") unless source_text.is_a?(String)
130
60
 
131
-
132
61
  begin
133
- parsed_result = parsing_method(source_text, filename)
62
+ parsed_result = parsing_method(source_text.encode('UTF-8'), filename)
134
63
  rescue => e
135
64
  raise(ArgumentError, "Error encountered while parsing '#{filename}'\n#{e.class} - #{e.message}")
136
65
  end
137
66
 
138
- adapted_result = adapter_class.new.adapt(parsed_result)
67
+ adapter_class.new.adapt(parsed_result)
68
+ end
69
+
139
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
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
140
124
 
141
- 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")
142
129
  end
143
130
 
144
131
  end