cuke_modeler 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +51 -33
  3. data/CHANGELOG.md +13 -2
  4. data/Gemfile +9 -2
  5. data/appveyor.yml +29 -2
  6. data/cuke_modeler.gemspec +1 -1
  7. data/lib/cuke_modeler/adapters/gherkin_4_adapter.rb +1 -1
  8. data/lib/cuke_modeler/adapters/gherkin_5_adapter.rb +12 -0
  9. data/lib/cuke_modeler/adapters/gherkin_7_adapter.rb +307 -0
  10. data/lib/cuke_modeler/adapters/gherkin_8_adapter.rb +12 -0
  11. data/lib/cuke_modeler/adapters/gherkin_9_adapter.rb +12 -0
  12. data/lib/cuke_modeler/parsing.rb +116 -108
  13. data/lib/cuke_modeler/version.rb +1 -1
  14. data/testing/gemfiles/gherkin7.gemfile +9 -0
  15. data/testing/gemfiles/gherkin8.gemfile +9 -0
  16. data/testing/gemfiles/gherkin9.gemfile +9 -0
  17. data/testing/helper_methods.rb +23 -0
  18. data/testing/rspec/spec/integration/adapters/gherkin_2_adapter_spec.rb +1 -1
  19. data/testing/rspec/spec/integration/adapters/gherkin_3_adapter_spec.rb +1 -1
  20. data/testing/rspec/spec/integration/adapters/gherkin_4_adapter_spec.rb +1 -1
  21. data/testing/rspec/spec/integration/adapters/gherkin_5_adapter_spec.rb +165 -0
  22. data/testing/rspec/spec/integration/adapters/gherkin_6_adapter_spec.rb +1 -8
  23. data/testing/rspec/spec/integration/adapters/gherkin_7_adapter_spec.rb +162 -0
  24. data/testing/rspec/spec/integration/adapters/gherkin_8_adapter_spec.rb +162 -0
  25. data/testing/rspec/spec/integration/adapters/gherkin_9_adapter_spec.rb +162 -0
  26. data/testing/rspec/spec/integration/models/background_integration_spec.rb +19 -23
  27. data/testing/rspec/spec/integration/models/cell_integration_spec.rb +27 -24
  28. data/testing/rspec/spec/integration/models/comment_integration_spec.rb +26 -23
  29. data/testing/rspec/spec/integration/models/doc_string_integration_spec.rb +19 -23
  30. data/testing/rspec/spec/integration/models/example_integration_spec.rb +50 -38
  31. data/testing/rspec/spec/integration/models/feature_file_integration_spec.rb +32 -28
  32. data/testing/rspec/spec/integration/models/feature_integration_spec.rb +28 -23
  33. data/testing/rspec/spec/integration/models/outline_integration_spec.rb +39 -44
  34. data/testing/rspec/spec/integration/models/row_integration_spec.rb +35 -23
  35. data/testing/rspec/spec/integration/models/scenario_integration_spec.rb +19 -23
  36. data/testing/rspec/spec/integration/models/step_integration_spec.rb +51 -47
  37. data/testing/rspec/spec/integration/models/table_integration_spec.rb +19 -23
  38. data/testing/rspec/spec/integration/models/tag_integration_spec.rb +35 -23
  39. data/testing/rspec/spec/integration/parsing_integration_spec.rb +27 -6
  40. data/testing/rspec/spec/spec_helper.rb +39 -46
  41. metadata +16 -4
