gherkin 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/.gitignore +8 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +47 -0
  4. data/Rakefile +48 -0
  5. data/VERSION.yml +4 -0
  6. data/bin/gherkin +10 -0
  7. data/cucumber.yml +3 -0
  8. data/ext/gherkin_lexer/.gitignore +6 -0
  9. data/ext/gherkin_lexer/extconf.rb +6 -0
  10. data/features/feature_parser.feature +206 -0
  11. data/features/native_lexer.feature +19 -0
  12. data/features/parser_with_native_lexer.feature +205 -0
  13. data/features/pretty_printer.feature +11 -0
  14. data/features/step_definitions/gherkin_steps.rb +34 -0
  15. data/features/step_definitions/pretty_printer_steps.rb +51 -0
  16. data/features/steps_parser.feature +46 -0
  17. data/features/support/env.rb +33 -0
  18. data/gherkin.gemspec +177 -0
  19. data/java/.gitignore +2 -0
  20. data/java/Gherkin.iml +24 -0
  21. data/java/build.xml +13 -0
  22. data/java/src/gherkin/FixJava.java +34 -0
  23. data/java/src/gherkin/Lexer.java +5 -0
  24. data/java/src/gherkin/LexingError.java +7 -0
  25. data/java/src/gherkin/Listener.java +27 -0
  26. data/java/src/gherkin/ParseError.java +22 -0
  27. data/java/src/gherkin/Parser.java +185 -0
  28. data/java/src/gherkin/lexer/.gitignore +1 -0
  29. data/java/src/gherkin/parser/StateMachineReader.java +62 -0
  30. data/lib/.gitignore +2 -0
  31. data/lib/gherkin.rb +2 -0
  32. data/lib/gherkin/c_lexer.rb +10 -0
  33. data/lib/gherkin/i18n.yml +535 -0
  34. data/lib/gherkin/i18n_lexer.rb +29 -0
  35. data/lib/gherkin/java_lexer.rb +10 -0
  36. data/lib/gherkin/lexer.rb +42 -0
  37. data/lib/gherkin/parser.rb +19 -0
  38. data/lib/gherkin/parser/meta.txt +4 -0
  39. data/lib/gherkin/parser/root.txt +9 -0
  40. data/lib/gherkin/parser/steps.txt +3 -0
  41. data/lib/gherkin/rb_lexer.rb +9 -0
  42. data/lib/gherkin/rb_lexer/.gitignore +1 -0
  43. data/lib/gherkin/rb_lexer/README.rdoc +8 -0
  44. data/lib/gherkin/rb_parser.rb +117 -0
  45. data/lib/gherkin/tools/pretty_printer.rb +77 -0
  46. data/ragel/i18n/.gitignore +1 -0
  47. data/ragel/lexer.c.rl.erb +385 -0
  48. data/ragel/lexer.java.rl.erb +198 -0
  49. data/ragel/lexer.rb.rl.erb +172 -0
  50. data/ragel/lexer_common.rl.erb +46 -0
  51. data/spec/gherkin/c_lexer_spec.rb +21 -0
  52. data/spec/gherkin/fixtures/complex.feature +43 -0
  53. data/spec/gherkin/fixtures/i18n_fr.feature +13 -0
  54. data/spec/gherkin/fixtures/i18n_no.feature +6 -0
  55. data/spec/gherkin/fixtures/i18n_zh-CN.feature +8 -0
  56. data/spec/gherkin/fixtures/simple.feature +3 -0
  57. data/spec/gherkin/fixtures/simple_with_comments.feature +7 -0
  58. data/spec/gherkin/fixtures/simple_with_tags.feature +11 -0
  59. data/spec/gherkin/i18n_spec.rb +57 -0
  60. data/spec/gherkin/java_lexer_spec.rb +20 -0
  61. data/spec/gherkin/parser_spec.rb +28 -0
  62. data/spec/gherkin/rb_lexer_spec.rb +18 -0
  63. data/spec/gherkin/sexp_recorder.rb +29 -0
  64. data/spec/gherkin/shared/lexer_spec.rb +420 -0
  65. data/spec/gherkin/shared/py_string_spec.rb +112 -0
  66. data/spec/gherkin/shared/table_spec.rb +97 -0
  67. data/spec/gherkin/shared/tags_spec.rb +50 -0
  68. data/spec/spec_helper.rb +53 -0
  69. data/tasks/bench.rake +176 -0
  70. data/tasks/bench/feature_builder.rb +49 -0
  71. data/tasks/bench/generated/.gitignore +1 -0
  72. data/tasks/bench/null_listener.rb +4 -0
  73. data/tasks/cucumber.rake +20 -0
  74. data/tasks/ext.rake +49 -0
  75. data/tasks/ragel.rake +94 -0
  76. data/tasks/rdoc.rake +12 -0
  77. data/tasks/rspec.rake +15 -0
  78. metadata +204 -0
