cucumber 0.6.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/History.txt +19 -0
  2. data/README.rdoc +1 -1
  3. data/Rakefile +2 -5
  4. data/VERSION.yml +1 -1
  5. data/cucumber.gemspec +12 -4
  6. data/examples/i18n/de/features/addition.feature +1 -1
  7. data/examples/i18n/de/features/step_definitons/calculator_steps.rb +3 -3
  8. data/examples/self_test/features/step_definitions/sample_steps.rb +22 -22
  9. data/examples/tickets/features/half_manual.feature +11 -0
  10. data/examples/tickets/features/step_definitons/half_manual_steps.rb +11 -0
  11. data/features/announce.feature +97 -97
  12. data/features/background.feature +1 -4
  13. data/features/bug_475.feature +1 -2
  14. data/features/call_many_steps.feature +4 -6
  15. data/features/exception_in_after_block.feature +2 -3
  16. data/features/exception_in_after_step_block.feature +2 -3
  17. data/features/exception_in_before_block.feature +1 -2
  18. data/features/html_formatter/a.html +50 -40
  19. data/features/negative_tagged_hooks.feature +2 -3
  20. data/features/profiles.feature +2 -2
  21. data/features/report_called_undefined_steps.feature +2 -2
  22. data/features/rerun_formatter.feature +45 -0
  23. data/features/table_diffing.feature +1 -1
  24. data/features/table_mapping.feature +2 -3
  25. data/features/wire_protocol_table_diffing.feature +25 -0
  26. data/features/wire_protocol_tags.feature +47 -0
  27. data/lib/cucumber/ast/table.rb +2 -2
  28. data/lib/cucumber/cli/configuration.rb +1 -1
  29. data/lib/cucumber/cli/drb_client.rb +27 -16
  30. data/lib/cucumber/cli/profile_loader.rb +3 -2
  31. data/lib/cucumber/core_ext/proc.rb +1 -2
  32. data/lib/cucumber/formatter/console.rb +6 -4
  33. data/lib/cucumber/formatter/cucumber.css +10 -0
  34. data/lib/cucumber/formatter/cucumber.sass +10 -0
  35. data/lib/cucumber/formatter/html.rb +197 -186
  36. data/lib/cucumber/formatter/pdf.rb +24 -7
  37. data/lib/cucumber/formatter/rerun.rb +6 -3
  38. data/lib/cucumber/formatter/unicode.rb +1 -1
  39. data/lib/cucumber/languages.yml +7 -7
  40. data/lib/cucumber/parser/natural_language.rb +7 -0
  41. data/lib/cucumber/parser/treetop_ext.rb +1 -0
  42. data/lib/cucumber/rb_support/rb_world.rb +5 -0
  43. data/lib/cucumber/step_match.rb +4 -2
  44. data/lib/cucumber/step_mother.rb +53 -4
  45. data/lib/cucumber/wire_support/configuration.rb +11 -1
  46. data/lib/cucumber/wire_support/wire_language.rb +6 -3
  47. data/lib/cucumber/wire_support/wire_protocol.rb +2 -2
  48. data/lib/cucumber/wire_support/wire_protocol/requests.rb +26 -4
  49. data/spec/cucumber/ast/background_spec.rb +3 -3
  50. data/spec/cucumber/ast/feature_element_spec.rb +2 -2
  51. data/spec/cucumber/ast/feature_spec.rb +5 -5
  52. data/spec/cucumber/ast/outline_table_spec.rb +1 -1
  53. data/spec/cucumber/ast/py_string_spec.rb +1 -1
  54. data/spec/cucumber/ast/scenario_outline_spec.rb +3 -3
  55. data/spec/cucumber/ast/scenario_spec.rb +3 -3
  56. data/spec/cucumber/ast/step_collection_spec.rb +3 -3
  57. data/spec/cucumber/ast/step_spec.rb +1 -1
  58. data/spec/cucumber/ast/table_spec.rb +23 -3
  59. data/spec/cucumber/ast/tree_walker_spec.rb +1 -1
  60. data/spec/cucumber/broadcaster_spec.rb +1 -1
  61. data/spec/cucumber/cli/configuration_spec.rb +16 -6
  62. data/spec/cucumber/cli/drb_client_spec.rb +1 -1
  63. data/spec/cucumber/cli/main_spec.rb +13 -14
  64. data/spec/cucumber/cli/options_spec.rb +1 -1
  65. data/spec/cucumber/cli/profile_loader_spec.rb +1 -1
  66. data/spec/cucumber/core_ext/proc_spec.rb +1 -1
  67. data/spec/cucumber/formatter/ansicolor_spec.rb +1 -1
  68. data/spec/cucumber/formatter/color_io_spec.rb +1 -1
  69. data/spec/cucumber/formatter/duration_spec.rb +1 -1
  70. data/spec/cucumber/formatter/html_spec.rb +2 -2
  71. data/spec/cucumber/formatter/junit_spec.rb +2 -2
  72. data/spec/cucumber/formatter/progress_spec.rb +3 -3
  73. data/spec/cucumber/parser/feature_parser_spec.rb +5 -5
  74. data/spec/cucumber/parser/table_parser_spec.rb +3 -3
  75. data/spec/cucumber/rb_support/rb_step_definition_spec.rb +7 -7
  76. data/spec/cucumber/rb_support/regexp_argument_matcher_spec.rb +1 -1
  77. data/spec/cucumber/step_match_spec.rb +6 -1
  78. data/spec/cucumber/step_mother_spec.rb +18 -10
  79. data/spec/cucumber/tag_expression_spec.rb +88 -90
  80. data/spec/cucumber/wire_support/configuration_spec.rb +29 -12
  81. data/spec/cucumber/wire_support/connection_spec.rb +1 -1
  82. data/spec/cucumber/wire_support/wire_exception_spec.rb +1 -1
  83. data/spec/cucumber/wire_support/wire_language_spec.rb +1 -1
  84. data/spec/cucumber/wire_support/wire_packet_spec.rb +1 -1
  85. data/spec/cucumber/wire_support/wire_step_definition_spec.rb +1 -1
  86. data/spec/cucumber/world/pending_spec.rb +5 -5
  87. metadata +128 -54
