cucumber 0.6.2 → 0.6.3

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.
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