cucumber 0.1.6 → 0.1.7

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