@@ -1,6 +1,14 @@
1
1
  require 'cucumber/formatter/console'
2
2
  require 'cucumber/formatter/io'
3
3
  require 'fileutils'
4
+
5
+ begin
6
+ require 'htmlentities'
7
+ rescue LoadError => e
8
+ e.message << "\nPlease gem install htmlentities"
9
+ raise e
10
+ end
11
+
4
12
  begin
5
13
  gem 'prawn', '=0.6.3'
6
14
  require 'prawn'
@@ -9,7 +17,7 @@ begin
9
17
  gem 'prawn-format', '=0.2.3'
10
18
  require "prawn/format"
11
19
  rescue LoadError => e
12
- e.message << "\nPlease gem install prawn --version 0.6.3 && gem install prawn-format --version 0.2.3"
20
+ e.message << "\nPlease gem install prawn --version 0.6.3 && gem install prawn-format --version 0.2.3. Newer versions are not known to work."
13
21
  raise e
14
22
  end
15
23
 
@@ -28,6 +36,7 @@ module Cucumber
28
36
  def initialize(step_mother, path_or_io, options)
29
37
  @step_mother = step_mother
30
38
  @file = ensure_file(path_or_io, "pdf")
39
+ @coder = HTMLEntities.new
31
40
 
32
41
  if(options[:dry_run])
33
42
  @status_colors = { :passed => BLACK, :skipped => BLACK, :undefined => BLACK, :failed => BLACK}
@@ -138,7 +147,7 @@ module Cucumber
138
147
 
139
148
  def step_name(keyword, step_match, status, source_indent, background)
140
149
  return if @hide_this_step
141
- line = "<b>#{keyword}</b> #{step_match.format_args("%s").gsub('<', '&lt;').gsub('>', '&gt;')}"
150
+ line = "<b>#{keyword}</b> #{encode(step_match.format_args("%s"))}"
142
151
  colorize(line, status)
