gherkin 2.2.5-x86-mingw32

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