gherkin 0.0.4-i386-mingw32

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.
Files changed (79) hide show
  1. data/.gitignore +7 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +66 -0
  4. data/Rakefile +49 -0
  5. data/VERSION.yml +4 -0
  6. data/bin/gherkin +10 -0
  7. data/cucumber.yml +3 -0
  8. data/features/feature_parser.feature +206 -0
  9. data/features/native_lexer.feature +19 -0
  10. data/features/parser_with_native_lexer.feature +205 -0
  11. data/features/pretty_printer.feature +14 -0
  12. data/features/step_definitions/gherkin_steps.rb +34 -0
  13. data/features/step_definitions/pretty_printer_steps.rb +56 -0
  14. data/features/steps_parser.feature +46 -0
  15. data/features/support/env.rb +33 -0
  16. data/gherkin.gemspec +180 -0
  17. data/java/.gitignore +2 -0
  18. data/java/Gherkin.iml +24 -0
  19. data/java/build.xml +13 -0
  20. data/java/src/gherkin/FixJava.java +34 -0
  21. data/java/src/gherkin/Lexer.java +5 -0
  22. data/java/src/gherkin/LexingError.java +7 -0
  23. data/java/src/gherkin/Listener.java +27 -0
  24. data/java/src/gherkin/ParseError.java +22 -0
  25. data/java/src/gherkin/Parser.java +185 -0
  26. data/java/src/gherkin/lexer/.gitignore +1 -0
  27. data/java/src/gherkin/parser/StateMachineReader.java +62 -0
  28. data/lib/.gitignore +4 -0
  29. data/lib/gherkin.rb +2 -0
  30. data/lib/gherkin/c_lexer.rb +10 -0
  31. data/lib/gherkin/core_ext/array.rb +5 -0
  32. data/lib/gherkin/i18n.yml +535 -0
  33. data/lib/gherkin/i18n_lexer.rb +29 -0
  34. data/lib/gherkin/java_lexer.rb +10 -0
  35. data/lib/gherkin/lexer.rb +43 -0
  36. data/lib/gherkin/parser.rb +19 -0
  37. data/lib/gherkin/parser/meta.txt +4 -0
  38. data/lib/gherkin/parser/root.txt +9 -0
  39. data/lib/gherkin/parser/steps.txt +3 -0
  40. data/lib/gherkin/rb_lexer.rb +10 -0
  41. data/lib/gherkin/rb_lexer/.gitignore +1 -0
  42. data/lib/gherkin/rb_lexer/README.rdoc +8 -0
  43. data/lib/gherkin/rb_parser.rb +117 -0
  44. data/lib/gherkin/tools/pretty_printer.rb +83 -0
  45. data/nativegems.sh +5 -0
  46. data/ragel/i18n/.gitignore +1 -0
  47. data/ragel/lexer.c.rl.erb +401 -0
  48. data/ragel/lexer.java.rl.erb +200 -0
  49. data/ragel/lexer.rb.rl.erb +171 -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/1.feature +8 -0
  53. data/spec/gherkin/fixtures/complex.feature +43 -0
  54. data/spec/gherkin/fixtures/i18n_fr.feature +13 -0
  55. data/spec/gherkin/fixtures/i18n_no.feature +6 -0
  56. data/spec/gherkin/fixtures/i18n_zh-CN.feature +8 -0
  57. data/spec/gherkin/fixtures/simple.feature +3 -0
  58. data/spec/gherkin/fixtures/simple_with_comments.feature +7 -0
  59. data/spec/gherkin/fixtures/simple_with_tags.feature +11 -0
  60. data/spec/gherkin/i18n_spec.rb +57 -0
  61. data/spec/gherkin/java_lexer_spec.rb +20 -0
  62. data/spec/gherkin/parser_spec.rb +28 -0
  63. data/spec/gherkin/rb_lexer_spec.rb +18 -0
  64. data/spec/gherkin/sexp_recorder.rb +29 -0
  65. data/spec/gherkin/shared/lexer_spec.rb +433 -0
  66. data/spec/gherkin/shared/py_string_spec.rb +124 -0
  67. data/spec/gherkin/shared/table_spec.rb +97 -0
  68. data/spec/gherkin/shared/tags_spec.rb +50 -0
  69. data/spec/spec_helper.rb +53 -0
  70. data/tasks/bench.rake +193 -0
  71. data/tasks/bench/feature_builder.rb +49 -0
  72. data/tasks/bench/generated/.gitignore +1 -0
  73. data/tasks/bench/null_listener.rb +4 -0
  74. data/tasks/compile.rake +70 -0
  75. data/tasks/cucumber.rake +20 -0
  76. data/tasks/ragel_task.rb +83 -0
  77. data/tasks/rdoc.rake +12 -0
  78. data/tasks/rspec.rake +15 -0
  79. metadata +214 -0
