cucumber 0.3.104 → 0.4.0.rc1

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 (81) hide show
  1. data/History.txt +50 -3
  2. data/Manifest.txt +20 -2
  3. data/Rakefile +4 -0
  4. data/config/hoe.rb +20 -14
  5. data/examples/i18n/tr/Rakefile +6 -0
  6. data/examples/i18n/tr/features/bo/314/210lme.feature +10 -0
  7. data/examples/i18n/tr/features/step_definitons/hesap_makinesi_ad/304/261mlar/304/261.rb +24 -0
  8. data/examples/i18n/tr/features/toplama.feature +18 -0
  9. data/examples/i18n/tr/lib/hesap_makinesi.rb +15 -0
  10. data/examples/python/features/step_definitions/fib_steps.py +9 -1
  11. data/examples/ruby2python/features/fibonacci.feature +19 -0
  12. data/examples/{python → ruby2python}/features/step_definitions/fib_steps.rb +0 -0
  13. data/examples/ruby2python/features/support/env.rb +21 -0
  14. data/examples/ruby2python/lib/fib.py +7 -0
  15. data/features/bug_475.feature +43 -0
  16. data/features/exception_in_before_block.feature +34 -1
  17. data/features/html_formatter.feature +1 -1
  18. data/features/language_help.feature +68 -0
  19. data/features/rake_task.feature +34 -60
  20. data/features/simplest.feature +11 -0
  21. data/features/step_definitions/simplest_steps.rb +6 -0
  22. data/features/support/env.rb +1 -1
  23. data/features/support/env.rb.simplest +7 -0
  24. data/features/table_diffing.feature +1 -1
  25. data/features/table_mapping.feature +35 -0
  26. data/features/transform.feature +133 -2
  27. data/features/usage_and_stepdefs_formatter.feature +4 -2
  28. data/gem_tasks/contributors.rake +2 -1
  29. data/lib/cucumber/ast/background.rb +17 -5
  30. data/lib/cucumber/ast/outline_table.rb +8 -1
  31. data/lib/cucumber/ast/scenario.rb +2 -1
  32. data/lib/cucumber/ast/step.rb +6 -1
  33. data/lib/cucumber/ast/step_invocation.rb +14 -1
  34. data/lib/cucumber/ast/table.rb +30 -6
  35. data/lib/cucumber/cli/configuration.rb +1 -1
  36. data/lib/cucumber/cli/language_help_formatter.rb +27 -14
  37. data/lib/cucumber/cli/options.rb +2 -1
  38. data/lib/cucumber/cli/profile_loader.rb +12 -4
  39. data/lib/cucumber/core_ext/instance_exec.rb +29 -25
  40. data/lib/cucumber/formatter/junit.rb +50 -45
  41. data/lib/cucumber/formatter/pdf.rb +16 -4
  42. data/lib/cucumber/formatter/usage.rb +2 -14
  43. data/lib/cucumber/language_support/language_methods.rb +23 -3
  44. data/lib/cucumber/languages.yml +14 -0
  45. data/lib/cucumber/parser/natural_language.rb +1 -1
  46. data/lib/cucumber/parser/table.rb +10 -7
  47. data/lib/cucumber/parser/table.tt +1 -1
  48. data/lib/cucumber/platform.rb +1 -0
  49. data/lib/cucumber/py_support/py_dsl.py +10 -8
  50. data/lib/cucumber/py_support/py_language.py +8 -0
  51. data/lib/cucumber/py_support/py_language.rb +24 -11
  52. data/lib/cucumber/rails/action_controller.rb +6 -1
  53. data/lib/cucumber/rails/active_record.rb +5 -4
  54. data/lib/cucumber/rake/task.rb +7 -82
  55. data/lib/cucumber/rb_support/rb_language.rb +0 -4
  56. data/lib/cucumber/rb_support/rb_step_definition.rb +4 -6
  57. data/lib/cucumber/rb_support/rb_transform.rb +9 -7
  58. data/lib/cucumber/step_definition_light.rb +20 -0
  59. data/lib/cucumber/step_match.rb +1 -1
  60. data/lib/cucumber/step_mother.rb +5 -1
  61. data/lib/cucumber/version.rb +3 -3
  62. data/lib/cucumber/webrat/element_locator.rb +2 -0
  63. data/rails_generators/cucumber/cucumber_generator.rb +8 -9
  64. data/rails_generators/cucumber/templates/cucumber +14 -6
  65. data/rails_generators/cucumber/templates/cucumber.rake +9 -1
  66. data/rails_generators/cucumber/templates/cucumber_environment.rb +5 -1
  67. data/rails_generators/cucumber/templates/env.rb +29 -14
  68. data/rails_generators/cucumber/templates/paths.rb +1 -1
  69. data/rails_generators/cucumber/templates/spork_env.rb +39 -17
  70. data/rails_generators/cucumber/templates/version_check.rb +29 -0
  71. data/rails_generators/cucumber/templates/webrat_steps.rb +5 -0
  72. data/spec/cucumber/ast/outline_table_spec.rb +21 -0
  73. data/spec/cucumber/ast/table_spec.rb +18 -0
  74. data/spec/cucumber/formatter/html_spec.rb +33 -69
  75. data/spec/cucumber/formatter/junit_spec.rb +73 -0
  76. data/spec/cucumber/formatter/spec_helper.rb +50 -0
  77. data/spec/cucumber/parser/table_parser_spec.rb +2 -2
  78. data/spec/cucumber/rb_support/rb_step_definition_spec.rb +10 -1
  79. data/spec/cucumber/step_mother_spec.rb +6 -0
  80. metadata +32 -8
  81. data/examples/python/features/support/env.rb +0 -21
