cucumber 0.3.104 → 0.4.0.rc1

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