aslakhellesoy-cucumber 0.1.99.15 → 0.1.99.17

Sign up to get free protection for your applications and to get access to all the features.
@@ -94,6 +94,7 @@ pure Ruby users have been enjoying for a while.
94
94
  * Run cucumber --language LANG help to see keywords for a given language. (Aslak Hellesøy)
95
95
  * Multiline arguments (tables and """ strings) are printed in the output. (Aslak Hellesøy)
96
96
  * It's no longer necessary to compile the Treetop grammar when adding a new language. Localised parser is generated at runtime. (Aslak Hellesøy)
97
+ * New --guess option tries to guess the best step definition match instead of raising Cucumber::Multiple. (Jake Howerton)
97
98
 
98
99
  === Removed features
99
100
  * "GivenScenario" is gone. Instead you can call Steps from Steps, or wait for "Background (#153)"
@@ -138,6 +138,9 @@ examples/test_unit/features/test_unit.feature
138
138
  examples/tickets/Rakefile
139
139
  examples/tickets/cucumber.yml
140
140
  examples/tickets/features/172.feature
141
+ examples/tickets/features/177-1.feature
142
+ examples/tickets/features/177-2.feature
143
+ examples/tickets/features/180.feature
141
144
  examples/tickets/features/lib/eatting_machine.rb
142
145
  examples/tickets/features/lib/pantry.rb
143
146
  examples/tickets/features/scenario_outline.feature
@@ -188,6 +191,7 @@ lib/cucumber/ast/table.rb
188
191
  lib/cucumber/ast/tags.rb
189
192
  lib/cucumber/ast/visitor.rb
190
193
  lib/cucumber/broadcaster.rb
194
+ lib/cucumber/cli/configuration.rb
191
195
  lib/cucumber/cli/language_help_formatter.rb
192
196
  lib/cucumber/cli/main.rb
193
197
  lib/cucumber/core_ext/exception.rb
@@ -240,6 +244,7 @@ spec/cucumber/ast/scenario_spec.rb
240
244
  spec/cucumber/ast/step_spec.rb
241
245
  spec/cucumber/ast/table_spec.rb
242
246
  spec/cucumber/broadcaster_spec.rb
247
+ spec/cucumber/cli/configuration_spec.rb
243
248
  spec/cucumber/cli/main_spec.rb
244
249
  spec/cucumber/core_ext/proc_spec.rb
245
250
  spec/cucumber/core_ext/string_spec.rb
@@ -0,0 +1,29 @@
1
+ Users want to know that nobody can masquerade as them. We want to extend trust
2
+ only to visitors who present the appropriate credentials. Everyone wants this
3
+ identity verification to be as secure and convenient as possible.
4
+
5
+ Feature: Logging in
6
+ As an anonymous user with an account
7
+ I want to log in to my account
8
+ So that I can be myself
9
+
10
+ #
11
+ # Log in: get form
12
+ #
13
+ Scenario: Anonymous user can get a login form.
14
+ Given I am logged out
15
+ When I go to "/login"
16
+ Then I should be at the "sessions/new" page
17
+
18
+ #
19
+ # Log in successfully, but don't remember me
20
+ #
21
+ Scenario: Anonymous user can log in
22
+ Given an "activated" user named "reggie" exists
23
+ And I am logged out
24
+ When I go to "/login"
25
+ And I fill in "Login" with "reggie"
26
+ And I fill in "Password" with "password"
27
+ And I press "Log in"
28
+ Then I should be at the "dashboard/index" page
29
+
@@ -0,0 +1,21 @@
1
+ Visitors may create an account, but for those who are not already in the
2
+ system an someone must activate the account for them before it can be used.
3
+
4
+ Feature: Activating an account
5
+ As a registered, but not yet activated, user
6
+ I want to be able to activate my account
7
+ So that I can log in to the site
8
+
9
+ Scenario: Not-yet-activated user can activate her account
10
+ Given a registered user named 'Reggie' # need to rewrite
11
+ # And the user has activation_code: 'activate_me', activated_at: nil!
12
+ # And we try hard to remember the user's updated_at, and created_at
13
+ # When she goes to /activate/activate_me
14
+ # Then she should be redirected to 'login'
15
+ # When she follows that redirect!
16
+ # Then she should see a notice message 'Signup complete!'
17
+ # And a user with login: 'reggie' should exist
18
+ # And the user should have login: 'reggie', and email: 'registered@example.com'
19
+ # And the user's activation_code should be nil
20
+ # And the user's activated_at should not be nil
21
+ # And she should not be logged in
@@ -0,0 +1,7 @@
1
+ Feature: Cucumber command line
2
+ In order to write better software
3
+ Developers should be able to execute requirements as tests
4
+
5
+
6
+ Scenario: Pending Scenario at the end of a file with whitespace after it
7
+
@@ -28,7 +28,7 @@ Feature: Cucumber command line
28
28
  @two @three
29
29
  Scenario: Missing
30
30
  Given missing
31
- Undefined step: "missing" (Cucumber::StepMother::Undefined)
31
+ Undefined step: "missing" (Cucumber::Undefined)
32
32
  features/sample.feature:6:in `Given missing'
33
33
 
34
34
  1 scenario
@@ -10,12 +10,12 @@ Feature: Cucumber command line
10
10
 
11
11
  Scenario: Call directly # features/call_undefined_step_from_step_def.feature:3
12
12
  Given a step definition that calls an undefined step # features/step_definitions/sample_steps.rb:19
13
- Undefined step: "this does not exist" (Cucumber::StepMother::Undefined)
13
+ Undefined step: "this does not exist" (Cucumber::Undefined)
14
14
  ./features/step_definitions/sample_steps.rb:20:in `/^a step definition that calls an undefined step$/'
15
15
 
16
16
  Scenario: Call via another # features/call_undefined_step_from_step_def.feature:6
17
17
  Given call step "a step definition that calls an undefined step" # features/step_definitions/sample_steps.rb:23
18
- Undefined step: "this does not exist" (Cucumber::StepMother::Undefined)
18
+ Undefined step: "this does not exist" (Cucumber::Undefined)
19
19
  ./features/step_definitions/sample_steps.rb:20:in `/^a step definition that calls an undefined step$/'
20
20
 
21
21
  2 scenarios
@@ -1,3 +1,4 @@
1
+ require 'rubygems'
1
2
  require 'spec/expectations'
2
3
  require 'fileutils'
3
4
 
@@ -0,0 +1,67 @@
1
+ # http://blog.nicksieger.com/articles/2009/01/10/jruby-1-1-6-gems-in-a-jar
2
+
3
+ USE_JRUBY_VERSION = '1.1.6'
4
+ USE_JBEHAVE_VERSION = '2.1'
5
+ USE_JUNIT_VERSION = '4.5'
6
+ USE_HAMCREST_VERSION = '1.1'
7
+ CUCUMBER_VERSIONED = "cucumber-#{Cucumber::VERSION::STRING}"
8
+
9
+ task :jar => [
10
+ :clean,
11
+ 'jar:download_jruby',
12
+ 'jar:install_gems',
13
+ 'jar:bundle_gems',
14
+ 'jar:download_jars_deps',
15
+ 'jar:unpack_jar_deps',
16
+ 'jar:bundle_jars',
17
+ 'jar:fix_gem_binaries',
18
+ 'jar:test_jar'
19
+ ]
20
+
21
+ namespace :jar do
22
+ task :download_jruby do
23
+ sh "wget http://dist.codehaus.org/jruby/#{USE_JRUBY_VERSION}/jruby-complete-#{USE_JRUBY_VERSION}.jar -O #{CUCUMBER_VERSIONED}.jar"
24
+ end
25
+
26
+ task :install_gems => :gem do
27
+ mkdir 'pkg/jar_gems'
28
+ sh "java -jar #{CUCUMBER_VERSIONED}.jar -S gem install -i ./pkg/jar_gems pkg/#{CUCUMBER_VERSIONED}.gem --no-ri --no-rdoc"
29
+ end
30
+
31
+ task :bundle_gems do
32
+ sh "jar uf #{CUCUMBER_VERSIONED}.jar -C pkg/jar_gems ."
33
+ end
34
+
35
+ task :download_jars_deps do
36
+ mkdir 'pkg/jar_deps'
37
+ sh "wget http://repository.codehaus.org/org/jbehave/jbehave-core/#{USE_JBEHAVE_VERSION}/jbehave-core-#{USE_JBEHAVE_VERSION}.jar -O pkg/jar_deps/jbehave-core-#{USE_JBEHAVE_VERSION}.jar"
38
+ sh "wget http://mirrors.ibiblio.org/pub/mirrors/maven2/junit/junit/#{USE_JUNIT_VERSION}/junit-#{USE_JUNIT_VERSION}.jar -O pkg/jar_deps/junit-#{USE_JUNIT_VERSION}.jar"
39
+ sh "wget http://hamcrest.googlecode.com/files/hamcrest-all-#{USE_HAMCREST_VERSION}.jar -O pkg/jar_deps/hamcrest-all-#{USE_HAMCREST_VERSION}.jar"
40
+ end
41
+
42
+ task :unpack_jar_deps do
43
+ Dir.chdir 'pkg/jar_deps' do
44
+ Dir['*.jar'].each do |jar|
45
+ sh "jar xvf #{jar}"
46
+ rm_rf jar
47
+ rm_rf 'META-INF'
48
+ end
49
+ end
50
+ end
51
+
52
+ task :bundle_jars do
53
+ sh "jar uf #{CUCUMBER_VERSIONED}.jar -C pkg/jar_deps ."
54
+ end
55
+
56
+ task :fix_gem_binaries do
57
+ mkdir_p 'pkg/gem_binaries/META-INF/jruby.home'
58
+ Dir.chdir 'pkg/gem_binaries/META-INF/jruby.home' do
59
+ sh "jar xvf ../../../../#{CUCUMBER_VERSIONED}.jar bin"
60
+ end
61
+ sh "jar uf #{CUCUMBER_VERSIONED}.jar -C pkg/gem_binaries ."
62
+ end
63
+
64
+ task :test_jar do
65
+ sh "java -cp examples/jbehave/target/classes -jar #{CUCUMBER_VERSIONED}.jar -S cucumber examples/jbehave/features"
66
+ end
67
+ end
@@ -3,7 +3,6 @@ $:.unshift(File.dirname(__FILE__)) unless
3
3
 
4
4
  require 'yaml'
5
5
  require 'cucumber/platform'
6
- require 'rubygems'
7
6
  require 'cucumber/parser'
8
7
  require 'cucumber/version'
9
8
  require 'cucumber/step_mother'
@@ -104,14 +104,14 @@ module Cucumber
104
104
  else
105
105
  @status = :skipped
106
106
  end
107
- rescue StepMother::Undefined => exception
107
+ rescue Undefined => exception
108
108
  if visitor.options[:strict]
109
109
  exception.set_backtrace([])
110
110
  failed(exception)
111
111
  else
112
112
  @status = :undefined
113
113
  end
114
- rescue StepMother::Pending => exception
114
+ rescue Pending => exception
115
115
  visitor.options[:strict] ? failed(exception) : @status = :pending
116
116
  rescue Exception => exception
117
117
  failed(exception)
@@ -0,0 +1,336 @@
1
+ module Cucumber
2
+ module Cli
3
+ class YmlLoadError < StandardError; end
4
+
5
+ class Configuration
6
+ FORMATS = %w{pretty profile progress rerun}
7
+ DEFAULT_FORMAT = 'pretty'
8
+
9
+ attr_reader :paths
10
+ attr_reader :options
11
+
12
+ def initialize(out_stream = STDOUT, error_stream = STDERR)
13
+ @out_stream = out_stream
14
+ @error_stream = error_stream
15
+
16
+ @paths = []
17
+ @options = default_options
18
+ @active_format = DEFAULT_FORMAT
19
+ end
20
+
21
+ def parse!(args)
22
+ @args = args
23
+ return parse_args_from_profile('default') if @args.empty?
24
+ @args.extend(::OptionParser::Arguable)
25
+
26
+ @args.options do |opts|
27
+ opts.banner = ["Usage: cucumber [options] [ [FILE|DIR|URL][:LINE[:LINE]*] ]+", "",
28
+ "Examples:",
29
+ "cucumber examples/i18n/en/features",
30
+ "cucumber --language it examples/i18n/it/features/somma.feature:6:98:113",
31
+ "cucumber -n -i http://rubyurl.com/eeCl", "", "",
32
+ ].join("\n")
33
+ opts.on("-r LIBRARY|DIR", "--require LIBRARY|DIR",
34
+ "Require files before executing the features. If this",
35
+ "option is not specified, all *.rb files that are",
36
+ "siblings or below the features will be loaded auto-",
37
+ "matically. Automatic loading is disabled when this",
38
+ "option is specified, and all loading becomes explicit.",
39
+ "Files under directories named \"support\" are always",
40
+ "loaded first.",
41
+ "This option can be specified multiple times.") do |v|
42
+ @options[:require] ||= []
43
+ @options[:require] << v
44
+ end
45
+ opts.on("-l LANG", "--language LANG",
46
+ "Specify language for features (Default: #{@options[:lang]})",
47
+ %{Run with "--language help" to see all languages},
48
+ %{Run with "--language LANG help" to list keywords for LANG}) do |v|
49
+ if v == 'help'
50
+ list_languages
51
+ elsif args==['help']
52
+ list_keywords(v)
53
+ else
54
+ @options[:lang] = v
55
+ end
56
+ end
57
+ opts.on("-f FORMAT", "--format FORMAT",
58
+ "How to format features (Default: #{DEFAULT_FORMAT})",
59
+ "Available formats: #{FORMATS.join(", ")}",
60
+ "You can also provide your own formatter classes as long",
61
+ "as they have been previously required using --require or",
62
+ "if they are in the folder structure such that cucumber",
63
+ "will require them automatically.",
64
+ "This option can be specified multiple times.") do |v|
65
+ @options[:formats][v] = @out_stream
66
+ @active_format = v
67
+ end
68
+ opts.on("-o", "--out FILE",
69
+ "Write output to a file instead of STDOUT. This option",
70
+ "applies to the previously specified --format, or the",
71
+ "default format if no format is specified.") do |v|
72
+ @options[:formats][@active_format] = v
73
+ end
74
+ opts.on("-t TAGS", "--tags TAGS",
75
+ "Only execute the features or scenarios with the specified tags.",
76
+ "TAGS must be comma-separated without spaces.") do |v|
77
+ @options[:tags] = v.split(",")
78
+ end
79
+ opts.on("-s SCENARIO", "--scenario SCENARIO",
80
+ "Only execute the scenario with the given name. If this option",
81
+ "is given more than once, run all the specified scenarios.") do |v|
82
+ @options[:scenario_names] << v
83
+ end
84
+ opts.on("-e", "--exclude PATTERN", "Don't run feature files matching PATTERN") do |v|
85
+ @options[:excludes] << v
86
+ end
87
+ opts.on("-p", "--profile PROFILE", "Pull commandline arguments from cucumber.yml.") do |v|
88
+ parse_args_from_profile(v)
89
+ end
90
+ opts.on("-c", "--[no-]color",
91
+ "Whether or not to use ANSI color in the output. Cucumber decides",
92
+ "based on your platform and the output destination if not specified.") do |v|
93
+ Term::ANSIColor.coloring = v
94
+ end
95
+ opts.on("-d", "--dry-run", "Invokes formatters without executing the steps.",
96
+ "Implies --quiet.") do
97
+ @options[:dry_run] = true
98
+ @quiet = true
99
+ end
100
+ opts.on("-a", "--autoformat DIRECTORY",
101
+ "Reformats (pretty prints) feature files and write them to DIRECTORY.",
102
+ "Be careful if you choose to overwrite the originals.",
103
+ "Implies --dry-run --formatter pretty.") do |directory|
104
+ @options[:autoformat] = directory
105
+ Term::ANSIColor.coloring = false
106
+ @options[:dry_run] = true
107
+ @quiet = true
108
+ end
109
+ opts.on("-m", "--no-multiline",
110
+ "Don't print multiline strings and tables under steps.") do
111
+ @options[:no_multiline] = true
112
+ end
113
+ opts.on("-n", "--no-source",
114
+ "Don't print the file and line of the step definition with the steps.") do
115
+ @options[:source] = false
116
+ end
117
+ opts.on("-i", "--no-snippets", "Don't print snippets for pending steps.") do
118
+ @options[:snippets] = false
119
+ end
120
+ opts.on("-q", "--quiet", "Alias for --no-snippets --no-source.") do
121
+ @quiet = true
122
+ end
123
+ opts.on("-b", "--backtrace", "Show full backtrace for all errors.") do
124
+ Exception.cucumber_full_backtrace = true
125
+ end
126
+ opts.on("--strict", "Fail if there are any undefined steps.") do
127
+ @options[:strict] = true
128
+ end
129
+ opts.on("-v", "--verbose", "Show the files and features loaded.") do
130
+ @options[:verbose] = true
131
+ end
132
+ opts.on("-g", "--guess", "Guess best match for Ambiguous steps.") do
133
+ @options[:guess] = true
134
+ end
135
+ opts.on_tail("--version", "Show version.") do
136
+ @out_stream.puts VERSION::STRING
137
+ Kernel.exit
138
+ end
139
+ opts.on_tail("--help", "You're looking at it.") do
140
+ @out_stream.puts opts.help
141
+ Kernel.exit
142
+ end
143
+ end.parse!
144
+
145
+ @options[:formats]['pretty'] = @out_stream if @options[:formats].empty?
146
+
147
+ @options[:snippets] = true if !@quiet && @options[:snippets].nil?
148
+ @options[:source] = true if !@quiet && @options[:source].nil?
149
+
150
+ # Whatever is left after option parsing is the FILE arguments
151
+ @paths += args
152
+ end
153
+
154
+ def ast_filter
155
+ Ast::Filter.new(@options)
156
+ end
157
+
158
+ def verbose?
159
+ @options[:verbose]
160
+ end
161
+
162
+ def strict?
163
+ @options[:strict]
164
+ end
165
+
166
+ def guess?
167
+ !!@options[:guess]
168
+ end
169
+
170
+ def load_language
171
+ Cucumber.load_language(@options[:lang])
172
+
173
+ if Cucumber.language_incomplete?
174
+ list_keywords(Cucumber.lang)
175
+ end
176
+ end
177
+
178
+ def build_formatter_broadcaster(step_mother)
179
+ return Formatter::Pretty.new(step_mother, nil, @options) if @options[:autoformat]
180
+ formatters = @options[:formats].map do |format, out|
181
+ if String === out # file name
182
+ out = File.open(out, Cucumber.file_mode('w'))
183
+ at_exit do
184
+ out.flush
185
+ out.close
186
+ end
187
+ end
188
+
189
+ begin
190
+ formatter_class = formatter_class(format)
191
+ formatter_class.new(step_mother, out, @options)
192
+ rescue Exception => e
193
+ exit_with_error("Error creating formatter: #{format}", e)
194
+ end
195
+ end
196
+
197
+ broadcaster = Broadcaster.new(formatters)
198
+ broadcaster.options = @options
199
+ return broadcaster
200
+ end
201
+
202
+ def formatter_class(format)
203
+ case format
204
+ when 'pretty' then Formatter::Pretty
205
+ when 'progress' then Formatter::Progress
206
+ when 'profile' then Formatter::Profile
207
+ when 'rerun' then Formatter::Rerun
208
+ else
209
+ constantize(format)
210
+ end
211
+ end
212
+
213
+ def files_to_require
214
+ requires = @options[:require] || feature_dirs
215
+ files = requires.map do |path|
216
+ path = path.gsub(/\\/, '/') # In case we're on windows. Globs don't work with backslashes.
217
+ File.directory?(path) ? Dir["#{path}/**/*.rb"] : path
218
+ end.flatten.uniq
219
+ files.sort { |a,b| (b =~ %r{/support/} || -1) <=> (a =~ %r{/support/} || -1) }.reject{|f| f =~ /^http/}
220
+ end
221
+
222
+ def feature_files
223
+ potential_feature_files = @paths.map do |path|
224
+ path = path.gsub(/\\/, '/') # In case we're on windows. Globs don't work with backslashes.
225
+ path = path.chomp('/')
226
+ File.directory?(path) ? Dir["#{path}/**/*.feature"] : path
227
+ end.flatten.uniq
228
+
229
+ @options[:excludes].each do |exclude|
230
+ potential_feature_files.reject! do |path|
231
+ path =~ /#{Regexp.escape(exclude)}/
232
+ end
233
+ end
234
+
235
+ potential_feature_files
236
+ end
237
+
238
+ protected
239
+
240
+ def feature_dirs
241
+ feature_files.map { |f| File.directory?(f) ? f : File.dirname(f) }.uniq
242
+ end
243
+
244
+ def constantize(camel_cased_word)
245
+ names = camel_cased_word.split('::')
246
+ names.shift if names.empty? || names.first.empty?
247
+
248
+ constant = Object
249
+ names.each do |name|
250
+ constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
251
+ end
252
+ constant
253
+ end
254
+
255
+ def parse_args_from_profile(profile)
256
+ unless cucumber_yml.has_key?(profile)
257
+ return(exit_with_error <<-END_OF_ERROR)
258
+ Could not find profile: '#{profile}'
259
+
260
+ Defined profiles in cucumber.yml:
261
+ * #{cucumber_yml.keys.join("\n * ")}
262
+ END_OF_ERROR
263
+ end
264
+
265
+ args_from_yml = cucumber_yml[profile] || ''
266
+
267
+ if !args_from_yml.is_a?(String)
268
+ exit_with_error "Profiles must be defined as a String. The '#{profile}' profile was #{args_from_yml.inspect} (#{args_from_yml.class}).\n"
269
+ elsif args_from_yml =~ /^\s*$/
270
+ exit_with_error "The 'foo' profile in cucumber.yml was blank. Please define the command line arguments for the 'foo' profile in cucumber.yml.\n"
271
+ else
272
+ parse!(args_from_yml.split(' '))
273
+ end
274
+
275
+ rescue YmlLoadError => e
276
+ exit_with_error(e.message)
277
+ end
278
+
279
+ def cucumber_yml
280
+ return @cucumber_yml if @cucumber_yml
281
+ unless File.exist?('cucumber.yml')
282
+ raise(YmlLoadError,"cucumber.yml was not found. Please refer to cucumber's documentaion on defining profiles in cucumber.yml. You must define a 'default' profile to use the cucumber command without any arguments.\nType 'cucumber --help' for usage.\n")
283
+ end
284
+
285
+ require 'yaml'
286
+ begin
287
+ @cucumber_yml = YAML::load(IO.read('cucumber.yml'))
288
+ rescue Exception => e
289
+ raise(YmlLoadError,"cucumber.yml was found, but could not be parsed. Please refer to cucumber's documentaion on correct profile usage.\n")
290
+ end
291
+
292
+ if @cucumber_yml.nil? || !@cucumber_yml.is_a?(Hash)
293
+ raise(YmlLoadError,"cucumber.yml was found, but was blank or malformed. Please refer to cucumber's documentaion on correct profile usage.\n")
294
+ end
295
+
296
+ return @cucumber_yml
297
+ end
298
+
299
+ def list_keywords(lang)
300
+ unless Cucumber::LANGUAGES[lang]
301
+ exit_with_error("No language with key #{lang}")
302
+ end
303
+ LanguageHelpFormatter.list_keywords(@out_stream, lang)
304
+ Kernel.exit
305
+ end
306
+
307
+ def list_languages
308
+ LanguageHelpFormatter.list_languages(@out_stream)
309
+ Kernel.exit
310
+ end
311
+
312
+ def default_options
313
+ {
314
+ :strict => false,
315
+ :require => nil,
316
+ :lang => 'en',
317
+ :dry_run => false,
318
+ :formats => {},
319
+ :excludes => [],
320
+ :tags => [],
321
+ :scenario_names => []
322
+ }
323
+ end
324
+
325
+ def exit_with_error(error_message, e=nil)
326
+ @error_stream.puts(error_message)
327
+ if e
328
+ @error_stream.puts("#{e.message} (#{e.class})")
329
+ @error_stream.puts(e.backtrace.join("\n"))
330
+ end
331
+ Kernel.exit 1
332
+ end
333
+ end
334
+
335
+ end
336
+ end