@@ -6,7 +6,8 @@ Feature: Cucumber command line
6
6
  @mri186
7
7
  Scenario: List usage of step definitions
8
8
  When I run cucumber features --format usage --dry-run
9
- Then it should pass with
9
+ Then STDERR should be empty
10
+ And it should pass with
10
11
  """
11
12
  -------------------------------------UU-U--------------UUUUU---------U-------U--------------U-UU-------------------------------------------------UU
12
13
 
@@ -139,7 +140,8 @@ Feature: Cucumber command line
139
140
  @mri186
140
141
  Scenario: --format steps
141
142
  When I run cucumber features --format stepdefs --dry-run
142
- Then it should pass with
143
+ Then STDERR should be empty
144
+ And it should pass with
143
145
  """
144
146
  -------------------------------------UU-U--------------UUUUU---------U-------U--------------U-UU-------------------------------------------------UU
145
147
 
@@ -1,3 +1,4 @@
1
+ # encoding: utf-8
1
2
  task :contributors do
2
3
  contributors = `git log --pretty=short --no-merges | git shortlog -ne | egrep -ve '^ +' | egrep -ve '^$'`
3
4
  puts contributors.split("\n").length
@@ -9,4 +10,4 @@ task :codeswarm do
9
10
  sh "sed -e 's/Aslak\ Hellesøy@.BEKK.no/aslak.hellesoy@gmail.com/g' .git/.code_swarm/log.xml > tmp.xml && mv tmp.xml .git/.code_swarm/log.xml"
10
11
  sh "sed -e 's/josephwilk@joesniff.co.uk/joe@josephwilk.net/g' .git/.code_swarm/log.xml > tmp.xml && mv tmp.xml .git/.code_swarm/log.xml"
11
12
  sh "code_swarm"
12
- end
13
+ end
@@ -26,12 +26,24 @@ module Cucumber
26
26
  return if $cucumber_interrupted
27
27
  visitor.visit_comment(@comment) unless @comment.empty?
28
28
  visitor.visit_background_name(@keyword, @name, file_colon_line(@line), source_indent(first_line_length))
29
- visitor.step_mother.before(hook_context)
30
- visitor.visit_steps(@step_invocations)
31
- @failed = @step_invocations.detect{|step_invocation| step_invocation.exception}
32
- visitor.step_mother.after(hook_context) if @failed || @feature_elements.empty?
29
+ with_visitor(hook_context, visitor) do
30
+ visitor.step_mother.before(hook_context)
31
+ visitor.visit_steps(@step_invocations)
32
+ @failed = @step_invocations.detect{|step_invocation| step_invocation.exception}
33
+ visitor.step_mother.after(hook_context) if @failed || @feature_elements.empty?
34
+ end
33
35
  end