@@ -0,0 +1,43 @@
1
+ #Comment on line 1
2
+ #Comment on line 2
3
+ @tag1 @tag2
4
+ Feature: Feature Text
5
+ In order to test multiline forms
6
+ As a ragel writer
7
+ I need to check for complex combinations
8
+
9
+ #Comment on line 9
10
+
11
+ #Comment on line 11
12
+
13
+ Background:
14
+ Given this is a background step
15
+ And this is another one
16
+
17
+ @tag3 @tag4
18
+ Scenario: Reading a Scenario
19
+ Given there is a step
20
+ But not another step
21
+
22
+ @tag3
23
+ Scenario: Reading a second scenario
24
+ #Comment on line 24
25
+ Given a third step with a table
26
+ |a|b|
27
+ |c|d|
28
+ |e|f|
29
+ And I am still testing things
30
+ |g|h|
31
+ |e|r|
32
+ |k|i|
33
+ |n||
34
+ And I am done testing these tables
35
+ #Comment on line 29
36
+ Then I am happy
37
+
38
+ Scenario: Hammerzeit
39
+ Given All work and no play
40
+ """
41
+ Makes Homer something something
42
+ """
43
+ Then crazy
@@ -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,433 @@
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 "Comment or tag between Feature elements where previous narrative starts with same letter as a keyword" do
367
+ it "should lex this feature properly" do
368
+ scan_file("1.feature")
369
+ @listener.to_sexp.should == [
370
+ [:feature, "Feature", "Logging in\nSo that I can be myself", 1],
371
+ [:comment, "# Comment", 3],
372
+ [:scenario, "Scenario", "Anonymous user can get a login form.\nScenery here", 4],
373
+ [:tag, "tag", 7],
374
+ [:scenario, "Scenario", "Another one", 8]
375
+ ]
376
+ end
377
+ end
378
+
379
+ describe "A complex feature with tags, comments, multiple scenarios, and multiple steps and tables" do
380
+ it "should find things in the right order" do
381
+ scan_file("complex.feature")
382
+ @listener.to_sexp.should == [
383
+ [:comment, "#Comment on line 1", 1],
384
+ [:comment, "#Comment on line 2", 2],
385
+ [:tag, "tag1", 3],
386
+ [:tag, "tag2", 3],
387
+ [:feature, "Feature", "Feature Text\nIn order to test multiline forms\nAs a ragel writer\nI need to check for complex combinations", 4],
388
+ [:comment, "#Comment on line 9", 9],
389
+ [:comment, "#Comment on line 11", 11],
390
+ [:background, "Background", "", 13],
391
+ [:step, "Given", "this is a background step", 14],
392
+ [:step, "And", "this is another one", 15],
393
+ [:tag, "tag3", 17],
394
+ [:tag, "tag4", 17],
395
+ [:scenario, "Scenario", "Reading a Scenario", 18],
396
+ [:step, "Given", "there is a step", 19],
397
+ [:step, "But", "not another step", 20],
398
+ [:tag, "tag3", 22],
399
+ [:scenario, "Scenario", "Reading a second scenario", 23],
400
+ [:comment, "#Comment on line 24", 24],
401
+ [:step, "Given", "a third step with a table", 25],
402
+ [:table, [['a','b'],['c','d'],['e','f']], 26],
403
+ [:step, "And", "I am still testing things", 29],
404
+ [:table, [['g','h'],['e','r'],['k','i'],['n','']], 30],
405
+ [:step, "And", "I am done testing these tables", 34],
406
+ [:comment, "#Comment on line 29", 35],
407
+ [:step, "Then", "I am happy", 36],
408
+ [:scenario, "Scenario", "Hammerzeit", 38],
409
+ [:step, "Given", "All work and no play", 39],
410
+ [:py_string, "Makes Homer something something", 40],
411
+ [:step, "Then", "crazy", 43]
412
+ ]
413
+ end
414
+ end
415
+
416
+ describe "errors" do
417
+ it "should raise a Lexing error if an unparseable token is found" do
418
+ ["Some text\nFeature: Hi",
419
+ "Feature: Hi\nBackground:\nGiven something\nScenario A scenario",
420
+ "Scenario: My scenario\nGiven foo\nAand bar\nScenario: another one\nGiven blah"].each do |text|
421
+ lambda { @lexer.scan(text) }.should raise_error(/Lexing error on line/)
422
+ end
423
+ end
424
+
425
+ it "should include the line number and context of the error" do
426
+ lambda {
427
+ @lexer.scan("Feature: hello\nScenario: My scenario\nGiven foo\nAand blah\nHmmm wrong\nThen something something")
428
+ }.should raise_error(/Lexing error on line 4/)
429
+ end
430
+ end
431
+ end
432
+ end
433
+ end