gherkin 2.0.1-universal-dotnet → 2.0.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.
@@ -4,6 +4,12 @@ Feature: Pretty Formatter
4
4
 
5
5
  Scenario: Parse all the features in Cucumber
6
6
  Given I have Cucumber's source code next to Gherkin's
7
- When I find all of the .feature files
8
- And I parse the prettified representation of each of them
9
- Then they should all be identical to the pretty output
7
+ And I find all of the .feature files
8
+ When I send each prettified original through the "pretty" machinery
9
+ Then the machinery output should be identical to the prettified original
10
+
11
+ Scenario: Parse all the features in Cucumber with JSON
12
+ Given I have Cucumber's source code next to Gherkin's
13
+ And I find all of the .feature files
14
+ When I send each prettified original through the "json" machinery
15
+ Then the machinery output should be identical to the prettified original
@@ -0,0 +1,20 @@
1
+ require 'stringio'
2
+ require 'gherkin/formatter/pretty_formatter'
3
+ require 'gherkin/json_lexer'
4
+
5
+ Given /^a PrettyFormatter$/ do
6
+ @io = StringIO.new
7
+ @formatter = Gherkin::Parser::FormatterListener.new(Gherkin::Formatter::PrettyFormatter.new(@io, true))
8
+ end
9
+
10
+ Given /^a JSON lexer$/ do
11
+ @json_parser = Gherkin::JSONLexer.new(@formatter)
12
+ end
13
+
14
+ Given /^the following JSON is parsed:$/ do |text|
15
+ @json_parser.scan(JSON.pretty_generate(JSON.parse(text)))
16
+ end
17
+
18
+ Then /^the outputted text should be:$/ do |expected_text|
19
+ expected_text.should == @io.string.strip
20
+ end
@@ -1,17 +1,50 @@
1
1
  require 'stringio'
2
+ require 'fileutils'
2
3
  require 'gherkin'
3
4
  require 'gherkin/formatter/pretty_formatter'
5
+ require 'gherkin/formatter/json_formatter'
6
+ require 'gherkin/json_lexer'
4
7
 
5
8
  module PrettyPlease
6
- def pretty(source)
9
+ def pretty_machinery(gherkin, feature_path)
7
10
  io = StringIO.new
8
11
  formatter = Gherkin::Formatter::PrettyFormatter.new(io, false)
9
12
  listener = Gherkin::Parser::FormatterListener.new(formatter)
10
13
  parser = Gherkin::Parser::Parser.new(listener, true)
11
14
  lexer = Gherkin::I18nLexer.new(parser)
12
- lexer.scan(source, "test.feature", 0)
15
+ scan(lexer, gherkin, feature_path)
13
16
  io.string
14
17
  end
18
+
19
+ def json_machinery(gherkin, feature_path)
20
+ json = StringIO.new
21
+ json_formatter = Gherkin::Formatter::JSONFormatter.new(json)
22
+ formatter_listener = Gherkin::Parser::FormatterListener.new(json_formatter)
23
+ gherkin_lexer = Gherkin::I18nLexer.new(formatter_listener)
24
+ scan(gherkin_lexer, gherkin, feature_path)
25
+
26
+ result = StringIO.new
27
+ pretty_formatter = Gherkin::Formatter::PrettyFormatter.new(result, false)
28
+ formatter_listener = Gherkin::Parser::FormatterListener.new(pretty_formatter)
29
+ json_lexer = Gherkin::JSONLexer.new(formatter_listener)
30
+ json_lexer.scan(json.string)
31
+
32
+ result.string
33
+ end
34
+
35
+ def scan(lexer, gherkin, feature_path)
36
+ begin
37
+ lexer.scan(gherkin, feature_path, 0)
38
+ rescue => e
39
+ if e.message =~ /Lexing error/
40
+ FileUtils.mkdir "tmp" unless File.directory?("tmp")
41
+ written_path = "tmp/#{File.basename(feature_path)}"
42
+ File.open(written_path, "w") {|io| io.write(gherkin)}
43
+ e.message << "\nSee #{written_path}"
44
+ end
45
+ raise e
46
+ end
47
+ end
15
48
  end
16
49
 
17
50
  World(PrettyPlease)
@@ -21,36 +54,33 @@ Given /^I have Cucumber's source code next to Gherkin's$/ do
21
54
  raise "No Cucumber source in #{@cucumber_home}" unless File.file?(@cucumber_home + '/bin/cucumber')
22
55
  end
23
56
 
24
- When /^I find all of the \.feature files$/ do
57
+ Given /^I find all of the \.feature files$/ do
25
58
  @feature_paths = Dir["#{@cucumber_home}/**/*.feature"].sort
26
59
  end
27
60
 
28
- When /^I parse the prettified representation of each of them$/ do
61
+ When /^I send each prettified original through the "([^"]*)" machinery$/ do |machinery|
29
62
  @error = false