143
152
  end
144
153
 
@@ -154,7 +163,7 @@ module Cucumber
154
163
  return if @hide_this_step
155
164
  if(table.kind_of? Cucumber::Ast::Table)
156
165
  keep_with do
157
- @doc.table(table.rows, :headers => table.headers, :position => :center, :row_colors => ['ffffff', 'f0f0f0'])
166
+ print_table(table, ['ffffff', 'f0f0f0'])
158
167
  end
159
168
  end
160
169
  end
@@ -164,7 +173,7 @@ module Cucumber
164
173
  return if @hide_this_step
165
174
  row_colors = table.example_rows.map { |r| @status_colors[r.status] unless r.status == :skipped}
166
175
  keep_with do
167
- @doc.table(table.rows, :headers => table.headers, :position => :center, :row_colors => row_colors)
176
+ print_table(table, row_colors)
168
177
  end
169
178
  end
170
179
 
@@ -173,9 +182,7 @@ module Cucumber
173
182
  s = %{"""\n#{string}\n"""}.indent(10)
174
183
  s = s.split("\n").map{|l| l =~ /^\s+$/ ? '' : l}
175
184
  s.each do |line|
176
- line.gsub!('<', '&lt;')
177
- line.gsub!('>', '&gt;')
178
- keep_with { @doc.text line, :size => 8 }
185
+ keep_with { @doc.text(encode(line), :size => 8) }
179
186
  end
180
187
  end
181
188
 
@@ -199,6 +206,10 @@ module Cucumber
199
206
 
200
207
  private
201
208
 
209
+ def encode(text)
210
+ @coder.encode(text, :decimal)
211
+ end
212
+
202
213
  def colorize(text, status)
203
214
  keep_with do
204
215
  @doc.fill_color(@status_colors[status] || BLACK)
@@ -233,6 +244,12 @@ module Cucumber
233
244
  @pdf.move_down(20)
234
245
  @buffer = []
235
246
  end
247
+
248
+ def print_table(table, row_colors)
249
+ rows = table.rows.map { |row| row.map{ |cell| encode(cell) }}
250
+ headers = table.headers.map { |text| encode(text) }
251
+ @doc.table(rows, :headers => headers, :position => :center, :row_colors => row_colors)
252
+ end
236
253
  end
237
254
  end
238
255
  end
@@ -35,9 +35,12 @@ module Cucumber
35
35
  @io.close
36
36
  end
37
37
 
38
- # feature_element() is never executed at all, either... ?
38
+ def before_feature_element(feature_element)
39
+ @rerun = false
40
+ end
41
+
39
42
  def after_feature_element(feature_element)
40
- if feature_element.failed?
43
+ if @rerun
41
44
  file, line = *feature_element.file_colon_line.split(':')
42
45
  @file_colon_lines[file] << line
43
46
  @file_names << file
@@ -45,7 +48,7 @@ module Cucumber
45
48
  end
46
49
 
47
50
  def step_name(keyword, step_match, status, source_indent, background)
48
- @rerun = true if [:failed].index(status)
51
+ @rerun = true if [:failed, :pending, :undefined].index(status)
49
52
  end
50
53
  end
51
54
  end
@@ -52,7 +52,7 @@ if Cucumber::WINDOWS
52
52
  elsif `cmd /c chcp` =~ /(\d+)/
53
53
  Cucumber::CODEPAGE = "cp#{$1.to_i}"
54
54
  else
55
- STDERR.cucumber_puts("WARNING: Couldn't detect your output codepage. Assuming it is 1252. You may have to chcp 1252.")
56
55
  Cucumber::CODEPAGE = "cp1252"
56
+ STDERR.cucumber_puts("WARNING: Couldn't detect your output codepage. Assuming it is 1252. You may have to chcp 1252 or SET CUCUMBER_OUTPUT_ENCODING=cp1252.")
57
57
  end
58
58
  end