@@ -0,0 +1,13 @@
1
+ Fonctionnalité: Addition
2
+ Plan du scénario: Addition de produits dérivés
3
+ Soit une calculatrice
4
+ Etant donné qu'on tape <a>
5
+ Et qu'on tape <b>
6
+ Lorsqu'on tape additionner
7
+ Alors le résultat doit être <somme>
8
+
9
+ Exemples:
10
+ | a | b | somme |
11
+ | 2 | 2 | 4 |
12
+ | 2 | 3 | 5 |
13
+
@@ -0,0 +1,6 @@
1
+ Egenskap: i18n support
2
+
3
+ Scenario: Parsing many languages
4
+ Gitt Gherkin supports many languages
5
+ Når Norwegian keywords are parsed
6
+ Så they should be recognized
@@ -0,0 +1,8 @@
1
+ 功能:加法
2
+
3
+ 场景: 两个数相加
4
+ 假如我已经在计算器里输入6
5
+ 而且我已经在计算器里输入7
6
+ 当我按相加按钮
7
+ 那么我应该在屏幕上看到的结果是13
8
+
@@ -0,0 +1,3 @@
1
+ Feature: Feature Text
2
+ Scenario: Reading a Scenario
3
+ Given there is a step
@@ -0,0 +1,7 @@
1
+ # Here is a comment
2
+ Feature: Feature Text
3
+ # Here is another # comment
4
+ Scenario: Reading a Scenario
5
+ # Here is a third comment
6
+ Given there is a step
7
+ # Here is a fourth comment
@@ -0,0 +1,11 @@
1
+ # FC
2
+ @ft
3
+ Feature: hi
4
+
5
+ @st1 @st2
6
+ Scenario: First
7
+ Given Pepper
8
+
9
+ @st3
10
+ @st4 @ST5 @#^%&ST6**!
11
+ Scenario: Second
@@ -0,0 +1,57 @@
1
+ #encoding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
3
+
4
+ module Gherkin
5
+ module Lexer
6
+ describe "i18n parsing" do
7
+ before do
8
+ @listener = Gherkin::SexpRecorder.new
9
+ end
10
+
11
+ def scan_file(lexer, file)
12
+ lexer.scan(File.new(File.dirname(__FILE__) + "/fixtures/" + file).read)
13
+ end
14
+
15
+ it "should recognize keywords in the language of the lexer" do
16
+ lexer = Gherkin::Lexer['no'].new(@listener)
17
+ scan_file(lexer, "i18n_no.feature")
18
+ @listener.to_sexp.should == [
19
+ [:feature, "Egenskap", "i18n support", 1],
20
+ [:scenario, "Scenario", "Parsing many languages", 3],
21
+ [:step, "Gitt", "Gherkin supports many languages", 4],
22
+ [:step, "Når", "Norwegian keywords are parsed", 5],
23
+ [:step, "Så", "they should be recognized", 6]
24
+ ]
25
+ end
26
+
27
+ it "should parse languages without a space after keywords" do
28
+ lexer = Gherkin::Lexer['zh-CN'].new(@listener)
29
+ scan_file(lexer, "i18n_zh-CN.feature")
30
+ @listener.to_sexp.should == [
31
+ [:feature, "功能", "加法", 1],
32
+ [:scenario, "场景", "两个数相加", 3],
33
+ [:step, "假如", "我已经在计算器里输入6", 4],
34
+ [:step, "而且", "我已经在计算器里输入7", 5],
35
+ [:step, "当", "我按相加按钮", 6],
36
+ [:step, "那么", "我应该在屏幕上看到的结果是13", 7]
37
+ ]
38
+ end
39
+
40
+ it "should parse languages with spaces after some keywords but not others" do
41
+ lexer = Gherkin::Lexer['fr'].new(@listener)
42
+ scan_file(lexer, "i18n_fr.feature")
43
+ @listener.to_sexp.should == [
44
+ [:feature, "Fonctionnalité", "Addition", 1],
45
+ [:scenario_outline, "Plan du scénario", "Addition de produits dérivés", 2],
46
+ [:step, "Soit", "une calculatrice", 3],
47
+ [:step, "Etant donné", "qu'on tape <a>", 4],
48
+ [:step, "Et", "qu'on tape <b>", 5],
49
+ [:step, "Lorsqu'", "on tape additionner", 6],
50
+ [:step, "Alors", "le résultat doit être <somme>", 7],
51
+ [:examples, "Exemples", "", 9],
52
+ [:table, [["a","b","somme"],["2","2","4"],["2","3","5"]], 10]
53
+ ]
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,20 @@
1
+ #encoding: utf-8
2
+ if defined?(JRUBY_VERSION)
3
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
4
+
5
+ module Gherkin
6
+ module JavaLexer
7
+ describe "Java Lexer" do
8
+ before do
9
+ @listener = Gherkin::SexpRecorder.new
10
+ @lexer = Gherkin::Lexer.java['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 tables"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'gherkin/parser'
3
+
4
+ module Gherkin
5
+ describe Parser do
6
+ before do
7
+ @listener = mock('listener')
8
+ @parser = Parser.new(@listener, true)
9
+ end
10
+
11
+ it "should delegate events to the listener" do
12
+ @listener.should_receive(:comment).with("# Content", 1)
13
+ @parser.comment("# Content", 1)
14
+ end
15
+
16
+ it "should raise helpful error messages by default" do
17
+ lambda {
18
+ @parser.scenario("Scenario", "My pet scenario", 12)
19
+ }.should raise_error(/Parse error on line 12\. Found scenario when expecting one of: comment, feature, tag\. \(Current state: root\)\.$/)
20
+ end
21
+
22
+ it "should delegate an error message when raise on error is false" do
23
+ @listener.should_receive(:syntax_error).with(sym(:root), sym(:background), a([:comment, :feature, :tag]), 1)
24
+ @parser = Parser.new(@listener, false)
25
+ @parser.background("Background", "Content", 1)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,18 @@
1
+ #encoding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
3
+
4
+ module Gherkin
5
+ module Lexer
6
+ describe "Ruby Lexer" do
7
+ before do
8
+ @listener = Gherkin::SexpRecorder.new
9
+ @lexer = Gherkin::Lexer.rb['en'].new(@listener)
10
+ end
11
+
12
+ it_should_behave_like "a Gherkin lexer"
13
+ it_should_behave_like "a Gherkin lexer lexing tags"
14
+ it_should_behave_like "a Gherkin lexer lexing py_strings"
15
+ it_should_behave_like "a Gherkin lexer lexing tables"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,29 @@
1
+ module Gherkin
2
+ class SexpRecorder
3
+ def initialize
4
+ @sexps = []
5
+ end
6
+
7
+ def method_missing(m, *args)
8
+ args[0] = args[0].map{|row| row.map{|cell| cell}} if m == :table
9
+ @sexps << [m] + args
10
+ end
11
+
12
+ def to_sexp
13
+ @sexps
14
+ end
15
+
16
+ # Useful in IRB
17
+ def reset!
18
+ @sexps = []
19
+ end
20
+
21
+ def errors
22
+ @sexps.select { |sexp| sexp[0] == :syntax_error }
23
+ end
24
+
25
+ def line(number)
26
+ @sexps.find { |sexp| sexp.last == number }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,420 @@
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 == [[:comment, "# My comment", 1]]
12
+ end
13
+
14
+ it "should parse a multiline comment" do
15
+ @lexer.scan("# Hello\n\n# World\n")
16
+ @listener.to_sexp.should == [
17
+ [:comment, "# Hello", 1],
18
+ [:comment, "# World", 3]
19
+ ]
20
+ end
21
+
22
+ it "should not consume comments as part of a multiline name" do
23
+ @lexer.scan("Scenario: test\n#hello\n Scenario: another")
24
+ @listener.to_sexp.should == [
25
+ [:scenario, "Scenario", "test", 1],
26
+ [:comment, "#hello", 2],
27
+ [:scenario, "Scenario", "another", 3]
28
+ ]
29
+ end
30
+
31
+ it "should allow empty comment lines" do
32
+ @lexer.scan("#\n # A comment\n #\n")
33
+ @listener.to_sexp.should == [
34
+ [:comment, "#", 1],
35
+ [:comment, "# A comment", 2],
36
+ [:comment, "#", 3]
37
+ ]
38
+ end
39
+
40
+ it "should not allow comments within the Feature description" do
41
+ lambda {
42
+ @lexer.scan("Feature: something\nAs a something\n# Comment\nI want something")
43
+ }.should raise_error(/Lexing error on line 4/)
44
+ end
45
+ end
46
+
47
+ describe "Tags" do
48
+ it "should not take the tags as part of a multiline name feature element" do
49
+ @lexer.scan("Feature: hi\n Scenario: test\n\n@hello\n Scenario: another")
50
+ @listener.to_sexp.should == [
51
+ [:feature, "Feature", "hi", 1],
52
+ [:scenario, "Scenario", "test", 2],
53
+ [:tag, "hello", 4],
54
+ [:scenario, "Scenario", "another", 5]
55
+ ]
56
+ end
57
+ end
58
+
59
+ describe "Background" do
60
+ it "should allow an empty background description" do
61
+ @lexer.scan("Background:\nGiven I am a step\n")
62
+ @listener.to_sexp.should == [
63
+ [:background, "Background", "", 1],
64
+ [:step, "Given", "I am a step", 2]
65
+ ]
66
+ end
67
+
68
+ it "should allow multiline names ending at eof" do
69
+ @lexer.scan("Background: I have several\n Lines to look at\n None starting with Given")
70
+ @listener.to_sexp.should == [
71
+ [:background, "Background", "I have several\nLines to look at\nNone starting with Given", 1]
72
+ ]
73
+ end
74
+
75
+ it "should allow multiline names" do
76
+ @lexer.scan(%{Feature: Hi
77
+ Background: It is my ambition to say
78
+ in ten sentences
79
+ what others say
80
+ in a whole book.
81
+ Given I am a step})
82
+ @listener.to_sexp.should == [
83
+ [:feature, "Feature", "Hi", 1],
84
+ [:background, "Background", "It is my ambition to say\nin ten sentences\nwhat others say\nin a whole book.",2],
85
+ [:step, "Given", "I am a step", 6]
86
+ ]
87
+ end
88
+ end
89
+
90
+ describe "Scenarios" do
91
+ it "should be parsed" do
92
+ @lexer.scan("Scenario: Hello\n")
93
+ @listener.to_sexp.should == [
94
+ [:scenario, "Scenario", "Hello", 1]
95
+ ]
96
+ end
97
+
98
+ it "should allow whitespace lines after the Scenario line" do
99
+ @lexer.scan(%{Scenario: bar
100
+
101
+ Given baz
102
+ })
103
+ @listener.to_sexp.should == [
104
+ [:scenario, "Scenario", "bar", 1],
105
+ [:step, "Given", "baz", 3]
106
+ ]
107
+ end
108
+
109
+ it "should allow multiline names" do
110
+ @lexer.scan(%{Scenario: It is my ambition to say
111
+ in ten sentences
112
+ what others say
113
+ in a whole book.
114
+ Given I am a step
115
+ })
116
+ @listener.to_sexp.should == [
117
+ [:scenario, "Scenario", "It is my ambition to say\nin ten sentences\nwhat others say\nin a whole book.", 1],
118
+ [:step, "Given", "I am a step", 5]
119
+ ]
120
+ end
121
+
122
+ it "should allow multiline names ending at eof" do
123
+ @lexer.scan("Scenario: I have several\n Lines to look at\n None starting with Given")
124
+ @listener.to_sexp.should == [
125
+ [:scenario, "Scenario", "I have several\nLines to look at\nNone starting with Given", 1]
126
+ ]
127
+ end
128
+
129
+ it "should ignore gherkin keywords embedded in other words" do
130
+ @lexer.scan(%{Scenario: I have a Button
131
+ Buttons are great
132
+ Given I have some
133
+ But I might not because I am a Charles Dickens character
134
+ })
135
+ @listener.to_sexp.should == [
136
+ [:scenario, "Scenario", "I have a Button\nButtons are great", 1],
137
+ [:step, "Given", "I have some", 3],
138
+ [:step, "But", "I might not because I am a Charles Dickens character", 4]
139
+ ]
140
+ end
141
+
142
+ it "should allow step names in Scenario descriptions" do
143
+ @lexer.scan(%{Scenario: When I have when in scenario
144
+ I should be fine
145
+ Given I am a step
146
+ })
147
+ @listener.to_sexp.should == [
148
+ [:scenario, "Scenario", "When I have when in scenario\nI should be fine", 1],
149
+ [:step, "Given", "I am a step", 3]
150
+ ]
151
+ end
152
+ end
153
+
154
+ describe "Scenario Outlines" do
155
+ it "should be parsed" do
156
+ @lexer.scan(%{Scenario Outline: Hello
157
+ Given a <what> cucumber
158
+ Examples:
159
+ |what|
160
+ |green|
161
+ })
162
+ @listener.to_sexp.should == [
163
+ [:scenario_outline, "Scenario Outline", "Hello", 1],
164
+ [:step, "Given", "a <what> cucumber", 2],
165
+ [:examples, "Examples", "", 3],
166
+ [:table, [["what"],["green"]], 4]
167
+ ]
168
+ end
169
+
170
+ it "should parse with no steps or examples" do
171
+ @lexer.scan(%{Scenario Outline: Hello
172
+
173
+ Scenario: My Scenario
174
+ })
175
+ @listener.to_sexp.should == [
176
+ [:scenario_outline, "Scenario Outline", "Hello", 1],
177
+ [:scenario, "Scenario", "My Scenario", 3]
178
+ ]
179
+ end
180
+
181
+ it "should allow multiline names" do
182
+ @lexer.scan(%{Scenario Outline: It is my ambition to say
183
+ in ten sentences
184
+ what others say
185
+ in a whole book.
186
+ Given I am a step
187
+
188
+ })
189
+ @listener.to_sexp.should == [
190
+ [:scenario_outline, "Scenario Outline", "It is my ambition to say\nin ten sentences\nwhat others say\nin a whole book.", 1],
191
+ [:step, "Given", "I am a step", 5]
192
+ ]
193
+ end
194
+ end
195
+
196
+ describe "Examples" do
197
+ it "should be parsed" do
198
+ @lexer.scan(%{Examples:
199
+ |x|y|
200
+ |5|6|
201
+ })
202
+ @listener.to_sexp.should == [
203
+ [:examples, "Examples", "", 1],
204
+ [:table, [["x","y"],["5","6"]], 2]
205
+ ]
206
+ end
207
+
208
+ it "should parse multiline example names" do
209
+ @lexer.scan(%{Examples: I'm a multiline name
210
+ and I'm ok
211
+ f'real
212
+ |x|
213
+ |5|
214
+ })
215
+ @listener.to_sexp.should == [
216
+ [:examples, "Examples", "I'm a multiline name\nand I'm ok\nf'real", 1],
217
+ [:table, [["x"],["5"]], 4]
218
+ ]
219
+ end
220
+ end
221
+
222
+ describe "Steps" do
223
+ it "should parse steps with inline table" do
224
+ @lexer.scan(%{Given I have a table
225
+ |a|b|
226
+ })
227
+ @listener.to_sexp.should == [
228
+ [:step, "Given", "I have a table", 1],
229
+ [:table, [['a','b']], 2]
230
+ ]
231
+ end
232
+
233
+ it "should parse steps with inline py_string" do
234
+ @lexer.scan("Given I have a string\n\"\"\"\nhello\nworld\n\"\"\"")
235
+ @listener.to_sexp.should == [
236
+ [:step, "Given", "I have a string", 1],
237
+ [:py_string, "hello\nworld", 2]
238
+ ]
239
+ end
240
+ end
241
+
242
+ describe "A single feature, single scenario, single step" do
243
+ it "should find the feature, scenario, and step" do
244
+ @lexer.scan("Feature: Feature Text\n Scenario: Reading a Scenario\n Given there is a step\n")
245
+ @listener.to_sexp.should == [
246
+ [:feature, "Feature", "Feature Text", 1],
247
+ [:scenario, "Scenario", "Reading a Scenario", 2],
248
+ [:step, "Given", "there is a step", 3]
249
+ ]
250
+ end
251
+ end
252
+
253
+ describe "A feature ending in whitespace" do
254
+ it "should not raise an error when whitespace follows the Feature, Scenario, and Steps" do
255
+ @lexer.scan("Feature: Feature Text\n Scenario: Reading a Scenario\n Given there is a step\n ")
256
+ @listener.to_sexp.should == [
257
+ [:feature, "Feature", "Feature Text", 1],
258
+ [:scenario, "Scenario", "Reading a Scenario", 2],
259
+ [:step, "Given", "there is a step", 3]
260
+ ]
261
+ end
262
+ end
263
+
264
+ describe "A single feature, single scenario, three steps" do
265
+
266
+ it "should find the feature, scenario, and three steps" do
267
+ @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")
268
+ @listener.to_sexp.should == [
269
+ [:feature, "Feature", "Feature Text", 1],
270
+ [:scenario, "Scenario", "Reading a Scenario", 2],
271
+ [:step, "Given", "there is a step", 3],
272
+ [:step, "And", "another step", 4],
273
+ [:step, "And", "a third step", 5]
274
+ ]
275
+ end
276
+ end
277
+
278
+ describe "A single feature with no scenario" do
279
+ it "should find the feature" do
280
+ @lexer.scan("Feature: Feature Text\n")
281
+ @listener.to_sexp.should == [[:feature, "Feature", "Feature Text", 1]]
282
+ end
283
+
284
+ it "should parse a one line feature with no newline" do
285
+ @lexer.scan("Feature: hi")
286
+ @listener.to_sexp.should == [[:feature, "Feature", "hi", 1]]
287
+ end
288
+ end
289
+
290
+ describe "A multi-line feature with no scenario" do
291
+ it "should find the feature" do
292
+ @lexer.scan("Feature: Feature Text\n And some more text")
293
+ @listener.to_sexp.should == [[:feature, "Feature", "Feature Text\nAnd some more text", 1]]
294
+ end
295
+ end
296
+
297
+ describe "A feature with a scenario but no steps" do
298
+ it "should find the feature and scenario" do
299
+ @lexer.scan("Feature: Feature Text\nScenario: Reading a Scenario\n")
300
+ @listener.to_sexp.should == [
301
+ [:feature, "Feature", "Feature Text", 1],
302
+ [:scenario, "Scenario", "Reading a Scenario", 2]
303
+ ]
304
+ end
305
+ end
306
+
307
+ describe "A feature with two scenarios" do
308
+ it "should find the feature and two scenarios" do
309
+ @lexer.scan("Feature: Feature Text\nScenario: Reading a Scenario\n Given a step\n\nScenario: A second scenario\n Given another step\n")
310
+ @listener.to_sexp.should == [
311
+ [:feature, "Feature", "Feature Text", 1],
312
+ [:scenario, "Scenario", "Reading a Scenario", 2],
313
+ [:step, "Given", "a step", 3],
314
+ [:scenario, "Scenario", "A second scenario", 5],
315
+ [:step, "Given", "another step", 6]
316
+ ]
317
+ end
318
+
319
+ it "should find the feature and two scenarios without indentation" do
320
+ @lexer.scan("Feature: Feature Text\nScenario: Reading a Scenario\nGiven a step\nScenario: A second scenario\nGiven another step\n")
321
+ @listener.to_sexp.should == [
322
+ [:feature, "Feature", "Feature Text", 1],
323
+ [:scenario, "Scenario", "Reading a Scenario", 2],
324
+ [:step, "Given", "a step", 3],
325
+ [:scenario, "Scenario", "A second scenario", 4],
326
+ [:step, "Given", "another step", 5]
327
+ ]
328
+ end
329
+ end
330
+
331
+ describe "A simple feature with comments" do
332
+ it "should find the feature, scenarios, steps, and comments in the proper order" do
333
+ scan_file("simple_with_comments.feature")
334
+ @listener.to_sexp.should == [
335
+ [:comment, "# Here is a comment", 1],
336
+ [:feature, "Feature", "Feature Text", 2],
337
+ [:comment, "# Here is another # comment", 3],
338
+ [:scenario, "Scenario", "Reading a Scenario", 4],
339
+ [:comment, "# Here is a third comment", 5],
340
+ [:step, "Given", "there is a step", 6],
341
+ [:comment, "# Here is a fourth comment", 7]
342
+ ]
343
+ end
344
+ end
345
+
346
+ describe "A feature with tags everywhere" do
347
+ it "should find the feature, scenario, step, and tags in the proper order" do
348
+ scan_file("simple_with_tags.feature")
349
+ @listener.to_sexp.should == [
350
+ [:comment, "# FC", 1],
351
+ [:tag, "ft",2],
352
+ [:feature, "Feature", "hi", 3],
353
+ [:tag, "st1", 5],
354
+ [:tag, "st2", 5],
355
+ [:scenario, "Scenario", "First", 6],
356
+ [:step, "Given", "Pepper", 7],
357
+ [:tag, "st3", 9],
358
+ [:tag, "st4", 10],
359
+ [:tag, "ST5", 10],
360
+ [:tag, "#^%&ST6**!", 10],
361
+ [:scenario, "Scenario", "Second", 11]
362
+ ]
363
+ end
364
+ end
365
+
366
+ describe "A complex feature with tags, comments, multiple scenarios, and multiple steps and tables" do
367
+ it "should find things in the right order" do
368
+ scan_file("complex.feature")
369
+ @listener.to_sexp.should == [
370
+ [:comment, "#Comment on line 1", 1],
371
+ [:tag, "tag1", 2],
372
+ [:tag, "tag2", 2],
373
+ [:comment, "#Comment on line 3", 3],
374
+ [:feature, "Feature", "Feature Text\nIn order to test multiline forms\nAs a ragel writer\nI need to check for complex combinations", 4],
375
+ [:comment, "#Comment on line 9", 9],
376
+ [:comment, "#Comment on line 11", 11],
377
+ [:background, "Background", "", 13],
378
+ [:step, "Given", "this is a background step", 14],
379
+ [:step, "And", "this is another one", 15],
380
+ [:tag, "tag3", 17],
381
+ [:tag, "tag4", 17],
382
+ [:scenario, "Scenario", "Reading a Scenario", 18],
383
+ [:step, "Given", "there is a step", 19],
384
+ [:step, "But", "not another step", 20],
385
+ [:tag, "tag3", 22],
386
+ [:scenario, "Scenario", "Reading a second scenario", 23],
387
+ [:comment, "#Comment on line 24", 24],
388
+ [:step, "Given", "a third step with a table", 25],
389
+ [:table, [['a','b'],['c','d'],['e','f']], 26],
390
+ [:step, "And", "I am still testing things", 29],
391
+ [:table, [['g','h'],['e','r'],['k','i'],['n','']], 30],
392
+ [:step, "And", "I am done testing these tables", 34],
393
+ [:comment, "#Comment on line 29", 35],
394
+ [:step, "Then", "I am happy", 36],
395
+ [:scenario, "Scenario", "Hammerzeit", 38],
396
+ [:step, "Given", "All work and no play", 39],
397
+ [:py_string, "Makes Homer something something", 40],
398
+ [:step, "Then", "crazy", 43]
399
+ ]
400
+ end
401
+ end
402
+
403
+ describe "errors" do
404
+ it "should raise a Lexing error if an unparseable token is found" do
405
+ ["Some text\nFeature: Hi",
406
+ "Feature: Hi\nBackground:\nGiven something\nScenario A scenario",
407
+ "Scenario: My scenario\nGiven foo\nAand bar\nScenario: another one\nGiven blah"].each do |text|
408
+ lambda { @lexer.scan(text) }.should raise_error(/Lexing error on line/)
409
+ end
410
+ end
411
+
412
+ it "should include the line number and context of the error" do
413
+ lambda {
414
+ @lexer.scan("Feature: hello\nScenario: My scenario\nGiven foo\nAand blah\nHmmm wrong\nThen something something")
415
+ }.should raise_error(/Lexing error on line 4/)
416
+ end
417
+ end
418
+ end
419
+ end
420
+ end