30
63
  @feature_paths.each do |feature_path|
31
- pretty1 = nil
32
- pretty2 = nil
33
64
  begin
34
- pretty1 = pretty(IO.read(feature_path))
35
- pretty2 = pretty(pretty1)
36
- pretty2.should == pretty1
65
+ original = pretty_machinery(IO.read(feature_path), feature_path)
66
+ via_machinery = self.__send__("#{machinery}_machinery", original, feature_path)
67
+ via_machinery.should == original
37
68
  rescue RSpec::Expectations::ExpectationNotMetError => e
38
69
  announce "=========="
39
70
  announce feature_path
40
71
  if(e.message =~ /(@@.*)/m)
41
72
  announce $1
42
73
  @error = true
43
- File.open("p1.feature", "wb") {|io| io.write(pretty1)}
44
- File.open("p2.feature", "wb") {|io| io.write(pretty2)}
45
74
  else
46
75
  announce "Identical, except for newlines"
47
76
  end
48
77
  rescue => e
49
- e.message << "\nFatal error happened when parsing #{feature_path}"
78
+ e.message << "\nFatal error happened when parsing #{feature_path}."
79
+ raise e
50
80
  end
51
81
  end
52
82
  end
53
83
 
54
- Then /^they should all be identical to the pretty output$/ do
55
- raise "Some features didn't do pretty well" if @error
84
+ Then /^the machinery output should be identical to the prettified original$/ do
85
+ raise "Some features didn't make it through the machinery" if @error
56
86
  end
@@ -21,7 +21,7 @@ module Gherkin
21
21
  cmd = Tools.const_get(cmd_name.capitalize.to_sym).new(args)
22
22
  cmd.run
23
23
  rescue => e
24
- Trollop::die(e.message + "\nCommand: #{cmd_name}")
24
+ Trollop::die(e.message + "\n#{e.backtrace.join("\n")}\n\nCommand: #{cmd_name}")
25
25
  end
26
26
  end
27
27
 
@@ -22,23 +22,36 @@ module Gherkin
22
22
  }
23
23
  end
24
24
 
25
+ def background(comments, keyword, name, description, line)
26
+ background = {
27
+ 'comments' => comments.to_a,
28
+ 'keyword' => keyword,
29
+ 'name' => name,
30
+ 'description' => description,
31
+ 'line' => line,
32
+ 'steps' => [],
33
+ }
34
+ @json_hash['background'] = background
35
+ @in_background = true
36
+ end
37
+
25
38
  def scenario(comments, tags, keyword, name, description, line)
26
- add_step_container(comments, tags, keyword, name, description, line)
39
+ @in_background = false
40
+ add_step_container(comments, tags, keyword, name, description, line, 'scenario')
27
41
  end
28
42
 
29
43
  def scenario_outline(comments, tags, keyword, name, description, line)
30
- add_step_container(comments, tags, keyword, name, description, line)
44
+ @in_background = false
45
+ add_step_container(comments, tags, keyword, name, description, line, 'scenario_outline')
31
46
  end
32
47
 
33
48
  def examples(comments, tags, keyword, name, description, line, table)
34
- @table_container = add_element(comments, tags, keyword, name, description, line)
35
- @table_container['examples_table'] = to_hash_array(table)
49
+ @table_container = add_examples(comments, tags, keyword, name, description, line)
50
+ @table_container['table'] = to_hash_array(table)
36
51
  end
37
52
 
38
53
  def step(comments, keyword, name, line, multiline_arg, status, exception, arguments, stepdef_location)
39
- multiline_arg = rubify(multiline_arg)
40
- multiline_arg = to_hash_array(multiline_arg) if Array === multiline_arg
41
- @table_container = {'comments' => comments.to_a, 'keyword' => keyword, 'name' => name, 'line' => line, 'multiline_arg' => multiline_arg}
54
+ @table_container = {'comments' => comments.to_a, 'keyword' => keyword, 'name' => name, 'line' => line}.merge(step_arg_to_hash(multiline_arg))
42
55
  last_element['steps'] ||= []
43
56
  last_element['steps'] << @table_container
44
57
  end
@@ -49,27 +62,44 @@ module Gherkin
49
62
 
50
63
  private
51
64
 
52
- def add_element(comments, tags, keyword, name, description, line)
65
+ def element_hash(comments, tags, keyword, name, description, line, type=nil)
53
66
  element = {
54
67
  'comments' => comments.to_a,
55
68
  'tags' => tags.to_a,
56
69
  'keyword' => keyword,
57
70
  'name' => name,
58
71
  'description' => description,
59
- 'line' => line
72
+ 'line' => line,
60
73
  }
