gherkin 2.3.1-x86-mswin32 → 2.3.2-x86-mswin32
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.
- data/History.txt +9 -0
- data/README.rdoc +2 -2
- data/VERSION +1 -1
- data/features/json_formatter.feature +99 -0
- data/features/step_definitions/json_parser_steps.rb +1 -1
- data/features/step_definitions/pretty_formatter_steps.rb +2 -2
- data/lib/gherkin/formatter/colors.rb +3 -3
- data/lib/gherkin/formatter/escaping.rb +1 -1
- data/lib/gherkin/formatter/json_formatter.rb +1 -1
- data/lib/gherkin/formatter/pretty_formatter.rb +10 -4
- data/lib/gherkin/i18n.rb +5 -10
- data/lib/gherkin/json_parser.rb +1 -1
- data/ragel/lexer.c.rl.erb +20 -50
- data/ragel/lexer.java.rl.erb +20 -36
- data/ragel/lexer.rb.rl.erb +9 -14
- data/spec/gherkin/formatter/filter_formatter_spec.rb +1 -1
- data/spec/gherkin/formatter/pretty_formatter_spec.rb +11 -10
- data/spec/gherkin/shared/lexer_group.rb +43 -42
- data/spec/gherkin/shared/row_group.rb +5 -0
- metadata +4 -4
data/History.txt
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
== 2.3.2 (2010-12-05)
|
2
|
+
|
3
|
+
(Somehow 2.3.1 was released improperly shortly after 2.3.0 - not sure what fixes went into that!)
|
4
|
+
|
5
|
+
=== Bugfixes
|
6
|
+
* Preserve whitespace in descriptions. Leading whitespace in descriptions are stripped upto preceding keyword + 2 spaces (#87 Matt Wynne, Gregory Hnatiuk, Aslak Hellesøy)
|
7
|
+
* Fix incorrect indentation of Examples descriptions (Gregory Hnatiuk)
|
8
|
+
* Can't define new line characters in Example Table Cell's Content. (#85 George Montana Harkin, Aslak Hellesøy)
|
9
|
+
|
1
10
|
== 2.3.0 (2010-11-12)
|
2
11
|
|
3
12
|
=== New Features
|
data/README.rdoc
CHANGED
@@ -39,8 +39,8 @@ Running RSpec and Cucumber tests
|
|
39
39
|
|
40
40
|
rake clean spec cucumber
|
41
41
|
|
42
|
-
If the
|
43
|
-
E.g. in Bash, export
|
42
|
+
If the RL_LANGS environment variable is set, only the parsers for the languages specified there will be built.
|
43
|
+
E.g. in Bash, export RL_LANGS="en,fr,no". This can be quite helpful when modifying the Ragel grammar.
|
44
44
|
|
45
45
|
See subsections for building for a specific platform.
|
46
46
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.3.
|
1
|
+
2.3.2
|
@@ -275,4 +275,103 @@ Feature: JSON formatter
|
|
275
275
|
]
|
276
276
|
}
|
277
277
|
"""
|
278
|
+
|
279
|
+
Scenario: Feature with a description
|
280
|
+
|
281
|
+
We want people to be able to put markdown formatting into their descriptions
|
282
|
+
but this means we need to respect whitespace at the start and end of lines
|
283
|
+
in the description.
|
284
|
+
|
285
|
+
Pay close attention to the whitespace in this example.
|
278
286
|
|
287
|
+
Given the following text is parsed:
|
288
|
+
"""
|
289
|
+
Feature: Foo
|
290
|
+
one line
|
291
|
+
another line
|
292
|
+
|
293
|
+
some pre-formatted stuff
|
294
|
+
|
295
|
+
Background: name
|
296
|
+
test
|
297
|
+
test
|
298
|
+
|
299
|
+
Scenario: name
|
300
|
+
test
|
301
|
+
test
|
302
|
+
|
303
|
+
Scenario Outline: name
|
304
|
+
test
|
305
|
+
test
|
306
|
+
|
307
|
+
Given <foo>
|
308
|
+
|
309
|
+
Examples: name
|
310
|
+
test
|
311
|
+
test
|
312
|
+
| foo |
|
313
|
+
| table |
|
314
|
+
"""
|
315
|
+
Then the outputted JSON should be:
|
316
|
+
"""
|
317
|
+
{
|
318
|
+
"keyword": "Feature",
|
319
|
+
"name": "Foo",
|
320
|
+
"description": "one line \nanother line \n\n some pre-formatted stuff",
|
321
|
+
"line": 1,
|
322
|
+
"elements": [
|
323
|
+
{
|
324
|
+
"description": " test \n test",
|
325
|
+
"keyword": "Background",
|
326
|
+
"line": 7,
|
327
|
+
"name": "name",
|
328
|
+
"type": "background"
|
329
|
+
},
|
330
|
+
{
|
331
|
+
"description": " test \n test",
|
332
|
+
"keyword": "Scenario",
|
333
|
+
"line": 11,
|
334
|
+
"name": "name",
|
335
|
+
"type": "scenario"
|
336
|
+
},
|
337
|
+
{
|
338
|
+
"description": " test \n test",
|
339
|
+
"examples": [
|
340
|
+
{
|
341
|
+
"description": " test \n test",
|
342
|
+
"keyword": "Examples",
|
343
|
+
"line": 21,
|
344
|
+
"name": "name",
|
345
|
+
"rows": [
|
346
|
+
{
|
347
|
+
"cells": [
|
348
|
+
"foo"
|
349
|
+
],
|
350
|
+
"line": 24
|
351
|
+
},
|
352
|
+
{
|
353
|
+
"cells": [
|
354
|
+
"table"
|
355
|
+
],
|
356
|
+
"line": 25
|
357
|
+
}
|
358
|
+
]
|
359
|
+
}
|
360
|
+
],
|
361
|
+
"keyword": "Scenario Outline",
|
362
|
+
"line": 15,
|
363
|
+
"name": "name",
|
364
|
+
"steps": [
|
365
|
+
{
|
366
|
+
"keyword": "Given ",
|
367
|
+
"line": 19,
|
368
|
+
"name": "<foo>"
|
369
|
+
}
|
370
|
+
],
|
371
|
+
"type": "scenario_outline"
|
372
|
+
}
|
373
|
+
]
|
374
|
+
}
|
375
|
+
"""
|
376
|
+
|
377
|
+
|
@@ -6,7 +6,7 @@ World(Gherkin::Formatter::Colors)
|
|
6
6
|
|
7
7
|
Given /^a PrettyFormatter$/ do
|
8
8
|
@io = StringIO.new
|
9
|
-
@formatter = Gherkin::Formatter::PrettyFormatter.new(@io, true)
|
9
|
+
@formatter = Gherkin::Formatter::PrettyFormatter.new(@io, true, false)
|
10
10
|
end
|
11
11
|
|
12
12
|
Given /^a JSON lexer$/ do
|
@@ -10,7 +10,7 @@ module PrettyPlease
|
|
10
10
|
|
11
11
|
def pretty_machinery(gherkin, feature_path)
|
12
12
|
io = StringIO.new
|
13
|
-
formatter = Gherkin::Formatter::PrettyFormatter.new(io, true)
|
13
|
+
formatter = Gherkin::Formatter::PrettyFormatter.new(io, true, false)
|
14
14
|
parser = Gherkin::Parser::Parser.new(formatter, true)
|
15
15
|
parse(parser, gherkin, feature_path)
|
16
16
|
io.string
|
@@ -23,7 +23,7 @@ module PrettyPlease
|
|
23
23
|
parse(gherkin_parser, gherkin, feature_path)
|
24
24
|
|
25
25
|
io = StringIO.new
|
26
|
-
pretty_formatter = Gherkin::Formatter::PrettyFormatter.new(io, true)
|
26
|
+
pretty_formatter = Gherkin::Formatter::PrettyFormatter.new(io, true, false)
|
27
27
|
json_parser = Gherkin::JSONParser.new(pretty_formatter)
|
28
28
|
json_parser.parse(json.string, "#{feature_path}.json", 0)
|
29
29
|
|
@@ -13,8 +13,8 @@ module Gherkin
|
|
13
13
|
# <tt>undefined</tt>:: defaults to <tt>yellow</tt>
|
14
14
|
# <tt>pending</tt>:: defaults to <tt>yellow</tt>
|
15
15
|
# <tt>pending_arg</tt>:: defaults to <tt>yellow,bold</tt>
|
16
|
-
# <tt>executing</tt>:: defaults to <tt>
|
17
|
-
# <tt>executing_arg</tt>:: defaults to <tt>
|
16
|
+
# <tt>executing</tt>:: defaults to <tt>grey</tt>
|
17
|
+
# <tt>executing_arg</tt>:: defaults to <tt>grey,bold</tt>
|
18
18
|
# <tt>failed</tt>:: defaults to <tt>red</tt>
|
19
19
|
# <tt>failed_arg</tt>:: defaults to <tt>red,bold</tt>
|
20
20
|
# <tt>passed</tt>:: defaults to <tt>green</tt>
|
@@ -49,7 +49,7 @@ module Gherkin
|
|
49
49
|
end.merge({
|
50
50
|
'undefined' => 'yellow',
|
51
51
|
'pending' => 'yellow',
|
52
|
-
'executing' => '
|
52
|
+
'executing' => 'grey',
|
53
53
|
'failed' => 'red',
|
54
54
|
'passed' => 'green',
|
55
55
|
'outline' => 'cyan',
|
@@ -14,10 +14,11 @@ module Gherkin
|
|
14
14
|
include Colors
|
15
15
|
include Escaping
|
16
16
|
|
17
|
-
def initialize(io, monochrome
|
17
|
+
def initialize(io, monochrome, executing)
|
18
18
|
@io = io
|
19
19
|
@step_printer = StepPrinter.new
|
20
20
|
@monochrome = monochrome
|
21
|
+
@executing = executing
|
21
22
|
end
|
22
23
|
|
23
24
|
def uri(uri)
|
@@ -55,14 +56,19 @@ module Gherkin
|
|
55
56
|
print_comments(examples.comments, ' ')
|
56
57
|
print_tags(examples.tags, ' ')
|
57
58
|
@io.puts " #{examples.keyword}: #{examples.name}"
|
58
|
-
print_description(examples.description, '
|
59
|
+
print_description(examples.description, ' ')
|
59
60
|
table(examples.rows)
|
60
61
|
end
|
61
62
|
|
62
63
|
def step(step)
|
63
64
|
@step = step
|
64
65
|
@step_index += 1 if @step_index
|
65
|
-
|
66
|
+
# TODO: It feels a little funny to have this logic here in the formatter.
|
67
|
+
# We may have to duplicate it across formatters. So maybe we should move
|
68
|
+
# this out to the callers instead.
|
69
|
+
#
|
70
|
+
# Maybe it's a Filter!! ExecuteFilter and PrettyFilter
|
71
|
+
match(Model::Match.new([], nil)) unless @executing
|
66
72
|
end
|
67
73
|
|
68
74
|
def match(match)
|
@@ -225,4 +231,4 @@ module Gherkin
|
|
225
231
|
end
|
226
232
|
end
|
227
233
|
end
|
228
|
-
end
|
234
|
+
end
|
data/lib/gherkin/i18n.rb
CHANGED
@@ -56,17 +56,13 @@ module Gherkin
|
|
56
56
|
require 'stringio'
|
57
57
|
require 'gherkin/formatter/pretty_formatter'
|
58
58
|
require 'gherkin/formatter/model'
|
59
|
-
io =
|
60
|
-
pf = Gherkin::Formatter::PrettyFormatter.new(io)
|
59
|
+
io = StringIO.new
|
60
|
+
pf = Gherkin::Formatter::PrettyFormatter.new(io, false, false)
|
61
61
|
table = all.map do |i18n|
|
62
62
|
Formatter::Model::Row.new([], [i18n.iso_code, i18n.keywords('name')[0], i18n.keywords('native')[0]], nil)
|
63
63
|
end
|
64
64
|
pf.table(table)
|
65
|
-
|
66
|
-
io.getBuffer.toString
|
67
|
-
else
|
68
|
-
io.string
|
69
|
-
end
|
65
|
+
io.string
|
70
66
|
end
|
71
67
|
|
72
68
|
def unicode_escape(word, prefix="\\u")
|
@@ -149,7 +145,7 @@ module Gherkin
|
|
149
145
|
require 'gherkin/formatter/pretty_formatter'
|
150
146
|
require 'gherkin/formatter/model'
|
151
147
|
io = StringIO.new
|
152
|
-
pf = Gherkin::Formatter::PrettyFormatter.new(io)
|
148
|
+
pf = Gherkin::Formatter::PrettyFormatter.new(io, false, false)
|
153
149
|
|
154
150
|
gherkin_keyword_table = KEYWORD_KEYS.map do |key|
|
155
151
|
Formatter::Model::Row.new([], [key, keywords(key).map{|keyword| %{"#{keyword}"}}.join(', ')], nil)
|
@@ -163,8 +159,7 @@ module Gherkin
|
|
163
159
|
end
|
164
160
|
|
165
161
|
pf.table(gherkin_keyword_table + code_keyword_table)
|
166
|
-
io.
|
167
|
-
io.read
|
162
|
+
io.string
|
168
163
|
end
|
169
164
|
|
170
165
|
private
|
data/lib/gherkin/json_parser.rb
CHANGED
@@ -78,7 +78,7 @@ module Gherkin
|
|
78
78
|
|
79
79
|
def embeddings(o)
|
80
80
|
(o['embeddings'] || []).each do |embedding|
|
81
|
-
@formatter.embedding(embedding['mime_type'], decode64(embedding['data']))
|
81
|
+
@formatter.embedding(embedding['mime_type'], Base64::decode64(embedding['data']))
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
data/ragel/lexer.c.rl.erb
CHANGED
@@ -20,11 +20,6 @@
|
|
20
20
|
rb_str_new(ptr, len)
|
21
21
|
#endif
|
22
22
|
|
23
|
-
#define LF_FLAG 0
|
24
|
-
#define CRLF_FLAG 1
|
25
|
-
#define LF "\n"
|
26
|
-
#define CRLF "\r\n"
|
27
|
-
|
28
23
|
#ifndef RSTRING_PTR
|
29
24
|
#define RSTRING_PTR(s) (RSTRING(s)->ptr)
|
30
25
|
#endif
|
@@ -44,7 +39,6 @@ typedef struct lexer_state {
|
|
44
39
|
int line_number;
|
45
40
|
int current_line;
|
46
41
|
int start_col;
|
47
|
-
int eol;
|
48
42
|
size_t mark;
|
49
43
|
size_t keyword_start;
|
50
44
|
size_t keyword_end;
|
@@ -70,7 +64,7 @@ static VALUE rb_eGherkinLexingError;
|
|
70
64
|
store_multiline_kw_con(listener, # EVENT, \
|
71
65
|
PTR_TO(keyword_start), LEN(keyword_start, PTR_TO(keyword_end - 1)), \
|
72
66
|
PTR_TO(content_start), LEN(content_start, PTR_TO(content_end)), \
|
73
|
-
lexer->current_line, lexer->
|
67
|
+
lexer->current_line, lexer->start_col); \
|
74
68
|
if (lexer->content_end != 0) { \
|
75
69
|
p = PTR_TO(content_end - 1); \
|
76
70
|
} \
|
@@ -87,6 +81,7 @@ static VALUE rb_eGherkinLexingError;
|
|
87
81
|
action begin_content {
|
88
82
|
MARK(content_start, p);
|
89
83
|
lexer->current_line = lexer->line_number;
|
84
|
+
lexer->start_col = lexer->content_start - lexer->last_newline - (lexer->keyword_end - lexer->keyword_start) + 2;
|
90
85
|
}
|
91
86
|
|
92
87
|
action begin_pystring_content {
|
@@ -183,8 +178,10 @@ static VALUE rb_eGherkinLexingError;
|
|
183
178
|
VALUE con = ENCODED_STR_NEW(PTR_TO(content_start), LEN(content_start, p));
|
184
179
|
rb_funcall(con, rb_intern("strip!"), 0);
|
185
180
|
VALUE re_pipe = rb_reg_regcomp(rb_str_new2("\\\\\\|"));
|
181
|
+
VALUE re_newline = rb_reg_regcomp(rb_str_new2("\\\\n"));
|
186
182
|
VALUE re_backslash = rb_reg_regcomp(rb_str_new2("\\\\\\\\"));
|
187
183
|
rb_funcall(con, rb_intern("gsub!"), 2, re_pipe, rb_str_new2("|"));
|
184
|
+
rb_funcall(con, rb_intern("gsub!"), 2, re_newline, rb_str_new2("\n"));
|
188
185
|
rb_funcall(con, rb_intern("gsub!"), 2, re_backslash, rb_str_new2("\\"));
|
189
186
|
|
190
187
|
rb_ary_push(current_row, con);
|
@@ -247,23 +244,16 @@ static VALUE rb_eGherkinLexingError;
|
|
247
244
|
%% write data;
|
248
245
|
|
249
246
|
static VALUE
|
250
|
-
|
247
|
+
unindent(VALUE con, int start_col)
|
251
248
|
{
|
252
|
-
|
253
|
-
|
254
|
-
|
249
|
+
// Gherkin will crash gracefully if the string representation of start_col pushes the pattern past 32 characters
|
250
|
+
char pat[32];
|
251
|
+
snprintf(pat, 32, "^[\t ]{0,%d}", start_col);
|
252
|
+
VALUE re = rb_reg_regcomp(rb_str_new2(pat));
|
253
|
+
rb_funcall(con, rb_intern("gsub!"), 2, re, rb_str_new2(""));
|
254
|
+
|
255
255
|
return Qnil;
|
256
|
-
}
|
257
256
|
|
258
|
-
static VALUE
|
259
|
-
multiline_strip(VALUE text)
|
260
|
-
{
|
261
|
-
VALUE map = rb_ary_new();
|
262
|
-
VALUE split = rb_str_split(text, "\n");
|
263
|
-
|
264
|
-
rb_iterate(rb_each, split, strip_i, map);
|
265
|
-
|
266
|
-
return split;
|
267
257
|
}
|
268
258
|
|
269
259
|
static void
|
@@ -283,18 +273,19 @@ static void
|
|
283
273
|
store_multiline_kw_con(VALUE listener, const char * event_name,
|
284
274
|
const char * keyword_at, size_t keyword_length,
|
285
275
|
const char * at, size_t length,
|
286
|
-
int current_line, int
|
276
|
+
int current_line, int start_col)
|
287
277
|
{
|
288
278
|
VALUE con = Qnil, kw = Qnil, name = Qnil, desc = Qnil;
|
289
279
|
|
290
280
|
kw = ENCODED_STR_NEW(keyword_at, keyword_length);
|
291
281
|
con = ENCODED_STR_NEW(at, length);
|
292
282
|
|
293
|
-
|
283
|
+
unindent(con, start_col);
|
294
284
|
|
285
|
+
VALUE split = rb_str_split(con, "\n");
|
286
|
+
|
295
287
|
name = rb_funcall(split, rb_intern("shift"), 0);
|
296
|
-
desc = rb_ary_join(split, rb_str_new2( \
|
297
|
-
eol == CRLF_FLAG ? CRLF : LF ));
|
288
|
+
desc = rb_ary_join(split, rb_str_new2( "\n" ));
|
298
289
|
|
299
290
|
if( name == Qnil )
|
300
291
|
{
|
@@ -305,7 +296,7 @@ store_multiline_kw_con(VALUE listener, const char * event_name,
|
|
305
296
|
desc = rb_str_new2("");
|
306
297
|
}
|
307
298
|
rb_funcall(name, rb_intern("strip!"), 0);
|
308
|
-
rb_funcall(desc, rb_intern("
|
299
|
+
rb_funcall(desc, rb_intern("rstrip!"), 0);
|
309
300
|
rb_funcall(listener, rb_intern(event_name), 4, kw, name, desc, INT2FIX(current_line));
|
310
301
|
}
|
311
302
|
|
@@ -325,13 +316,11 @@ store_pystring_content(VALUE listener,
|
|
325
316
|
int current_line)
|
326
317
|
{
|
327
318
|
VALUE con = ENCODED_STR_NEW(at, length);
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
VALUE re = rb_reg_regcomp(rb_str_new2(pat));
|
319
|
+
|
320
|
+
unindent(con, start_col);
|
321
|
+
|
332
322
|
VALUE re2 = rb_reg_regcomp(rb_str_new2("\r\\Z"));
|
333
323
|
VALUE unescape_escaped_quotes = rb_reg_regcomp(rb_str_new2("\\\\\"\\\\\"\\\\\""));
|
334
|
-
rb_funcall(con, rb_intern("gsub!"), 2, re, rb_str_new2(""));
|
335
324
|
rb_funcall(con, rb_intern("sub!"), 2, re2, rb_str_new2(""));
|
336
325
|
rb_funcall(con, rb_intern("gsub!"), 2, unescape_escaped_quotes, rb_str_new2("\"\"\""));
|
337
326
|
rb_funcall(listener, rb_intern("py_string"), 2, con, INT2FIX(current_line));
|
@@ -343,20 +332,6 @@ raise_lexer_error(const char * at, int line)
|
|
343
332
|
rb_raise(rb_eGherkinLexingError, "Lexing error on line %d: '%s'. See http://wiki.github.com/aslakhellesoy/gherkin/lexingerror for more information.", line, at);
|
344
333
|
}
|
345
334
|
|
346
|
-
static int
|
347
|
-
count_char(char char_to_count, char *str) {
|
348
|
-
|
349
|
-
int count = 0;
|
350
|
-
int i = 0;
|
351
|
-
while(str[i] != '\0') {
|
352
|
-
if(str[i] == char_to_count) {
|
353
|
-
count++;
|
354
|
-
}
|
355
|
-
i++;
|
356
|
-
}
|
357
|
-
return count;
|
358
|
-
}
|
359
|
-
|
360
335
|
static void lexer_init(lexer_state *lexer) {
|
361
336
|
lexer->content_start = 0;
|
362
337
|
lexer->content_end = 0;
|
@@ -369,7 +344,6 @@ static void lexer_init(lexer_state *lexer) {
|
|
369
344
|
lexer->last_newline = 0;
|
370
345
|
lexer->final_newline = 0;
|
371
346
|
lexer->start_col = 0;
|
372
|
-
lexer->eol = LF_FLAG;
|
373
347
|
}
|
374
348
|
|
375
349
|
static VALUE CLexer_alloc(VALUE klass)
|
@@ -407,10 +381,6 @@ static VALUE CLexer_scan(VALUE self, VALUE input)
|
|
407
381
|
char *data = RSTRING_PTR(input_copy);
|
408
382
|
size_t len = RSTRING_LEN(input_copy);
|
409
383
|
|
410
|
-
if (count_char('\r', data) > (count_char('\n', data) / 2)) {
|
411
|
-
lexer->eol = CRLF_FLAG;
|
412
|
-
}
|
413
|
-
|
414
384
|
if (len == 0) {
|
415
385
|
rb_raise(rb_eGherkinLexingError, "No content to lex.");
|
416
386
|
} else {
|
data/ragel/lexer.java.rl.erb
CHANGED
@@ -4,7 +4,6 @@ import java.io.UnsupportedEncodingException;
|
|
4
4
|
import java.util.List;
|
5
5
|
import java.util.ArrayList;
|
6
6
|
import java.util.regex.Pattern;
|
7
|
-
import java.util.regex.Matcher;
|
8
7
|
import gherkin.lexer.Lexer;
|
9
8
|
import gherkin.lexer.Listener;
|
10
9
|
import gherkin.lexer.LexingError;
|
@@ -17,6 +16,9 @@ public class <%= @i18n.underscored_iso_code.upcase %> implements Lexer {
|
|
17
16
|
action begin_content {
|
18
17
|
contentStart = p;
|
19
18
|
currentLine = lineNumber;
|
19
|
+
if(keyword != null) {
|
20
|
+
startCol = p - lastNewline - (keyword.length() + 1);
|
21
|
+
}
|
20
22
|
}
|
21
23
|
|
22
24
|
action start_pystring {
|
@@ -34,35 +36,35 @@ public class <%= @i18n.underscored_iso_code.upcase %> implements Lexer {
|
|
34
36
|
}
|
35
37
|
|
36
38
|
action store_feature_content {
|
37
|
-
String[] nameDescription =
|
39
|
+
String[] nameDescription = nameAndUnindentedDescription(startCol, keywordContent(data, p, eof, nextKeywordStart, contentStart));
|
38
40
|
listener.feature(keyword, nameDescription[0], nameDescription[1], currentLine);
|
39
41
|
if(nextKeywordStart != -1) p = nextKeywordStart - 1;
|
40
42
|
nextKeywordStart = -1;
|
41
43
|
}
|
42
44
|
|
43
45
|
action store_background_content {
|
44
|
-
String[] nameDescription =
|
46
|
+
String[] nameDescription = nameAndUnindentedDescription(startCol, keywordContent(data, p, eof, nextKeywordStart, contentStart));
|
45
47
|
listener.background(keyword, nameDescription[0], nameDescription[1], currentLine);
|
46
48
|
if(nextKeywordStart != -1) p = nextKeywordStart - 1;
|
47
49
|
nextKeywordStart = -1;
|
48
50
|
}
|
49
51
|
|
50
52
|
action store_scenario_content {
|
51
|
-
String[] nameDescription =
|
53
|
+
String[] nameDescription = nameAndUnindentedDescription(startCol, keywordContent(data, p, eof, nextKeywordStart, contentStart));
|
52
54
|
listener.scenario(keyword, nameDescription[0], nameDescription[1], currentLine);
|
53
55
|
if(nextKeywordStart != -1) p = nextKeywordStart - 1;
|
54
56
|
nextKeywordStart = -1;
|
55
57
|
}
|
56
58
|
|
57
59
|
action store_scenario_outline_content {
|
58
|
-
String[] nameDescription =
|
60
|
+
String[] nameDescription = nameAndUnindentedDescription(startCol, keywordContent(data, p, eof, nextKeywordStart, contentStart));
|
59
61
|
listener.scenarioOutline(keyword, nameDescription[0], nameDescription[1], currentLine);
|
60
62
|
if(nextKeywordStart != -1) p = nextKeywordStart - 1;
|
61
63
|
nextKeywordStart = -1;
|
62
64
|
}
|
63
65
|
|
64
66
|
action store_examples_content {
|
65
|
-
String[] nameDescription =
|
67
|
+
String[] nameDescription = nameAndUnindentedDescription(startCol, keywordContent(data, p, eof, nextKeywordStart, contentStart));
|
66
68
|
listener.examples(keyword, nameDescription[0], nameDescription[1], currentLine);
|
67
69
|
if(nextKeywordStart != -1) p = nextKeywordStart - 1;
|
68
70
|
nextKeywordStart = -1;
|
@@ -115,7 +117,11 @@ public class <%= @i18n.underscored_iso_code.upcase %> implements Lexer {
|
|
115
117
|
|
116
118
|
action store_cell_content {
|
117
119
|
String con = substring(data, contentStart, p).trim();
|
118
|
-
currentRow.add(con
|
120
|
+
currentRow.add(con
|
121
|
+
.replaceAll("\\\\\\|", "|")
|
122
|
+
.replaceAll("\\\\n", "\n")
|
123
|
+
.replaceAll("\\\\\\\\", "\\\\")
|
124
|
+
);
|
119
125
|
}
|
120
126
|
|
121
127
|
action store_row {
|
@@ -173,37 +179,15 @@ public class <%= @i18n.underscored_iso_code.upcase %> implements Lexer {
|
|
173
179
|
return substring(data, contentStart, endPoint);
|
174
180
|
}
|
175
181
|
|
176
|
-
private
|
177
|
-
|
178
|
-
|
179
|
-
private static final String LF = "\n";
|
180
|
-
|
181
|
-
private String[] nameAndDescriptionWithPlatformNewlinesIntact(String text) {
|
182
|
-
int crlfCount = matchCount(CRLF_RE.matcher(text));
|
183
|
-
int lfCount = matchCount(LF_RE.matcher(text));
|
184
|
-
String eol = crlfCount > lfCount ? CRLF : LF;
|
185
|
-
|
186
|
-
String name = null;
|
182
|
+
private String[] nameAndUnindentedDescription(int startCol, String text) {
|
183
|
+
String[] lines = text.split("\n");
|
184
|
+
String name = lines.length > 0 ? lines[0].trim() : "";
|
187
185
|
StringBuffer description = new StringBuffer();
|
188
|
-
for(
|
189
|
-
|
190
|
-
|
191
|
-
} else {
|
192
|
-
description.append(s.trim()).append(eol);
|
193
|
-
}
|
194
|
-
}
|
195
|
-
if(name == null) {
|
196
|
-
name = "";
|
197
|
-
}
|
198
|
-
return new String[]{name, description.toString().trim()};
|
199
|
-
}
|
200
|
-
|
201
|
-
private int matchCount(Matcher m) {
|
202
|
-
int count = 0;
|
203
|
-
while(m.find()) {
|
204
|
-
count++;
|
186
|
+
for(int i = 1; i < lines.length; i++) {
|
187
|
+
description.append(lines[i]);
|
188
|
+
description.append("\n");
|
205
189
|
}
|
206
|
-
return
|
190
|
+
return new String[]{name, unindent(startCol+2, description.toString()).replaceAll("\\s+$", "")};
|
207
191
|
}
|
208
192
|
|
209
193
|
private String unindent(int startCol, String text) {
|
data/ragel/lexer.rb.rl.erb
CHANGED
@@ -5,10 +5,11 @@ module Gherkin
|
|
5
5
|
class <%= @i18n.underscored_iso_code.capitalize %> #:nodoc:
|
6
6
|
%%{
|
7
7
|
machine lexer;
|
8
|
-
|
8
|
+
|
9
9
|
action begin_content {
|
10
10
|
@content_start = p
|
11
11
|
@current_line = @line_number
|
12
|
+
@start_col = p - @last_newline - "#{@keyword}:".length
|
12
13
|
}
|
13
14
|
|
14
15
|
action start_pystring {
|
@@ -26,31 +27,32 @@ module Gherkin
|
|
26
27
|
}
|
27
28
|
|
28
29
|
action store_feature_content {
|
29
|
-
|
30
|
+
|
31
|
+
store_keyword_content(:feature, data, p, eof) { |con| unindent(@start_col + 2, con).rstrip }
|
30
32
|
p = @next_keyword_start - 1 if @next_keyword_start
|
31
33
|
@next_keyword_start = nil
|
32
34
|
}
|
33
35
|
|
34
36
|
action store_background_content {
|
35
|
-
store_keyword_content(:background, data, p, eof) { |con|
|
37
|
+
store_keyword_content(:background, data, p, eof) { |con| unindent(@start_col + 2, con).rstrip }
|
36
38
|
p = @next_keyword_start - 1 if @next_keyword_start
|
37
39
|
@next_keyword_start = nil
|
38
40
|
}
|
39
41
|
|
40
42
|
action store_scenario_content {
|
41
|
-
store_keyword_content(:scenario, data, p, eof) { |con|
|
43
|
+
store_keyword_content(:scenario, data, p, eof) { |con| unindent(@start_col + 2, con).rstrip }
|
42
44
|
p = @next_keyword_start - 1 if @next_keyword_start
|
43
45
|
@next_keyword_start = nil
|
44
46
|
}
|
45
47
|
|
46
48
|
action store_scenario_outline_content {
|
47
|
-
store_keyword_content(:scenario_outline, data, p, eof) { |con|
|
49
|
+
store_keyword_content(:scenario_outline, data, p, eof) { |con| unindent(@start_col + 2, con).rstrip }
|
48
50
|
p = @next_keyword_start - 1 if @next_keyword_start
|
49
51
|
@next_keyword_start = nil
|
50
52
|
}
|
51
53
|
|
52
54
|
action store_examples_content {
|
53
|
-
store_keyword_content(:examples, data, p, eof) { |con|
|
55
|
+
store_keyword_content(:examples, data, p, eof) { |con| unindent(@start_col + 2, con).rstrip }
|
54
56
|
p = @next_keyword_start - 1 if @next_keyword_start
|
55
57
|
@next_keyword_start = nil
|
56
58
|
}
|
@@ -105,7 +107,7 @@ module Gherkin
|
|
105
107
|
|
106
108
|
action store_cell_content {
|
107
109
|
con = utf8_pack(data[@content_start...p]).strip
|
108
|
-
current_row << con.gsub(/\\\|/, "|").gsub(/\\\\/, "\\")
|
110
|
+
current_row << con.gsub(/\\\|/, "|").gsub(/\\n/, "\n").gsub(/\\\\/, "\\")
|
109
111
|
}
|
110
112
|
|
111
113
|
action store_row {
|
@@ -145,13 +147,6 @@ module Gherkin
|
|
145
147
|
CRLF = "\r\n"
|
146
148
|
LF = "\n"
|
147
149
|
|
148
|
-
def multiline_strip(text)
|
149
|
-
crlf_count = text.scan(CRLF_RE).size
|
150
|
-
lf_count = text.scan(LF_RE).size
|
151
|
-
eol = crlf_count > lf_count ? CRLF : LF
|
152
|
-
text.split(/\r?\n/).map{|s| s.strip}.join(eol).strip
|
153
|
-
end
|
154
|
-
|
155
150
|
def unindent(startcol, text)
|
156
151
|
text.gsub(/^[\t ]{0,#{startcol}}/, "")
|
157
152
|
end
|
@@ -18,7 +18,7 @@ module Gherkin
|
|
18
18
|
|
19
19
|
def verify_filter(filters, *line_ranges)
|
20
20
|
io = StringIO.new
|
21
|
-
pretty_formatter = Gherkin::Formatter::PrettyFormatter.new(io, true)
|
21
|
+
pretty_formatter = Gherkin::Formatter::PrettyFormatter.new(io, true, false)
|
22
22
|
filter_formatter = Gherkin::Formatter::FilterFormatter.new(pretty_formatter, filters)
|
23
23
|
parser = Gherkin::Parser::Parser.new(filter_formatter)
|
24
24
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
2
3
|
require 'gherkin/formatter/pretty_formatter'
|
3
4
|
require 'gherkin/formatter/argument'
|
4
5
|
require 'gherkin/formatter/model'
|
@@ -20,7 +21,7 @@ module Gherkin
|
|
20
21
|
def assert_pretty(input, expected_output=input)
|
21
22
|
[true, false].each do |force_ruby|
|
22
23
|
io = StringIO.new
|
23
|
-
pf = Gherkin::Formatter::PrettyFormatter.new(io, true)
|
24
|
+
pf = Gherkin::Formatter::PrettyFormatter.new(io, true, false)
|
24
25
|
parser = Gherkin::Parser::Parser.new(pf, true, "root", force_ruby)
|
25
26
|
parser.parse(input, "test.feature", 0)
|
26
27
|
output = io.string
|
@@ -30,7 +31,7 @@ module Gherkin
|
|
30
31
|
|
31
32
|
before do
|
32
33
|
@io = StringIO.new
|
33
|
-
@f = Gherkin::Formatter::PrettyFormatter.new(@io, false)
|
34
|
+
@f = Gherkin::Formatter::PrettyFormatter.new(@io, false, true)
|
34
35
|
end
|
35
36
|
|
36
37
|
it "should print comments when scenario is longer" do
|
@@ -60,9 +61,9 @@ module Gherkin
|
|
60
61
|
World
|
61
62
|
|
62
63
|
Scenario: The scenario #{grey('# features/foo.feature:4')}
|
63
|
-
#{
|
64
|
+
#{grey('Given ')}#{grey('some stuff')} #{grey('# features/step_definitions/bar.rb:56')}
|
64
65
|
\033[1A #{green('Given ')}#{green('some stuff')} #{grey('# features/step_definitions/bar.rb:56')}
|
65
|
-
#{
|
66
|
+
#{grey('When ')}#{grey('foo')} #{grey('# features/step_definitions/bar.rb:96')}
|
66
67
|
\033[1A #{green('When ')}#{green('foo')} #{grey('# features/step_definitions/bar.rb:96')}
|
67
68
|
})
|
68
69
|
end
|
@@ -84,7 +85,7 @@ module Gherkin
|
|
84
85
|
World
|
85
86
|
|
86
87
|
Scenario: The scenario #{grey('# features/foo.feature:4')}
|
87
|
-
#{
|
88
|
+
#{grey('Given ')}#{grey('some stuff that is longer')} #{grey('# features/step_definitions/bar.rb:56')}
|
88
89
|
\033[1A #{green('Given ')}#{green('some stuff that is longer')} #{grey('# features/step_definitions/bar.rb:56')}
|
89
90
|
})
|
90
91
|
end
|
@@ -102,12 +103,12 @@ module Gherkin
|
|
102
103
|
if defined?(JRUBY_VERSION)
|
103
104
|
# Not terribly readable. The result on Java is different because JANSI uses semicolons when there are several codes.
|
104
105
|
assert_io(
|
105
|
-
" \e[
|
106
|
-
"\
|
106
|
+
" \e[90mGiven \e[0m\e[90mI have \e[0m\e[90m\e[1m999\e[0m\e[90m cukes in my belly\e[0m\n" +
|
107
|
+
"\e[1A \e[32mGiven \e[0m\e[32mI have \e[0m\e[32;1m999\e[0m\e[32m cukes in my belly\e[0m\n"
|
107
108
|
)
|
108
109
|
else
|
109
110
|
assert_io(
|
110
|
-
" #{
|
111
|
+
" #{grey('Given ')}#{grey('I have ')}#{grey(bold('999'))}#{grey(' cukes in my belly')}\n" +
|
111
112
|
"\033[1A #{green('Given ')}#{green('I have ')}#{green(bold('999'))}#{green(' cukes in my belly')}\n"
|
112
113
|
)
|
113
114
|
end
|
@@ -167,8 +168,8 @@ Feature: Feature Description
|
|
167
168
|
|
168
169
|
it "should escape backslashes and pipes" do
|
169
170
|
io = StringIO.new
|
170
|
-
|
171
|
-
|
171
|
+
f = Gherkin::Formatter::PrettyFormatter.new(io, true, false)
|
172
|
+
f.__send__(:table, [Gherkin::Formatter::Model::Row.new([], ['|', '\\'], nil)])
|
172
173
|
io.string.should == ' | \\| | \\\\ |' + "\n"
|
173
174
|
end
|
174
175
|
end
|
@@ -96,24 +96,24 @@ module Gherkin
|
|
96
96
|
]
|
97
97
|
end
|
98
98
|
|
99
|
-
it "should allow multiline
|
100
|
-
scan("Background: I have several\n Lines to look at\n
|
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
101
|
@listener.to_sexp.should == [
|
102
|
-
[:background, "Background", "I have several", "Lines to look at\
|
102
|
+
[:background, "Background", "I have several", " Lines to look at\n None starting with Given", 1],
|
103
103
|
[:eof]
|
104
104
|
]
|
105
105
|
end
|
106
106
|
|
107
|
-
it "should allow multiline
|
107
|
+
it "should allow multiline descriptions, including whitespace" do
|
108
108
|
scan(%{Feature: Hi
|
109
109
|
Background: It is my ambition to say
|
110
|
-
|
111
|
-
|
112
|
-
|
110
|
+
in ten sentences
|
111
|
+
what others say
|
112
|
+
in a whole book.
|
113
113
|
Given I am a step})
|
114
114
|
@listener.to_sexp.should == [
|
115
115
|
[:feature, "Feature", "Hi", "", 1],
|
116
|
-
[:background, "Background", "It is my ambition to say", "in ten sentences\
|
116
|
+
[:background, "Background", "It is my ambition to say", "in ten sentences\n what others say \nin a whole book.",2],
|
117
117
|
[:step, "Given ", "I am a step", 6],
|
118
118
|
[:eof]
|
119
119
|
]
|
@@ -141,22 +141,22 @@ Given I am a step})
|
|
141
141
|
]
|
142
142
|
end
|
143
143
|
|
144
|
-
it "should allow multiline
|
144
|
+
it "should allow multiline descriptions, including whitespace" do
|
145
145
|
scan(%{Scenario: It is my ambition to say
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
146
|
+
in ten sentences
|
147
|
+
what others say
|
148
|
+
in a whole book.
|
149
|
+
Given I am a step
|
150
|
+
})
|
151
151
|
@listener.to_sexp.should == [
|
152
|
-
[:scenario, "Scenario", "It is my ambition to say", "in ten sentences\nwhat others say\
|
152
|
+
[:scenario, "Scenario", "It is my ambition to say", "in ten sentences\nwhat others say \n in a whole book.", 1],
|
153
153
|
[:step, "Given ", "I am a step", 5],
|
154
154
|
[:eof]
|
155
155
|
]
|
156
156
|
end
|
157
157
|
|
158
158
|
it "should allow multiline names ending at eof" do
|
159
|
-
scan("Scenario: I have several\
|
159
|
+
scan("Scenario: I have several\nLines to look at\n None starting with Given")
|
160
160
|
@listener.to_sexp.should == [
|
161
161
|
[:scenario, "Scenario", "I have several", "Lines to look at\nNone starting with Given", 1],
|
162
162
|
[:eof]
|
@@ -165,9 +165,9 @@ Given I am a step})
|
|
165
165
|
|
166
166
|
it "should ignore gherkin keywords embedded in other words" do
|
167
167
|
scan(%{Scenario: I have a Button
|
168
|
-
|
169
|
-
|
170
|
-
|
168
|
+
Buttons are great
|
169
|
+
Given I have some
|
170
|
+
But I might not because I am a Charles Dickens character
|
171
171
|
})
|
172
172
|
@listener.to_sexp.should == [
|
173
173
|
[:scenario, "Scenario", "I have a Button", "Buttons are great", 1],
|
@@ -179,7 +179,7 @@ Given I am a step})
|
|
179
179
|
|
180
180
|
it "should allow step keywords in Scenario names" do
|
181
181
|
scan(%{Scenario: When I have when in scenario
|
182
|
-
|
182
|
+
I should be fine
|
183
183
|
Given I am a step
|
184
184
|
})
|
185
185
|
@listener.to_sexp.should == [
|
@@ -192,14 +192,15 @@ Given I am a step
|
|
192
192
|
|
193
193
|
describe "Scenario Outlines" do
|
194
194
|
it "should be parsed" do
|
195
|
-
scan(
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
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
|
203
204
|
@listener.to_sexp.should == [
|
204
205
|
[:scenario_outline, "Scenario Outline", "Hello", "With a description", 1],
|
205
206
|
[:step, "Given ", "a <what> cucumber", 3],
|
@@ -224,15 +225,15 @@ Given I am a step
|
|
224
225
|
end
|
225
226
|
|
226
227
|
it "should allow multiline description" do
|
227
|
-
scan(
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
@listener.to_sexp.should == [
|
235
|
-
[:scenario_outline, "Scenario Outline", "It is my ambition to say", "in ten sentences\
|
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],
|
236
237
|
[:step, "Given ", "I am a step", 5],
|
237
238
|
[:eof]
|
238
239
|
]
|
@@ -255,11 +256,11 @@ Given I am a step
|
|
255
256
|
|
256
257
|
it "should parse multiline example names" do
|
257
258
|
scan(%{Examples: I'm a multiline name
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
259
|
+
and I'm ok
|
260
|
+
f'real
|
261
|
+
|x|
|
262
|
+
|5|
|
263
|
+
})
|
263
264
|
@listener.to_sexp.should == [
|
264
265
|
[:examples, "Examples", "I'm a multiline name", "and I'm ok\nf'real", 1],
|
265
266
|
[:row, ["x"], 4],
|
@@ -34,6 +34,11 @@ module Gherkin
|
|
34
34
|
@listener.should_receive(:row).with(r(['|', 'the', '\a', '\\', '|\\|']), 1)
|
35
35
|
scan('| \| | the | \a | \\ | \|\\\| |' + "\n")
|
36
36
|
end
|
37
|
+
|
38
|
+
it "should parse cells with newlines" do
|
39
|
+
@listener.should_receive(:row).with(r(["\n"]), 1)
|
40
|
+
scan("|\\n|" + "\n")
|
41
|
+
end
|
37
42
|
|
38
43
|
it "should parse cells with spaces within the content" do
|
39
44
|
@listener.should_receive(:row).with(r(["Dill pickle", "Valencia orange"]), 1)
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 2
|
7
7
|
- 3
|
8
|
-
-
|
9
|
-
version: 2.3.
|
8
|
+
- 2
|
9
|
+
version: 2.3.2
|
10
10
|
platform: x86-mswin32
|
11
11
|
authors:
|
12
12
|
- Mike Sassak
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2010-
|
19
|
+
date: 2010-12-05 00:00:00 +00:00
|
20
20
|
default_executable: gherkin
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -367,7 +367,7 @@ rubyforge_project:
|
|
367
367
|
rubygems_version: 1.3.7
|
368
368
|
signing_key:
|
369
369
|
specification_version: 3
|
370
|
-
summary: gherkin-2.3.
|
370
|
+
summary: gherkin-2.3.2
|
371
371
|
test_files:
|
372
372
|
- features/escaped_pipes.feature
|
373
373
|
- features/feature_parser.feature
|