34
-
36
+
37
+ def with_visitor(scenario, visitor)
38
+ if self != scenario && scenario.respond_to?(:with_visitor)
39
+ scenario.with_visitor(visitor) do
40
+ yield
41
+ end
42
+ else
43
+ yield
44
+ end
45
+ end
46
+
35
47
  def accept_hook?(hook)
36
48
  if hook_context != self
37
49
  hook_context.accept_hook?(hook)
@@ -45,6 +45,12 @@ module Cucumber
45
45
  end
46
46
 
47
47
  class ExampleRow < Cells #:nodoc:
48
+ class InvalidForHeaderRowError < NoMethodError
49
+ def initialize(*args)
50
+ super 'This is a header row and cannot pass or fail'
51
+ end
52
+ end
53
+
48
54
  attr_reader :scenario_outline # https://rspec.lighthouseapp.com/projects/16211/tickets/342
49
55
 
50
56
  def create_step_invocations!(scenario_outline)
@@ -113,6 +119,7 @@ module Cucumber
113
119
 
114
120
  # Returns true if one or more steps failed
115
121
  def failed?
122
+ raise InvalidForHeaderRowError if header?
116
123
  @step_invocations.failed? || !!@scenario_exception
117
124
  end
118
125
 
@@ -134,7 +141,7 @@ module Cucumber
134
141
  def name
135
142
  "| #{@cells.collect{|c| c.value }.join(' | ')} |"
136
143
  end
137
-
144
+
138
145
  private
139
146
 
140
147
  def header?
@@ -92,7 +92,6 @@ module Cucumber
92
92
  sexp
93
93
  end
94
94
 
95
- private
96
95
 
97
96
  def with_visitor(visitor)
98
97
  @current_visitor = visitor
@@ -100,6 +99,8 @@ module Cucumber
100
99
  @current_visitor = nil
101
100
  end
102
101
 
102
+ private
103
+
103
104
  def skip_hooks?
104
105
  @background.failed? || @executed
105
106
  end
@@ -18,6 +18,11 @@ module Cucumber
18
18
  false
19
19
  end
20
20
 
21
+ def status
22
+ # Step always has status skipped, because Step is always in a ScenarioOutline
23
+ :skipped
24
+ end
25
+
21
26
  def step_invocation
22
27
  StepInvocation.new(self, @name, @multiline_arg, [])
23
28
  end
@@ -107,7 +112,7 @@ module Cucumber
107
112
  name_with_arguments_replaced = @name
108
113
  argument_hash.each do |name, value|
109
114
  value ||= ''
110
- name_with_arguments_replaced = name_with_arguments_replaced.gsub(name, value) if value
115
+ name_with_arguments_replaced = name_with_arguments_replaced.gsub(name, value)
111
116
  end
112
117
  name_with_arguments_replaced
113
118
  end
@@ -1,4 +1,5 @@
1
1
  require 'cucumber/step_match'
2
+ require 'cucumber/ast/table'
2
3
 
3
4
  module Cucumber
4
5
  module Ast
@@ -38,7 +39,15 @@ module Cucumber
38
39
  end
39
40
 
40
41
  def visit_step_result(visitor)
41
- visitor.visit_step_result(keyword, @step_match, @multiline_arg, @status, @reported_exception, source_indent, @background)
42
+ visitor.visit_step_result(
43
+ keyword,
44
+ @step_match,
45
+ (@different_table || @multiline_arg),
46
+ @status,
47
+ @reported_exception,
48
+ source_indent,
49
+ @background
50
+ )
42
51
  end
43
52
 
44
53
  def invoke(step_mother, options)
@@ -55,6 +64,10 @@ module Cucumber
55
64
  rescue Undefined => e
56
65
  failed(options, e, false)
57
66
  status!(:undefined)
67
+ rescue Cucumber::Ast::Table::Different => e
68
+ @different_table = e.table
69
+ failed(options, e, false)
70
+ status!(:failed)
58
71
  rescue Exception => e
59
72
  failed(options, e, false)
60
73
  status!(:failed)
@@ -20,6 +20,15 @@ module Cucumber
20
20
  # This will store <tt>[['a', 'b'], ['c', 'd']]</tt> in the <tt>data</tt> variable.