74
+ element['type'] = type if type
75
+ element
76
+ end
77
+
78
+ def add_element(comments, tags, keyword, name, description, line, type)
79
+ element = element_hash(comments, tags, keyword, name, description, line, type)
61
80
  @json_hash['elements'] ||= []
62
81
  @json_hash['elements'] << element
63
82
  element
64
83
  end
65
84
 
66
- def add_step_container(comments, tags, keyword, name, description, line)
67
- add_element(comments, tags, keyword, name, description, line)
85
+ def add_examples(comments, tags, keyword, name, description, line)
86
+ element = element_hash(comments, tags, keyword, name, description, line)
87
+ last_element['examples'] ||= []
88
+ last_element['examples'] << element
89
+ element
90
+ end
91
+
92
+ def add_step_container(type, comments, tags, keyword, name, description, line)
93
+ add_element(type, comments, tags, keyword, name, description, line)
68
94
  last_element['steps'] = []
69
95
  end
70
96
 
71
97
  def last_element
72
- @json_hash['elements'][-1]
98
+ if @in_background
99
+ @json_hash['background']
100
+ else
101
+ @json_hash['elements'][-1]
102
+ end
73
103
  end
74
104
 
75
105
  def to_hash_array(rows)
@@ -77,6 +107,13 @@ module Gherkin
77
107
  {"cells" => row.cells.to_a, "comments" => row.comments.to_a, "line" => row.line}
78
108
  end
79
109
  end
110
+
111
+ def step_arg_to_hash(multiline_arg)
112
+ return {} if multiline_arg.nil?
113
+ multiline_arg = rubify(multiline_arg)
114
+ Array === multiline_arg ? {"table" => to_hash_array(multiline_arg) } : { "py_string" => multiline_arg }
115
+ end
80
116
  end
81
117
  end
82
- end
118
+ end
119
+
@@ -24,14 +24,14 @@ module Gherkin
24
24
  print_comments(comments, '')
25
25
  print_tags(tags, '')
26
26
  @io.puts "#{keyword}: #{name}"
27
- @io.puts indent(description, ' ') unless description == ""
27
+ print_description(description, ' ', false)
28
28
  end
29
29
 
30
30
  def background(comments, keyword, name, description, line)
31
31
  @io.puts
32
32
  print_comments(comments, ' ')
33
33
  @io.puts " #{keyword}: #{name}#{indented_element_uri!(keyword, name, line)}"
34
- @io.puts indent(description, ' ') unless description == ""
34
+ print_description(description, ' ')
35
35
  end
36
36
 
37
37
  def scenario(comments, tags, keyword, name, description, line)
@@ -39,7 +39,7 @@ module Gherkin
39
39
  print_comments(comments, ' ')
40
40
  print_tags(tags, ' ')
41
41
  @io.puts " #{keyword}: #{name}#{indented_element_uri!(keyword, name, line)}"
42
- @io.puts indent(description, ' ') unless description == ""
42
+ print_description(description, ' ')
43
43
  end
44
44
 
45
45
  def scenario_outline(comments, tags, keyword, name, description, line)
@@ -51,7 +51,7 @@ module Gherkin
51
51
  print_comments(comments, ' ')
52
52
  print_tags(tags, ' ')
53
53
  @io.puts " #{keyword}: #{name}"
54
- @io.puts indent(description, ' ') unless description == ""
54
+ print_description(description, ' ')
55
55
  table(examples_table)
56
56
  end
57
57
 
@@ -98,6 +98,9 @@ module Gherkin
98
98
  max_lengths = cell_lengths.transpose.map { |col_lengths| col_lengths.max }.flatten
99
99
 
100
100
  rows.each_with_index do |row, i|
101
+ row.comments.each do |comment|
102
+ @io.puts " #{comment}"
103
+ end
101
104
  j = -1
