gherkin 1.0.30-universal-dotnet
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.
- data/.gitattributes +2 -0
- data/.gitignore +9 -0
- data/.mailmap +2 -0
- data/History.txt +187 -0
- data/LICENSE +20 -0
- data/README.rdoc +59 -0
- data/Rakefile +58 -0
- data/VERSION.yml +5 -0
- data/bin/gherkin +5 -0
- data/cucumber.yml +3 -0
- data/features/escaped_pipes.feature +8 -0
- data/features/feature_parser.feature +226 -0
- data/features/native_lexer.feature +19 -0
- data/features/parser_with_native_lexer.feature +205 -0
- data/features/pretty_printer.feature +14 -0
- data/features/step_definitions/eyeball_steps.rb +3 -0
- data/features/step_definitions/gherkin_steps.rb +30 -0
- data/features/step_definitions/pretty_formatter_steps.rb +55 -0
- data/features/steps_parser.feature +46 -0
- data/features/support/env.rb +33 -0
- data/ikvm/.gitignore +3 -0
- data/java/.gitignore +2 -0
- data/java/src/main/java/gherkin/lexer/.gitignore +1 -0
- data/java/src/main/resources/gherkin/.gitignore +1 -0
- data/lib/.gitignore +4 -0
- data/lib/gherkin.rb +2 -0
- data/lib/gherkin/c_lexer.rb +17 -0
- data/lib/gherkin/cli/main.rb +33 -0
- data/lib/gherkin/formatter/argument.rb +27 -0
- data/lib/gherkin/formatter/colors.rb +119 -0
- data/lib/gherkin/formatter/escaping.rb +15 -0
- data/lib/gherkin/formatter/monochrome_format.rb +9 -0
- data/lib/gherkin/formatter/pretty_formatter.rb +168 -0
- data/lib/gherkin/i18n.rb +176 -0
- data/lib/gherkin/i18n.yml +588 -0
- data/lib/gherkin/i18n_lexer.rb +38 -0
- data/lib/gherkin/native.rb +7 -0
- data/lib/gherkin/native/ikvm.rb +55 -0
- data/lib/gherkin/native/java.rb +47 -0
- data/lib/gherkin/native/null.rb +9 -0
- data/lib/gherkin/parser/event.rb +45 -0
- data/lib/gherkin/parser/filter_listener.rb +199 -0
- data/lib/gherkin/parser/meta.txt +5 -0
- data/lib/gherkin/parser/parser.rb +142 -0
- data/lib/gherkin/parser/root.txt +11 -0
- data/lib/gherkin/parser/steps.txt +4 -0
- data/lib/gherkin/parser/tag_expression.rb +50 -0
- data/lib/gherkin/rb_lexer.rb +8 -0
- data/lib/gherkin/rb_lexer/.gitignore +1 -0
- data/lib/gherkin/rb_lexer/README.rdoc +8 -0
- data/lib/gherkin/rubify.rb +18 -0
- data/lib/gherkin/tools.rb +8 -0
- data/lib/gherkin/tools/files.rb +35 -0
- data/lib/gherkin/tools/reformat.rb +19 -0
- data/lib/gherkin/tools/stats.rb +21 -0
- data/lib/gherkin/tools/stats_listener.rb +57 -0
- data/ragel/i18n/.gitignore +1 -0
- data/ragel/lexer.c.rl.erb +425 -0
- data/ragel/lexer.java.rl.erb +216 -0
- data/ragel/lexer.rb.rl.erb +173 -0
- data/ragel/lexer_common.rl.erb +50 -0
- data/spec/gherkin/c_lexer_spec.rb +21 -0
- data/spec/gherkin/csharp_lexer_spec.rb +20 -0
- data/spec/gherkin/fixtures/1.feature +8 -0
- data/spec/gherkin/fixtures/comments_in_table.feature +9 -0
- data/spec/gherkin/fixtures/complex.feature +45 -0
- data/spec/gherkin/fixtures/dos_line_endings.feature +45 -0
- data/spec/gherkin/fixtures/i18n_fr.feature +14 -0
- data/spec/gherkin/fixtures/i18n_no.feature +7 -0
- data/spec/gherkin/fixtures/i18n_zh-CN.feature +9 -0
- data/spec/gherkin/fixtures/simple_with_comments.feature +7 -0
- data/spec/gherkin/fixtures/simple_with_tags.feature +11 -0
- data/spec/gherkin/fixtures/with_bom.feature +3 -0
- data/spec/gherkin/formatter/argument_spec.rb +28 -0
- data/spec/gherkin/formatter/colors_spec.rb +19 -0
- data/spec/gherkin/formatter/pretty_formatter_spec.rb +162 -0
- data/spec/gherkin/formatter/spaces.feature +9 -0
- data/spec/gherkin/formatter/tabs.feature +9 -0
- data/spec/gherkin/i18n_lexer_spec.rb +26 -0
- data/spec/gherkin/i18n_spec.rb +144 -0
- data/spec/gherkin/java_lexer_spec.rb +21 -0
- data/spec/gherkin/parser/filter_listener_spec.rb +390 -0
- data/spec/gherkin/parser/parser_spec.rb +50 -0
- data/spec/gherkin/parser/tag_expression_spec.rb +116 -0
- data/spec/gherkin/rb_lexer_spec.rb +19 -0
- data/spec/gherkin/sexp_recorder.rb +32 -0
- data/spec/gherkin/shared/lexer_spec.rb +550 -0
- data/spec/gherkin/shared/py_string_spec.rb +150 -0
- data/spec/gherkin/shared/row_spec.rb +104 -0
- data/spec/gherkin/shared/tags_spec.rb +50 -0
- data/spec/spec_helper.rb +87 -0
- data/tasks/bench.rake +188 -0
- data/tasks/bench/feature_builder.rb +49 -0
- data/tasks/bench/generated/.gitignore +1 -0
- data/tasks/bench/null_listener.rb +4 -0
- data/tasks/compile.rake +89 -0
- data/tasks/cucumber.rake +26 -0
- data/tasks/gems.rake +45 -0
- data/tasks/ikvm.rake +47 -0
- data/tasks/ragel_task.rb +70 -0
- data/tasks/rdoc.rake +12 -0
- data/tasks/release.rake +26 -0
- data/tasks/rspec.rake +15 -0
- metadata +257 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
require 'gherkin/parser/parser'
|
3
|
+
|
4
|
+
module Gherkin
|
5
|
+
module Parser
|
6
|
+
describe Parser do
|
7
|
+
before do
|
8
|
+
@listener = mock('listener')
|
9
|
+
@parser = Parser.new(@listener, true)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should delegate events to the listener" do
|
13
|
+
@listener.should_receive(:comment).with("# Content", 1)
|
14
|
+
@parser.comment("# Content", 1)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should raise helpful error messages by default" do
|
18
|
+
lambda {
|
19
|
+
@parser.scenario("Scenario", "My pet scenario", 12)
|
20
|
+
}.should raise_error(/Parse error on line 12\. Found scenario when expecting one of: comment, feature, tag\. \(Current state: root\)\.$/)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should allow empty files" do
|
24
|
+
@listener.should_receive(:eof)
|
25
|
+
@parser.eof
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should delegate an error message when raise on error is false" do
|
29
|
+
@listener.should_receive(:syntax_error).with(sym(:root), sym(:background), a([:comment, :feature, :tag]), 1)
|
30
|
+
@parser = Parser.new(@listener, false)
|
31
|
+
@parser.background("Background", "Content", 1)
|
32
|
+
end
|
33
|
+
|
34
|
+
[true, false].each do |native|
|
35
|
+
it "should be reusable for several feature files (native lexer: #{native})" do
|
36
|
+
listener = mock('listener', :null_object => true)
|
37
|
+
parser = Parser.new(listener, true)
|
38
|
+
lexer = Gherkin::I18nLexer.new(parser, native)
|
39
|
+
feature = <<-EOF
|
40
|
+
Feature: foo
|
41
|
+
Scenario: bar
|
42
|
+
Given zap
|
43
|
+
EOF
|
44
|
+
lexer.scan(feature)
|
45
|
+
lexer.scan(feature)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
require 'gherkin/parser/tag_expression'
|
3
|
+
|
4
|
+
module Gherkin
|
5
|
+
module Parser
|
6
|
+
describe TagExpression do
|
7
|
+
context "no tags" do
|
8
|
+
before(:each) do
|
9
|
+
@e = TagExpression.new([])
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should match @foo" do
|
13
|
+
@e.eval(['@foo']).should == true
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should match empty tags" do
|
17
|
+
@e.eval([]).should == true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "@foo" do
|
22
|
+
before(:each) do
|
23
|
+
@e = TagExpression.new(['@foo'])
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should match @foo" do
|
27
|
+
@e.eval(['@foo']).should == true
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should not match @bar" do
|
31
|
+
@e.eval(['@bar']).should == false
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should not match no tags" do
|
35
|
+
@e.eval([]).should == false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "!@foo" do
|
40
|
+
before(:each) do
|
41
|
+
@e = TagExpression.new(['~@foo'])
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should match @bar" do
|
45
|
+
@e.eval(['@bar']).should == true
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should not match @foo" do
|
49
|
+
@e.eval(['@foo']).should == false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "@foo || @bar" do
|
54
|
+
before(:each) do
|
55
|
+
@e = TagExpression.new(['@foo,@bar'])
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should match @foo" do
|
59
|
+
@e.eval(['@foo']).should == true
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should match @bar" do
|
63
|
+
@e.eval(['@bar']).should == true
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should not match @zap" do
|
67
|
+
@e.eval(['@zap']).should == false
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "(@foo || @bar) && !@zap" do
|
72
|
+
before(:each) do
|
73
|
+
@e = TagExpression.new(['@foo,@bar', '~@zap'])
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should match @foo" do
|
77
|
+
@e.eval(['@foo']).should == true
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should not match @foo @zap" do
|
81
|
+
@e.eval(['@foo', '@zap']).should == false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context "(@foo:3 || !@bar:4) && @zap:5" do
|
86
|
+
before(:each) do
|
87
|
+
@e = TagExpression.new(['@foo:3,~@bar','@zap:5'])
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should count tags for positive tags" do
|
91
|
+
rubify_hash(@e.limits).should == {'@foo' => 3, '@zap' => 5}
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should match @foo @zap" do
|
95
|
+
@e.eval(['@foo', '@zap']).should == true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context "Parsing '@foo:3,~@bar', '@zap:5'" do
|
100
|
+
before(:each) do
|
101
|
+
@e = TagExpression.new([' @foo:3 , ~@bar ', ' @zap:5 '])
|
102
|
+
end
|
103
|
+
|
104
|
+
unless defined?(JRUBY_VERSION)
|
105
|
+
it "should split and trim (ruby implementation detail)" do
|
106
|
+
@e.__send__(:ruby_expression).should == "(!vars['@bar']||vars['@foo'])&&(vars['@zap'])"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should have limits" do
|
111
|
+
rubify_hash(@e.limits).should == {"@zap"=>5, "@foo"=>3}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
3
|
+
require 'gherkin/rb_lexer/en'
|
4
|
+
|
5
|
+
module Gherkin
|
6
|
+
module Lexer
|
7
|
+
describe "Ruby Lexer" do
|
8
|
+
before do
|
9
|
+
@listener = Gherkin::SexpRecorder.new
|
10
|
+
@lexer = Gherkin::RbLexer::En.new(@listener)
|
11
|
+
end
|
12
|
+
|
13
|
+
it_should_behave_like "a Gherkin lexer"
|
14
|
+
it_should_behave_like "a Gherkin lexer lexing tags"
|
15
|
+
it_should_behave_like "a Gherkin lexer lexing py_strings"
|
16
|
+
it_should_behave_like "a Gherkin lexer lexing rows"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Gherkin
|
2
|
+
class SexpRecorder
|
3
|
+
def initialize
|
4
|
+
@sexps = []
|
5
|
+
end
|
6
|
+
|
7
|
+
def method_missing(event, *args)
|
8
|
+
event = :scenario_outline if event == :scenarioOutline # Special Java Lexer handling
|
9
|
+
event = :py_string if event == :pyString # Special Java Lexer handling
|
10
|
+
event = :syntax_error if event == :syntaxError # Special Java Lexer handling
|
11
|
+
args[0] = args[0].to_a if event == :row # Special JRuby handling
|
12
|
+
@sexps << [event] + args
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_sexp
|
16
|
+
@sexps
|
17
|
+
end
|
18
|
+
|
19
|
+
# Useful in IRB
|
20
|
+
def reset!
|
21
|
+
@sexps = []
|
22
|
+
end
|
23
|
+
|
24
|
+
def errors
|
25
|
+
@sexps.select { |sexp| sexp[0] == :syntax_error }
|
26
|
+
end
|
27
|
+
|
28
|
+
def line(number)
|
29
|
+
@sexps.find { |sexp| sexp.last == number }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,550 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
3
|
+
|
4
|
+
module Gherkin
|
5
|
+
module Lexer
|
6
|
+
shared_examples_for "a Gherkin lexer" do
|
7
|
+
|
8
|
+
describe "Comments" do
|
9
|
+
it "should parse a one line comment" do
|
10
|
+
@lexer.scan("# My comment\n")
|
11
|
+
@listener.to_sexp.should == [
|
12
|
+
[:comment, "# My comment", 1],
|
13
|
+
[:eof]
|
14
|
+
]
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should parse a multiline comment" do
|
18
|
+
@lexer.scan("# Hello\n\n# World\n")
|
19
|
+
@listener.to_sexp.should == [
|
20
|
+
[:comment, "# Hello", 1],
|
21
|
+
[:comment, "# World", 3],
|
22
|
+
[:eof]
|
23
|
+
]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should not consume comments as part of a multiline name" do
|
27
|
+
@lexer.scan("Scenario: test\n#hello\n Scenario: another")
|
28
|
+
@listener.to_sexp.should == [
|
29
|
+
[:scenario, "Scenario", "test", 1],
|
30
|
+
[:comment, "#hello", 2],
|
31
|
+
[:scenario, "Scenario", "another", 3],
|
32
|
+
[:eof]
|
33
|
+
]
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should allow empty comment lines" do
|
37
|
+
@lexer.scan("#\n # A comment\n #\n")
|
38
|
+
@listener.to_sexp.should == [
|
39
|
+
[:comment, "#", 1],
|
40
|
+
[:comment, "# A comment", 2],
|
41
|
+
[:comment, "#", 3],
|
42
|
+
[:eof]
|
43
|
+
]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should not allow comments within the Feature description" do
|
47
|
+
lambda {
|
48
|
+
@lexer.scan("Feature: something\nAs a something\n# Comment\nI want something")
|
49
|
+
}.should raise_error(/Lexing error on line 4/)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "Tags" do
|
54
|
+
it "should not take the tags as part of a multiline name feature element" do
|
55
|
+
@lexer.scan("Feature: hi\n Scenario: test\n\n@hello\n Scenario: another")
|
56
|
+
@listener.to_sexp.should == [
|
57
|
+
[:feature, "Feature", "hi", 1],
|
58
|
+
[:scenario, "Scenario", "test", 2],
|
59
|
+
[:tag, "@hello", 4],
|
60
|
+
[:scenario, "Scenario", "another", 5],
|
61
|
+
[:eof]
|
62
|
+
]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "Background" do
|
67
|
+
it "should allow an empty background description" do
|
68
|
+
@lexer.scan("Background:\nGiven I am a step\n")
|
69
|
+
@listener.to_sexp.should == [
|
70
|
+
[:background, "Background", "", 1],
|
71
|
+
[:step, "Given ", "I am a step", 2],
|
72
|
+
[:eof]
|
73
|
+
]
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should allow multiline names ending at eof" do
|
77
|
+
@lexer.scan("Background: I have several\n Lines to look at\n None starting with Given")
|
78
|
+
@listener.to_sexp.should == [
|
79
|
+
[:background, "Background", "I have several\nLines to look at\nNone starting with Given", 1],
|
80
|
+
[:eof]
|
81
|
+
]
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should allow multiline names" do
|
85
|
+
@lexer.scan(%{Feature: Hi
|
86
|
+
Background: It is my ambition to say
|
87
|
+
in ten sentences
|
88
|
+
what others say
|
89
|
+
in a whole book.
|
90
|
+
Given I am a step})
|
91
|
+
@listener.to_sexp.should == [
|
92
|
+
[:feature, "Feature", "Hi", 1],
|
93
|
+
[:background, "Background", "It is my ambition to say\nin ten sentences\nwhat others say\nin a whole book.",2],
|
94
|
+
[:step, "Given ", "I am a step", 6],
|
95
|
+
[:eof]
|
96
|
+
]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "Scenarios" do
|
101
|
+
it "should be parsed" do
|
102
|
+
@lexer.scan("Scenario: Hello\n")
|
103
|
+
@listener.to_sexp.should == [
|
104
|
+
[:scenario, "Scenario", "Hello", 1],
|
105
|
+
[:eof]
|
106
|
+
]
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should allow whitespace lines after the Scenario line" do
|
110
|
+
@lexer.scan(%{Scenario: bar
|
111
|
+
|
112
|
+
Given baz
|
113
|
+
})
|
114
|
+
@listener.to_sexp.should == [
|
115
|
+
[:scenario, "Scenario", "bar", 1],
|
116
|
+
[:step, "Given ", "baz", 3],
|
117
|
+
[:eof]
|
118
|
+
]
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should allow multiline names" do
|
122
|
+
@lexer.scan(%{Scenario: It is my ambition to say
|
123
|
+
in ten sentences
|
124
|
+
what others say
|
125
|
+
in a whole book.
|
126
|
+
Given I am a step
|
127
|
+
})
|
128
|
+
@listener.to_sexp.should == [
|
129
|
+
[:scenario, "Scenario", "It is my ambition to say\nin ten sentences\nwhat others say\nin a whole book.", 1],
|
130
|
+
[:step, "Given ", "I am a step", 5],
|
131
|
+
[:eof]
|
132
|
+
]
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should allow multiline names ending at eof" do
|
136
|
+
@lexer.scan("Scenario: I have several\n Lines to look at\n None starting with Given")
|
137
|
+
@listener.to_sexp.should == [
|
138
|
+
[:scenario, "Scenario", "I have several\nLines to look at\nNone starting with Given", 1],
|
139
|
+
[:eof]
|
140
|
+
]
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should ignore gherkin keywords embedded in other words" do
|
144
|
+
@lexer.scan(%{Scenario: I have a Button
|
145
|
+
Buttons are great
|
146
|
+
Given I have some
|
147
|
+
But I might not because I am a Charles Dickens character
|
148
|
+
})
|
149
|
+
@listener.to_sexp.should == [
|
150
|
+
[:scenario, "Scenario", "I have a Button\nButtons are great", 1],
|
151
|
+
[:step, "Given ", "I have some", 3],
|
152
|
+
[:step, "But ", "I might not because I am a Charles Dickens character", 4],
|
153
|
+
[:eof]
|
154
|
+
]
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should allow step names in Scenario descriptions" do
|
158
|
+
@lexer.scan(%{Scenario: When I have when in scenario
|
159
|
+
I should be fine
|
160
|
+
Given I am a step
|
161
|
+
})
|
162
|
+
@listener.to_sexp.should == [
|
163
|
+
[:scenario, "Scenario", "When I have when in scenario\nI should be fine", 1],
|
164
|
+
[:step, "Given ", "I am a step", 3],
|
165
|
+
[:eof]
|
166
|
+
]
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe "Scenario Outlines" do
|
171
|
+
it "should be parsed" do
|
172
|
+
@lexer.scan(%{Scenario Outline: Hello
|
173
|
+
Given a <what> cucumber
|
174
|
+
Examples:
|
175
|
+
|what|
|
176
|
+
|green|
|
177
|
+
})
|
178
|
+
@listener.to_sexp.should == [
|
179
|
+
[:scenario_outline, "Scenario Outline", "Hello", 1],
|
180
|
+
[:step, "Given ", "a <what> cucumber", 2],
|
181
|
+
[:examples, "Examples", "", 3],
|
182
|
+
[:row, ["what"], 4],
|
183
|
+
[:row, ["green"], 5],
|
184
|
+
[:eof]
|
185
|
+
]
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should parse with no steps or examples" do
|
189
|
+
@lexer.scan(%{Scenario Outline: Hello
|
190
|
+
|
191
|
+
Scenario: My Scenario
|
192
|
+
})
|
193
|
+
@listener.to_sexp.should == [
|
194
|
+
[:scenario_outline, "Scenario Outline", "Hello", 1],
|
195
|
+
[:scenario, "Scenario", "My Scenario", 3],
|
196
|
+
[:eof]
|
197
|
+
]
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should allow multiline names" do
|
201
|
+
@lexer.scan(%{Scenario Outline: It is my ambition to say
|
202
|
+
in ten sentences
|
203
|
+
what others say
|
204
|
+
in a whole book.
|
205
|
+
Given I am a step
|
206
|
+
|
207
|
+
})
|
208
|
+
@listener.to_sexp.should == [
|
209
|
+
[:scenario_outline, "Scenario Outline", "It is my ambition to say\nin ten sentences\nwhat others say\nin a whole book.", 1],
|
210
|
+
[:step, "Given ", "I am a step", 5],
|
211
|
+
[:eof]
|
212
|
+
]
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe "Examples" do
|
217
|
+
it "should be parsed" do
|
218
|
+
@lexer.scan(%{Examples:
|
219
|
+
|x|y|
|
220
|
+
|5|6|
|
221
|
+
})
|
222
|
+
@listener.to_sexp.should == [
|
223
|
+
[:examples, "Examples", "", 1],
|
224
|
+
[:row, ["x","y"], 2],
|
225
|
+
[:row, ["5","6"], 3],
|
226
|
+
[:eof]
|
227
|
+
]
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should parse multiline example names" do
|
231
|
+
@lexer.scan(%{Examples: I'm a multiline name
|
232
|
+
and I'm ok
|
233
|
+
f'real
|
234
|
+
|x|
|
235
|
+
|5|
|
236
|
+
})
|
237
|
+
@listener.to_sexp.should == [
|
238
|
+
[:examples, "Examples", "I'm a multiline name\nand I'm ok\nf'real", 1],
|
239
|
+
[:row, ["x"], 4],
|
240
|
+
[:row, ["5"], 5],
|
241
|
+
[:eof]
|
242
|
+
]
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
describe "Steps" do
|
247
|
+
it "should parse steps with inline table" do
|
248
|
+
@lexer.scan(%{Given I have a table
|
249
|
+
|a|b|
|
250
|
+
})
|
251
|
+
@listener.to_sexp.should == [
|
252
|
+
[:step, "Given ", "I have a table", 1],
|
253
|
+
[:row, ['a','b'], 2],
|
254
|
+
[:eof]
|
255
|
+
]
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should parse steps with inline py_string" do
|
259
|
+
@lexer.scan("Given I have a string\n\"\"\"\nhello\nworld\n\"\"\"")
|
260
|
+
@listener.to_sexp.should == [
|
261
|
+
[:step, "Given ", "I have a string", 1],
|
262
|
+
[:py_string, "hello\nworld", 2],
|
263
|
+
[:eof]
|
264
|
+
]
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
describe "A single feature, single scenario, single step" do
|
269
|
+
it "should find the feature, scenario, and step" do
|
270
|
+
@lexer.scan("Feature: Feature Text\n Scenario: Reading a Scenario\n Given there is a step\n")
|
271
|
+
@listener.to_sexp.should == [
|
272
|
+
[:feature, "Feature", "Feature Text", 1],
|
273
|
+
[:scenario, "Scenario", "Reading a Scenario", 2],
|
274
|
+
[:step, "Given ", "there is a step", 3],
|
275
|
+
[:eof]
|
276
|
+
]
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
describe "A feature ending in whitespace" do
|
281
|
+
it "should not raise an error when whitespace follows the Feature, Scenario, and Steps" do
|
282
|
+
@lexer.scan("Feature: Feature Text\n Scenario: Reading a Scenario\n Given there is a step\n ")
|
283
|
+
@listener.to_sexp.should == [
|
284
|
+
[:feature, "Feature", "Feature Text", 1],
|
285
|
+
[:scenario, "Scenario", "Reading a Scenario", 2],
|
286
|
+
[:step, "Given ", "there is a step", 3],
|
287
|
+
[:eof]
|
288
|
+
]
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
describe "A single feature, single scenario, three steps" do
|
293
|
+
|
294
|
+
it "should find the feature, scenario, and three steps" do
|
295
|
+
@lexer.scan("Feature: Feature Text\n Scenario: Reading a Scenario\n Given there is a step\n And another step\n And a third step\n")
|
296
|
+
@listener.to_sexp.should == [
|
297
|
+
[:feature, "Feature", "Feature Text", 1],
|
298
|
+
[:scenario, "Scenario", "Reading a Scenario", 2],
|
299
|
+
[:step, "Given ", "there is a step", 3],
|
300
|
+
[:step, "And ", "another step", 4],
|
301
|
+
[:step, "And ", "a third step", 5],
|
302
|
+
[:eof]
|
303
|
+
]
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
describe "A single feature with no scenario" do
|
308
|
+
it "should find the feature" do
|
309
|
+
@lexer.scan("Feature: Feature Text\n")
|
310
|
+
@listener.to_sexp.should == [
|
311
|
+
[:feature, "Feature", "Feature Text", 1],
|
312
|
+
[:eof]
|
313
|
+
]
|
314
|
+
end
|
315
|
+
|
316
|
+
it "should parse a one line feature with no newline" do
|
317
|
+
@lexer.scan("Feature: hi")
|
318
|
+
@listener.to_sexp.should == [
|
319
|
+
[:feature, "Feature", "hi", 1],
|
320
|
+
[:eof]
|
321
|
+
]
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
describe "A multi-line feature with no scenario" do
|
326
|
+
it "should find the feature" do
|
327
|
+
@lexer.scan("Feature: Feature Text\n And some more text")
|
328
|
+
@listener.to_sexp.should == [
|
329
|
+
[:feature, "Feature", "Feature Text\nAnd some more text", 1],
|
330
|
+
[:eof]
|
331
|
+
]
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
describe "A feature with a scenario but no steps" do
|
336
|
+
it "should find the feature and scenario" do
|
337
|
+
@lexer.scan("Feature: Feature Text\nScenario: Reading a Scenario\n")
|
338
|
+
@listener.to_sexp.should == [
|
339
|
+
[:feature, "Feature", "Feature Text", 1],
|
340
|
+
[:scenario, "Scenario", "Reading a Scenario", 2],
|
341
|
+
[:eof]
|
342
|
+
]
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
describe "A feature with two scenarios" do
|
347
|
+
it "should find the feature and two scenarios" do
|
348
|
+
@lexer.scan("Feature: Feature Text\nScenario: Reading a Scenario\n Given a step\n\nScenario: A second scenario\n Given another step\n")
|
349
|
+
@listener.to_sexp.should == [
|
350
|
+
[:feature, "Feature", "Feature Text", 1],
|
351
|
+
[:scenario, "Scenario", "Reading a Scenario", 2],
|
352
|
+
[:step, "Given ", "a step", 3],
|
353
|
+
[:scenario, "Scenario", "A second scenario", 5],
|
354
|
+
[:step, "Given ", "another step", 6],
|
355
|
+
[:eof]
|
356
|
+
]
|
357
|
+
end
|
358
|
+
|
359
|
+
it "should find the feature and two scenarios without indentation" do
|
360
|
+
@lexer.scan("Feature: Feature Text\nScenario: Reading a Scenario\nGiven a step\nScenario: A second scenario\nGiven another step\n")
|
361
|
+
@listener.to_sexp.should == [
|
362
|
+
[:feature, "Feature", "Feature Text", 1],
|
363
|
+
[:scenario, "Scenario", "Reading a Scenario", 2],
|
364
|
+
[:step, "Given ", "a step", 3],
|
365
|
+
[:scenario, "Scenario", "A second scenario", 4],
|
366
|
+
[:step, "Given ", "another step", 5],
|
367
|
+
[:eof]
|
368
|
+
]
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
describe "A simple feature with comments" do
|
373
|
+
it "should find the feature, scenarios, steps, and comments in the proper order" do
|
374
|
+
scan_file("simple_with_comments.feature")
|
375
|
+
@listener.to_sexp.should == [
|
376
|
+
[:comment, "# Here is a comment", 1],
|
377
|
+
[:feature, "Feature", "Feature Text", 2],
|
378
|
+
[:comment, "# Here is another # comment", 3],
|
379
|
+
[:scenario, "Scenario", "Reading a Scenario", 4],
|
380
|
+
[:comment, "# Here is a third comment", 5],
|
381
|
+
[:step, "Given ", "there is a step", 6],
|
382
|
+
[:comment, "# Here is a fourth comment", 7],
|
383
|
+
[:eof]
|
384
|
+
]
|
385
|
+
end
|
386
|
+
|
387
|
+
it "should support comments in tables" do
|
388
|
+
scan_file("comments_in_table.feature")
|
389
|
+
@listener.to_sexp.should == [
|
390
|
+
[:feature, "Feature", "x", 1],
|
391
|
+
[:scenario_outline, "Scenario Outline", "x", 3],
|
392
|
+
[:step, "Then ", "x is <state>", 4],
|
393
|
+
[:examples, "Examples", "", 6],
|
394
|
+
[:row, ["state"], 7],
|
395
|
+
[:comment, "# comment", 8],
|
396
|
+
[:row, ["1"], 9],
|
397
|
+
[:eof]
|
398
|
+
]
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
describe "A feature with tags everywhere" do
|
403
|
+
it "should find the feature, scenario, step, and tags in the proper order" do
|
404
|
+
scan_file("simple_with_tags.feature")
|
405
|
+
@listener.to_sexp.should == [
|
406
|
+
[:comment, "# FC", 1],
|
407
|
+
[:tag, "@ft",2],
|
408
|
+
[:feature, "Feature", "hi", 3],
|
409
|
+
[:tag, "@st1", 5],
|
410
|
+
[:tag, "@st2", 5],
|
411
|
+
[:scenario, "Scenario", "First", 6],
|
412
|
+
[:step, "Given ", "Pepper", 7],
|
413
|
+
[:tag, "@st3", 9],
|
414
|
+
[:tag, "@st4", 10],
|
415
|
+
[:tag, "@ST5", 10],
|
416
|
+
[:tag, "@#^%&ST6**!", 10],
|
417
|
+
[:scenario, "Scenario", "Second", 11],
|
418
|
+
[:eof]
|
419
|
+
]
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
describe "Comment or tag between Feature elements where previous narrative starts with same letter as a keyword" do
|
424
|
+
it "should lex this feature properly" do
|
425
|
+
scan_file("1.feature")
|
426
|
+
@listener.to_sexp.should == [
|
427
|
+
[:feature, "Feature", "Logging in\nSo that I can be myself", 1],
|
428
|
+
[:comment, "# Comment", 3],
|
429
|
+
[:scenario, "Scenario", "Anonymous user can get a login form.\nScenery here", 4],
|
430
|
+
[:tag, "@tag", 7],
|
431
|
+
[:scenario, "Scenario", "Another one", 8],
|
432
|
+
[:eof]
|
433
|
+
]
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
describe "A complex feature with tags, comments, multiple scenarios, and multiple steps and tables" do
|
438
|
+
it "should find things in the right order" do
|
439
|
+
scan_file("complex.feature")
|
440
|
+
@listener.to_sexp.should == [
|
441
|
+
[:comment, "#Comment on line 1", 1],
|
442
|
+
[:comment, "#Comment on line 2", 2],
|
443
|
+
[:tag, "@tag1", 3],
|
444
|
+
[:tag, "@tag2", 3],
|
445
|
+
[:feature, "Feature", "Feature Text\nIn order to test multiline forms\nAs a ragel writer\nI need to check for complex combinations", 4],
|
446
|
+
[:comment, "#Comment on line 9", 9],
|
447
|
+
[:comment, "#Comment on line 11", 11],
|
448
|
+
[:background, "Background", "", 13],
|
449
|
+
[:step, "Given ", "this is a background step", 14],
|
450
|
+
[:step, "And ", "this is another one", 15],
|
451
|
+
[:tag, "@tag3", 17],
|
452
|
+
[:tag, "@tag4", 17],
|
453
|
+
[:scenario, "Scenario", "Reading a Scenario", 18],
|
454
|
+
[:step, "Given ", "there is a step", 19],
|
455
|
+
[:step, "But ", "not another step", 20],
|
456
|
+
[:tag, "@tag3", 22],
|
457
|
+
[:scenario, "Scenario", "Reading a second scenario\nWith two lines of text", 23],
|
458
|
+
[:comment, "#Comment on line 24", 25],
|
459
|
+
[:step, "Given ", "a third step with a table", 26],
|
460
|
+
[:row, %w{a b}, 27],
|
461
|
+
[:row, %w{c d}, 28],
|
462
|
+
[:row, %w{e f}, 29],
|
463
|
+
[:step, "And ", "I am still testing things", 30],
|
464
|
+
[:row, %w{g h}, 31],
|
465
|
+
[:row, %w{e r}, 32],
|
466
|
+
[:row, %w{k i}, 33],
|
467
|
+
[:row, ['n', ''], 34],
|
468
|
+
[:step, "And ", "I am done testing these tables", 35],
|
469
|
+
[:comment, "#Comment on line 29", 36],
|
470
|
+
[:step, "Then ", "I am happy", 37],
|
471
|
+
[:scenario, "Scenario", "Hammerzeit", 39],
|
472
|
+
[:step, "Given ", "All work and no play", 40],
|
473
|
+
[:py_string, "Makes Homer something something\nAnd something else", 41 ],
|
474
|
+
[:step, "Then ", "crazy", 45],
|
475
|
+
[:eof]
|
476
|
+
]
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
describe "Windows stuff" do
|
481
|
+
it "should find things in the right order for CRLF features" do
|
482
|
+
scan_file("dos_line_endings.feature")
|
483
|
+
@listener.to_sexp.should == [
|
484
|
+
[:comment, "#Comment on line 1", 1],
|
485
|
+
[:comment, "#Comment on line 2", 2],
|
486
|
+
[:tag, "@tag1", 3],
|
487
|
+
[:tag, "@tag2", 3],
|
488
|
+
[:feature, "Feature", "Feature Text\r\nIn order to test multiline forms\r\nAs a ragel writer\r\nI need to check for complex combinations", 4],
|
489
|
+
[:comment, "#Comment on line 9", 9],
|
490
|
+
[:comment, "#Comment on line 11", 11],
|
491
|
+
[:background, "Background", "", 13],
|
492
|
+
[:step, "Given ", "this is a background step", 14],
|
493
|
+
[:step, "And ", "this is another one", 15],
|
494
|
+
[:tag, "@tag3", 17],
|
495
|
+
[:tag, "@tag4", 17],
|
496
|
+
[:scenario, "Scenario", "Reading a Scenario", 18],
|
497
|
+
[:step, "Given ", "there is a step", 19],
|
498
|
+
[:step, "But ", "not another step", 20],
|
499
|
+
[:tag, "@tag3", 22],
|
500
|
+
[:scenario, "Scenario", "Reading a second scenario\r\nWith two lines of text", 23],
|
501
|
+
[:comment, "#Comment on line 24", 25],
|
502
|
+
[:step, "Given ", "a third step with a table", 26],
|
503
|
+
[:row, %w{a b}, 27],
|
504
|
+
[:row, %w{c d}, 28],
|
505
|
+
[:row, %w{e f}, 29],
|
506
|
+
[:step, "And ", "I am still testing things", 30],
|
507
|
+
[:row, %w{g h}, 31],
|
508
|
+
[:row, %w{e r}, 32],
|
509
|
+
[:row, %w{k i}, 33],
|
510
|
+
[:row, ['n', ''], 34],
|
511
|
+
[:step, "And ", "I am done testing these tables", 35],
|
512
|
+
[:comment, "#Comment on line 29", 36],
|
513
|
+
[:step, "Then ", "I am happy", 37],
|
514
|
+
[:scenario, "Scenario", "Hammerzeit", 39],
|
515
|
+
[:step, "Given ", "All work and no play", 40],
|
516
|
+
[:py_string, "Makes Homer something something\r\nAnd something else", 41],
|
517
|
+
[:step, "Then ", "crazy", 45],
|
518
|
+
[:eof]
|
519
|
+
]
|
520
|
+
end
|
521
|
+
|
522
|
+
it "should cope with the retarded BOM that many Windows editors insert at the beginning of a file" do
|
523
|
+
scan_file("with_bom.feature")
|
524
|
+
@listener.to_sexp.should == [
|
525
|
+
[:feature, "Feature", "Feature Text", 1],
|
526
|
+
[:scenario, "Scenario", "Reading a Scenario", 2],
|
527
|
+
[:step, "Given ", "there is a step", 3],
|
528
|
+
[:eof]
|
529
|
+
]
|
530
|
+
end
|
531
|
+
end
|
532
|
+
|
533
|
+
describe "errors" do
|
534
|
+
it "should raise a Lexing error if an unparseable token is found" do
|
535
|
+
["Some text\nFeature: Hi",
|
536
|
+
"Feature: Hi\nBackground:\nGiven something\nScenario A scenario",
|
537
|
+
"Scenario: My scenario\nGiven foo\nAand bar\nScenario: another one\nGiven blah"].each do |text|
|
538
|
+
lambda { @lexer.scan(text) }.should raise_error(/Lexing error on line/)
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
it "should include the line number and context of the error" do
|
543
|
+
lambda {
|
544
|
+
@lexer.scan("Feature: hello\nScenario: My scenario\nGiven foo\nAand blah\nHmmm wrong\nThen something something")
|
545
|
+
}.should raise_error(/Lexing error on line 4/)
|
546
|
+
end
|
547
|
+
end
|
548
|
+
end
|
549
|
+
end
|
550
|
+
end
|