@@ -56,13 +56,13 @@
56
56
  name: Catalan
57
57
  native: català
58
58
  background: Rerefons|Antecedents
59
- feature: Característica
59
+ feature: Característica|Funcionalitat
60
60
  scenario: Escenari
61
61
  scenario_outline: Esquema de l'escenari
62
62
  examples: Exemples
63
- given: "*|Donat|Donada"
63
+ given: "*|Donat|Donada|Atès|Atesa"
64
64
  when: "*|Quan"
65
- then: "*|Aleshores"
65
+ then: "*|Aleshores|Cal"
66
66
  and: "*|I"
67
67
  but: "*|Però"
68
68
  "cy":
@@ -112,7 +112,7 @@
112
112
  scenario: Szenario
113
113
  scenario_outline: Szenariogrundriss
114
114
  examples: Beispiele
115
- given: "*|Gegeben sei"
115
+ given: "*|Angenommen|Gegeben sei"
116
116
  when: "*|Wenn"
117
117
  then: "*|Dann"
118
118
  and: "*|Und"
@@ -294,11 +294,11 @@
294
294
  scenario: 시나리오
295
295
  scenario_outline: 시나리오 개요
296
296
  examples: 예
297
- given: "*|조건<"
298
- when: "*|만일<"
297
+ given: "*|조건<|먼저<"
298
+ when: "*|만일<|만약<"
299
299
  then: "*|그러면<"
300
300
  and: "*|그리고<"
301
- but: "*|하지만<"
301
+ but: "*|하지만<|단<"
302
302
  "lt":
303
303
  name: Lithuanian
304
304
  native: lietuvių kalba
@@ -39,6 +39,13 @@ module Cucumber
39
39
  template = File.open(i18n_tt, Cucumber.file_mode('r')).read
40
40
  erb = ERB.new(template)
41
41
  grammar = erb.result(binding)
42
+
43
+ # The Rails 2-3-stable branch has decided to monkey-patch ERB so that ERB#result
44
+ # returns a subclass of String (SafeBuffer). This class will escape '&', '>', '<' and '"',
45
+ # effectively breaking any other library that relies on ERB behavig the way it _should_.
46
+ # This is a workaround hack until this has been fixed in Rails.
47
+ grammar = "" + grammar # Make SafeBuffer a String again.
48
+
42
49
  Treetop.load_from_string(grammar)
43
50
  @parser = Parser::I18n.const_get("#{@keywords['grammar_name']}Parser").new
44
51
  def @parser.inspect
@@ -47,6 +47,7 @@ module Treetop #:nodoc:
47
47
  end
48
48
 
49
49
  class CompiledParser #:nodoc:
50
+ public :prepare_to_parse
50
51
  include Cucumber::Parser::TreetopExt
51
52
  end
52
53
  end
@@ -42,6 +42,11 @@ module Cucumber
42
42
  @__cucumber_step_mother.announce(announcement)
43
43
  end
44
44
 
45
+ # See StepMother#ask
46
+ def ask(question, timeout_seconds=60)
47
+ @__cucumber_step_mother.ask(question, timeout_seconds)
48
+ end
49
+
45
50
  # See StepMother#embed
46
51
  def embed(file, mime_type)
47
52
  @__cucumber_step_mother.embed(file, mime_type)
@@ -57,9 +57,10 @@ module Cucumber
57
57
 
58
58
  def replace_arguments(string, step_arguments, format, &proc)
59
59
  s = string.dup
60
- offset = 0
60
+ offset = past_offset = 0
61
61
  step_arguments.each do |step_argument|
62
- next if step_argument.byte_offset.nil?
62
+ next if step_argument.byte_offset.nil? || step_argument.byte_offset < past_offset
63
+
63
64
  replacement = if block_given?
64
65
  proc.call(step_argument.val)
65
66
  elsif Proc === format
@@ -70,6 +71,7 @@ module Cucumber
70
71
 
71
72
  s[step_argument.byte_offset + offset, step_argument.val.length] = replacement
72
73
  offset += replacement.jlength - step_argument.val.jlength