102
105
  @io.puts ' | ' + row.cells.zip(max_lengths).map { |cell, max_length|
103
106
  j += 1
@@ -109,7 +112,7 @@ module Gherkin
109
112
  private
110
113
 
111
114
  def py_string(string)
112
- @io.puts " \"\"\"\n" + string.gsub(START, ' ').gsub(/"""/,'\"\"\"') + "\n \"\"\""
115
+ @io.puts " \"\"\"\n" + escape_triple_quotes(indent(string, ' ')) + "\n \"\"\""
113
116
  end
114
117
 
115
118
  def exception(exception)
@@ -126,15 +129,19 @@ module Gherkin
126
129
  end
127
130
 
128
131
  if(RUBY_VERSION =~ /^1\.9/)
129
- START = /#{"^".encode('UTF-8')}/
130
- CRLF = Regexp.new("\r\n".encode('UTF-8'))
132
+ START = /#{'^'.encode('UTF-8')}/
133
+ TRIPLE_QUOTES = /#{'"""'.encode('UTF-8')}/
131
134
  else
132
135
  START = /^/
133
- CRLF = /\r\n/n
136
+ TRIPLE_QUOTES = /"""/
134
137
  end
135
138
 
136
139
  def indent(string, indentation)
137
- string.gsub(/^/, indentation)
140
+ string.gsub(START, indentation)
141
+ end
142
+
143
+ def escape_triple_quotes(s)
144
+ s.gsub(TRIPLE_QUOTES, '\"\"\"')
138
145
  end
139
146
 
140
147
  def print_tags(tags, indent)
@@ -145,6 +152,13 @@ module Gherkin
145
152
  @io.write(comments.empty? ? '' : indent + comments.join("\n#{indent}") + "\n")
146
153
  end
147
154
 
155
+ def print_description(description, indent, newline=true)
156
+ if description != ""
157
+ @io.puts indent(description, indent)
158
+ @io.puts if newline
159
+ end
160
+ end
161
+
148
162
  def indented_element_uri!(keyword, name, line)
149
163
  return '' if @max_step_length.nil?
150
164
  l = (keyword+name).unpack("U*").length
data/lib/gherkin/i18n.rb CHANGED
@@ -58,7 +58,7 @@ module Gherkin
58
58
  require 'gherkin/parser/row'
59
59
  io = defined?(JRUBY_VERSION) ? Java.java.io.StringWriter.new : StringIO.new
60
60
  pf = Gherkin::Formatter::PrettyFormatter.new(io, true)
61
- table = all.map do |i18n|
61
+ table = all.map do |i18n|
62
62
  Parser::Row.new([i18n.iso_code, i18n.keywords('name')[0], i18n.keywords('native')[0]], [], nil)
63
63
  end
64
64
  pf.table(table)
@@ -0,0 +1,103 @@
1
+ require 'json'
2
+
3
+ module Gherkin
4
+ class JSONLexer
5
+
6
+ def initialize(listener)
7
+ @listener = listener
8
+ end
9
+
10
+ def scan(src, uri='unknown.json', offset=0)
11
+ feature = JSON.parse(src)
12
+
13
+ comments_for(feature)
14
+ tags_for(feature)
15
+ multiline_event(:feature, feature)
16
+
17
+ if feature["background"]
18
+ comments_for(feature["background"])
19
+ multiline_event(:background, feature["background"])
20
+ steps_for(feature["background"])
21
+ end
22
+
23
+ feature["elements"].each do |feature_element|
24
+ parse_element(feature_element)
25
+ end if feature["elements"]
26
+
27
+ @listener.eof
28
+ end
29
+
30
+ private
31
+
32
+ def parse_element(feature_element)
33
+ comments_for(feature_element)
34
+ tags_for(feature_element)
35
+ case feature_element["type"]
36
+ when "scenario" then parse_scenario(feature_element)
37
+ when "scenario_outline" then parse_outline(feature_element)
38
+ end
39
+ end
40
+
41
+ def parse_outline(scenario_outline)
42
+ multiline_event(:scenario_outline, scenario_outline)
43
+ steps_for(scenario_outline)
44
+ scenario_outline["examples"].each do |examples|
45
+ comments_for(examples)
46
+ tags_for(examples)
47
+ multiline_event(:examples, examples)
48
+ rows_for(examples)
49
+ end
50
+ end
51
+
52
+ def parse_scenario(scenario)
53
+ multiline_event(:scenario, scenario)
54
+ steps_for(scenario)
55
+ end
56
+
57
+ def comments_for(element)
58
+ element["comments"].each do |comment|
59
+ @listener.comment(comment, line_for(comment))
60
+ end if element["comments"]
61
+ end
62
+
63
+ def tags_for(element)
64
+ element["tags"].each do |tag|
65
+ @listener.tag(tag, line_for(tag))
66
+ end if element["tags"]
67
+ end
68
+
69
+ def steps_for(element)
70
+ element["steps"].each do |step|
71
+ comments_for(step)
72
+ @listener.step(step["keyword"], step["name"], line_for(step))
73
+ py_string_for(step)
74
+ rows_for(step)
75
+ end
76
+ end
77
+
78
+ def py_string_for(element)
79
+ @listener.py_string(element["py_string"], 0) if element["py_string"]
80
+ end
81
+
82
+ def rows_for(element)
83
+ element["table"].each do |row|
84
+ comments_for(row)
85
+ @listener.row(cells_for(row), 0)
86
+ end if element["table"]
87
+ end
88
+
89
+ def cells_for(row)
90
+ row["cells"]
91
+ end
92
+
93
+ def line_for(element)
94
+ element["line"].to_i || 0
95
+ end
96
+
97
+ def multiline_event(type, element)
98
+ if element["keyword"]
99
+ @listener.__send__(type, element["keyword"], element["name"] || "", element["description"] || "", line_for(element))
100
+ end
101
+ end
102
+ end
103
+ end