cucumber 1.1.9 → 1.2.0

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 (53) hide show
  1. data/.travis.yml +9 -0
  2. data/History.md +20 -3
  3. data/README.md +1 -2
  4. data/cucumber.gemspec +10 -13
  5. data/cucumber.yml +2 -2
  6. data/features/.cucumber/stepdefs.json +13 -391
  7. data/features/backtraces.feature +36 -0
  8. data/features/{issue_117.feature → drb_server_integration.feature} +3 -3
  9. data/features/formatter_step_file_colon_line.feature +46 -0
  10. data/features/{issue_57.feature → rerun_formatter.feature} +2 -2
  11. data/features/run_specific_scenarios.feature +47 -0
  12. data/gem_tasks/cucumber.rake +15 -8
  13. data/legacy_features/cucumber_cli.feature +0 -7
  14. data/legacy_features/junit_formatter.feature +60 -10
  15. data/legacy_features/language_help.feature +1 -0
  16. data/lib/cucumber.rb +2 -1
  17. data/lib/cucumber/ast/step.rb +1 -1
  18. data/lib/cucumber/ast/step_invocation.rb +2 -15
  19. data/lib/cucumber/ast/table.rb +16 -6
  20. data/lib/cucumber/ast/tree_walker.rb +5 -5
  21. data/lib/cucumber/cli/options.rb +5 -8
  22. data/lib/cucumber/formatter/ansicolor.rb +7 -12
  23. data/lib/cucumber/formatter/cucumber.css +7 -1
  24. data/lib/cucumber/formatter/gherkin_formatter_adapter.rb +1 -1
  25. data/lib/cucumber/formatter/html.rb +5 -5
  26. data/lib/cucumber/formatter/interceptor.rb +62 -0
  27. data/lib/cucumber/formatter/junit.rb +30 -14
  28. data/lib/cucumber/formatter/pretty.rb +3 -3
  29. data/lib/cucumber/formatter/progress.rb +1 -1
  30. data/lib/cucumber/formatter/rerun.rb +1 -1
  31. data/lib/cucumber/formatter/usage.rb +1 -1
  32. data/lib/cucumber/js_support/js_snippets.rb +1 -1
  33. data/lib/cucumber/platform.rb +1 -1
  34. data/lib/cucumber/rb_support/rb_dsl.rb +15 -8
  35. data/lib/cucumber/rb_support/rb_language.rb +3 -3
  36. data/lib/cucumber/rb_support/rb_step_definition.rb +17 -5
  37. data/lib/cucumber/term/ansicolor.rb +118 -0
  38. data/spec/cucumber/ast/table_spec.rb +9 -0
  39. data/spec/cucumber/cli/configuration_spec.rb +12 -6
  40. data/spec/cucumber/cli/options_spec.rb +9 -3
  41. data/spec/cucumber/constantize_spec.rb +5 -1
  42. data/spec/cucumber/formatter/ansicolor_spec.rb +1 -1
  43. data/spec/cucumber/formatter/interceptor_spec.rb +111 -0
  44. data/spec/cucumber/formatter/junit_spec.rb +36 -20
  45. data/spec/cucumber/formatter/progress_spec.rb +2 -2
  46. data/spec/cucumber/rb_support/rb_language_spec.rb +5 -5
  47. data/spec/cucumber/rb_support/rb_step_definition_spec.rb +17 -1
  48. data/spec/cucumber/rb_support/regexp_argument_matcher_spec.rb +6 -2
  49. data/spec/cucumber/step_match_spec.rb +8 -4
  50. data/spec/spec_helper.rb +15 -1
  51. metadata +215 -82
  52. data/.gitmodules +0 -3
  53. data/lib/cucumber/formatter/pdf.rb +0 -244
@@ -26,7 +26,7 @@ module Cucumber
26
26
  @duration = Time.now - @start_time
27
27
  end
28
28
 
29
- def after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
29
+ def after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background, file_colon_line)
30
30
  if step_match.name.nil? # nil if it's from a scenario outline
31
31
  stepdef_key = StepDefKey.new(step_match.step_definition.regexp_source, step_match.step_definition.file_colon_line)
32
32
 
@@ -1,7 +1,7 @@
1
1
  module Cucumber
2
2
  module JsSupport
3
3
  module JsSnippets
4
- PARAM_PATTERN = /"([^"]*)"/
4
+ PARAM_PATTERN = /"(.*?)"/
5
5
  ESCAPED_PARAM_PATTERN = '"([^\\"]*)"'
