gherkin 1.0.30-universal-dotnet

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. data/.gitattributes +2 -0
  2. data/.gitignore +9 -0
  3. data/.mailmap +2 -0
  4. data/History.txt +187 -0
  5. data/LICENSE +20 -0
  6. data/README.rdoc +59 -0
  7. data/Rakefile +58 -0
  8. data/VERSION.yml +5 -0
  9. data/bin/gherkin +5 -0
  10. data/cucumber.yml +3 -0
  11. data/features/escaped_pipes.feature +8 -0
  12. data/features/feature_parser.feature +226 -0
  13. data/features/native_lexer.feature +19 -0
  14. data/features/parser_with_native_lexer.feature +205 -0
  15. data/features/pretty_printer.feature +14 -0
  16. data/features/step_definitions/eyeball_steps.rb +3 -0
  17. data/features/step_definitions/gherkin_steps.rb +30 -0
  18. data/features/step_definitions/pretty_formatter_steps.rb +55 -0
  19. data/features/steps_parser.feature +46 -0
  20. data/features/support/env.rb +33 -0
  21. data/ikvm/.gitignore +3 -0
  22. data/java/.gitignore +2 -0
  23. data/java/src/main/java/gherkin/lexer/.gitignore +1 -0
  24. data/java/src/main/resources/gherkin/.gitignore +1 -0
  25. data/lib/.gitignore +4 -0
  26. data/lib/gherkin.rb +2 -0
  27. data/lib/gherkin/c_lexer.rb +17 -0
  28. data/lib/gherkin/cli/main.rb +33 -0
  29. data/lib/gherkin/formatter/argument.rb +27 -0
  30. data/lib/gherkin/formatter/colors.rb +119 -0
  31. data/lib/gherkin/formatter/escaping.rb +15 -0
  32. data/lib/gherkin/formatter/monochrome_format.rb +9 -0
  33. data/lib/gherkin/formatter/pretty_formatter.rb +168 -0
  34. data/lib/gherkin/i18n.rb +176 -0
  35. data/lib/gherkin/i18n.yml +588 -0
  36. data/lib/gherkin/i18n_lexer.rb +38 -0
  37. data/lib/gherkin/native.rb +7 -0
  38. data/lib/gherkin/native/ikvm.rb +55 -0
  39. data/lib/gherkin/native/java.rb +47 -0
  40. data/lib/gherkin/native/null.rb +9 -0
  41. data/lib/gherkin/parser/event.rb +45 -0
  42. data/lib/gherkin/parser/filter_listener.rb +199 -0
  43. data/lib/gherkin/parser/meta.txt +5 -0
  44. data/lib/gherkin/parser/parser.rb +142 -0
  45. data/lib/gherkin/parser/root.txt +11 -0
  46. data/lib/gherkin/parser/steps.txt +4 -0
  47. data/lib/gherkin/parser/tag_expression.rb +50 -0
  48. data/lib/gherkin/rb_lexer.rb +8 -0
  49. data/lib/gherkin/rb_lexer/.gitignore +1 -0
  50. data/lib/gherkin/rb_lexer/README.rdoc +8 -0
  51. data/lib/gherkin/rubify.rb +18 -0
  52. data/lib/gherkin/tools.rb +8 -0
  53. data/lib/gherkin/tools/files.rb +35 -0
  54. data/lib/gherkin/tools/reformat.rb +19 -0
  55. data/lib/gherkin/tools/stats.rb +21 -0
  56. data/lib/gherkin/tools/stats_listener.rb +57 -0
  57. data/ragel/i18n/.gitignore +1 -0
  58. data/ragel/lexer.c.rl.erb +425 -0
  59. data/ragel/lexer.java.rl.erb +216 -0
  60. data/ragel/lexer.rb.rl.erb +173 -0
  61. data/ragel/lexer_common.rl.erb +50 -0
  62. data/spec/gherkin/c_lexer_spec.rb +21 -0
  63. data/spec/gherkin/csharp_lexer_spec.rb +20 -0
  64. data/spec/gherkin/fixtures/1.feature +8 -0
  65. data/spec/gherkin/fixtures/comments_in_table.feature +9 -0
  66. data/spec/gherkin/fixtures/complex.feature +45 -0
  67. data/spec/gherkin/fixtures/dos_line_endings.feature +45 -0
  68. data/spec/gherkin/fixtures/i18n_fr.feature +14 -0
  69. data/spec/gherkin/fixtures/i18n_no.feature +7 -0
  70. data/spec/gherkin/fixtures/i18n_zh-CN.feature +9 -0
  71. data/spec/gherkin/fixtures/simple_with_comments.feature +7 -0
  72. data/spec/gherkin/fixtures/simple_with_tags.feature +11 -0
  73. data/spec/gherkin/fixtures/with_bom.feature +3 -0
  74. data/spec/gherkin/formatter/argument_spec.rb +28 -0
  75. data/spec/gherkin/formatter/colors_spec.rb +19 -0
  76. data/spec/gherkin/formatter/pretty_formatter_spec.rb +162 -0
  77. data/spec/gherkin/formatter/spaces.feature +9 -0
  78. data/spec/gherkin/formatter/tabs.feature +9 -0
  79. data/spec/gherkin/i18n_lexer_spec.rb +26 -0
  80. data/spec/gherkin/i18n_spec.rb +144 -0
  81. data/spec/gherkin/java_lexer_spec.rb +21 -0
  82. data/spec/gherkin/parser/filter_listener_spec.rb +390 -0
  83. data/spec/gherkin/parser/parser_spec.rb +50 -0
  84. data/spec/gherkin/parser/tag_expression_spec.rb +116 -0
  85. data/spec/gherkin/rb_lexer_spec.rb +19 -0
  86. data/spec/gherkin/sexp_recorder.rb +32 -0
  87. data/spec/gherkin/shared/lexer_spec.rb +550 -0
  88. data/spec/gherkin/shared/py_string_spec.rb +150 -0
  89. data/spec/gherkin/shared/row_spec.rb +104 -0
  90. data/spec/gherkin/shared/tags_spec.rb +50 -0
  91. data/spec/spec_helper.rb +87 -0
  92. data/tasks/bench.rake +188 -0
  93. data/tasks/bench/feature_builder.rb +49 -0
  94. data/tasks/bench/generated/.gitignore +1 -0
  95. data/tasks/bench/null_listener.rb +4 -0
  96. data/tasks/compile.rake +89 -0
  97. data/tasks/cucumber.rake +26 -0
  98. data/tasks/gems.rake +45 -0
  99. data/tasks/ikvm.rake +47 -0
  100. data/tasks/ragel_task.rb +70 -0
  101. data/tasks/rdoc.rake +12 -0
  102. data/tasks/release.rake +26 -0
  103. data/tasks/rspec.rake +15 -0
  104. 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