cuke_modeler 1.5.1 → 3.2.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 (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