74
+ past_offset = step_argument.byte_offset + step_argument.val.length
73
75
  end
74
76
  s
75
77
  end
@@ -3,6 +3,7 @@ require 'cucumber/core_ext/instance_exec'
3
3
  require 'cucumber/parser/natural_language'
4
4
  require 'cucumber/language_support/language_methods'
5
5
  require 'cucumber/formatter/duration'
6
+ require 'timeout'
6
7
 
7
8
  module Cucumber
8
9
  # Raised when there is no matching StepDefinition for a step.
@@ -131,14 +132,42 @@ module Cucumber
131
132
  # This is an alternative to using Kernel#puts - it will display
132
133
  # nicer, and in all outputs (in case you use several formatters)
133
134
  #
134
- # Beware that the output will be printed *before* the corresponding
135
- # step. This is because the step itself will not be printed until
136
- # after it has run, so it can be coloured according to its status.
137
- #
138
135
  def announce(msg)
139
136
  @visitor.announce(msg)
140
137
  end
141
138
 
139
+ # Suspends execution and prompts +question+ to the console (STDOUT).
140
+ # An operator (manual tester) can then enter a line of text and hit
141
+ # <ENTER>. The entered text is returned, and both +question+ and
142
+ # the result is added to the output using #announce.
143
+ #
144
+ # If you want a beep to happen (to grab the manual tester's attention),
145
+ # just prepend ASCII character 7 to the question:
146
+ #
147
+ # ask("#{7.chr}How many cukes are in the external system?")
148
+ #
149
+ # If that doesn't issue a beep, you can shell out to something else
150
+ # that makes a sound before invoking #ask.
151
+ #
152
+ def ask(question, timeout_seconds)
153
+ STDOUT.puts(question)
154
+ STDOUT.flush
155
+ announce(question)
156
+
157
+ if(Cucumber::JRUBY)
158
+ answer = jruby_gets(timeout_seconds)
159
+ else
160
+ answer = mri_gets(timeout_seconds)
161
+ end
162
+
163
+ if(answer)
164
+ announce(answer)
165
+ answer
166
+ else
167
+ raise("Waited for input for #{timeout_seconds} seconds, then timed out.")
168
+ end
169
+ end
170
+
142
171
  # Embed +file+ of MIME type +mime_type+ into the output. This may or may
143
172
  # not be ignored, depending on what kind of formatter(s) are active.
144
173
  #
@@ -346,5 +375,25 @@ module Cucumber
346
375
  def log
347
376
  @log ||= Logger.new(STDOUT)
348
377
  end
378
+
379
+ def mri_gets(timeout_seconds)
380
+ begin
381
+ Timeout.timeout(timeout_seconds) do
382
+ STDIN.gets
383
+ end
384
+ rescue Timeout::Error => e
385
+ nil
386
+ end
387
+ end
388
+
389
+ def jruby_gets(timeout_seconds)
390
+ answer = nil
391
+ t = java.lang.Thread.new do
392
+ answer = STDIN.gets
393
+ end
394
+ t.start
395
+ t.join(timeout_seconds * 1000)
396
+ answer
397
+ end
349
398
  end
350
399
  end
@@ -7,12 +7,22 @@ module Cucumber
7
7
  params = YAML.load_file(wire_file)
8
8
  @host = params['host']
9
9
  @port = params['port']
10
- @timeouts = params['timeout'] || {}
10
+ @timeouts = default_timeouts.merge(params['timeout'] || {})
11
11
  end
12
12
 
13
13
  def timeout(message = nil)
14
14
  return @timeouts[message.to_s] || 3
15
15
  end
16
+
17
+ private
18
+
19
+ def default_timeouts
20
+ {
21
+ 'invoke' => 120,
22
+ 'begin_scenario' => 120,
23
+ 'end_scenario' => 120
24
+ }
25
+ end
16
26
  end
17
27
  end
18
28
  end
@@ -41,17 +41,20 @@ module Cucumber
41
41
  end
42
42
 