6
6
 
7
7
  def snippet_text(code_keyword, step_name, multiline_arg_class)
@@ -4,7 +4,7 @@ require 'rbconfig'
4
4
 
5
5
  module Cucumber
6
6
  unless defined?(Cucumber::VERSION)
7
- VERSION = '1.1.9'
7
+ VERSION = '1.2.0'
8
8
  BINARY = File.expand_path(File.dirname(__FILE__) + '/../../bin/cucumber')
9
9
  LIBDIR = File.expand_path(File.dirname(__FILE__) + '/../../lib')
10
10
  JRUBY = defined?(JRUBY_VERSION)
@@ -23,8 +23,8 @@ module Cucumber
23
23
  @rb_language.register_rb_transform(regexp, proc)
24
24
  end
25
25
 
26
- def register_rb_step_definition(regexp, proc)
27
- @rb_language.register_rb_step_definition(regexp, proc)
26
+ def register_rb_step_definition(regexp, proc_or_sym, options = {})
27
+ @rb_language.register_rb_step_definition(regexp, proc_or_sym, options)
28
28
  end
29
29
  end
30
30
 
@@ -98,12 +98,19 @@ module Cucumber
98
98
  # also to the i18n translations whenever a feature of a
99
99
  # new language is loaded.
100
100
  #
101
- # The +&proc+ gets executed in the context of a <tt>World</tt>
102
- # object, which is defined by #World. A new <tt>World</tt>
103
- # object is created for each scenario and is shared across
104
- # step definitions within that scenario.
105
- def register_rb_step_definition(regexp, &proc)
106
- RbDsl.register_rb_step_definition(regexp, proc)
101
+ # If provided, the +symbol+ is sent to the <tt>World</tt> object
102
+ # as defined by #World. A new <tt>World</tt> object is created
103
+ # for each scenario and is shared across step definitions within
104
+ # that scenario. If the +options+ hash contains an <tt>:on</tt>
105
+ # key, the value for this is assumed to be a proc. This proc
106
+ # will be executed in the context of the <tt>World</tt> object
107
+ # and then sent the +symbol+.
108
+ #
109
+ # If no +symbol+ if provided then the +&proc+ gets executed in
110
+ # the context of the <tt>World</tt> object.
111
+ def register_rb_step_definition(regexp, symbol = nil, options = {}, &proc)
112
+ proc_or_sym = symbol || proc
113
+ RbDsl.register_rb_step_definition(regexp, proc_or_sym, options)
107
114
  end
108
115
  end
109
116
  end
@@ -75,7 +75,7 @@ module Cucumber
75
75
  end.compact
76
76
  end
77
77
 
78
- ARGUMENT_PATTERNS = ['"([^"]*)"', '(\d+)']
78
+ ARGUMENT_PATTERNS = ['"(.*?)"', '(\d+)']
79
79
 
80
80
  def snippet_text(code_keyword, step_name, multiline_arg_class)
81
81
  snippet_pattern = Regexp.escape(step_name).gsub('\ ', ' ').gsub('/', '\/')
@@ -110,8 +110,8 @@ module Cucumber
110
110
  add_transform(RbTransform.new(self, regexp, proc))
111
111
  end
112
112
 
113
- def register_rb_step_definition(regexp, proc)
114
- step_definition = RbStepDefinition.new(self, regexp, proc)
113
+ def register_rb_step_definition(regexp, proc_or_sym, options)
114
+ step_definition = RbStepDefinition.new(self, regexp, proc_or_sym, options)
115
115
  @step_definitions << step_definition
116
116
  step_definition
117
117
  end
@@ -19,18 +19,25 @@ module Cucumber
19
19
 
20
20
  class MissingProc < StandardError
21
21
  def message
22
- "Step definitions must always have a proc"
22
+ "Step definitions must always have a proc or symbol"
23
23
  end
24
24
  end
25
25
 
26
- def initialize(rb_language, regexp, proc)
27
- raise MissingProc if proc.nil?
26
+ def initialize(rb_language, regexp, proc_or_sym, options)
27
+ raise MissingProc if proc_or_sym.nil?
28
28
  if String === regexp
29
29
  p = Regexp.escape(regexp)
30
30
  p = p.gsub(/\\\$\w+/, '(.*)') # Replace $var with (.*)
