aslakhellesoy-gherkin 0.0.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.
@@ -0,0 +1,11 @@
1
+ require 'gherkin/parser/table'
2
+ require 'gherkin/parser/misc'
3
+
4
+ module Gherkin
5
+ module Parser
6
+ def self.[](lang)
7
+ require "gherkin/parser/feature_#{lang}"
8
+ Feature
9
+ end
10
+ end
11
+ end
File without changes
@@ -0,0 +1,117 @@
1
+ module Gherkin
2
+ module Parser
3
+ class Feature
4
+ %%{
5
+ machine feature;
6
+
7
+ action begin_content {
8
+ @content_start = p
9
+ }
10
+
11
+ action store_feature_content {
12
+ if !@backup or (p==eof)
13
+ con = data[@content_start...p].pack("c*")
14
+ else
15
+ con = data[@content_start...@backup].pack("c*")
16
+ end
17
+ con.strip!
18
+ @listener.feature(@keyword, con, @current_line)
19
+ if @backup
20
+ p = @backup-1
21
+ end
22
+ @backup = nil
23
+ }
24
+
25
+ action store_background_content {
26
+ if !@backup or (p==eof)
27
+ con = data[@content_start...p].pack("c*")
28
+ else
29
+ con = data[@content_start...@backup].pack("c*")
30
+ end
31
+ @listener.background(@keyword, multiline_strip(con), @current_line)
32
+ if @backup
33
+ p = @backup-1
34
+ end
35
+ @backup = nil
36
+ }
37
+
38
+ action store_scenario_content {
39
+ if !@backup or (p==eof)
40
+ con = data[@content_start...p].pack("c*")
41
+ else
42
+ con = data[@content_start...@backup].pack("c*")
43
+ end
44
+ @listener.scenario(@keyword, multiline_strip(con), @current_line)
45
+ if @backup
46
+ p = @backup-1
47
+ end
48
+ @backup = nil
49
+ }
50
+
51
+ action store_step_content {
52
+ con = data[@content_start...p].pack("c*")
53
+ con.strip!
54
+ @listener.step(@keyword, con, @current_line)
55
+ }
56
+
57
+ action store_comment_content {
58
+ con = data[@content_start...p].pack("c*")
59
+ con.strip!
60
+ @listener.comment(con, @line_number)
61
+ }
62
+
63
+ action store_tag_content {
64
+ con = data[@content_start...p].pack("c*")
65
+ con.strip!
66
+ @listener.tag(con, @current_line)
67
+ }
68
+
69
+ action inc_line_number {
70
+ @line_number += 1
71
+ }
72
+
73
+ action current_line {
74
+ @current_line = @line_number
75
+ }
76
+
77
+ action start_keyword {
78
+ @keyword_start ||= p
79
+ }
80
+
81
+ action end_keyword {
82
+ @keyword = data[@keyword_start...p].pack("c*").sub(/:$/,'').strip
83
+ @keyword_start = nil
84
+ }
85
+
86
+ action backup {
87
+ @backup = p
88
+ }
89
+
90
+ action end_table {
91
+ table_to_parse = '|' + data[@content_start...p].pack("c*").strip
92
+ Gherkin::Parser::Table.new(@listener, @current_line).scan(table_to_parse)
93
+ p = p-1
94
+ }
95
+
96
+ include feature_common "feature_common.<%= lang %>.rl";
97
+ }%%
98
+
99
+ def initialize(listener)
100
+ @listener = listener
101
+ %% write data;
102
+ end
103
+
104
+ def scan(data)
105
+ data = data.unpack("c*") if data.is_a?(String)
106
+ @line_number = 1
107
+ eof = data.size
108
+ %% write init;
109
+ %% write exec;
110
+ end
111
+
112
+ def multiline_strip(text)
113
+ text.split("\n").map{|s| s.strip!}.join("\n")
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,37 @@
1
+ %%{
2
+ machine feature_common;
3
+
4
+ # Language specific
5
+ FEATURE = '<%= i18n['feature'] %>:' >start_keyword %end_keyword;
6
+ BACKGROUND = '<%= i18n['background'] %>:' >start_keyword %end_keyword;
7
+ SCENARIO = '<%= i18n['scenario'] %>:' >start_keyword %end_keyword;
8
+ STEP = ('<%= i18n['given'] %> ' | '<%= i18n['when'] %> ' | '<%= i18n['and'] %> ' | '<%= i18n['then'] %> ' | '<%= i18n['but'] %> ') >start_keyword %end_keyword;
9
+
10
+ EOL = ('\r'? '\n') @inc_line_number;
11
+ BAR = '|' >start_keyword %end_keyword;
12
+
13
+ # Terminators
14
+ EndFeatureHeading = EOL+ space* (BACKGROUND | SCENARIO | '@' | '#');
15
+ EndScenarioHeading = EOL+ space* ( SCENARIO | STEP | '@' | '#' );
16
+ EndBackgroundHeading = EOL+ space* ( SCENARIO | STEP | '@' | '#' );
17
+ StartTable = space* '|';
18
+ EndTable = EOL space* ^('|' | space);
19
+
20
+ FeatureHeading = space* FEATURE %begin_content %current_line ^EndFeatureHeading* %/store_feature_content :>> EndFeatureHeading >backup @store_feature_content;
21
+ BackgroundHeading = space* BACKGROUND %begin_content %current_line ^EndBackgroundHeading* %/store_background_content :>> EndBackgroundHeading >backup @store_background_content;
22
+ ScenarioHeading = space* SCENARIO %begin_content %current_line ^EndScenarioHeading* %/store_scenario_content :>> EndScenarioHeading >backup @store_scenario_content;
23
+
24
+ Step = space* STEP %begin_content %current_line ^EOL+ %store_step_content %/store_step_content EOL+;
25
+ Comment = space* '#' >begin_content ^EOL+ %store_comment_content %/store_comment_content EOL+;
26
+ Tag = ( '@' [^@\r\n\t ]+ >begin_content ) %store_tag_content;
27
+ Tags = space* (Tag @current_line space*)+ EOL+;
28
+ Table = StartTable %begin_content %current_line any+ %/end_table :>> EndTable >backup @end_table;
29
+
30
+ MultilineStep = Step Table?;
31
+ Scenario = ScenarioHeading (Comment | MultilineStep)*;
32
+ Background = BackgroundHeading (Comment | MultilineStep)*;
33
+
34
+ Feature = (Tags | Comment)* FeatureHeading (Tags | Comment)* Background? ((Tags | Comment)* Scenario)*;
35
+
36
+ main := Feature;
37
+ }%%
data/ragel/misc.c.rl ADDED
@@ -0,0 +1,4 @@
1
+ int main() {
2
+ printf("hello, world");
3
+ return 0;
4
+ }
data/ragel/misc.rb.rl ADDED
@@ -0,0 +1,52 @@
1
+ module Gherkin
2
+ module Parser
3
+ class Misc
4
+ %%{
5
+ machine misc;
6
+
7
+ action start {
8
+ start_col = p - @last_newline
9
+ start = p + 4
10
+ }
11
+
12
+ action start_line {
13
+ line_col = p - @last_newline
14
+ line_start = p
15
+ }
16
+
17
+ action end_line {
18
+ line = data[line_start...p].pack("U*")
19
+ offset = line_col - start_col
20
+ @lines << (offset >= 0 ? line.gsub(/^/, ' ' * offset) : line)
21
+ }
22
+
23
+ newline = ('\r'? '\n') @{ @last_newline = p + 1} ;
24
+
25
+ PyStringStart = '"""' >start space* newline ;
26
+ PyStringEnd = '"""' ;
27
+ PyStringLine = space* ^newline* >start_line %end_line newline;
28
+
29
+ PyString = PyStringStart PyStringLine* PyStringEnd ;
30
+
31
+ main := space* PyString ;
32
+ }%%
33
+
34
+ def initialize(listener)
35
+ @listener = listener
36
+ @last_newline = 0
37
+ @lines = []
38
+ %% write data;
39
+ end
40
+
41
+ def scan(data)
42
+ data = data.unpack("U*") if data.is_a?(String)
43
+ eof = data.length
44
+
45
+ %% write init;
46
+ %% write exec;
47
+
48
+ @listener.pystring(@lines.join("\n"))
49
+ end
50
+ end
51
+ end
52
+ end
data/ragel/table.rb.rl ADDED
@@ -0,0 +1,54 @@
1
+ module Gherkin
2
+ module Parser
3
+ class Table
4
+ %%{
5
+ machine table;
6
+
7
+ action initialize {
8
+ current_row = []
9
+ }
10
+
11
+ action begin_content {
12
+ @content_start = p
13
+ }
14
+
15
+ action store_row {
16
+ @rows << current_row
17
+ }
18
+
19
+ action store_cell_content {
20
+ con = data[@content_start...p].pack("U*")
21
+ con.strip!
22
+ current_row << (con.empty? ? nil : con)
23
+ }
24
+
25
+ action no_content {
26
+ current_row << nil
27
+ }
28
+
29
+ include table_common "table_common.rl";
30
+ }%%
31
+
32
+ def initialize(listener,line=nil)
33
+ @line = line
34
+ @listener = listener
35
+ %% write data;
36
+ end
37
+
38
+ def scan(data)
39
+ @rows = []
40
+ data = data.unpack("U*") if data.is_a?(String)
41
+ eof = data.size
42
+
43
+ %% write init;
44
+ %% write exec;
45
+
46
+ if @line
47
+ @listener.table(@rows, @line)
48
+ else
49
+ @listener.table(@rows)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,12 @@
1
+ %%{
2
+ machine table_common;
3
+
4
+ EOL = '\r'? '\n';
5
+ BAR = '|';
6
+ cell_content = ^('|' | EOL);
7
+
8
+ cell = cell_content+ >begin_content BAR >store_cell_content | BAR >no_content;
9
+ table_row = space* BAR >initialize cell+ space* %/store_row space* :>> EOL;
10
+ table = table_row+ @store_row;
11
+ main := table;
12
+ }%%
@@ -0,0 +1,439 @@
1
+ #encoding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
3
+
4
+ module Gherkin
5
+ module Parser
6
+ describe "parsing" do
7
+ before do
8
+ @listener = Gherkin::SexpRecorder.new
9
+ @feature = Parser['en'].new(@listener)
10
+ end
11
+
12
+ def scan_file(file)
13
+ Feature.new(@listener).scan(File.new(File.dirname(__FILE__) + "/gherkin_parser/" + file).read)
14
+ end
15
+
16
+ describe "Comments" do
17
+ it "should parse a file with only a one line comment" do
18
+ @feature.scan("# My comment\nFeature: hi")
19
+ @listener.to_sexp.should == [
20
+ [:comment, "# My comment", 1],
21
+ [:feature, "Feature", "hi", 2],
22
+ ]
23
+ end
24
+
25
+ it "should parse a one line comment" do
26
+ @feature.scan("# My comment")
27
+ @listener.to_sexp.should == [[:comment, "# My comment", 1]]
28
+ end
29
+
30
+ it "should parse a file with only a multiline comment" do
31
+ @feature.scan("#Hello\n#World\nFeature: hi")
32
+ @listener.to_sexp.should == [
33
+ [:comment, "#Hello", 1],
34
+ [:comment, "#World", 2],
35
+ [:feature, "Feature", "hi", 3]
36
+ ]
37
+ end
38
+
39
+ it "should parse a file with only a multiline comment" do
40
+ pending("TODO: Do multiline comments need to be compressed into a single message?")
41
+ @feature.scan("# Hello\n# World\nFeature: hi")
42
+ @listener.to_sexp.should == [
43
+ [:comment, "# Hello\n# World", 1],
44
+ [:feature, "hi", 3]
45
+ ]
46
+ end
47
+
48
+ it "should parse a file with no comments" do
49
+ @feature.scan("Feature: hi\n")
50
+ @listener.to_sexp.should == [[:feature, "Feature", "hi", 1]]
51
+ end
52
+
53
+ it "should parse a file with only a multiline comment with newlines" do
54
+ pending("TODO: Do multiline comments need to be compressed into a single message?")
55
+ @feature.scan("# Hello\n\n# World\n")
56
+ @listener.to_sexp.should == [[:comment, "# Hello\n\n# World\n"]]
57
+ end
58
+
59
+ it "should not consume comments as part of a multiline name" do
60
+ @feature.scan("Feature: hi\n Scenario: test\n\n#hello\n Scenario: another")
61
+ @listener.to_sexp.should == [
62
+ [:feature, "Feature", "hi", 1],
63
+ [:scenario, "Scenario", "test", 2],
64
+ [:comment, "#hello", 4],
65
+ [:scenario, "Scenario", "another", 5]
66
+ ]
67
+ end
68
+ end
69
+
70
+ describe "Tags" do
71
+ it "should parse a file with tags on a feature" do
72
+ @feature.scan("# My comment\n@hello @world\nFeature: hi\n")
73
+ @listener.to_sexp.should == [
74
+ [:comment, "# My comment", 1],
75
+ [:tag, "hello", 2],
76
+ [:tag, "world", 2],
77
+ [:feature, "Feature", "hi", 3]
78
+ ]
79
+ end
80
+
81
+ it "should not take the tags as part of a multiline name feature element" do
82
+ @feature.scan("Feature: hi\n Scenario: test\n\n@hello\n Scenario: another")
83
+ @listener.to_sexp.should == [
84
+ [:feature, "Feature", "hi", 1],
85
+ [:scenario, "Scenario", "test", 2],
86
+ [:tag, "hello", 4],
87
+ [:scenario, "Scenario", "another", 5]
88
+ ]
89
+ end
90
+
91
+ it "should parse a file with tags on a scenario" do
92
+ @feature.scan(%{# FC
93
+ @ft
94
+ Feature: hi
95
+
96
+ @st1 @st2
97
+ Scenario: First
98
+ Given Pepper
99
+
100
+ @st3
101
+ @st4 @ST5 @#^%&ST6**!
102
+ Scenario: Second})
103
+ @listener.to_sexp.should == [
104
+ [:comment, "# FC", 1],
105
+ [:tag, "ft",2],
106
+ [:feature, "Feature", "hi", 3],
107
+ [:tag, "st1", 5],
108
+ [:tag, "st2", 5],
109
+ [:scenario, "Scenario", "First", 6],
110
+ [:step, "Given", "Pepper", 7],
111
+ [:tag, "st3", 9],
112
+ [:tag, "st4", 10],
113
+ [:tag, "ST5", 10],
114
+ [:tag, "#^%&ST6**!", 10],
115
+ [:scenario, "Scenario", "Second", 11]
116
+ ]
117
+ end
118
+ end
119
+
120
+ describe "Background" do
121
+ it "should allow an empty background description" do
122
+ @feature.scan("Feature: Hi\nBackground:\nGiven I am a step\n")
123
+ @listener.to_sexp.should == [
124
+ [:feature, "Feature", "Hi", 1],
125
+ [:background, "Background", "", 2],
126
+ [:step, "Given", "I am a step", 3]
127
+ ]
128
+ end
129
+
130
+ it "should allow multiline names ending at eof" do
131
+ @feature.scan("Feature: Feature Text\n Background: I have several\n Lines to look at\n None starting with Given")
132
+ @listener.to_sexp.should == [
133
+ [:feature, "Feature", "Feature Text", 1],
134
+ [:background, "Background", "I have several\nLines to look at\nNone starting with Given", 2]
135
+ ]
136
+ end
137
+
138
+ it "should have steps" do
139
+ @feature.scan("Feature: Hi\nBackground: Run this first\nGiven I am a step\n")
140
+ @listener.to_sexp.should == [
141
+ [:feature, "Feature", "Hi", 1],
142
+ [:background, "Background", "Run this first", 2],
143
+ [:step, "Given", "I am a step", 3]
144
+ ]
145
+ end
146
+
147
+ it "should find scenarios after background" do
148
+ @feature.scan("Feature: Hi\n#This needs to run first\nBackground: Run this first\nGiven I am a step\n\n Scenario: A Scenario\nGiven I am a step")
149
+ @listener.to_sexp.should == [
150
+ [:feature, "Feature", "Hi", 1],
151
+ [:comment, "#This needs to run first", 2],
152
+ [:background, "Background", "Run this first", 3],
153
+ [:step, "Given", "I am a step", 4],
154
+ [:scenario, "Scenario", "A Scenario", 6],
155
+ [:step, "Given", "I am a step", 7]
156
+ ]
157
+ end
158
+
159
+ it "should allow multiline names" do
160
+ @feature.scan(%{Feature: Hi
161
+ Background: It is my ambition to say
162
+ in ten sentences
163
+ what others say
164
+ in a whole book.
165
+ Given I am a step})
166
+ @listener.to_sexp.should == [
167
+ [:feature, "Feature", "Hi", 1],
168
+ [:background, "Background", "It is my ambition to say\nin ten sentences\nwhat others say\nin a whole book.",2],
169
+ [:step, "Given", "I am a step", 6]
170
+ ]
171
+ end
172
+ end
173
+
174
+ describe "Scenarios" do
175
+ it "can be empty" do
176
+ @feature.scan("Feature: Hi\n\nScenario: Hello\n")
177
+ @listener.to_sexp.should == [
178
+ [:feature, "Feature", "Hi", 1],
179
+ [:scenario, "Scenario", "Hello", 3]
180
+ ]
181
+ end
182
+
183
+ it "should allow whitespace lines after the Scenario line" do
184
+ @feature.scan(%{Feature: Foo
185
+
186
+ Scenario: bar
187
+
188
+ Given baz})
189
+ @listener.to_sexp.should == [
190
+ [:feature, "Feature", "Foo", 1],
191
+ [:scenario, "Scenario", "bar", 3],
192
+ [:step, "Given", "baz", 5]
193
+ ]
194
+ end
195
+
196
+ it "should have steps" do
197
+ @feature.scan("Feature: Hi\nScenario: Hello\nGiven I am a step\n")
198
+ @listener.to_sexp.should == [
199
+ [:feature, "Feature", "Hi", 1],
200
+ [:scenario, "Scenario", "Hello", 2],
201
+ [:step, "Given", "I am a step", 3]
202
+ ]
203
+ end
204
+
205
+ it "should have steps with inline table" do
206
+ @feature.scan(%{Feature: Hi
207
+ Scenario: Hello
208
+ Given I have a table
209
+ |a|b|
210
+ })
211
+ @listener.to_sexp.should == [
212
+ [:feature, "Feature", "Hi", 1],
213
+ [:scenario, "Scenario", "Hello", 2],
214
+ [:step, "Given", "I have a table", 3],
215
+ [:table, [['a','b']], 4]
216
+ ]
217
+ end
218
+
219
+ it "should allow multiple steps each with tables" do
220
+ @feature.scan(%{Feature: Hi
221
+ Scenario: Hello
222
+ Given I have a table
223
+ |a|b|
224
+ |c|d|
225
+ |e|f|
226
+ And I am still testing things
227
+ |g|h|
228
+ |e|r|
229
+ |k|i|
230
+ |n||
231
+ And I am done testing these tables
232
+ })
233
+ @listener.to_sexp.should == [
234
+ [:feature, "Feature", "Hi", 1],
235
+ [:scenario, "Scenario", "Hello", 2],
236
+ [:step, "Given", "I have a table", 3],
237
+ [:table, [['a','b'],['c','d'],['e','f']], 4],
238
+ [:step, "And", "I am still testing things", 7],
239
+ [:table, [['g','h'],['e','r'],['k','i'],['n',nil]], 8],
240
+ [:step, "And", "I am done testing these tables", 12],
241
+ ]
242
+ end
243
+
244
+ it "should have steps with inline py_string" do
245
+ pending
246
+ @feature.scan(%{Feature: Hi
247
+ Scenario: Hello
248
+ Given I have a string
249
+
250
+
251
+ """
252
+ hello
253
+ world
254
+ """
255
+
256
+ })
257
+ @listener.to_sexp.should == [
258
+ [:feature, "Feature", "Hi", 1],
259
+ [:scenario, "Scenario", "Hello", 2],
260
+ [:step, "Given", "I have a string", 3],
261
+ [:py_string, "hello\nworld"]
262
+ ]
263
+ end
264
+
265
+ it "should allow multiline names" do
266
+ @feature.scan(%{Feature: Hi
267
+ Scenario: It is my ambition to say
268
+ in ten sentences
269
+ what others say
270
+ in a whole book.
271
+ Given I am a step
272
+
273
+ })
274
+ @listener.to_sexp.should == [
275
+ [:feature, "Feature", "Hi", 1],
276
+ [:scenario, "Scenario", "It is my ambition to say\nin ten sentences\nwhat others say\nin a whole book.", 2],
277
+ [:step, "Given", "I am a step", 6]
278
+ ]
279
+ end
280
+
281
+ it "should allow multiline names ending at eof" do
282
+ @feature.scan("Feature: Feature Text\n And some more text\n\n Scenario: I have several\n Lines to look at\n None starting with Given")
283
+ @listener.to_sexp.should == [
284
+ [:feature, "Feature", "Feature Text\n And some more text", 1],
285
+ [:scenario, "Scenario", "I have several\nLines to look at\nNone starting with Given", 4]
286
+ ]
287
+ end
288
+
289
+ it "should ignore gherkin keywords which are parts of other words in the name" do
290
+ @feature.scan(%{Feature: Parser bug
291
+ Scenario: I have a Button
292
+ Buttons are great
293
+ Given I have it
294
+ })
295
+ @listener.to_sexp.should == [
296
+ [:feature, "Feature", "Parser bug", 1],
297
+ [:scenario, "Scenario", "I have a Button\nButtons are great", 2],
298
+ [:step, "Given", "I have it", 4]
299
+ ]
300
+ end
301
+ end
302
+
303
+ describe "A single feature, single scenario, single step" do
304
+
305
+ it "should find the feature, scenario, and step" do
306
+ @feature.scan("Feature: Feature Text\n Scenario: Reading a Scenario\n Given there is a step\n")
307
+ @listener.to_sexp.should == [
308
+ [:feature, "Feature", "Feature Text", 1],
309
+ [:scenario, "Scenario", "Reading a Scenario", 2],
310
+ [:step, "Given", "there is a step", 3]
311
+ ]
312
+ end
313
+ end
314
+
315
+ describe "A single feature, single scenario, three steps" do
316
+
317
+ it "should find the feature, scenario, and three steps" do
318
+ @feature.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")
319
+ @listener.to_sexp.should == [
320
+ [:feature, "Feature", "Feature Text", 1],
321
+ [:scenario, "Scenario", "Reading a Scenario", 2],
322
+ [:step, "Given", "there is a step", 3],
323
+ [:step, "And", "another step", 4],
324
+ [:step, "And", "a third step", 5]
325
+ ]
326
+ end
327
+ end
328
+
329
+ describe "A single feature with no scenario" do
330
+ it "should find the feature" do
331
+ @feature.scan("Feature: Feature Text\n")
332
+ @listener.to_sexp.should == [[:feature, "Feature", "Feature Text", 1]]
333
+ end
334
+
335
+ it "should parse a one line feature with no newline" do
336
+ @feature.scan("Feature: hi")
337
+ @listener.to_sexp.should == [[:feature, "Feature", "hi", 1]]
338
+ end
339
+ end
340
+
341
+ describe "A multi-line feature with no scenario" do
342
+ it "should find the feature" do
343
+ @feature.scan("Feature: Feature Text\n And some more text")
344
+ @listener.to_sexp.should == [[:feature, "Feature", "Feature Text\n And some more text", 1]]
345
+ end
346
+ end
347
+
348
+ describe "A feature with a scenario but no steps" do
349
+ it "should find the feature and scenario" do
350
+ @feature.scan("Feature: Feature Text\nScenario: Reading a Scenario\n")
351
+ @listener.to_sexp.should == [
352
+ [:feature, "Feature", "Feature Text", 1],
353
+ [:scenario, "Scenario", "Reading a Scenario", 2]
354
+ ]
355
+ end
356
+ end
357
+
358
+ describe "A feature with two scenarios" do
359
+ it "should find the feature and two scenarios" do
360
+ @feature.scan("Feature: Feature Text\nScenario: Reading a Scenario\n Given a step\n\nScenario: A second scenario\n Given another step\n")
361
+ @listener.to_sexp.should == [
362
+ [:feature, "Feature", "Feature Text", 1],
363
+ [:scenario, "Scenario", "Reading a Scenario", 2],
364
+ [:step, "Given", "a step", 3],
365
+ [:scenario, "Scenario", "A second scenario", 5],
366
+ [:step, "Given", "another step", 6]
367
+ ]
368
+ end
369
+
370
+ it "should find the feature and two scenarios without indentation" do
371
+ @feature.scan("Feature: Feature Text\nScenario: Reading a Scenario\nGiven a step\nScenario: A second scenario\nGiven another step\n")
372
+ @listener.to_sexp.should == [
373
+ [:feature, "Feature", "Feature Text", 1],
374
+ [:scenario, "Scenario", "Reading a Scenario", 2],
375
+ [:step, "Given", "a step", 3],
376
+ [:scenario, "Scenario", "A second scenario", 4],
377
+ [:step, "Given", "another step", 5]
378
+ ]
379
+ end
380
+ end
381
+
382
+ describe "A simple feature with comments" do
383
+ it "should find the feature, scenarios, steps, and comments in the proper order" do
384
+ scan_file("simple_with_comments.feature")
385
+ @listener.to_sexp.should == [
386
+ [:comment, "# Here is a comment", 1],
387
+ [:feature, "Feature", "Feature Text", 2],
388
+ [:comment, "# Here is another # comment", 3],
389
+ [:scenario, "Scenario", "Reading a Scenario", 4],
390
+ [:comment, "# Here is a third comment", 5],
391
+ [:step, "Given", "there is a step", 6],
392
+ [:comment, "# Here is a fourth comment", 7]
393
+ ]
394
+ end
395
+ end
396
+
397
+ describe "A simple feature with tags" do
398
+ it "should find the feature, scenario, step, and tags in the proper order" do
399
+ scan_file("simple_with_tags.feature")
400
+ @listener.to_sexp.should == [
401
+ [:tag, "tag1", 1],
402
+ [:tag, "tag2", 1],
403
+ [:feature, "Feature", "Feature Text", 2],
404
+ [:tag, "tag3", 3],
405
+ [:tag, "tag4", 3],
406
+ [:scenario, "Scenario", "Reading a Scenario", 4],
407
+ [:step, "Given", "there is a step", 5]
408
+ ]
409
+ end
410
+ end
411
+
412
+ describe "A complex feature with tags, comments, multiple scenarios, and multiple steps" do
413
+ it "should find things in the right order" do
414
+ scan_file("complex.feature")
415
+ @listener.to_sexp.should == [
416
+ [:comment, "#Comment on line 1", 1],
417
+ [:tag, "tag1", 2],
418
+ [:tag, "tag2", 2],
419
+ [:comment, "#Comment on line 3", 3],
420
+ [:feature, "Feature", "Feature Text\n In order to test multiline forms\n As a ragel writer\n I need to check for complex combinations", 4],
421
+ [:comment, "#Comment on line 9", 9],
422
+ [:comment, "#Comment on line 11", 11],
423
+ [:tag, "tag3", 13],
424
+ [:tag, "tag4", 13],
425
+ [:scenario, "Scenario", "Reading a Scenario", 14],
426
+ [:step, "Given", "there is a step", 15],
427
+ [:step, "But", "not another step", 16],
428
+ [:tag, "tag3", 18],
429
+ [:scenario, "Scenario", "Reading a second scenario", 19],
430
+ [:comment, "#Comment on line 20", 20],
431
+ [:step, "Given", "a third step", 21],
432
+ [:comment, "#Comment on line 22", 22],
433
+ [:step, "Then", "I am happy", 23]
434
+ ]
435
+ end
436
+ end
437
+ end
438
+ end
439
+ end