@@ -0,0 +1,12 @@
1
+ require_relative 'gherkin_7_adapter'
2
+
3
+ module CukeModeler
4
+
5
+ # NOT A PART OF THE PUBLIC API
6
+ # An adapter that can convert the output of version 8.x of the *gherkin* gem into input that is consumable by this gem.
7
+
8
+ class Gherkin8Adapter < Gherkin7Adapter
9
+
10
+ end
11
+
12
+ end
@@ -0,0 +1,12 @@
1
+ require_relative 'gherkin_7_adapter'
2
+
3
+ module CukeModeler
4
+
5
+ # NOT A PART OF THE PUBLIC API
6
+ # An adapter that can convert the output of version 9.x of the *gherkin* gem into input that is consumable by this gem.
7
+
8
+ class Gherkin9Adapter < Gherkin7Adapter
9
+
10
+ end
11
+
12
+ end
@@ -1,115 +1,48 @@
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'
50
-
51
-
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
1
+ # Have to at least load some version of the gem before which version of the gem has been loaded can
2
+ # be determined and the rest of the needed files can be loaded. The entry points vary across versions,
3
+ # so try them all until one of them works.
4
+ begin
5
+ # Gherkin 2.x, 8.x, 9.x
6
+ require 'gherkin'
7
+ rescue LoadError
8
+ begin
9
+ require 'gherkin/parser'
10
+ rescue LoadError
11
+ # Gherkin 6.x, 7.x
12
+ require 'gherkin/gherkin'
13
+ end
14
+ end
65
15
 
66
- when /^3\./
67
- require 'gherkin/parser'
68
- require 'cuke_modeler/adapters/gherkin_3_adapter'
69
16
 
17
+ # The *gherkin* gem loads differently and has different grammar rules across major versions. Parsing
18
+ # will be done with an 'adapter' appropriate to the version of the *gherkin* gem that has been activated.
19
+
20
+ gherkin_version = Gem.loaded_specs['gherkin'].version.version
21
+ gherkin_major_version = gherkin_version.match(/^(\d+)\./)[1].to_i
22
+
23
+ case gherkin_major_version
24
+ when 6, 7, 8, 9
25
+ require 'gherkin/dialect'
26
+ when 3, 4, 5
27
+ require 'gherkin/parser'
28
+ when 2
29
+ require 'stringio'
30
+ require 'gherkin/formatter/json_formatter'
31
+ require 'gherkin'
32
+ require 'json'
33
+ require 'multi_json'
34
+ else
35
+ raise("Unknown Gherkin version: '#{gherkin_version}'")
36
+ end
70
37
 
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
38
+ require "cuke_modeler/adapters/gherkin_#{gherkin_major_version}_adapter"
77
39
 
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
40
 
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
41
+ module CukeModeler
108
42
 
109
- else
110
- raise("Unknown Gherkin version: '#{gherkin_version}'")
111
- end
43
+ # A module providing source text parsing functionality.
112
44
 
45
+ module Parsing
113
46
 
114
47
  class << self
115
48
 
@@ -136,17 +69,92 @@ module CukeModeler
136
69
  def parse_text(source_text, filename = 'cuke_modeler_fake_file.feature')
137
70
  raise(ArgumentError, "Text to parse must be a String but got #{source_text.class}") unless source_text.is_a?(String)
138
71
 
139
-
140
72
  begin
141
73
  parsed_result = parsing_method(source_text, filename)
142
74
  rescue => e
143
75
  raise(ArgumentError, "Error encountered while parsing '#{filename}'\n#{e.class} - #{e.message}")
144
76
  end
145
77
 
146
- adapted_result = adapter_class.new.adapt(parsed_result)
78
+ adapter_class.new.adapt(parsed_result)
79
+ end
80
+
147
81
 