31
31
  regexp = Regexp.new("^#{p}$")
32
32
  end
33
- @rb_language, @regexp, @proc = rb_language, regexp, proc
33
+ @rb_language, @regexp, @proc = rb_language, regexp, proc_or_sym
34
+ if @proc.kind_of? Symbol
35
+ @proc = lambda do |*args|
36
+ target = options[:on] ? instance_exec(&options[:on]) : self
37
+ target.send(proc_or_sym, *args)
38
+ end
39
+ end
40
+
34
41
  @rb_language.available_step_definition(regexp_source, file_colon_line)
35
42
  end
36
43
 
@@ -71,7 +78,12 @@ module Cucumber
71
78
  end
72
79
 
73
80
  def file_colon_line
74
- @proc.file_colon_line
81
+ case @proc
82
+ when Proc
83
+ @proc.file_colon_line
84
+ when Symbol
85
+ ":#{@proc}"
86
+ end
75
87
  end
76
88
 
77
89
  def file
@@ -0,0 +1,118 @@
1
+ module Cucumber
2
+ module Term
3
+ # The ANSIColor module can be used for namespacing and mixed into your own
4
+ # classes.
5
+ module ANSIColor
6
+ # :stopdoc:
7
+ ATTRIBUTES = [
8
+ [ :clear , 0 ],
9
+ [ :reset , 0 ], # synonym for :clear
10
+ [ :bold , 1 ],
11
+ [ :dark , 2 ],
12
+ [ :italic , 3 ], # not widely implemented
13
+ [ :underline , 4 ],
14
+ [ :underscore , 4 ], # synonym for :underline
15
+ [ :blink , 5 ],
16
+ [ :rapid_blink , 6 ], # not widely implemented
17
+ [ :negative , 7 ], # no reverse because of String#reverse
18
+ [ :concealed , 8 ],
19
+ [ :strikethrough, 9 ], # not widely implemented
20
+ [ :black , 30 ],
21
+ [ :red , 31 ],
22
+ [ :green , 32 ],
23
+ [ :yellow , 33 ],
24
+ [ :blue , 34 ],
25
+ [ :magenta , 35 ],
26
+ [ :cyan , 36 ],
27
+ [ :white , 37 ],
28
+ [ :on_black , 40 ],
29
+ [ :on_red , 41 ],
30
+ [ :on_green , 42 ],
31
+ [ :on_yellow , 43 ],
32
+ [ :on_blue , 44 ],
33
+ [ :on_magenta , 45 ],
34
+ [ :on_cyan , 46 ],
35
+ [ :on_white , 47 ],
36
+ ]
37
+
38
+ ATTRIBUTE_NAMES = ATTRIBUTES.transpose.first
39
+ # :startdoc:
40
+
41
+ # Returns true, if the coloring function of this module
42
+ # is switched on, false otherwise.
43
+ def self.coloring?
44
+ @coloring
45
+ end
46
+
47
+ # Turns the coloring on or off globally, so you can easily do
48
+ # this for example:
49
+ # Cucumber::Term::ANSIColor::coloring = STDOUT.isatty
50
+ def self.coloring=(val)
51
+ @coloring = val
52
+ end
53
+ self.coloring = true
54
+
55
+ ATTRIBUTES.each do |c, v|
56
+ eval %Q{
57
+ def #{c}(string = nil)
58
+ result = ''
59
+ result << "\e[#{v}m" if Cucumber::Term::ANSIColor.coloring?
60
+ if block_given?
61
+ result << yield
62
+ elsif string
63
+ result << string
64
+ elsif respond_to?(:to_str)
65
+ result << to_str
66
+ else
67
+ return result #only switch on
68
+ end
69
+ result << "\e[0m" if Cucumber::Term::ANSIColor.coloring?
70
+ result
71
+ end
72
+ }
73
+ end
74
+
75
+ # Regular expression that is used to scan for ANSI-sequences while
76
+ # uncoloring strings.
77
+ COLORED_REGEXP = /\e\[(?:[34][0-7]|[0-9])?m/
78
+
79
+
80
+ def self.included(klass)
81
+ if version_is_greater_than_18? and klass == String
82
+ ATTRIBUTES.delete(:clear)
83
+ ATTRIBUTE_NAMES.delete(:clear)
84
+ end
85
+ end
86
+
87
+ # Returns an uncolored version of the string, that is all
88
+ # ANSI-sequences are stripped from the string.
89
+ def uncolored(string = nil) # :yields:
90
+ if block_given?
91
+ yield.gsub(COLORED_REGEXP, '')
92
+ elsif string
93
+ string.gsub(COLORED_REGEXP, '')
94
+ elsif respond_to?(:to_str)
95
+ to_str.gsub(COLORED_REGEXP, '')
96
+ else
97
+ ''
98
+ end
99
+ end
100
+
101
+ module_function
102
+
103
+ # Returns an array of all Cucumber::Term::ANSIColor attributes as symbols.
104
+ def attributes
105
+ ATTRIBUTE_NAMES
106
+ end
107
+ extend self
108
+
109
+ private
110
+
111
+ def version_is_greater_than_18?
112
+ version = RUBY_VERSION.split('.')
113
+ version.map! &:to_i
114
+ version[0] >= 1 && version[1] > 8
115
+ end
116
+ end
117
+ end
118
+ end
@@ -474,6 +474,15 @@ module Cucumber
474
474
  lambda { @t.dup.diff!(t) }.should_not raise_error
475
475
  lambda { @t.dup.diff!(t, :surplus_col => true) }.should raise_error
476
476
  end
477
+
478
+ it "should not raise on misplaced columns" do
479
+ t = table(%{
480
+ | b | a |
481
+ | d | c |
482
+ }, __FILE__, __LINE__)
483
+ lambda { @t.dup.diff!(t) }.should_not raise_error
484
+ lambda { @t.dup.diff!(t, :misplaced_col => true) }.should raise_error
485
+ end
477
486
  end
478
487
 
479
488
  def table(text, file, offset)
@@ -321,12 +321,12 @@ END_OF_MESSAGE
321
321
  end
322
322
 
323
323
  it "should accept --color option" do
324
- Term::ANSIColor.should_receive(:coloring=).with(true)
324
+ Cucumber::Term::ANSIColor.should_receive(:coloring=).with(true)
325
325
  config.parse!(['--color'])
326
326
  end
327
327
 
328
328
  it "should accept --no-color option" do
329
- Term::ANSIColor.should_receive(:coloring=).with(false)
329
+ Cucumber::Term::ANSIColor.should_receive(:coloring=).with(false)
330
330
  config = Configuration.new(StringIO.new)
331
331
  config.parse!(['--no-color'])
332
332
  end
@@ -404,14 +404,20 @@ END_OF_MESSAGE
404
404
  end
405
405
 
406
406
  describe "#tag_expression" do
407
+ include RSpec::WorkInProgress
408
+
407
409
  it "returns an empty expression when no tags are specified" do
408
- config.parse!([])
409
- config.tag_expression.should be_empty
410
+ pending_under :java, 'the java class Gherkin::TagExpression has no isEmpty method' do
411
+ config.parse!([])
412
+ config.tag_expression.should be_empty
413
+ end
410
414
  end
411
415
 
412
416
  it "returns an expression when tags are specified" do
413
- config.parse!(['--tags','@foo'])
414
- config.tag_expression.should_not be_empty
417
+ pending_under :java, 'the java class Gherkin::TagExpression has no isEmpty method' do
418
+ config.parse!(['--tags','@foo'])
419
+ config.tag_expression.should_not be_empty
420
+ end
415
421
  end
416
422
  end
417
423
 
@@ -55,14 +55,20 @@ module Cli
55
55
 
56
56
  context '--i18n' do
57
57
  context "with LANG specified as 'help'" do
58
+ include RSpec::WorkInProgress
59
+
58
60
  it "lists all known langues" do
59
- when_parsing '--i18n help' do
60
- Kernel.should_receive(:exit)
61
+ pending_under :java, "require gherkin >= b5e96f13" do
62
+ when_parsing '--i18n help' do
63
+ Kernel.should_receive(:exit)
64
+ end
61
65
  end
62
66
  end
63
67
 
64
68
  it "exits the program" do
65
- when_parsing('--i18n help') { Kernel.should_receive(:exit) }
69
+ pending_under :java, "require gherkin >= b5e96f13" do
70
+ when_parsing('--i18n help') { Kernel.should_receive(:exit) }
71
+ end
66
72
  end
67
73
  end
68
74
  end
@@ -8,5 +8,9 @@ module Cucumber
8
8
  clazz = constantize('Cucumber::Formatter::Html')
9
9
  clazz.name.should == 'Cucumber::Formatter::Html'
10
10
  end
11
+
12
+ it "fails to load a made up class" do
13
+ expect { constantize('My::MadeUp::ClassName') }.to raise_error(LoadError)
14
+ end
11
15
  end
12
- end
16
+ end
@@ -23,7 +23,7 @@ module Cucumber
23
23
  end
24
24
 
25
25
  it "should not generate ansi codes when colors are disabled" do
26
- ::Term::ANSIColor.coloring = false
26
+ ::Cucumber::Term::ANSIColor.coloring = false
27
27
  passed("foo").should == "foo"
28
28
  end
29
29
  end
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+ require 'cucumber/formatter/interceptor'
3
+
4
+ module Cucumber::Formatter
5
+ describe Interceptor::Pipe do
6
+ let(:pipe) do
7
+ pipe = double('original pipe')
8
+ pipe.stub(:instance_of?).and_return(true)
9
+ pipe
10
+ end
11
+
12
+ describe '#wrap!' do
13
+ it 'should raise an ArgumentError if its not passed :stderr/:stdout' do
14
+ expect {
15
+ Interceptor::Pipe.wrap(:nonsense)
16
+ }.to raise_error(ArgumentError)
17
+
18
+ end
19
+
20
+ context 'when passed :stderr' do
21
+ before :each do
22
+ @stderr = $stdout
23
+ end
24
+
25
+ it 'should wrap $stderr' do
26
+ wrapped = Interceptor::Pipe.wrap(:stderr)
27
+ $stderr.should be_instance_of Interceptor::Pipe
28
+ $stderr.should be wrapped
29
+ end
30
+
31
+ after :each do
32
+ $stderr = @stderr
33
+ end
34
+ end
35
+
36
+ context 'when passed :stdout' do
37
+ before :each do
38
+ @stdout = $stdout
39
+ end
40
+
41
+ it 'should wrap $stdout' do
42
+ wrapped = Interceptor::Pipe.wrap(:stdout)
43
+ $stdout.should be_instance_of Interceptor::Pipe
44
+ $stdout.should be wrapped
45
+ end
46
+
47
+ after :each do
48
+ $stdout = @stdout
49
+ end
50
+ end
51
+ end
52
+
53
+ describe '#unwrap!' do
54
+ before :each do
55
+ @stdout = $stdout
56
+ @wrapped = Interceptor::Pipe.wrap(:stdout)
57
+ end
58
+
59
+ it 'should raise an ArgumentError if it wasn\'t passed :stderr/:stdout' do
60
+ expect {
61
+ Interceptor::Pipe.unwrap!(:nonsense)
62
+ }.to raise_error(ArgumentError)
63
+ end
64
+
65
+ it 'should reset $stdout when #unwrap! is called' do
66
+ interceptor = Interceptor::Pipe.unwrap! :stdout
67
+ interceptor.should be_instance_of Interceptor::Pipe
68
+ $stdout.should_not be interceptor
69
+ end
70
+
71
+ it 'should disable the pipe bypass' do
72
+ buffer = '(::)'
73
+ Interceptor::Pipe.unwrap! :stdout
74
+
75
+ @wrapped.should_receive(:write).with(buffer)
76
+ @wrapped.buffer.should_not_receive(:<<)
77
+ @wrapped.write(buffer)
78
+ end
79
+
80
+ after :each do
81
+ $stdout = @stdout
82
+ end
83
+ end
84
+
85
+ describe '#write' do
86
+ let(:buffer) { 'Some stupid buffer' }
87
+ let(:pi) { Interceptor::Pipe.new(pipe) }
88
+
89
+ it 'should write arguments to the original pipe' do
90
+ pipe.should_receive(:write).with(buffer).and_return(buffer.size)
91
+ pi.write(buffer).should == buffer.size
92
+ end
93
+
94
+ it 'should add the buffer to its stored output' do
95
+ pipe.stub(:write)
96
+ pi.write(buffer)
97
+ pi.buffer.should_not be_empty
98
+ pi.buffer.first.should == buffer
99
+ end
100
+ end
101
+
102
+ describe '#method_missing' do
103
+ let(:pi) { Interceptor::Pipe.new(pipe) }
104
+
105
+ it 'should pass #tty? to the original pipe' do
106
+ pipe.should_receive(:tty?).and_return(true)
107
+ pi.tty?.should be true
108
+ end
109
+ end
110
+ end
111
+ end