cucumber 0.1.6 → 0.1.7

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 (44) hide show
  1. data/History.txt +53 -0
  2. data/Manifest.txt +4 -0
  3. data/Rakefile +1 -0
  4. data/examples/calculator/cucumber.yml +1 -1
  5. data/examples/dos_line_endings/Rakefile +6 -0
  6. data/examples/dos_line_endings/features/dos_line_endings.feature +9 -0
  7. data/examples/tickets/features/steps/tickets_steps.rb +13 -1
  8. data/examples/tickets/features/tickets.feature +5 -1
  9. data/gem_tasks/treetop.rake +10 -0
  10. data/generators/cucumber/templates/common_webrat.rb +1 -1
  11. data/lib/cucumber.rb +3 -1
  12. data/lib/cucumber/cli.rb +16 -15
  13. data/lib/cucumber/core_ext/proc.rb +11 -2
  14. data/lib/cucumber/executor.rb +32 -15
  15. data/lib/cucumber/formatters/ansicolor.rb +25 -3
  16. data/lib/cucumber/formatters/pretty_formatter.rb +44 -15
  17. data/lib/cucumber/languages.yml +10 -0
  18. data/lib/cucumber/step_methods.rb +8 -4
  19. data/lib/cucumber/step_mother.rb +4 -3
  20. data/lib/cucumber/tree/scenario.rb +22 -10
  21. data/lib/cucumber/tree/step.rb +9 -1
  22. data/lib/cucumber/treetop_parser/feature.treetop.erb +5 -5
  23. data/lib/cucumber/treetop_parser/feature_da.rb +146 -108
  24. data/lib/cucumber/treetop_parser/feature_de.rb +146 -108
  25. data/lib/cucumber/treetop_parser/feature_en.rb +146 -108
  26. data/lib/cucumber/treetop_parser/feature_es.rb +146 -108
  27. data/lib/cucumber/treetop_parser/feature_et.rb +146 -108
  28. data/lib/cucumber/treetop_parser/feature_fr.rb +146 -108
  29. data/lib/cucumber/treetop_parser/feature_no.rb +146 -108
  30. data/lib/cucumber/treetop_parser/feature_parser.rb +4 -2
  31. data/lib/cucumber/treetop_parser/feature_pt.rb +146 -108
  32. data/lib/cucumber/treetop_parser/feature_ru.rb +146 -108
  33. data/lib/cucumber/treetop_parser/feature_se.rb +146 -108
  34. data/lib/cucumber/version.rb +1 -1
  35. data/spec/cucumber/cli_spec.rb +14 -4
  36. data/spec/cucumber/executor_spec.rb +4 -3
  37. data/spec/cucumber/formatters/ansicolor_spec.rb +4 -0
  38. data/spec/cucumber/formatters/pretty_formatter_spec.rb +67 -2
  39. data/spec/cucumber/tree/scenario_spec.rb +2 -2
  40. data/spec/cucumber/tree/step_spec.rb +21 -0
  41. data/spec/cucumber/treetop_parser/feature_parser_spec.rb +5 -0
  42. data/spec/cucumber/treetop_parser/test_dos.feature +24 -0
  43. data/spec/spec_helper.rb +1 -7
  44. metadata +6 -2