82
+ gherkin_version = Gem.loaded_specs['gherkin'].version.version
83
+ gherkin_major_version = gherkin_version.match(/^(\d+)\./)[1].to_i
84
+
85
+ case gherkin_major_version
86
+ when 9
87
+ # NOT A PART OF THE PUBLIC API
88
+ # The method to use for parsing Gherkin text
89
+ def parsing_method(source_text, filename)
90
+ messages = Gherkin.from_source(filename, source_text, { :include_gherkin_document => true }).to_a.map(&:to_hash)
91
+
92
+ potential_error_message = messages.find { |message| message[:attachment] }
93
+ gherkin_ast_message = messages.find { |message| message[:gherkin_document] }
94
+
95
+ if potential_error_message
96
+ raise potential_error_message[:attachment][:data] if potential_error_message[:attachment][:data] =~ /expected.*got/
97
+ end
98
+
99
+ gherkin_ast_message[:gherkin_document]
100
+ end
101
+ when 8
102
+ # NOT A PART OF THE PUBLIC API
103
+ # The method to use for parsing Gherkin text
104
+ def parsing_method(source_text, filename)
105
+ messages = Gherkin.from_source(filename, source_text, { :include_gherkin_document => true }).to_a.map(&:to_hash)
106
+
107
+ potential_error_message = messages.find { |message| message[:attachment] }
108
+ gherkin_ast_message = messages.find { |message| message[:gherkinDocument] }
109
+
110
+ if potential_error_message
111
+ raise potential_error_message[:attachment][:data] if potential_error_message[:attachment][:data] =~ /expected.*got/
112
+ end
113
+
114
+ gherkin_ast_message[:gherkinDocument]
115
+ end
116
+ when 6, 7
117
+ # NOT A PART OF THE PUBLIC API
118
+ # The method to use for parsing Gherkin text
119
+ def parsing_method(source_text, filename)
120
+ messages = Gherkin::Gherkin.from_source(filename, source_text).to_a.map(&:to_hash)
121
+
122
+ potential_error_message = messages.find { |message| message[:attachment] }
123
+ gherkin_ast_message = messages.find { |message| message[:gherkinDocument] }
124
+
125
+ if potential_error_message
126
+ raise potential_error_message[:attachment][:data] if potential_error_message[:attachment][:data] =~ /expected.*got/
127
+ end
128
+
129
+ gherkin_ast_message[:gherkinDocument]
130
+ end
131
+ when 3, 4, 5
132
+ # todo - make these methods private?
133
+ # NOT A PART OF THE PUBLIC API
134
+ # The method to use for parsing Gherkin text
135
+ # Filename isn't used by this version of Gherkin but keeping the parameter so that the calling method only has to know one method signature
136
+ def parsing_method(source_text, _filename)
137
+ Gherkin::Parser.new.parse(source_text)
138
+ end
139
+ when 2
140
+ # NOT A PART OF THE PUBLIC API
141
+ # The method to use for parsing Gherkin text
142
+ def parsing_method(source_text, filename)
143
+ io = StringIO.new
144
+ formatter = Gherkin::Formatter::JSONFormatter.new(io)
145
+ parser = Gherkin::Parser::Parser.new(formatter)
146
+ parser.parse(source_text, filename, 0)
147
+ formatter.done
148
+ MultiJson.load(io.string)
149
+ end
150
+ else
151
+ raise("Unknown Gherkin version: '#{gherkin_version}'")
152
+ end
148
153
 
149
- adapted_result
154
+ # NOT A PART OF THE PUBLIC API
155
+ # The adapter to use when converting an AST to a standard internal shape
156
+ define_method('adapter_class') do
157
+ CukeModeler.const_get("Gherkin#{gherkin_major_version}Adapter")
150
158
  end
151
159
 
152
160
  end
@@ -1,4 +1,4 @@
1
1
  module CukeModeler
2
2
  # The gem version
3
- VERSION = '2.0.0'
3
+ VERSION = '2.1.0'
4
4
  end
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => "../../"
4
+
5
+ gem "gherkin", "~> 7.0"
6
+
7
+ if RUBY_VERSION =~ /^2\.[23456789]/
8
+ gem 'test-unit'
9
+ end
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => "../../"
4
+
5
+ gem "gherkin", "~> 8.0"
6
+
7
+ if RUBY_VERSION =~ /^2\.[23456789]/
8
+ gem 'test-unit'
9
+ end
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => "../../"
4
+
5
+ gem "gherkin", "~> 9.0"
6
+
7
+ if RUBY_VERSION =~ /^2\.[23456789]/
8
+ gem 'test-unit'
9
+ end
@@ -0,0 +1,23 @@
1
+ module CukeModeler
2
+ module HelperMethods
3
+
4
+ def assert_bidirectional_equality(base_thing, compared_thing)
5
+ expect(base_thing).to eq(compared_thing)
6
+ expect(compared_thing).to eq(base_thing)
7
+ end
8
+
9
+ def assert_bidirectional_inequality(base_thing, compared_thing)
10
+ expect(base_thing).to_not eq(compared_thing)
11
+ expect(compared_thing).to_not eq(base_thing)
12
+ end
13
+
14
+ def gherkin?(*versions)
15
+ versions.include?(gherkin_major_version)
16
+ end
17
+
18
+ def gherkin_major_version
19
+ Gem.loaded_specs['gherkin'].version.version.match(/^(\d+)\./)[1].to_i
20
+ end
21
+
22
+ end
23
+ end
@@ -1,7 +1,7 @@
1
1
  require "#{File.dirname(__FILE__)}/../../spec_helper"
