bryanl-gherkin 2.11.1.1

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