@@ -1,3 +1,56 @@
1
+ == 0.1.7
2
+
3
+ This release fixes a few bugs and adds some new features. The most notable features are:
4
+
5
+ === Calling steps from steps
6
+
7
+ Step definitions are a little bit closer to having regular method semantics.
8
+ You define them, but now you can also call them from other steps. Here is an
9
+ example:
10
+
11
+ Given /I am logged in as an (.*) named (.*)$/ do |role, name|
12
+ Given "I am registered as #{role}, #{name}, secret"
13
+ When "I log in with #{name}, secret"
14
+ end
15
+
16
+ Given /I am registered as (.*), (.*), (.*)/ do |role, name, password|
17
+ # (Code removed for brevity)
18
+ end
19
+
20
+ When /I log in with (.*), (.*)/ do |name, password|
21
+ # (Code removed for brevity)
22
+ end
23
+
24
+ This means that steps can be reused in other steps. The GivenScenario feature achieves a similar
25
+ effect (on the scenario level), but this feature is something we're not very happy with, mostly
26
+ because it's not parameterisable. Calling steps from steps is.
27
+
28
+ GivenScenario will still be working several releases, but the plan is to remove it completely in
29
+ the 0.3.0 release.
30
+
31
+ === Seeing where a step is defined
32
+
33
+ Prior to this release it could be hard to find out where the ruby step definition matching
34
+ a plain text step is defined. Not anymore! Cucumber will now output this:
35
+
36
+ Scenario: Regular numbers
37
+ Given I have entered 3 into the calculator # features/steps/calculator_steps.rb:12
38
+ And I have entered 2 into the calculator # features/steps/calculator_steps.rb:12
39
+ When I press divide # features/steps/calculator_steps.rb:16
40
+ Then the result should be 1.5 on the screen # features/steps/calculator_steps.rb:20
41
+ And the result class should be Float # features/steps/calculator_steps.rb:24
42
+
43
+ === Bugfixes
44
+ * Fixed a bug in the command line args being lost when using --profile (#27, Joseph Wilk)
45
+ * Fixed a bug in Webrat selects (Tim Glen)
46
+ * Fixed parsing of DOS line endings (#2, #28, Aslak Hellesøy)
47
+
48
+ === New features
49
+ * Steps can be called from other steps (#3, Bryan Helmkamp, Aslak Hellesøy)
50
+ * Added But keyword to all languages (#21, Aslak Hellesøy)
51
+ * Added --no-source option to display step definition location next to step text (#26, Joseph Wilk, Aslak Hellesøy)
52
+ * Added more Webrat steps (#25, Tim Glen)
53
+
1
54
  == 0.1.6
2
55
 
3
56
  First gem release!
@@ -31,6 +31,8 @@ examples/danish_calculator/Rakefile
31
31
  examples/danish_calculator/features/steps/kalkulator_steps.rb
32
32
  examples/danish_calculator/features/summering.feature
33
33
  examples/danish_calculator/lib/kalkulator.rb
34
+ examples/dos_line_endings/Rakefile
35
+ examples/dos_line_endings/features/dos_line_endings.feature
34
36
  examples/estonian_calculator/Rakefile
35
37
  examples/estonian_calculator/features/kalkulaator_steps.rb
36
38
  examples/estonian_calculator/features/liitmine.feature
@@ -140,6 +142,7 @@ spec/cucumber/formatters/progress_formatter_spec.rb
140
142
  spec/cucumber/sell_cucumbers.feature
141
143
  spec/cucumber/step_mother_spec.rb
142
144
  spec/cucumber/tree/scenario_spec.rb
145
+ spec/cucumber/tree/step_spec.rb
143
146
  spec/cucumber/treetop_parser/empty_feature.feature
144
147
  spec/cucumber/treetop_parser/empty_scenario.feature
145
148
  spec/cucumber/treetop_parser/feature_parser_spec.rb
@@ -147,6 +150,7 @@ spec/cucumber/treetop_parser/fit_scenario.feature
147
150
  spec/cucumber/treetop_parser/given_scenario.feature
148
151
  spec/cucumber/treetop_parser/multiple_tables.feature
149
152
  spec/cucumber/treetop_parser/spaces.feature
153
+ spec/cucumber/treetop_parser/test_dos.feature
150
154
  spec/cucumber/treetop_parser/with_comments.feature
151
155
  spec/spec.opts
152
156
  spec/spec_helper.rb
data/Rakefile CHANGED
@@ -1,3 +1,4 @@
1
+ ENV['NODOT'] = 'true' # We don't want class diagrams in RDoc
1
2
  require 'config/requirements'
2
3
  require 'config/hoe' # setup Hoe + all gem configuration
3
4
 
@@ -1 +1 @@
1
- default: --format progress features
1
+ default: --format pretty features
@@ -0,0 +1,6 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../../lib')
2
+ require 'cucumber/rake/task'
3
+
4
+ Cucumber::Rake::Task.new do |t|
5
+ t.cucumber_opts = "--format pretty"
6
+ end
@@ -0,0 +1,9 @@
1
+ Feature: DOS line endings
2
+ In order to have less bug reports
3
+ As a windows developer
4
+ I want to write features with DOS line endigs
5
+
6
+ Scenario: Just lots of DOS
7
+ Given I am on DOS
8
+ And Any version of Windows is really just DOS
9
+ Then Windows still sucks
@@ -2,4 +2,16 @@ require 'spec'
2
2
 
3
3
  Given "be_empty" do
4
4
  [1,2].should_not be_empty
5
- end
5
+ end
6
+
7
+ Given "nested step is called" do
8
+ Given "nested step"
9
+ end
10
+
11
+ Given "nested step" do
12
+ @magic = 'mushroom'
13
+ end
14
+
15
+ Then "nested step should be executed" do
16
+ @magic.should == 'mushroom'
17
+ end
@@ -4,4 +4,8 @@ Feature: Cucumber
4
4
  I don't want no stinkin bugs
5
5
 
6
6
  Scenario: RSpec be_*
7
- Given be_empty
7
+ Given be_empty
8
+
9
+ Scenario: Call step from step
10
+ Given nested step is called
11
+ Then nested step should be executed
@@ -9,12 +9,22 @@ class FeatureCompiler
9
9
 
10
10
  langs.each do |lang, words|
11
11
  grammar_file = File.dirname(__FILE__) + "/../lib/cucumber/treetop_parser/feature_#{lang}.treetop"
12
+ ruby_file = File.dirname(__FILE__) + "/../lib/cucumber/treetop_parser/feature_#{lang}.rb"
12
13
  grammar = template.result(binding)
13
14
  File.open(grammar_file, "wb") do |io|
14
15
  io.write(grammar)
15
16
  end
16
17
  sh "#{tt} #{grammar_file}"
17
18
  FileUtils.rm(grammar_file)
19
+
20
+ # Change code so it isn't part of RDoc
21
+ lines = IO.read(ruby_file).split("\n")
22
+ lines.each do |line|
23
+ if line =~ /\s*(def|class|module)/
24
+ line << " #:nodoc:"
25
+ end
26
+ end
27
+ File.open(ruby_file, 'wb'){|io| io.write(lines.join("\n"))}
18
28
  end
19
29
  end
20
30
  end
@@ -14,7 +14,7 @@ When /^I fill in "(.*)" with "(.*)"$/ do |field, value|
14
14
  end
15
15
 
16
16
  When /^I select "(.*)" from "(.*)"$/ do |field, value|
17
- selects(field, :with => value)
17
+ selects(value, :from => field)
18
18
  end
19
19
 
20
20
  When /^I check "(.*)"$/ do |field|
@@ -13,6 +13,8 @@ require 'cucumber/treetop_parser/feature_parser'
13
13
  require 'cucumber/cli'
14
14
 
15
15
  module Cucumber
16
+ LANGUAGE_FILE = File.expand_path(File.dirname(__FILE__) + '/cucumber/languages.yml')
17
+
16
18
  class << self
17
19
  attr_reader :language
18
20
 
@@ -26,7 +28,7 @@ module Cucumber
26
28
 
27
29
  def config
28
30
  require 'yaml'
29
- @config ||= YAML.load_file(File.dirname(__FILE__) + '/cucumber/languages.yml')
31
+ @config ||= YAML.load_file(LANGUAGE_FILE)
30
32
  end
31
33
  end
32
34
  end
@@ -4,11 +4,11 @@ require 'cucumber'
4
4
  module Cucumber
5
5
  class CLI
6
6
  class << self
7
- attr_writer :step_mother, :features
7
+ attr_writer :step_mother, :executor, :features
8
8
 
9
9
  def execute
10
10
  @execute_called = true
11
- parse(ARGV).execute!(@step_mother, @features)
11
+ parse(ARGV).execute!(@step_mother, @executor, @features)
12
12
  end
13
13
 
14
14
  def execute_called?
@@ -33,7 +33,7 @@ module Cucumber
33
33
  return parse_args_from_profile('default') if args.empty?
34
34
  args.extend(OptionParser::Arguable)
35
35
 
36
- @options = { :require => nil, :lang => 'en', :format => 'pretty', :dry_run => false }
36
+ @options ||= { :require => nil, :lang => 'en', :format => 'pretty', :dry_run => false, :source => true }
37
37
  args.options do |opts|
38
38
  opts.banner = "Usage: cucumber [options] FILES|DIRS"
39
39
  opts.on("-r LIBRARY|DIR", "--require LIBRARY|DIR", "Require files before executing the features.",
@@ -46,7 +46,8 @@ module Cucumber
46
46
  @options[:line] = v
47
47
  end
48
48
  opts.on("-a LANG", "--language LANG", "Specify language for features (Default: #{@options[:lang]})",
49
- "Available languages: #{Cucumber.languages.join(", ")}") do |v|
49
+ "Available languages: #{Cucumber.languages.join(", ")}",
50
+ "Look at #{Cucumber::LANGUAGE_FILE} for keywords") do |v|
50
51
  @options[:lang] = v
51
52
  end
52
53
  opts.on("-f FORMAT", "--format FORMAT", "How to format features (Default: #{@options[:format]})",
@@ -64,6 +65,9 @@ module Cucumber
64
65
  opts.on("-d", "--dry-run", "Invokes formatters without executing the steps.") do
65
66
  @options[:dry_run] = true
66
67
  end
68
+ opts.on("-n", "--no-source", "Don't show the file and line of the step definition with the steps.") do
69
+ @options[:source] = false
70
+ end
67
71
  opts.on_tail("--version", "Show version") do
68
72
  puts VERSION::STRING
69
73
  exit
@@ -86,14 +90,14 @@ module Cucumber
86
90
  parse_options!(args_from_yml.split(' '))
87
91
  end
88
92
 
89
- def execute!(step_mother, features)
93
+ def execute!(step_mother, executor, features)
90
94
  Cucumber.load_language(@options[:lang])
91
- $executor = Executor.new(formatter(step_mother), step_mother)
95
+ executor.formatter = formatter(step_mother)
92
96
  require_files
93
97
  load_plain_text_features(features)
94
- $executor.line = @options[:line].to_i if @options[:line]
95
- $executor.visit_features(features)
96
- exit 1 if $executor.failed
98
+ executor.line = @options[:line].to_i if @options[:line]
99
+ executor.visit_features(features)
100
+ exit 1 if executor.failed
97
101
  end
98
102
 
99
103
  private
@@ -141,7 +145,7 @@ module Cucumber
141
145
  def formatter(step_mother)
142
146
  case @options[:format]
143
147
  when 'pretty'
144
- Formatters::PrettyFormatter.new(STDOUT)
148
+ Formatters::PrettyFormatter.new(STDOUT, step_mother, @options)
145
149
  when 'progress'
146
150
  Formatters::ProgressFormatter.new(STDOUT)
147
151
  when 'html'
@@ -154,10 +158,7 @@ end
154
158
 
155
159
  extend Cucumber::StepMethods
156
160
  Cucumber::CLI.step_mother = step_mother
161
+ Cucumber::CLI.executor = executor
157
162
 
158
- extend(Cucumber::Tree)
163
+ extend Cucumber::Tree
159
164
  Cucumber::CLI.features = features
160
-
161
- at_exit do
162
- Cucumber::CLI.execute unless Cucumber::CLI.execute_called?
163
- end
@@ -23,10 +23,19 @@ module Cucumber
23
23
  arity == -1 ? 0 : arity
24
24
  end
25
25
 
26
- def backtrace_line
27
- to_s.match(/[\d\w]+@(.*)>/)[1] + ":in `#{name}'"
26
+ def to_backtrace_line
27
+ "#{file}:in `#{name}'"
28
28
  end
29
29
 
30
+ def to_comment_line
31
+ "# #{file}"
32
+ end
33
+
34
+ def file
35
+ file = to_s.match(/[\d\w]+@(.*)>/)[1]
36
+ file =~ /^\.\/(.*)/ ? $1 : file
37
+ end
38
+
30
39
  def meth
31
40
  @meth ||= "__cucumber_#{object_id}"
32
41
  end
@@ -3,17 +3,15 @@ require 'cucumber/core_ext/proc'
3
3
  module Cucumber
4
4
  class Executor
5
5
  attr_reader :failed
6
+ attr_accessor :formatter
6
7
 
7
8
  def line=(line)
8
9
  @line = line
9
10
  end
10
11
 
11
- def initialize(formatter, step_mother)
12
- @formatter = formatter
12
+ def initialize(step_mother)
13
13
  @world_proc = lambda do
14
- world = Object.new
15
- world.extend(Spec::Matchers) if defined?(Spec::Matchers)
16
- world
14
+ Object.new
17
15
  end
18
16
  @before_scenario_procs = []
19
17
  @after_scenario_procs = []
@@ -42,9 +40,9 @@ module Cucumber
42
40
 
43
41
  def visit_features(features)
44
42
  raise "Line number can only be specified when there is 1 feature. There were #{features.length}." if @line && features.length != 1
45
- @formatter.visit_features(features) if @formatter.respond_to?(:visit_features)
43
+ formatter.visit_features(features) if formatter.respond_to?(:visit_features)
46
44
  features.accept(self)
47
- @formatter.dump
45
+ formatter.dump
48
46
  end
49
47
 
50
48
  def visit_feature(feature)
@@ -52,7 +50,7 @@ module Cucumber
52
50
  end
53
51
 
54
52
  def visit_header(header)
55
- @formatter.header_executing(header) if @formatter.respond_to?(:header_executing)
53
+ formatter.header_executing(header) if formatter.respond_to?(:header_executing)
56
54
  end
57
55
 
58
56
  def visit_row_scenario(scenario)
@@ -67,12 +65,16 @@ module Cucumber
67
65
  if @line.nil? || scenario.at_line?(@line)
68
66
  @error = nil
69
67
  @pending = nil
68
+
70
69
  @world = @world_proc.call
71
- @formatter.scenario_executing(scenario) if @formatter.respond_to?(:scenario_executing)
70
+ @world.extend(Spec::Matchers) if defined?(Spec::Matchers)
71
+ define_step_call_methods(@world)
72
+
73
+ formatter.scenario_executing(scenario) if formatter.respond_to?(:scenario_executing)
72
74
  @before_scenario_procs.each{|p| p.call_in(@world, *[])}
73
75
  scenario.accept(self)
74
76
  @after_scenario_procs.each{|p| p.call_in(@world, *[])}
75
- @formatter.scenario_executed(scenario) if @formatter.respond_to?(:scenario_executed)
77
+ formatter.scenario_executed(scenario) if formatter.respond_to?(:scenario_executed)
76
78
  end
77
79
  end
78
80
 
@@ -90,31 +92,46 @@ module Cucumber
90
92
  regexp, args, proc = step.regexp_args_proc(@step_mother)
91
93
  step.execute_in(@world, regexp, args, proc)
92
94
  @after_step_procs.each{|p| p.call_in(@world, *[])}
93
- @formatter.step_passed(step, regexp, args)
95
+ formatter.step_passed(step, regexp, args)
94
96
  rescue Pending
95
97
  record_pending_step(step, regexp, args)
96
98
  rescue => e
97
99
  @failed = true
98
100
  @error = step.error = e
99
- @formatter.step_failed(step, regexp, args)
101
+ formatter.step_failed(step, regexp, args)
100
102
  end
101
103
  else
102
104
  begin
103
105
  regexp, args, proc = step.regexp_args_proc(@step_mother)
104
106
  step.execute_in(@world, regexp, args, proc)
105
- @formatter.step_skipped(step, regexp, args)
107
+ formatter.step_skipped(step, regexp, args)
106
108
  rescue Pending
107
109
  record_pending_step(step, regexp, args)
108
110
  rescue Exception
109
- @formatter.step_skipped(step, regexp, args)
111
+ formatter.step_skipped(step, regexp, args)
110
112
  end
111
113
  end
112
114
  end
113
115
 
114
116
  def record_pending_step(step, regexp, args)
115
117
  @pending = true
116
- @formatter.step_pending(step, regexp, args)
118
+ formatter.step_pending(step, regexp, args)
117
119
  end
118
120
 
121
+ def define_step_call_methods(world)
122
+ world.instance_variable_set('@__executor', self)
123
+ world.instance_eval do
124
+ class << self
125
+ def run_step(name)
126
+ _, args, proc = @__executor.instance_variable_get(:@step_mother).regexp_args_proc(name)
127
+ proc.call_in(self, *args)
128
+ end
129
+
130
+ %w{given when then and but}.each do |keyword|
131
+ alias_method Cucumber.language[keyword], :run_step
132
+ end
133
+ end
134
+ end
135
+ end
119
136
  end
120
137
  end
@@ -1,11 +1,22 @@
1
+ gem 'term-ansicolor'
2
+ # Hack to work around Win32/Console, which bundles a licence-violating, outdated
3
+ # copy of term/ansicolor that doesn't implement Term::ANSIColor#coloring=.
4
+ # We want the official one!
5
+ $LOAD_PATH.each{|path| $LOAD_PATH.unshift($LOAD_PATH.delete(path)) if path =~ /term-ansicolor/}
6
+
1
7
  require 'term/ansicolor'
8
+ require 'rbconfig'
9
+
10
+ win = Config::CONFIG['host_os'] =~ /mswin|mingw/
11
+ jruby = defined?(JRUBY_VERSION)
12
+
2
13
  begin
3
- require 'Win32/Console/ANSI' if PLATFORM =~ /mswin|mingw/
14
+ require 'Win32/Console/ANSI' if (win && !jruby)
4
15
  rescue LoadError
5
16
  STDERR.puts "You must gem install win32console to get coloured output on this ruby platform (#{PLATFORM})"
6
17
  ::Term::ANSIColor.coloring = false
7
18
  end
8
- ::Term::ANSIColor.coloring = false if !STDOUT.tty?
19
+ ::Term::ANSIColor.coloring = false if !STDOUT.tty? || (win && jruby)
9
20
 
10
21
  module Cucumber
11
22
  module Formatters
@@ -44,6 +55,7 @@ module Cucumber
44
55
  # ** magenta
45
56
  # ** cyan
46
57
  # ** white
58
+ # ** grey
47
59
  # ** on_black
48
60
  # ** on_red
49
61
  # ** on_green
@@ -67,7 +79,8 @@ module Cucumber
67
79
  :failed_param => "#{param}red",
68
80
  :skipped => 'bold,cyan',
69
81
  :skipped_param => "#{param}cyan",
70
- :pending => 'bold,yellow' # No pending_param
82
+ :pending => 'bold,yellow', # No pending_param
83
+ :comment => 'grey'
71
84
  }
72
85
  if ENV['CUCUMBER_COLORS']
73
86
  ENV['CUCUMBER_COLORS'].split(':').each do |pair|
@@ -76,6 +89,15 @@ module Cucumber
76
89
  end
77
90
  end
78
91
 
92
+ #Not supported in Term::ANSIColor
93
+ def grey(m)
94
+ if ENV['CUCUMBER_COLORS_DISABLED'] == '1'
95
+ m
96
+ else
97
+ "\e[90m#{m}\e[0m"
98
+ end
99
+ end
100
+
79
101
  ALIASES.each do |m, color_string|
80
102
  colors = color_string.split(",").reverse
81
103
  define_method(m) do |*s|