2
2
 
3
3
 
4
- describe 'Gherkin2Adapter, Integration', :gherkin2 => true do
4
+ describe 'Gherkin2Adapter, Integration', :if => gherkin?(2) do
5
5
 
6
6
  let(:clazz) { CukeModeler::Gherkin2Adapter }
7
7
  let(:adapter) { clazz.new }
@@ -1,7 +1,7 @@
1
1
  require "#{File.dirname(__FILE__)}/../../spec_helper"
2
2
 
3
3
 
4
- describe 'Gherkin3Adapter, Integration', :gherkin3 => true do
4
+ describe 'Gherkin3Adapter, Integration', :if => gherkin?(3) do
5
5
 
6
6
  let(:clazz) { CukeModeler::Gherkin3Adapter }
7
7
  let(:adapter) { clazz.new }
@@ -1,7 +1,7 @@
1
1
  require "#{File.dirname(__FILE__)}/../../spec_helper"
2
2
 
3
3
 
4
- describe 'Gherkin4Adapter, Integration', :gherkin4_5 => true do
4
+ describe 'Gherkin4Adapter, Integration', :if => gherkin?(4) do
5
5
 
6
6
  let(:clazz) { CukeModeler::Gherkin4Adapter }
7
7
  let(:adapter) { clazz.new }
