gherkin 2.3.1-universal-dotnet → 2.3.2-universal-dotnet

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.
@@ -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
@@ -39,8 +39,8 @@ Running RSpec and Cucumber tests
39
39
 
40
40
  rake clean spec cucumber
41
41
 
42
- If the RL_LANG environment variable is set, only the parsers for the languages specified there will be built.
43
- E.g. in Bash, export RL_LANG="en,fr,no". This can be quite helpful when modifying the Ragel grammar.
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
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>magenta</tt>
17
- # <tt>executing_arg</tt>:: defaults to <tt>magenta,bold</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' => 'magenta',
52
+ 'executing' => 'grey',
53
53
  'failed' => 'red',
54
54
  'passed' => 'green',
55
55
  'outline' => 'cyan',
@@ -8,7 +8,7 @@ module Gherkin
8
8
  #
9
9
  # This is used in the pretty formatter.
10
10
  def escape_cell(s)
11
- s.gsub(/\|/, "\\|").gsub(/\\(?!\|)/, "\\\\\\\\")
11
+ s.gsub(/\\(?!\|)/, "\\\\\\\\").gsub(/\n/, "\\n").gsub(/\|/, "\\|")
12
12
  end
13
13
  end
14
14
  end
@@ -94,7 +94,7 @@ module Gherkin
94
94
 
95
95
  def encode64s(data)
96
96
  # Strip newlines
97
- encode64(data).gsub(/\n/, '')
97
+ Base64.encode64(data).gsub(/\n/, '')
98
98
  end
99
99
  end
100
100
  end
@@ -14,10 +14,11 @@ module Gherkin
14
14
  include Colors
15
15
  include Escaping
16
16
 
17
- def initialize(io, monochrome=false)
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
- match(Model::Match.new([], nil)) if @monochrome
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
@@ -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 = defined?(JRUBY_VERSION) ? Java.java.io.StringWriter.new : StringIO.new
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
- if defined?(JRUBY_VERSION)
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.rewind
167
- io.read
162
+ io.string
168
163
  end
169
164
 
170
165
  private
@@ -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
 
@@ -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->eol); \
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
- strip_i(VALUE str, VALUE ary)
247
+ unindent(VALUE con, int start_col)
251
248
  {
252
- rb_funcall(str, rb_intern("strip!"), 0);
253
- rb_ary_push(ary, str);
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 eol)
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
- VALUE split = multiline_strip(con);
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("strip!"), 0);
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
- // Gherkin will crash gracefully if the string representation of start_col pushes the pattern past 32 characters
329
- char pat[32];
330
- snprintf(pat, 32, "^[\t ]{0,%d}", start_col);
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 {
@@ -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 = nameAndDescriptionWithPlatformNewlinesIntact(keywordContent(data, p, eof, nextKeywordStart, contentStart));
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 = nameAndDescriptionWithPlatformNewlinesIntact(keywordContent(data, p, eof, nextKeywordStart, contentStart));
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 = nameAndDescriptionWithPlatformNewlinesIntact(keywordContent(data, p, eof, nextKeywordStart, contentStart));
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 = nameAndDescriptionWithPlatformNewlinesIntact(keywordContent(data, p, eof, nextKeywordStart, contentStart));
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 = nameAndDescriptionWithPlatformNewlinesIntact(keywordContent(data, p, eof, nextKeywordStart, contentStart));
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.replaceAll("\\\\\\|", "|").replaceAll("\\\\\\\\", "\\\\"));
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 static final Pattern CRLF_RE = Pattern.compile("\r\n");
177
- private static final Pattern LF_RE = Pattern.compile("[^\r]\n");
178
- private static final String CRLF = "\r\n";
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(String s : text.split("\r?\n")) {
189
- if(name == null) {
190
- name = s.trim();
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 count;
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) {
@@ -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
- store_keyword_content(:feature, data, p, eof) { |con| multiline_strip(con) }
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| multiline_strip(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| multiline_strip(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| multiline_strip(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| multiline_strip(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
- #{magenta('Given ')}#{magenta('some stuff')} #{grey('# features/step_definitions/bar.rb:56')}
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
- #{magenta('When ')}#{magenta('foo')} #{grey('# features/step_definitions/bar.rb:96')}
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
- #{magenta('Given ')}#{magenta('some stuff that is longer')} #{grey('# features/step_definitions/bar.rb:56')}
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[35mGiven \e[0m\e[35mI have \e[0m\e[35;1m999\e[0m\e[35m cukes in my belly\e[0m\n" +
106
- "\033[1A \e[32mGiven \e[0m\e[32mI have \e[0m\e[32;1m999\e[0m\e[32m cukes in my belly\e[0m\n"
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
- " #{magenta('Given ')}#{magenta('I have ')}#{magenta(bold('999'))}#{magenta(' cukes in my belly')}\n" +
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
- l = Gherkin::Formatter::PrettyFormatter.new(io, true)
171
- l.__send__(:table, [Gherkin::Formatter::Model::Row.new([], ['|', '\\'], nil)])
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 names ending at eof" do
100
- scan("Background: I have several\n Lines to look at\n None starting with Given")
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\nNone starting with Given", 1],
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 names" do
107
+ it "should allow multiline descriptions, including whitespace" do
108
108
  scan(%{Feature: Hi
109
109
  Background: It is my ambition to say
110
- in ten sentences
111
- what others say
112
- in a whole book.
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\nwhat others say\nin a whole book.",2],
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 names" do
144
+ it "should allow multiline descriptions, including whitespace" do
145
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
- })
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\nin a whole book.", 1],
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\n Lines to look at\n None starting with Given")
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
- Buttons are great
169
- Given I have some
170
- But I might not because I am a Charles Dickens character
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
- I should be fine
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(%{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
- })
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(%{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],
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
- and I'm ok
259
- f'real
260
- |x|
261
- |5|
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
- - 1
9
- version: 2.3.1
8
+ - 2
9
+ version: 2.3.2
10
10
  platform: universal-dotnet
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-11-13 00:00:00 +00:00
19
+ date: 2010-12-05 00:00:00 +00:00
20
20
  default_executable: gherkin
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -282,7 +282,7 @@ rubyforge_project:
282
282
  rubygems_version: 1.3.7
283
283
  signing_key:
284
284
  specification_version: 3
285
- summary: gherkin-2.3.1
285
+ summary: gherkin-2.3.2
286
286
  test_files:
287
287
  - features/escaped_pipes.feature
288
288
  - features/feature_parser.feature