gherkin 5.1.0 → 6.0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -4
- data/bin/gherkin +43 -16
- data/gherkin-go/gherkin-go-darwin-386 +0 -0
- data/gherkin-go/gherkin-go-darwin-amd64 +0 -0
- data/gherkin-go/gherkin-go-freebsd-386 +0 -0
- data/gherkin-go/gherkin-go-freebsd-amd64 +0 -0
- data/gherkin-go/gherkin-go-freebsd-arm +0 -0
- data/gherkin-go/gherkin-go-linux-386 +0 -0
- data/gherkin-go/gherkin-go-linux-amd64 +0 -0
- data/gherkin-go/gherkin-go-linux-arm +0 -0
- data/gherkin-go/gherkin-go-linux-mips +2 -0
- data/gherkin-go/gherkin-go-linux-mips64 +2 -0
- data/gherkin-go/gherkin-go-linux-mips64le +2 -0
- data/gherkin-go/gherkin-go-linux-mipsle +2 -0
- data/gherkin-go/gherkin-go-linux-s390x +2 -0
- data/gherkin-go/gherkin-go-netbsd-386 +0 -0
- data/gherkin-go/gherkin-go-netbsd-amd64 +0 -0
- data/gherkin-go/gherkin-go-netbsd-arm +0 -0
- data/gherkin-go/gherkin-go-openbsd-386 +0 -0
- data/gherkin-go/gherkin-go-openbsd-amd64 +0 -0
- data/gherkin-go/gherkin-go-windows-386.exe +0 -0
- data/gherkin-go/gherkin-go-windows-amd64.exe +0 -0
- data/lib/gherkin/dialect.rb +16 -2
- data/lib/gherkin/exe_file_path.rb +3 -0
- data/lib/gherkin/gherkin.rb +75 -0
- data/lib/gherkin/protobuf_cucumber_messages.rb +30 -0
- data/spec/capture_warnings.rb +11 -5
- data/spec/coverage.rb +3 -6
- data/spec/gherkin/dialect_spec.rb +13 -0
- data/spec/gherkin/gherkin_spec.rb +354 -0
- metadata +49 -27
- data/lib/gherkin/ast_builder.rb +0 -257
- data/lib/gherkin/ast_node.rb +0 -30
- data/lib/gherkin/errors.rb +0 -45
- data/lib/gherkin/gherkin-languages.json +0 -3239
- data/lib/gherkin/gherkin_line.rb +0 -95
- data/lib/gherkin/parser.rb +0 -2310
- data/lib/gherkin/pickles/compiler.rb +0 -163
- data/lib/gherkin/stream/gherkin_events.rb +0 -71
- data/lib/gherkin/stream/source_events.rb +0 -26
- data/lib/gherkin/token.rb +0 -18
- data/lib/gherkin/token_formatter_builder.rb +0 -39
- data/lib/gherkin/token_matcher.rb +0 -169
- data/lib/gherkin/token_scanner.rb +0 -40
- data/spec/gherkin/parser_spec.rb +0 -280
- data/spec/gherkin/stream/gherkin_events_spec.rb +0 -27
- data/spec/gherkin/stream/test_fr.feature +0 -3
@@ -1,163 +0,0 @@
|
|
1
|
-
module Gherkin
|
2
|
-
module Pickles
|
3
|
-
class Compiler
|
4
|
-
def compile(gherkin_document)
|
5
|
-
pickles = []
|
6
|
-
|
7
|
-
return pickles unless gherkin_document[:feature]
|
8
|
-
feature = gherkin_document[:feature]
|
9
|
-
feature_tags = feature[:tags]
|
10
|
-
background_steps = []
|
11
|
-
|
12
|
-
feature[:children].each do |scenario_definition|
|
13
|
-
if(scenario_definition[:type] == :Background)
|
14
|
-
background_steps = pickle_steps(scenario_definition)
|
15
|
-
elsif(scenario_definition[:type] == :Scenario)
|
16
|
-
compile_scenario(feature_tags, background_steps, scenario_definition, feature[:language], pickles)
|
17
|
-
else
|
18
|
-
compile_scenario_outline(feature_tags, background_steps, scenario_definition, feature[:language], pickles)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
return pickles
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def compile_scenario(feature_tags, background_steps, scenario, language, pickles)
|
27
|
-
steps = scenario[:steps].empty? ? [] : [].concat(background_steps)
|
28
|
-
|
29
|
-
tags = [].concat(feature_tags).concat(scenario[:tags])
|
30
|
-
|
31
|
-
scenario[:steps].each do |step|
|
32
|
-
steps.push(pickle_step(step))
|
33
|
-
end
|
34
|
-
|
35
|
-
pickle = {
|
36
|
-
tags: pickle_tags(tags),
|
37
|
-
name: scenario[:name],
|
38
|
-
language: language,
|
39
|
-
locations: [pickle_location(scenario[:location])],
|
40
|
-
steps: steps
|
41
|
-
}
|
42
|
-
pickles.push(pickle)
|
43
|
-
end
|
44
|
-
|
45
|
-
def compile_scenario_outline(feature_tags, background_steps, scenario_outline, language, pickles)
|
46
|
-
scenario_outline[:examples].reject { |examples| examples[:tableHeader].nil? }.each do |examples|
|
47
|
-
variable_cells = examples[:tableHeader][:cells]
|
48
|
-
examples[:tableBody].each do |values|
|
49
|
-
value_cells = values[:cells]
|
50
|
-
steps = scenario_outline[:steps].empty? ? [] : [].concat(background_steps)
|
51
|
-
tags = [].concat(feature_tags).concat(scenario_outline[:tags]).concat(examples[:tags])
|
52
|
-
|
53
|
-
scenario_outline[:steps].each do |scenario_outline_step|
|
54
|
-
step_text = interpolate(scenario_outline_step[:text], variable_cells, value_cells);
|
55
|
-
arguments = create_pickle_arguments(scenario_outline_step[:argument], variable_cells, value_cells)
|
56
|
-
pickle_step = {
|
57
|
-
text: step_text,
|
58
|
-
arguments: arguments,
|
59
|
-
locations: [
|
60
|
-
pickle_location(values[:location]),
|
61
|
-
pickle_step_location(scenario_outline_step)
|
62
|
-
]
|
63
|
-
}
|
64
|
-
steps.push(pickle_step)
|
65
|
-
end
|
66
|
-
|
67
|
-
pickle = {
|
68
|
-
name: interpolate(scenario_outline[:name], variable_cells, value_cells),
|
69
|
-
language: language,
|
70
|
-
steps: steps,
|
71
|
-
tags: pickle_tags(tags),
|
72
|
-
locations: [
|
73
|
-
pickle_location(values[:location]),
|
74
|
-
pickle_location(scenario_outline[:location])
|
75
|
-
]
|
76
|
-
}
|
77
|
-
pickles.push(pickle);
|
78
|
-
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def create_pickle_arguments(argument, variable_cells, value_cells)
|
84
|
-
result = []
|
85
|
-
return result if argument.nil?
|
86
|
-
if (argument[:type] == :DataTable)
|
87
|
-
table = {
|
88
|
-
rows: argument[:rows].map do |row|
|
89
|
-
{
|
90
|
-
cells: row[:cells].map do |cell|
|
91
|
-
{
|
92
|
-
location: pickle_location(cell[:location]),
|
93
|
-
value: interpolate(cell[:value], variable_cells, value_cells)
|
94
|
-
}
|
95
|
-
end
|
96
|
-
}
|
97
|
-
end
|
98
|
-
}
|
99
|
-
result.push(table)
|
100
|
-
elsif (argument[:type] == :DocString)
|
101
|
-
doc_string = {
|
102
|
-
location: pickle_location(argument[:location]),
|
103
|
-
content: interpolate(argument[:content], variable_cells, value_cells)
|
104
|
-
}
|
105
|
-
if argument.key?(:contentType)
|
106
|
-
doc_string[:contentType] = interpolate(argument[:contentType], variable_cells, value_cells)
|
107
|
-
end
|
108
|
-
result.push(doc_string)
|
109
|
-
else
|
110
|
-
raise 'Internal error'
|
111
|
-
end
|
112
|
-
result
|
113
|
-
end
|
114
|
-
|
115
|
-
def interpolate(name, variable_cells, value_cells)
|
116
|
-
variable_cells.each_with_index do |variable_cell, n|
|
117
|
-
value_cell = value_cells[n]
|
118
|
-
name = name.gsub('<' + variable_cell[:value] + '>', value_cell[:value])
|
119
|
-
end
|
120
|
-
name
|
121
|
-
end
|
122
|
-
|
123
|
-
def pickle_steps(scenario_definition)
|
124
|
-
scenario_definition[:steps].map do |step|
|
125
|
-
pickle_step(step)
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
def pickle_step(step)
|
130
|
-
{
|
131
|
-
text: step[:text],
|
132
|
-
arguments: create_pickle_arguments(step[:argument], [], []),
|
133
|
-
locations: [pickle_step_location(step)]
|
134
|
-
}
|
135
|
-
end
|
136
|
-
|
137
|
-
def pickle_step_location(step)
|
138
|
-
{
|
139
|
-
line: step[:location][:line],
|
140
|
-
column: step[:location][:column] + (step[:keyword] ? step[:keyword].length : 0)
|
141
|
-
}
|
142
|
-
end
|
143
|
-
|
144
|
-
def pickle_location(location)
|
145
|
-
{
|
146
|
-
line: location[:line],
|
147
|
-
column: location[:column]
|
148
|
-
}
|
149
|
-
end
|
150
|
-
|
151
|
-
def pickle_tags(tags)
|
152
|
-
tags.map {|tag| pickle_tag(tag)}
|
153
|
-
end
|
154
|
-
|
155
|
-
def pickle_tag(tag)
|
156
|
-
{
|
157
|
-
name: tag[:name],
|
158
|
-
location: pickle_location(tag[:location])
|
159
|
-
}
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
@@ -1,71 +0,0 @@
|
|
1
|
-
require 'gherkin/parser'
|
2
|
-
require 'gherkin/token_matcher'
|
3
|
-
require 'gherkin/pickles/compiler'
|
4
|
-
|
5
|
-
module Gherkin
|
6
|
-
module Stream
|
7
|
-
class GherkinEvents
|
8
|
-
def initialize(options, language='en')
|
9
|
-
@options = options
|
10
|
-
@parser = Gherkin::Parser.new
|
11
|
-
@compiler = Gherkin::Pickles::Compiler.new
|
12
|
-
@language = language
|
13
|
-
end
|
14
|
-
|
15
|
-
def enum(source_event)
|
16
|
-
Enumerator.new do |y|
|
17
|
-
uri = source_event[:uri]
|
18
|
-
source = source_event[:data]
|
19
|
-
begin
|
20
|
-
gherkin_document = @parser.parse(source, TokenMatcher.new(@language))
|
21
|
-
|
22
|
-
if (@options[:print_source])
|
23
|
-
y.yield source_event
|
24
|
-
end
|
25
|
-
if (@options[:print_ast])
|
26
|
-
y.yield({
|
27
|
-
type: 'gherkin-document',
|
28
|
-
uri: uri,
|
29
|
-
document: gherkin_document
|
30
|
-
})
|
31
|
-
end
|
32
|
-
if (@options[:print_pickles])
|
33
|
-
pickles = @compiler.compile(gherkin_document)
|
34
|
-
pickles.each do |pickle|
|
35
|
-
y.yield({
|
36
|
-
type: 'pickle',
|
37
|
-
uri: uri,
|
38
|
-
pickle: pickle
|
39
|
-
})
|
40
|
-
end
|
41
|
-
end
|
42
|
-
rescue Gherkin::CompositeParserException => e
|
43
|
-
yield_errors(y, e.errors, uri)
|
44
|
-
rescue Gherkin::ParserError => e
|
45
|
-
yield_errors(y, [e], uri)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def yield_errors(y, errors, uri)
|
51
|
-
errors.each do |error|
|
52
|
-
y.yield({
|
53
|
-
type: 'attachment',
|
54
|
-
source: {
|
55
|
-
uri: uri,
|
56
|
-
start: {
|
57
|
-
line: error.location[:line],
|
58
|
-
column: error.location[:column]
|
59
|
-
}
|
60
|
-
},
|
61
|
-
data: error.message,
|
62
|
-
media: {
|
63
|
-
encoding: 'utf-8',
|
64
|
-
type: 'text/x.cucumber.stacktrace+plain'
|
65
|
-
}
|
66
|
-
})
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
module Gherkin
|
2
|
-
module Stream
|
3
|
-
class SourceEvents
|
4
|
-
def initialize(paths)
|
5
|
-
@paths = paths
|
6
|
-
end
|
7
|
-
|
8
|
-
def enum
|
9
|
-
Enumerator.new do |y|
|
10
|
-
@paths.each do |path|
|
11
|
-
event = {
|
12
|
-
type: 'source',
|
13
|
-
uri: path,
|
14
|
-
data: File.open(path, 'r:UTF-8', &:read),
|
15
|
-
media: {
|
16
|
-
encoding: 'utf-8',
|
17
|
-
type: 'text/x.cucumber.gherkin+plain'
|
18
|
-
}
|
19
|
-
}
|
20
|
-
y.yield(event)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
data/lib/gherkin/token.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
module Gherkin
|
2
|
-
class Token < Struct.new(:line, :location)
|
3
|
-
attr_accessor :matched_type, :matched_text, :matched_keyword, :matched_indent,
|
4
|
-
:matched_items, :matched_gherkin_dialect
|
5
|
-
|
6
|
-
def eof?
|
7
|
-
line.nil?
|
8
|
-
end
|
9
|
-
|
10
|
-
def detach
|
11
|
-
# TODO: detach line - is this needed?
|
12
|
-
end
|
13
|
-
|
14
|
-
def token_value
|
15
|
-
eof? ? "EOF" : line.get_line_text(-1)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
module Gherkin
|
2
|
-
class TokenFormatterBuilder
|
3
|
-
def initialize
|
4
|
-
reset
|
5
|
-
end
|
6
|
-
|
7
|
-
def reset
|
8
|
-
@tokens_text = ""
|
9
|
-
end
|
10
|
-
|
11
|
-
def build(token)
|
12
|
-
@tokens_text << "#{format_token(token)}\n"
|
13
|
-
end
|
14
|
-
|
15
|
-
def start_rule(rule_type)
|
16
|
-
end
|
17
|
-
|
18
|
-
def end_rule(rule_type)
|
19
|
-
end
|
20
|
-
|
21
|
-
def get_result
|
22
|
-
@tokens_text
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
def format_token(token)
|
27
|
-
return "EOF" if token.eof?
|
28
|
-
|
29
|
-
sprintf "(%s:%s)%s:%s/%s/%s",
|
30
|
-
token.location[:line],
|
31
|
-
token.location[:column],
|
32
|
-
token.matched_type,
|
33
|
-
token.matched_keyword,
|
34
|
-
token.matched_text,
|
35
|
-
Array(token.matched_items).map { |i| "#{i.column}:#{i.text}"}.join(',')
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
end
|
@@ -1,169 +0,0 @@
|
|
1
|
-
require 'gherkin/dialect'
|
2
|
-
require 'gherkin/errors'
|
3
|
-
|
4
|
-
module Gherkin
|
5
|
-
class TokenMatcher
|
6
|
-
LANGUAGE_PATTERN = /^\s*#\s*language\s*:\s*([a-zA-Z\-_]+)\s*$/
|
7
|
-
|
8
|
-
def initialize(dialect_name = 'en')
|
9
|
-
@default_dialect_name = dialect_name
|
10
|
-
change_dialect(dialect_name, nil)
|
11
|
-
reset
|
12
|
-
end
|
13
|
-
|
14
|
-
def reset
|
15
|
-
change_dialect(@default_dialect_name, nil) unless @dialect_name == @default_dialect_name
|
16
|
-
@active_doc_string_separator = nil
|
17
|
-
@indent_to_remove = 0
|
18
|
-
end
|
19
|
-
|
20
|
-
def match_TagLine(token)
|
21
|
-
return false unless token.line.start_with?('@')
|
22
|
-
|
23
|
-
set_token_matched(token, :TagLine, nil, nil, nil, token.line.tags)
|
24
|
-
true
|
25
|
-
end
|
26
|
-
|
27
|
-
def match_FeatureLine(token)
|
28
|
-
match_title_line(token, :FeatureLine, @dialect.feature_keywords)
|
29
|
-
end
|
30
|
-
|
31
|
-
def match_ScenarioLine(token)
|
32
|
-
match_title_line(token, :ScenarioLine, @dialect.scenario_keywords)
|
33
|
-
end
|
34
|
-
|
35
|
-
def match_ScenarioOutlineLine(token)
|
36
|
-
match_title_line(token, :ScenarioOutlineLine, @dialect.scenario_outline_keywords)
|
37
|
-
end
|
38
|
-
|
39
|
-
def match_BackgroundLine(token)
|
40
|
-
match_title_line(token, :BackgroundLine, @dialect.background_keywords)
|
41
|
-
end
|
42
|
-
|
43
|
-
def match_ExamplesLine(token)
|
44
|
-
match_title_line(token, :ExamplesLine, @dialect.examples_keywords)
|
45
|
-
end
|
46
|
-
|
47
|
-
def match_TableRow(token)
|
48
|
-
return false unless token.line.start_with?('|')
|
49
|
-
# TODO: indent
|
50
|
-
set_token_matched(token, :TableRow, nil, nil, nil, token.line.table_cells)
|
51
|
-
true
|
52
|
-
end
|
53
|
-
|
54
|
-
def match_Empty(token)
|
55
|
-
return false unless token.line.empty?
|
56
|
-
set_token_matched(token, :Empty, nil, nil, 0)
|
57
|
-
true
|
58
|
-
end
|
59
|
-
|
60
|
-
def match_Comment(token)
|
61
|
-
return false unless token.line.start_with?('#')
|
62
|
-
text = token.line.get_line_text(0) #take the entire line, including leading space
|
63
|
-
set_token_matched(token, :Comment, text, nil, 0)
|
64
|
-
true
|
65
|
-
end
|
66
|
-
|
67
|
-
def match_Language(token)
|
68
|
-
return false unless token.line.trimmed_line_text =~ LANGUAGE_PATTERN
|
69
|
-
|
70
|
-
dialect_name = $1
|
71
|
-
set_token_matched(token, :Language, dialect_name)
|
72
|
-
|
73
|
-
change_dialect(dialect_name, token.location)
|
74
|
-
|
75
|
-
true
|
76
|
-
end
|
77
|
-
|
78
|
-
def match_DocStringSeparator(token)
|
79
|
-
if @active_doc_string_separator.nil?
|
80
|
-
# open
|
81
|
-
_match_DocStringSeparator(token, '"""', true) ||
|
82
|
-
_match_DocStringSeparator(token, '```', true)
|
83
|
-
else
|
84
|
-
# close
|
85
|
-
_match_DocStringSeparator(token, @active_doc_string_separator, false)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def _match_DocStringSeparator(token, separator, is_open)
|
90
|
-
return false unless token.line.start_with?(separator)
|
91
|
-
|
92
|
-
content_type = nil
|
93
|
-
if is_open
|
94
|
-
content_type = token.line.get_rest_trimmed(separator.length)
|
95
|
-
@active_doc_string_separator = separator
|
96
|
-
@indent_to_remove = token.line.indent
|
97
|
-
else
|
98
|
-
@active_doc_string_separator = nil
|
99
|
-
@indent_to_remove = 0
|
100
|
-
end
|
101
|
-
|
102
|
-
# TODO: Use the separator as keyword. That's needed for pretty printing.
|
103
|
-
set_token_matched(token, :DocStringSeparator, content_type)
|
104
|
-
true
|
105
|
-
end
|
106
|
-
|
107
|
-
def match_EOF(token)
|
108
|
-
return false unless token.eof?
|
109
|
-
set_token_matched(token, :EOF)
|
110
|
-
true
|
111
|
-
end
|
112
|
-
|
113
|
-
def match_Other(token)
|
114
|
-
text = token.line.get_line_text(@indent_to_remove) # take the entire line, except removing DocString indents
|
115
|
-
set_token_matched(token, :Other, unescape_docstring(text), nil, 0)
|
116
|
-
true
|
117
|
-
end
|
118
|
-
|
119
|
-
def match_StepLine(token)
|
120
|
-
keywords = @dialect.given_keywords +
|
121
|
-
@dialect.when_keywords +
|
122
|
-
@dialect.then_keywords +
|
123
|
-
@dialect.and_keywords +
|
124
|
-
@dialect.but_keywords
|
125
|
-
|
126
|
-
keyword = keywords.detect { |k| token.line.start_with?(k) }
|
127
|
-
|
128
|
-
return false unless keyword
|
129
|
-
|
130
|
-
title = token.line.get_rest_trimmed(keyword.length)
|
131
|
-
set_token_matched(token, :StepLine, title, keyword)
|
132
|
-
return true
|
133
|
-
end
|
134
|
-
|
135
|
-
private
|
136
|
-
|
137
|
-
def change_dialect(dialect_name, location)
|
138
|
-
dialect = Dialect.for(dialect_name)
|
139
|
-
raise NoSuchLanguageException.new(dialect_name, location) if dialect.nil?
|
140
|
-
|
141
|
-
@dialect_name = dialect_name
|
142
|
-
@dialect = dialect
|
143
|
-
end
|
144
|
-
|
145
|
-
def match_title_line(token, token_type, keywords)
|
146
|
-
keyword = keywords.detect { |k| token.line.start_with_title_keyword?(k) }
|
147
|
-
|
148
|
-
return false unless keyword
|
149
|
-
|
150
|
-
title = token.line.get_rest_trimmed(keyword.length + ':'.length)
|
151
|
-
set_token_matched(token, token_type, title, keyword)
|
152
|
-
true
|
153
|
-
end
|
154
|
-
|
155
|
-
def set_token_matched(token, matched_type, text=nil, keyword=nil, indent=nil, items=[])
|
156
|
-
token.matched_type = matched_type
|
157
|
-
token.matched_text = text && text.chomp
|
158
|
-
token.matched_keyword = keyword
|
159
|
-
token.matched_indent = indent || (token.line && token.line.indent) || 0
|
160
|
-
token.matched_items = items
|
161
|
-
token.location[:column] = token.matched_indent + 1
|
162
|
-
token.matched_gherkin_dialect = @dialect_name
|
163
|
-
end
|
164
|
-
|
165
|
-
def unescape_docstring(text)
|
166
|
-
@active_doc_string_separator ? text.gsub("\\\"\\\"\\\"", "\"\"\"") : text
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|