@@ -0,0 +1,165 @@
1
+ require "#{File.dirname(__FILE__)}/../../spec_helper"
2
+
3
+
4
+ describe 'Gherkin4Adapter, Integration', :if => gherkin?(5) do
5
+
6
+ let(:clazz) { CukeModeler::Gherkin5Adapter }
7
+ let(:adapter) { clazz.new }
8
+ let(:source_text) { "# feature comment
9
+ @tag1 @tag2 @tag3
10
+ #{FEATURE_KEYWORD}: A feature with everything it could have
11
+
12
+ Including a description
13
+ and then some.
14
+
15
+ # background comment
16
+ #{BACKGROUND_KEYWORD}:
17
+
18
+ Background
19
+ description
20
+
21
+ #{STEP_KEYWORD} a step
22
+ # table comment
23
+ | value1 |
24
+ # table row comment
25
+ | value2 |
26
+ #{STEP_KEYWORD} another step
27
+
28
+ # scenario comment
29
+ @scenario_tag
30
+ #{SCENARIO_KEYWORD}:
31
+
32
+ Scenario
33
+ description
34
+
35
+ #{STEP_KEYWORD} a step
36
+ #{STEP_KEYWORD} another step
37
+ \"\"\"
38
+ some text
39
+ \"\"\"
40
+
41
+ # outline comment
42
+ @outline_tag
43
+ #{OUTLINE_KEYWORD}:
44
+
45
+ Outline
46
+ description
47
+
48
+ # step comment
49
+ #{STEP_KEYWORD} a step
50
+ # table comment
51
+ | value2 |
52
+ # step comment
53
+ #{STEP_KEYWORD} another step
54
+ # doc string comment
55
+ \"\"\"
56
+ some text
57
+ \"\"\"
58
+
59
+ # example comment
60
+ @example_tag
61
+ #{EXAMPLE_KEYWORD}:
62
+
63
+ Example
64
+ description
65
+
66
+ # row comment
67
+ | param |
68
+ | value |
69
+ # final comment" }
70
+ let(:feature_file_model) { test_file_path = CukeModeler::FileHelper.create_feature_file(:text => source_text, :name => 'adapter_test_file')
71
+ CukeModeler::FeatureFile.new(test_file_path) }
72
+ let(:feature_model) { feature_file_model.feature }
73
+
74
+
75
+ it "does not store parsing data for a feature file's children" do
76
+ model = feature_file_model
77
+
78
+ expect(model.parsing_data[:comments]).to be_nil
79
+ expect(model.parsing_data[:feature]).to be_nil
80
+ end
81
+
82
+ it "does not store parsing data for a feature's children" do
83
+ model = feature_model
84
+
85
+ expect(model.parsing_data[:tags]).to be_nil
86
+ expect(model.parsing_data[:children]).to be_nil
87
+ end
88
+
89
+ it "does not store parsing data for a background's children" do
90
+ model = feature_model.background
91
+
92
+ expect(model.parsing_data[:steps]).to be_nil
93
+ end
94
+
95
+ it "does not store parsing data for a scenario's children" do
96
+ model = feature_model.scenarios.first
97
+
98
+ expect(model.parsing_data[:tags]).to be_nil
99
+ expect(model.parsing_data[:steps]).to be_nil
100
+ end
101
+
102
+ it "does not store parsing data for an outline's children" do
103
+ model = feature_model.outlines.first
104
+
105
+ expect(model.parsing_data[:tags]).to be_nil
106
+ expect(model.parsing_data[:steps]).to be_nil
107
+ expect(model.parsing_data[:examples]).to be_nil
108
+ end
109
+
110
+ it "does not store parsing data for an example's children" do
111
+ model = feature_model.outlines.first.examples.first
112
+
113
+ expect(model.parsing_data[:tags]).to be_nil
114
+ expect(model.parsing_data[:tableHeader]).to be_nil
115
+ expect(model.parsing_data[:tableBody]).to be_nil
116
+ end
117
+
118
+ it "does not store parsing data for an example row's children" do
119
+ model = feature_model.outlines.first.examples.first.rows.first
120
+
121
+ expect(model.parsing_data[:cells]).to be_nil
122
+ end
123
+
124
+ it "does not store parsing data for a step's children, table" do
125
+ model = feature_model.outlines.first.steps.first
126
+
127
+ expect(model.parsing_data[:argument]).to be_nil
128
+ end
129
+
130
+ it "does not store parsing data for a step's children, doc string" do
131
+ model = feature_model.outlines.first.steps.last
132
+
133
+ expect(model.parsing_data[:argument]).to be_nil
134
+ end
135
+
136
+ it "does not store parsing data for a table's children" do
137
+ model = feature_model.outlines.first.steps.first.block
138
+
139
+ expect(model.parsing_data[:rows]).to be_nil
140
+ end
141
+
142
+ it "does not store parsing data for a table row's children" do
143
+ model = feature_model.outlines.first.steps.first.block.rows.first
144
+
145
+ expect(model.parsing_data[:cells]).to be_nil
146
+ end
147
+
148
+
149
+ describe 'stuff that is in no way part of the public API and entirely subject to change' do
150
+
151
+ it 'provides a useful explosion message if it encounters an entirely new type of test' do
152
+ partial_feature_ast = { :type => :Feature, :location => { :line => 1, :column => 1 }, :children => [{ :type => :some_unknown_type }] }
153
+
154
+ expect { adapter.adapt_feature!(partial_feature_ast) }.to raise_error(ArgumentError, /Unknown.*some_unknown_type/)
155
+ end
156
+
157
+ it 'provides a useful explosion message if it encounters an entirely new type of step block' do
158
+ partial_feature_ast = { :type => :Feature, :location => { :line => 1, :column => 1 }, :children => [{ :type => :Scenario, :tags => [], :location => { :line => 1, :column => 1 }, :steps => [{ :type => :Step, :location => { :line => 1, :column => 1 }, :argument => { :type => :some_unknown_type, :location => { :line => 1, :column => 1 }, :content => "" } }] }] }
159
+
160
+ expect { adapter.adapt_feature!(partial_feature_ast) }.to raise_error(ArgumentError, /Unknown.*some_unknown_type/)
161
+ end
162
+
163
+ end
164
+
165
+ end