21
21
  #
22
22
  class Table
23
+ class Different < StandardError
24
+ attr_reader :table
25
+
26
+ def initialize(table)
27
+ super('Tables were not identical')
28
+ @table = table
29
+ end
30
+ end
31
+
23
32
  include Enumerable
24
33
 
25
34
  NULL_CONVERSIONS = Hash.new(lambda{ |cell_value| cell_value }).freeze
@@ -83,7 +92,7 @@ module Cucumber
83
92
  def hashes
84
93
  @hashes ||= cells_rows[1..-1].map do |row|
85
94
  row.to_hash
86
- end.freeze
95
+ end
87
96
  end
88
97
 
89
98
  # Converts this table into a Hash where the first column is
@@ -101,7 +110,7 @@ module Cucumber
101
110
  def rows_hash
102
111
  return @rows_hash if @rows_hash
103
112
  verify_table_width(2)
104
- @rows_hash = self.transpose.hashes[0].freeze
113
+ @rows_hash = self.transpose.hashes[0]
105
114
  end
106
115
 
107
116
  # Gets the raw data of this table. For example, a Table built from
@@ -139,6 +148,21 @@ module Cucumber
139
148
  nil
140
149
  end
141
150
 
151
+ # Matches +pattern+ against the header row of the table.
152
+ # This is used especially for argument transforms.
153
+ #
154
+ # Example:
155
+ # | column_1_name | column_2_name |
156
+ # | x | y |
157
+ #
158
+ # table.match(/table:column_1_name,column_2_name/) #=> non-nil
159
+ #
160
+ # Note: must use 'table:' prefix on match
161
+ def match(pattern)
162
+ header_to_match = "table:#{headers.join(',')}"
163
+ pattern.match(header_to_match)
164
+ end
165
+
142
166
  # For testing only
143
167
  def to_sexp #:nodoc:
144
168
  [:table, *cells_rows.map{|row| row.to_sexp}]
@@ -316,7 +340,7 @@ module Cucumber
316
340
  insert_row_pos && options[:surplus_row] ||
317
341
  missing_col && options[:missing_col] ||
318
342
  surplus_col && options[:surplus_col]
319
- raise 'Tables were not identical' if should_raise
343
+ raise Different.new(self) if should_raise
320
344
  end
321
345
 
322
346
  def to_hash(cells) #:nodoc:
@@ -370,7 +394,7 @@ module Cucumber
370
394
  def headers #:nodoc:
371
395
  raw.first
372
396
  end
373
-
397
+
374
398
  def header_cell(col) #:nodoc:
375
399
  cells_rows[0][col]
376
400
  end
@@ -451,7 +475,7 @@ module Cucumber
451
475
  def columns #:nodoc:
452
476
  @columns ||= cell_matrix.transpose.map do |cell_row|
453
477
  @cells_class.new(self, cell_row)
454
- end.freeze
478
+ end
455
479
  end
456
480
 
457
481
  def new_cell(raw_cell, line) #:nodoc:
@@ -551,7 +575,7 @@ module Cucumber
551
575
  end
552
576
 
553
577
  def to_hash #:nodoc:
554
- @to_hash ||= @table.to_hash(self).freeze
578
+ @to_hash ||= @table.to_hash(self)
555
579
  end
556
580
 
557
581
  def value(n) #:nodoc:
@@ -109,7 +109,7 @@ module Cucumber
109
109
  end
110
110
  end.flatten.uniq
111
111
  remove_excluded_files_from(potential_feature_files)
112
- potential_feature_files
112
+ potential_feature_files.sort
113
113
  end
114
114
 
115
115
  def feature_dirs
@@ -1,5 +1,6 @@
1
1
  require 'cucumber/formatter/pretty'
2
2
  require 'cucumber/parser/natural_language'
3
+ require 'cucumber/formatter/unicode'
3
4
 
4
5
  module Cucumber
5
6
  module Cli
@@ -13,25 +14,37 @@ Please help us complete the translation by translating the missing words in
13
14
  Then contribute back to the Cucumber project. Details here:
14
15
  http://wiki.github.com/aslakhellesoy/cucumber/spoken-languages
15
16
  }
