gherkin 2.0.1-i386-mswin32 → 2.0.2-i386-mswin32

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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