cuke_modeler 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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