17
+
18
+ class << self
19
+ def list_languages(io)
20
+ raw = Cucumber::LANGUAGES.keys.sort.map do |lang|
21
+ [
22
+ lang,
23
+ Cucumber::LANGUAGES[lang]['name'],
24
+ Cucumber::LANGUAGES[lang]['native']
25
+ ]
26
+ end
16
27
 
17
- def self.list_languages(io)
18
- raw = Cucumber::LANGUAGES.keys.sort.map do |lang|
19
- [lang, Cucumber::LANGUAGES[lang]['name'], Cucumber::LANGUAGES[lang]['native']]
28
+ print_table io, raw, :check_lang => true
20
29
  end
21
- table = Ast::Table.new(raw)
22
- formatter = new(nil, io, {:check_lang=>true})
23
- Ast::TreeWalker.new(nil, [formatter]).visit_multiline_arg(table)
24
- end
25
30
 
26
- def self.list_keywords(io, lang)
27
- language = Parser::NaturalLanguage.get(nil, lang)
28
- raw = Parser::NaturalLanguage::KEYWORD_KEYS.map do |key|
29
- [key, language.keywords(key)]
31
+ def list_keywords(io, lang)
32
+ language = Parser::NaturalLanguage.get(nil, lang)
33
+ raw = Parser::NaturalLanguage::KEYWORD_KEYS.map do |key|
34
+ [key, language.keywords(key)]
35
+ end
36
+
37
+ print_table io, raw, :incomplete => language.incomplete?
30
38
  end
31
- table = Ast::Table.new(raw)
32
- new(nil, io, {:incomplete => language.incomplete?}).visit_multiline_arg(table)
39
+
40
+ private
41
+ def print_table(io, raw, options)
42
+ table = Ast::Table.new(raw)
43
+ formatter = new(nil, io, options)
44
+ Ast::TreeWalker.new(nil, [formatter]).visit_multiline_arg(table)
45
+ end
33
46
  end
34
-
47
+
35
48
  def before_visit_multiline_arg(table)
36
49
  if @options[:incomplete]
37
50
  @io.puts(format_string(INCOMPLETE, :failed))
@@ -8,7 +8,8 @@ module Cucumber
8
8
  'pretty' => ['Cucumber::Formatter::Pretty', 'Prints the feature as is - in colours.'],
9
9
  'pdf' => ['Cucumber::Formatter::Pdf', "Generates a PDF report. You need to have the\n" +
10
10
  "#{' ' * 51}prawn gem installed. Will pick up logo from\n" +
11
- "#{' ' * 51}features/support/logo.png if present."],
11
+ "#{' ' * 51}features/support/logo.png or\n" +
12
+ "#{' ' * 51}features/support/logo.jpg if present."],
12
13
  'progress' => ['Cucumber::Formatter::Progress', 'Prints one character per scenario.'],
13
14
  'rerun' => ['Cucumber::Formatter::Rerun', 'Prints failing files with line numbers.'],
