cucumber-gherkin 9.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e29214ad6a0eae41b18a082346911c0bae2fdd0297e8b878549c2d8c8cd241ac
4
+ data.tar.gz: 1a3dd2275a2bf3e4999f360135b7f86342e84a30d146668ecf83ad111b103faa
5
+ SHA512:
6
+ metadata.gz: 3889715de4087bae76cc68de729ec3d4fd613ad7fc07f815d6f47ec94d517fa364cc38a93620f132c3db5aeb2cafe25261f52328f90022ad1631f0271b18631b
7
+ data.tar.gz: 529b1867c7dfcfca824ee6a8a35bcfe7542bf42a5f6ed1c067d66c5f79ea26430c7ab7e82f27ce426c210684aee72802c951ffc2714189538b01987cfb4b6847
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) Cucumber Ltd, Gaspar Nagy, Björn Rasmusson, Peter Sergeant
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,10 @@
1
+ [![Build Status](https://secure.travis-ci.org/cucumber/gherkin-ruby.svg)](http://travis-ci.org/cucumber/gherkin-ruby)
2
+
3
+ Gherkin parser/compiler for Ruby. Please see [Gherkin](https://github.com/cucumber/gherkin) for details.
4
+
5
+ ## To build
6
+
7
+ ~~~bash
8
+ cd <project_root>/ruby
9
+ make
10
+ ~~~
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env ruby
2
+ $VERBOSE=nil # Shut up JRuby warnings on Travis
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__),"../lib"))
4
+
5
+ require 'optparse'
6
+ require 'json'
7
+ require 'cucumber/messages'
8
+ require 'gherkin'
9
+ require 'gherkin/stream/subprocess_message_stream'
10
+
11
+ options = {
12
+ include_source: true,
13
+ include_gherkin_document: true,
14
+ include_pickles: true,
15
+ format: 'protobuf',
16
+ predictable_ids: false
17
+ }
18
+
19
+ OptionParser.new do |opts|
20
+ opts.on("--[no-]source", "Don't print source messages") do |v|
21
+ options[:include_source] = v
22
+ end
23
+ opts.on("--[no-]ast", "Don't print ast messages") do |v|
24
+ options[:include_gherkin_document] = v
25
+ end
26
+ opts.on("--[no-]pickles", "Don't print pickle messages") do |v|
27
+ options[:include_pickles] = v
28
+ end
29
+ opts.on("--format ndjson|protobuf", "Output format") do |v|
30
+ options[:format] = v
31
+ end
32
+ opts.on("--predictable-ids", "Generate incrementing ids rather than UUIDs") do |v|
33
+ options[:id_generator] = Cucumber::Messages::IdGenerator::Incrementing.new if v
34
+ end
35
+ end.parse!
36
+
37
+ def process_messages(messages, options)
38
+ messages.each do |message|
39
+ if options[:format] == 'ndjson'
40
+ message.write_ndjson_to(STDOUT)
41
+ elsif options[:format] == 'protobuf'
42
+ message.write_delimited_to(STDOUT)
43
+ else
44
+ raise "Unsupported format: #{options[:format]}"
45
+ end
46
+ end
47
+ end
48
+
49
+ gherkin_executable = ENV['GHERKIN_EXECUTABLE']
50
+ if ARGV.empty?
51
+ # Read protobuf from STDIN
52
+ messages = Cucumber::Messages::BinaryToMessageEnumerator.new(STDIN)
53
+ elsif gherkin_executable
54
+ # Read protobuf from STDIN
55
+ messages = Gherkin::Stream::SubprocessMessageStream.new(
56
+ gherkin_executable,
57
+ ARGV,
58
+ options[:include_source],
59
+ options[:include_gherkin_document],
60
+ options[:include_pickles]
61
+ ).messages
62
+ else
63
+ messages = Gherkin.from_paths(
64
+ ARGV,
65
+ options
66
+ )
67
+ end
68
+ process_messages(messages, options)
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+ load(File.dirname(__FILE__) + '/gherkin')
@@ -0,0 +1,39 @@
1
+ require 'gherkin/stream/parser_message_stream'
2
+
3
+ module Gherkin
4
+ DEFAULT_OPTIONS = {
5
+ include_source: true,
6
+ include_gherkin_document: true,
7
+ include_pickles: true
8
+ }.freeze
9
+
10
+ def self.from_paths(paths, options={})
11
+ Stream::ParserMessageStream.new(
12
+ paths,
13
+ [],
14
+ options
15
+ ).messages
16
+ end
17
+
18
+ def self.from_sources(sources, options={})
19
+ Stream::ParserMessageStream.new(
20
+ [],
21
+ sources,
22
+ options
23
+ ).messages
24
+ end
25
+
26
+ def self.from_source(uri, data, options={})
27
+ from_sources([encode_source_message(uri, data)], options)
28
+ end
29
+
30
+ private
31
+
32
+ def self.encode_source_message(uri, data)
33
+ Cucumber::Messages::Source.new({
34
+ uri: uri,
35
+ data: data,
36
+ media_type: 'text/x.cucumber.gherkin+plain'
37
+ })
38
+ end
39
+ end
@@ -0,0 +1,257 @@
1
+ require 'cucumber/messages'
2
+ require 'gherkin/ast_node'
3
+
4
+ module Gherkin
5
+ class AstBuilder
6
+ def initialize(id_generator)
7
+ @id_generator = id_generator
8
+ reset
9
+ end
10
+
11
+ def reset
12
+ @stack = [AstNode.new(:None)]
13
+ @comments = []
14
+ end
15
+
16
+ def start_rule(rule_type)
17
+ @stack.push AstNode.new(rule_type)
18
+ end
19
+
20
+ def end_rule(rule_type)
21
+ node = @stack.pop
22
+ current_node.add(node.rule_type, transform_node(node))
23
+ end
24
+
25
+ def build(token)
26
+ if token.matched_type == :Comment
27
+ @comments.push(Cucumber::Messages::GherkinDocument::Comment.new(
28
+ location: get_location(token, 0),
29
+ text: token.matched_text
30
+ ))
31
+ else
32
+ current_node.add(token.matched_type, token)
33
+ end
34
+ end
35
+
36
+ def get_result
37
+ current_node.get_single(:GherkinDocument)
38
+ end
39
+
40
+ def current_node
41
+ @stack.last
42
+ end
43
+
44
+ def get_location(token, column)
45
+ column = column == 0 ? token.location[:column] : column
46
+ Cucumber::Messages::Location.new(
47
+ line: token.location[:line],
48
+ column: column
49
+ )
50
+ end
51
+
52
+ def get_tags(node)
53
+ tags = []
54
+ tags_node = node.get_single(:Tags)
55
+ return tags unless tags_node
56
+
57
+ tags_node.get_tokens(:TagLine).each do |token|
58
+ token.matched_items.each do |tag_item|
59
+ tags.push(Cucumber::Messages::GherkinDocument::Feature::Tag.new(
60
+ location: get_location(token, tag_item.column),
61
+ name: tag_item.text,
62
+ id: @id_generator.new_id
63
+ ))
64
+ end
65
+ end
66
+
67
+ tags
68
+ end
69
+
70
+ def get_table_rows(node)
71
+ rows = node.get_tokens(:TableRow).map do |token|
72
+ Cucumber::Messages::GherkinDocument::Feature::TableRow.new(
73
+ id: @id_generator.new_id,
74
+ location: get_location(token, 0),
75
+ cells: get_cells(token)
76
+ )
77
+ end
78
+ ensure_cell_count(rows)
79
+ rows
80
+ end
81
+
82
+ def ensure_cell_count(rows)
83
+ return if rows.empty?
84
+ cell_count = rows[0].cells.length
85
+ rows.each do |row|
86
+ if row.cells.length != cell_count
87
+ location = {line: row.location.line, column: row.location.column}
88
+ raise AstBuilderException.new("inconsistent cell count within the table", location)
89
+ end
90
+ end
91
+ end
92
+
93
+ def get_cells(table_row_token)
94
+ table_row_token.matched_items.map do |cell_item|
95
+ Cucumber::Messages::GherkinDocument::Feature::TableRow::TableCell.new(
96
+ location: get_location(table_row_token, cell_item.column),
97
+ value: cell_item.text
98
+ )
99
+ end
100
+ end
101
+
102
+ def get_description(node)
103
+ node.get_single(:Description)
104
+ end
105
+
106
+ def get_steps(node)
107
+ node.get_items(:Step)
108
+ end
109
+
110
+ def transform_node(node)
111
+ case node.rule_type
112
+ when :Step
113
+ step_line = node.get_token(:StepLine)
114
+ data_table = node.get_single(:DataTable)
115
+ doc_string = node.get_single(:DocString)
116
+
117
+ Cucumber::Messages::GherkinDocument::Feature::Step.new(
118
+ location: get_location(step_line, 0),
119
+ keyword: step_line.matched_keyword,
120
+ text: step_line.matched_text,
121
+ data_table: data_table,
122
+ doc_string: doc_string,
123
+ id: @id_generator.new_id
124
+ )
125
+ when :DocString
126
+ separator_token = node.get_tokens(:DocStringSeparator)[0]
127
+ media_type = separator_token.matched_text == '' ? nil : separator_token.matched_text
128
+ line_tokens = node.get_tokens(:Other)
129
+ content = line_tokens.map { |t| t.matched_text }.join("\n")
130
+
131
+ Cucumber::Messages::GherkinDocument::Feature::Step::DocString.new(
132
+ location: get_location(separator_token, 0),
133
+ content: content,
134
+ delimiter: separator_token.matched_keyword,
135
+ media_type: media_type,
136
+ )
137
+ when :DataTable
138
+ rows = get_table_rows(node)
139
+ Cucumber::Messages::GherkinDocument::Feature::Step::DataTable.new(
140
+ location: rows[0].location,
141
+ rows: rows,
142
+ )
143
+ when :Background
144
+ background_line = node.get_token(:BackgroundLine)
145
+ description = get_description(node)
146
+ steps = get_steps(node)
147
+
148
+ Cucumber::Messages::GherkinDocument::Feature::Background.new(
149
+ location: get_location(background_line, 0),
150
+ keyword: background_line.matched_keyword,
151
+ name: background_line.matched_text,
152
+ description: description,
153
+ steps: steps
154
+ )
155
+ when :ScenarioDefinition
156
+ tags = get_tags(node)
157
+ scenario_node = node.get_single(:Scenario)
158
+ scenario_line = scenario_node.get_token(:ScenarioLine)
159
+ description = get_description(scenario_node)
160
+ steps = get_steps(scenario_node)
161
+ examples = scenario_node.get_items(:ExamplesDefinition)
162
+ Cucumber::Messages::GherkinDocument::Feature::Scenario.new(
163
+ id: @id_generator.new_id,
164
+ tags: tags,
165
+ location: get_location(scenario_line, 0),
166
+ keyword: scenario_line.matched_keyword,
167
+ name: scenario_line.matched_text,
168
+ description: description,
169
+ steps: steps,
170
+ examples: examples
171
+ )
172
+ when :ExamplesDefinition
173
+ tags = get_tags(node)
174
+ examples_node = node.get_single(:Examples)
175
+ examples_line = examples_node.get_token(:ExamplesLine)
176
+ description = get_description(examples_node)
177
+ rows = examples_node.get_single(:ExamplesTable)
178
+
179
+ table_header = rows.nil? ? nil : rows.first
180
+ table_body = rows.nil? ? nil : rows[1..-1]
181
+
182
+ Cucumber::Messages::GherkinDocument::Feature::Scenario::Examples.new(
183
+ tags: tags,
184
+ location: get_location(examples_line, 0),
185
+ keyword: examples_line.matched_keyword,
186
+ name: examples_line.matched_text,
187
+ description: description,
188
+ table_header: table_header,
189
+ table_body: table_body,
190
+ )
191
+ when :ExamplesTable
192
+ get_table_rows(node)
193
+ when :Description
194
+ line_tokens = node.get_tokens(:Other)
195
+ # Trim trailing empty lines
196
+ last_non_empty = line_tokens.rindex { |token| !token.line.trimmed_line_text.empty? }
197
+ description = line_tokens[0..last_non_empty].map { |token| token.matched_text }.join("\n")
198
+ return description
199
+ when :Feature
200
+ header = node.get_single(:FeatureHeader)
201
+ return unless header
202
+ tags = get_tags(header)
203
+ feature_line = header.get_token(:FeatureLine)
204
+ return unless feature_line
205
+ children = []
206
+ background = node.get_single(:Background)
207
+ children.push(Cucumber::Messages::GherkinDocument::Feature::FeatureChild.new(background: background)) if background
208
+ node.get_items(:ScenarioDefinition).each do |scenario|
209
+ children.push(Cucumber::Messages::GherkinDocument::Feature::FeatureChild.new(scenario: scenario))
210
+ end
211
+ node.get_items(:Rule).each do |rule|
212
+ children.push(Cucumber::Messages::GherkinDocument::Feature::FeatureChild.new(rule: rule))
213
+ end
214
+ description = get_description(header)
215
+ language = feature_line.matched_gherkin_dialect
216
+
217
+ Cucumber::Messages::GherkinDocument::Feature.new(
218
+ tags: tags,
219
+ location: get_location(feature_line, 0),
220
+ language: language,
221
+ keyword: feature_line.matched_keyword,
222
+ name: feature_line.matched_text,
223
+ description: description,
224
+ children: children,
225
+ )
226
+ when :Rule
227
+ header = node.get_single(:RuleHeader)
228
+ return unless header
229
+ rule_line = header.get_token(:RuleLine)
230
+ return unless rule_line
231
+ children = []
232
+ background = node.get_single(:Background)
233
+ children.push(Cucumber::Messages::GherkinDocument::Feature::FeatureChild::RuleChild.new(background: background)) if background
234
+ node.get_items(:ScenarioDefinition).each do |scenario|
235
+ children.push(Cucumber::Messages::GherkinDocument::Feature::FeatureChild::RuleChild.new(scenario: scenario))
236
+ end
237
+ description = get_description(header)
238
+
239
+ Cucumber::Messages::GherkinDocument::Feature::FeatureChild::Rule.new(
240
+ location: get_location(rule_line, 0),
241
+ keyword: rule_line.matched_keyword,
242
+ name: rule_line.matched_text,
243
+ description: description,
244
+ children: children,
245
+ )
246
+ when :GherkinDocument
247
+ feature = node.get_single(:Feature)
248
+ {
249
+ feature: feature,
250
+ comments: @comments
251
+ }
252
+ else
253
+ return node
254
+ end
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,30 @@
1
+ module Gherkin
2
+ class AstNode
3
+ attr_reader :rule_type
4
+
5
+ def initialize(rule_type)
6
+ @rule_type = rule_type
7
+ @_sub_items = Hash.new { |hash, key| hash[key] = [] } # returns [] for unknown key
8
+ end
9
+
10
+ def add(rule_type, obj)
11
+ @_sub_items[rule_type].push(obj)
12
+ end
13
+
14
+ def get_single(rule_type)
15
+ @_sub_items[rule_type].first
16
+ end
17
+
18
+ def get_items(rule_type)
19
+ @_sub_items[rule_type]
20
+ end
21
+
22
+ def get_token(token_type)
23
+ get_single(token_type)
24
+ end
25
+
26
+ def get_tokens(token_type)
27
+ @_sub_items[token_type]
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,62 @@
1
+ require 'json'
2
+
3
+ module Gherkin
4
+ DIALECT_FILE_PATH = File.expand_path("gherkin-languages.json", File.dirname(__FILE__))
5
+ DIALECTS = JSON.parse File.open(DIALECT_FILE_PATH, 'r:UTF-8').read
6
+
7
+ class Dialect
8
+ def self.for(name)
9
+ spec = DIALECTS[name]
10
+ return nil unless spec
11
+ new(spec)
12
+ end
13
+
14
+ def initialize(spec)
15
+ @spec = spec
16
+ end
17
+
18
+ def feature_keywords
19
+ @spec.fetch('feature')
20
+ end
21
+
22
+ def rule_keywords
23
+ @spec.fetch('rule')
24
+ end
25
+
26
+ def scenario_keywords
27
+ @spec.fetch('scenario')
28
+ end
29
+
30
+ def scenario_outline_keywords
31
+ @spec.fetch('scenarioOutline')
32
+ end
33
+
34
+ def examples_keywords
35
+ @spec.fetch('examples')
36
+ end
37
+
38
+ def background_keywords
39
+ @spec.fetch('background')
40
+ end
41
+
42
+ def given_keywords
43
+ @spec.fetch('given')
44
+ end
45
+
46
+ def when_keywords
47
+ @spec.fetch('when')
48
+ end
49
+
50
+ def then_keywords
51
+ @spec.fetch('then')
52
+ end
53
+
54
+ def and_keywords
55
+ @spec.fetch('and')
56
+ end
57
+
58
+ def but_keywords
59
+ @spec.fetch('but')
60
+ end
61
+ end
62
+ end