43
43
  def step_matches(step_name, formatted_step_name)
44
- @connections.map{ |remote| remote.step_matches(step_name, formatted_step_name)}.flatten
44
+ @connections.map{ |c| c.step_matches(step_name, formatted_step_name)}.flatten
45
45
  end
46
46
 
47
47
  protected
48
48
 
49
49
  def begin_scenario(scenario)
50
- @connections.each { |remote| remote.begin_scenario(scenario) }
50
+ @connections.each { |c| c.begin_scenario(scenario) }
51
+ @current_scenario = scenario
51
52
  end
52
53
 
53
54
  def end_scenario
54
- @connections.each { |remote| remote.end_scenario }
55
+ scenario = @current_scenario
56
+ @connections.each { |c| c.end_scenario(scenario) }
57
+ @current_scenario = nil
55
58
  end
56
59
  end
57
60
  end
@@ -34,9 +34,9 @@ module Cucumber
34
34
  handler.execute(scenario)
35
35
  end
36
36
 
37
- def end_scenario
37
+ def end_scenario(scenario)
38
38
  handler = Requests::EndScenario.new(self)
39
- handler.execute
39
+ handler.execute(scenario)
40
40
  end
41
41
 
42
42
  end
@@ -64,12 +64,16 @@ module Cucumber
64
64
  def handle_pending(message)
65
65
  raise Pending, message || "TODO"
66
66
  end
67
-
68
- def handle_diff(tables)
67
+
68
+ def handle_diff!(tables)
69
69
  table1 = Ast::Table.new(tables[0])
70
70
  table2 = Ast::Table.new(tables[1])
71
+ table1.diff!(table2)
72
+ end
73
+
74
+ def handle_diff(tables)
71
75
  begin
72
- table1.diff!(table2)
76
+ handle_diff!(tables)
73
77
  rescue Cucumber::Ast::Table::Different
74
78
  @connection.diff_failed
75
79
  end
@@ -87,13 +91,31 @@ module Cucumber
87
91
  alias :handle_step_failed :handle_fail
88
92
  end
89
93
 
94
+ module Tags
95
+ def clean_tags(scenario)
96
+ scenario.source_tag_names.map { |tag| tag.gsub(/^@/, '') }.sort
97
+ end
98
+
99
+ def request_params(scenario)
100
+ return nil unless scenario.source_tag_names.any?
101
+ { "tags" => clean_tags(scenario) }
102
+ end
103
+ end
104
+
90
105
  class BeginScenario < RequestHandler
106
+ include Tags
107
+
91
108
  def execute(scenario)
92
- super(nil) # not passing the scenario yet
109
+ super(request_params(scenario))
93
110
  end
94
111
  end
95
112
 
96
113
  class EndScenario < RequestHandler
114
+ include Tags
115
+
116
+ def execute(scenario)
117
+ super(request_params(scenario))
118
+ end
97
119
  end
98
120
  end
99
121
  end
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
2
  require 'cucumber/ast'
3
3
  require 'cucumber/rb_support/rb_language'
4
4
 
@@ -7,8 +7,8 @@ module Cucumber
7
7
  describe Background do
8
8
 
9
9
  before do
10
- extend(RbSupport::RbDsl)
11
- @step_mother = StepMother.new
10
+ extend(Cucumber::RbSupport::RbDsl)
11
+ @step_mother = Cucumber::StepMother.new
12
12
  @step_mother.load_natural_language('en')
13
13
  @rb = @step_mother.load_programming_language('rb')
14
14
 
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
2
  require 'cucumber/step_mother'
3
3
  require 'cucumber/ast'
4
4
 
@@ -11,7 +11,7 @@ module Cucumber
11
11
  it "should select the longest line as the text length" do
12
12
  @keyword = "key"
13
13
  @name = "short\nvery longer\ntiny"
14
- text_length.should == 11 + Ast::Step::INDENT - 1
14
+ text_length.should == 11 + Step::INDENT - 1
15
15
  end
16
16
 
17
17
  it "should add keyword to first lines length" do