14
15
  'usage' => ['Cucumber::Formatter::Usage', "Prints where step definitions are used.\n" +
@@ -32,11 +32,12 @@ Defined profiles in cucumber.yml:
32
32
  end
33
33
 
34
34
  def cucumber_yml_defined?
35
- @defined ||= File.exist?('cucumber.yml')
35
+ cucumber_file && File.exist?(cucumber_file)
36
36
  end
37
37
 
38
38
  private
39
39
 
40
+ # Loads the profile, processing it through ERB and YAML, and returns it as a hash.
40
41
  def cucumber_yml
41
42
  return @cucumber_yml if @cucumber_yml
42
43
  unless cucumber_yml_defined?
@@ -45,9 +46,9 @@ Defined profiles in cucumber.yml:
45
46
 
46
47
  require 'erb'
47
48
  require 'yaml'
48
- begin
49
- @cucumber_erb = ERB.new(IO.read('cucumber.yml')).result
50
- rescue Exception => e
49
+ begin
50
+ @cucumber_erb = ERB.new(IO.read(cucumber_file)).result
51
+ rescue Exception => e
51
52
  raise(YmlLoadError,"cucumber.yml was found, but could not be parsed with ERB. Please refer to cucumber's documentation on correct profile usage.\n#{$!.inspect}")
52
53
  end
53
54
 
@@ -64,6 +65,13 @@ Defined profiles in cucumber.yml:
64
65
  return @cucumber_yml
65
66
  end
66
67
 
68
+ # Locates cucumber.yml file. The file can end in .yml or .yaml,
69
+ # and be located in the current directory (eg. project root) or
70
+ # in a .config/ or config/ subdirectory of the current directory.
71
+ def cucumber_file
72
+ @cucumber_file ||= Dir.glob('{,.config/,config/}cucumber{.yml,.yaml}').first
73
+ end
74
+
67
75
  end
68
76
  end
69
77
  end
@@ -8,6 +8,29 @@ module Cucumber
8
8
  end
9
9
 
10
10
  class Object #:nodoc:
11
+ unless defined? instance_exec # 1.9
12
+ # http://eigenclass.org/hiki/bounded+space+instance_exec
13
+ module InstanceExecHelper #:nodoc:
14
+ end
15
+ include InstanceExecHelper
16
+ def instance_exec(*args, &block)
17
+ begin
18
+ old_critical, Thread.critical = Thread.critical, true
19
+ n = 0
20
+ n += 1 while respond_to?(mname="__instance_exec#{n}")
21
+ InstanceExecHelper.module_eval{ define_method(mname, &block) }
22
+ ensure
23
+ Thread.critical = old_critical
24
+ end
25
+ begin
26
+ ret = send(mname, *args)
27
+ ensure
28
+ InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
29
+ end
30
+ ret
31
+ end
32
+ end
33
+
11
34
  # TODO: Move most of this stuff out to an InstanceExecutor class.
12
35
  def cucumber_instance_exec(check_arity, pseudo_method, *args, &block)
13
36
  cucumber_run_with_backtrace_filtering(pseudo_method) do
@@ -51,7 +74,7 @@ class Object #:nodoc:
51
74
  end
52
75
  end
53
76
 
54
- INSTANCE_EXEC_OFFSET = (Cucumber::RUBY_1_9 || Cucumber::JRUBY) ? -3 : -4
77
+ INSTANCE_EXEC_OFFSET = (Cucumber::RUBY_1_9 || Cucumber::RUBY_1_8_7 || Cucumber::JRUBY) ? -3 : -4
55
78
 
56
79
  def replace_instance_exec_invocation_line!(backtrace, instance_exec_invocation_line, pseudo_method)
57
80
  return if Cucumber.use_full_backtrace
@@ -59,7 +82,11 @@ class Object #:nodoc:
59
82
  instance_exec_pos = backtrace.index(instance_exec_invocation_line)
60
83
  if instance_exec_pos
61
84
  replacement_line = instance_exec_pos + INSTANCE_EXEC_OFFSET
62
- backtrace[replacement_line].gsub!(/`.*'/, "`#{pseudo_method}'") if pseudo_method
85
+ if Cucumber::RUBY_1_8_7
86
+ backtrace[replacement_line] += ":in `#{pseudo_method}'" if pseudo_method
87
+ else
88
+ backtrace[replacement_line].gsub!(/`.*'/, "`#{pseudo_method}'") if pseudo_method
89
+ end
63
90
  backtrace[replacement_line+1..-1] = nil
64
91
 
65
92
  backtrace.compact!
@@ -68,27 +95,4 @@ class Object #:nodoc:
68
95
  # before we get here (injecting erb stacktrace and such)
69
96
  end
70
97
  end
71
-
72
- unless defined? instance_exec # 1.9
73
- # http://eigenclass.org/hiki/bounded+space+instance_exec
74
- module InstanceExecHelper #:nodoc:
75
- end
76
- include InstanceExecHelper
77
- def instance_exec(*args, &block)
78
- begin
79
- old_critical, Thread.critical = Thread.critical, true
80
- n = 0
81
- n += 1 while respond_to?(mname="__instance_exec#{n}")
82
- InstanceExecHelper.module_eval{ define_method(mname, &block) }
83
- ensure
84
- Thread.critical = old_critical
85
- end
86
- begin
87
- ret = send(mname, *args)
88
- ensure
89
- InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
90
- end
91
- ret
92
- end
93
- end
94
98
  end