aslakhellesoy-cucumber 0.1.99.15 → 0.1.99.17
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.
- data/History.txt +1 -0
- data/Manifest.txt +5 -0
- data/examples/tickets/features/177-1.feature +29 -0
- data/examples/tickets/features/177-2.feature +21 -0
- data/examples/tickets/features/180.feature +7 -0
- data/features/cucumber_cli.feature +1 -1
- data/features/report_called_undefined_steps.feature +2 -2
- data/features/support/env.rb +1 -0
- data/gem_tasks/jar.rake +67 -0
- data/lib/cucumber.rb +0 -1
- data/lib/cucumber/ast/step.rb +2 -2
- data/lib/cucumber/cli/configuration.rb +336 -0
- data/lib/cucumber/cli/language_help_formatter.rb +1 -1
- data/lib/cucumber/cli/main.rb +65 -355
- data/lib/cucumber/formatter/ansicolor.rb +2 -2
- data/lib/cucumber/formatter/console.rb +1 -1
- data/lib/cucumber/parser/feature.rb +34 -21
- data/lib/cucumber/parser/feature.tt +16 -5
- data/lib/cucumber/parser/table.rb +6 -0
- data/lib/cucumber/parser/table.tt +6 -0
- data/lib/cucumber/parser/treetop_ext.rb +12 -4
- data/lib/cucumber/step_mother.rb +52 -33
- data/lib/cucumber/version.rb +1 -1
- data/spec/cucumber/cli/configuration_spec.rb +239 -0
- data/spec/cucumber/cli/main_spec.rb +10 -264
- data/spec/cucumber/parser/feature_parser_spec.rb +1 -1
- data/spec/cucumber/step_definition_spec.rb +2 -2
- data/spec/cucumber/step_mother_spec.rb +27 -3
- metadata +9 -2
data/lib/cucumber/cli/main.rb
CHANGED
@@ -4,387 +4,97 @@ require 'ostruct'
|
|
4
4
|
require 'cucumber/parser'
|
5
5
|
require 'cucumber/formatter'
|
6
6
|
require 'cucumber/cli/language_help_formatter'
|
7
|
+
require 'cucumber/cli/configuration'
|
7
8
|
|
8
9
|
module Cucumber
|
9
|
-
module Cli
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@step_mother.extend(StepMother)
|
17
|
-
@step_mother.snippet_generator = StepDefinition
|
18
|
-
end
|
19
|
-
|
20
|
-
def execute(args)
|
21
|
-
parse(args).execute!(@step_mother)
|
22
|
-
end
|
23
|
-
|
24
|
-
def parse(args)
|
25
|
-
cli = new
|
26
|
-
cli.parse_options!(args)
|
27
|
-
cli
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
attr_reader :options, :paths
|
32
|
-
FORMATS = %w{pretty profile progress rerun}
|
33
|
-
DEFAULT_FORMAT = 'pretty'
|
34
|
-
|
35
|
-
def initialize(out_stream = STDOUT, error_stream = STDERR)
|
36
|
-
@out_stream = out_stream
|
37
|
-
|
38
|
-
@error_stream = error_stream
|
39
|
-
@paths = []
|
40
|
-
@options = {
|
41
|
-
:strict => false,
|
42
|
-
:require => nil,
|
43
|
-
:lang => 'en',
|
44
|
-
:dry_run => false,
|
45
|
-
:formats => {},
|
46
|
-
:excludes => [],
|
47
|
-
:tags => [],
|
48
|
-
:scenario_names => []
|
49
|
-
}
|
50
|
-
@active_format = DEFAULT_FORMAT
|
51
|
-
end
|
52
|
-
|
53
|
-
def parse_options!(args)
|
54
|
-
@args = args
|
55
|
-
return parse_args_from_profile('default') if args.empty?
|
56
|
-
args.extend(OptionParser::Arguable)
|
57
|
-
|
58
|
-
args.options do |opts|
|
59
|
-
opts.banner = ["Usage: cucumber [options] [ [FILE|DIR|URL][:LINE[:LINE]*] ]+", "",
|
60
|
-
"Examples:",
|
61
|
-
"cucumber examples/i18n/en/features",
|
62
|
-
"cucumber --language it examples/i18n/it/features/somma.feature:6:98:113",
|
63
|
-
"cucumber -n -i http://rubyurl.com/eeCl", "", "",
|
64
|
-
].join("\n")
|
65
|
-
opts.on("-r LIBRARY|DIR", "--require LIBRARY|DIR",
|
66
|
-
"Require files before executing the features. If this",
|
67
|
-
"option is not specified, all *.rb files that are",
|
68
|
-
"siblings or below the features will be loaded auto-",
|
69
|
-
"matically. Automatic loading is disabled when this",
|
70
|
-
"option is specified, and all loading becomes explicit.",
|
71
|
-
"Files under directories named \"support\" are always",
|
72
|
-
"loaded first.",
|
73
|
-
"This option can be specified multiple times.") do |v|
|
74
|
-
@options[:require] ||= []
|
75
|
-
@options[:require] << v
|
76
|
-
end
|
77
|
-
opts.on("-l LANG", "--language LANG",
|
78
|
-
"Specify language for features (Default: #{@options[:lang]})",
|
79
|
-
%{Run with "--language help" to see all languages},
|
80
|
-
%{Run with "--language LANG help" to list keywords for LANG}) do |v|
|
81
|
-
if v == 'help'
|
82
|
-
list_languages
|
83
|
-
elsif args==['help']
|
84
|
-
list_keywords(v)
|
85
|
-
else
|
86
|
-
@options[:lang] = v
|
87
|
-
end
|
88
|
-
end
|
89
|
-
opts.on("-f FORMAT", "--format FORMAT",
|
90
|
-
"How to format features (Default: #{DEFAULT_FORMAT})",
|
91
|
-
"Available formats: #{FORMATS.join(", ")}",
|
92
|
-
"You can also provide your own formatter classes as long",
|
93
|
-
"as they have been previously required using --require or",
|
94
|
-
"if they are in the folder structure such that cucumber",
|
95
|
-
"will require them automatically.",
|
96
|
-
"This option can be specified multiple times.") do |v|
|
97
|
-
@options[:formats][v] = @out_stream
|
98
|
-
@active_format = v
|
99
|
-
end
|
100
|
-
opts.on("-o", "--out FILE",
|
101
|
-
"Write output to a file instead of STDOUT. This option",
|
102
|
-
"applies to the previously specified --format, or the",
|
103
|
-
"default format if no format is specified.") do |v|
|
104
|
-
@options[:formats][@active_format] = v
|
105
|
-
end
|
106
|
-
opts.on("-t TAGS", "--tags TAGS",
|
107
|
-
"Only execute the features or scenarios with the specified tags.",
|
108
|
-
"TAGS must be comma-separated without spaces.") do |v|
|
109
|
-
@options[:tags] = v.split(",")
|
110
|
-
end
|
111
|
-
opts.on("-s SCENARIO", "--scenario SCENARIO",
|
112
|
-
"Only execute the scenario with the given name. If this option",
|
113
|
-
"is given more than once, run all the specified scenarios.") do |v|
|
114
|
-
@options[:scenario_names] << v
|
115
|
-
end
|
116
|
-
opts.on("-e", "--exclude PATTERN", "Don't run feature files matching PATTERN") do |v|
|
117
|
-
@options[:excludes] << v
|
118
|
-
end
|
119
|
-
opts.on("-p", "--profile PROFILE", "Pull commandline arguments from cucumber.yml.") do |v|
|
120
|
-
parse_args_from_profile(v)
|
121
|
-
end
|
122
|
-
opts.on("-c", "--[no-]color",
|
123
|
-
"Whether or not to use ANSI color in the output. Cucumber decides",
|
124
|
-
"based on your platform and the output destination if not specified.") do |v|
|
125
|
-
Term::ANSIColor.coloring = v
|
126
|
-
end
|
127
|
-
opts.on("-d", "--dry-run", "Invokes formatters without executing the steps.",
|
128
|
-
"Implies --quiet.") do
|
129
|
-
@options[:dry_run] = true
|
130
|
-
@quiet = true
|
10
|
+
module Cli
|
11
|
+
class Main
|
12
|
+
class << self
|
13
|
+
def step_mother=(step_mother)
|
14
|
+
@step_mother = step_mother
|
15
|
+
@step_mother.extend(StepMother)
|
16
|
+
@step_mother.snippet_generator = StepDefinition
|
131
17
|
end
|
132
|
-
opts.on("-a", "--autoformat DIRECTORY",
|
133
|
-
"Reformats (pretty prints) feature files and write them to DIRECTORY.",
|
134
|
-
"Be careful if you choose to overwrite the originals.",
|
135
|
-
"Implies --dry-run --formatter pretty.") do |directory|
|
136
|
-
@options[:autoformat] = directory
|
137
|
-
Term::ANSIColor.coloring = false
|
138
|
-
@options[:dry_run] = true
|
139
|
-
@quiet = true
|
140
|
-
end
|
141
|
-
opts.on("-m", "--no-multiline",
|
142
|
-
"Don't print multiline strings and tables under steps.") do
|
143
|
-
@options[:no_multiline] = true
|
144
|
-
end
|
145
|
-
opts.on("-n", "--no-source",
|
146
|
-
"Don't print the file and line of the step definition with the steps.") do
|
147
|
-
@options[:source] = false
|
148
|
-
end
|
149
|
-
opts.on("-i", "--no-snippets", "Don't print snippets for pending steps.") do
|
150
|
-
@options[:snippets] = false
|
151
|
-
end
|
152
|
-
opts.on("-q", "--quiet", "Alias for --no-snippets --no-source.") do
|
153
|
-
@quiet = true
|
154
|
-
end
|
155
|
-
opts.on("-b", "--backtrace", "Show full backtrace for all errors.") do
|
156
|
-
Exception.cucumber_full_backtrace = true
|
157
|
-
end
|
158
|
-
opts.on("--strict", "Fail if there are any undefined steps.") do
|
159
|
-
@options[:strict] = true
|
160
|
-
end
|
161
|
-
opts.on("-v", "--verbose", "Show the files and features loaded.") do
|
162
|
-
@options[:verbose] = true
|
163
|
-
end
|
164
|
-
opts.on_tail("--version", "Show version.") do
|
165
|
-
@out_stream.puts VERSION::STRING
|
166
|
-
Kernel.exit
|
167
|
-
end
|
168
|
-
opts.on_tail("--help", "You're looking at it.") do
|
169
|
-
@out_stream.puts opts.help
|
170
|
-
Kernel.exit
|
171
|
-
end
|
172
|
-
end.parse!
|
173
|
-
|
174
|
-
@options[:formats]['pretty'] = @out_stream if @options[:formats].empty?
|
175
|
-
|
176
|
-
@options[:snippets] = true if !@quiet && @options[:snippets].nil?
|
177
|
-
@options[:source] = true if !@quiet && @options[:source].nil?
|
178
|
-
|
179
|
-
# Whatever is left after option parsing is the FILE arguments
|
180
|
-
@paths += args
|
181
|
-
end
|
182
|
-
|
183
|
-
|
184
|
-
def execute!(step_mother)
|
185
|
-
Cucumber.load_language(@options[:lang])
|
186
|
-
if Cucumber.language_incomplete?
|
187
|
-
list_keywords(Cucumber.lang)
|
188
|
-
end
|
189
|
-
|
190
|
-
require_files
|
191
|
-
enable_diffing
|
192
|
-
features = load_plain_text_features
|
193
|
-
|
194
|
-
visitor = build_formatter_broadcaster(step_mother)
|
195
|
-
visitor.options = @options
|
196
|
-
visitor.visit_features(features)
|
197
|
-
|
198
|
-
failure = features.steps[:failed].any? || (@options[:strict] && features.steps[:undefined].length)
|
199
|
-
Kernel.exit(failure ? 1 : 0)
|
200
|
-
end
|
201
|
-
|
202
|
-
private
|
203
|
-
|
204
|
-
def cucumber_yml
|
205
|
-
return @cucumber_yml if @cucumber_yml
|
206
|
-
unless File.exist?('cucumber.yml')
|
207
|
-
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")
|
208
|
-
end
|
209
|
-
|
210
|
-
require 'yaml'
|
211
|
-
begin
|
212
|
-
@cucumber_yml = YAML::load(IO.read('cucumber.yml'))
|
213
|
-
rescue Exception => e
|
214
|
-
raise(YmlLoadError,"cucumber.yml was found, but could not be parsed. Please refer to cucumber's documentaion on correct profile usage.\n")
|
215
|
-
end
|
216
|
-
|
217
|
-
if @cucumber_yml.nil? || !@cucumber_yml.is_a?(Hash)
|
218
|
-
raise(YmlLoadError,"cucumber.yml was found, but was blank or malformed. Please refer to cucumber's documentaion on correct profile usage.\n")
|
219
|
-
end
|
220
|
-
|
221
|
-
return @cucumber_yml
|
222
|
-
end
|
223
|
-
|
224
|
-
def parse_args_from_profile(profile)
|
225
|
-
unless cucumber_yml.has_key?(profile)
|
226
|
-
return(exit_with_error <<-END_OF_ERROR)
|
227
|
-
Could not find profile: '#{profile}'
|
228
|
-
|
229
|
-
Defined profiles in cucumber.yml:
|
230
|
-
* #{cucumber_yml.keys.join("\n * ")}
|
231
|
-
END_OF_ERROR
|
232
|
-
end
|
233
|
-
|
234
|
-
args_from_yml = cucumber_yml[profile] || ''
|
235
18
|
|
236
|
-
|
237
|
-
|
238
|
-
elsif args_from_yml =~ /^\s*$/
|
239
|
-
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"
|
240
|
-
else
|
241
|
-
parse_options!(args_from_yml.split(' '))
|
242
|
-
end
|
243
|
-
|
244
|
-
rescue YmlLoadError => e
|
245
|
-
exit_with_error(e.message)
|
246
|
-
end
|
247
|
-
|
248
|
-
# Requires files - typically step files and ruby feature files.
|
249
|
-
def require_files
|
250
|
-
verbose_log("Ruby files required:")
|
251
|
-
files_to_require.each do |lib|
|
252
|
-
begin
|
253
|
-
require lib
|
254
|
-
verbose_log(" * #{lib}")
|
255
|
-
rescue LoadError => e
|
256
|
-
e.message << "\nFailed to load #{lib}"
|
257
|
-
raise e
|
19
|
+
def execute(args)
|
20
|
+
new(args).execute!(@step_mother)
|
258
21
|
end
|
259
22
|
end
|
260
|
-
verbose_log("\n")
|
261
|
-
end
|
262
|
-
|
263
|
-
def files_to_require
|
264
|
-
requires = @options[:require] || feature_dirs
|
265
|
-
files = requires.map do |path|
|
266
|
-
path = path.gsub(/\\/, '/') # In case we're on windows. Globs don't work with backslashes.
|
267
|
-
File.directory?(path) ? Dir["#{path}/**/*.rb"] : path
|
268
|
-
end.flatten.uniq
|
269
|
-
files.sort { |a,b| (b =~ %r{/support/} || -1) <=> (a =~ %r{/support/} || -1) }.reject{|f| f =~ /^http/}
|
270
|
-
end
|
271
23
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
File.directory?(path) ? Dir["#{path}/**/*.feature"] : path
|
277
|
-
end.flatten.uniq
|
278
|
-
|
279
|
-
@options[:excludes].each do |exclude|
|
280
|
-
potential_feature_files.reject! do |path|
|
281
|
-
path =~ /#{Regexp.escape(exclude)}/
|
282
|
-
end
|
24
|
+
def initialize(args, out_stream = STDOUT, error_stream = STDERR)
|
25
|
+
@args = args
|
26
|
+
@out_stream = out_stream
|
27
|
+
@error_stream = error_stream
|
283
28
|
end
|
29
|
+
|
30
|
+
def execute!(step_mother)
|
31
|
+
configuration.load_language
|
32
|
+
step_mother.options = configuration.options
|
284
33
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
feature_files.map{|f| File.directory?(f) ? f : File.dirname(f)}.uniq
|
290
|
-
end
|
291
|
-
|
292
|
-
def load_plain_text_features
|
293
|
-
filter = Ast::Filter.new(@options)
|
294
|
-
features = Ast::Features.new(filter)
|
295
|
-
parser = Parser::FeatureParser.new
|
34
|
+
require_files
|
35
|
+
enable_diffing
|
36
|
+
|
37
|
+
features = load_plain_text_features
|
296
38
|
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
39
|
+
visitor = configuration.build_formatter_broadcaster(step_mother)
|
40
|
+
visitor.visit_features(features)
|
41
|
+
|
42
|
+
failure = features.steps[:failed].any? || (configuration.strict? && features.steps[:undefined].length)
|
43
|
+
Kernel.exit(failure ? 1 : 0)
|
301
44
|
end
|
302
|
-
verbose_log("\n"*2)
|
303
|
-
features
|
304
|
-
end
|
305
45
|
|
306
|
-
|
307
|
-
return Formatter::Pretty.new(step_mother, nil, @options) if @options[:autoformat]
|
308
|
-
formatters = @options[:formats].map do |format, out|
|
309
|
-
if String === out # file name
|
310
|
-
out = File.open(out, Cucumber.file_mode('w'))
|
311
|
-
at_exit do
|
312
|
-
out.flush
|
313
|
-
out.close
|
314
|
-
end
|
315
|
-
end
|
46
|
+
private
|
316
47
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
48
|
+
def configuration
|
49
|
+
return @configuration if @configuration
|
50
|
+
|
51
|
+
@configuration = Configuration.new(@out_stream, @error_stream)
|
52
|
+
@configuration.parse!(@args)
|
53
|
+
@configuration
|
54
|
+
end
|
55
|
+
|
56
|
+
def require_files
|
57
|
+
verbose_log("Ruby files required:")
|
58
|
+
configuration.files_to_require.each do |lib|
|
327
59
|
begin
|
328
|
-
|
329
|
-
|
330
|
-
rescue
|
331
|
-
|
60
|
+
require lib
|
61
|
+
verbose_log(" * #{lib}")
|
62
|
+
rescue LoadError => e
|
63
|
+
e.message << "\nFailed to load #{lib}"
|
64
|
+
raise e
|
332
65
|
end
|
333
66
|
end
|
67
|
+
verbose_log("\n")
|
334
68
|
end
|
335
|
-
Broadcaster.new(formatters)
|
336
|
-
end
|
337
69
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
constant = Object
|
343
|
-
names.each do |name|
|
344
|
-
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
345
|
-
end
|
346
|
-
constant
|
347
|
-
end
|
70
|
+
def load_plain_text_features
|
71
|
+
filter = configuration.ast_filter
|
72
|
+
features = Ast::Features.new(filter)
|
73
|
+
parser = Parser::FeatureParser.new
|
348
74
|
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
def exit_with_error(error_message, e=nil)
|
358
|
-
@error_stream.puts(error_message)
|
359
|
-
if e
|
360
|
-
@error_stream.puts("#{e.message} (#{e.class})")
|
361
|
-
@error_stream.puts(e.backtrace.join("\n"))
|
75
|
+
verbose_log("Features:")
|
76
|
+
configuration.feature_files.each do |f|
|
77
|
+
features.add_feature(parser.parse_file(f))
|
78
|
+
verbose_log(" * #{f}")
|
79
|
+
end
|
80
|
+
verbose_log("\n"*2)
|
81
|
+
features
|
362
82
|
end
|
363
|
-
Kernel.exit 1
|
364
|
-
end
|
365
83
|
|
366
|
-
|
367
|
-
|
368
|
-
require 'spec/expectations/differs/default'
|
369
|
-
options = OpenStruct.new(:diff_format => :unified, :context_lines => 3)
|
370
|
-
::Spec::Expectations.differ = ::Spec::Expectations::Differs::Default.new(options)
|
84
|
+
def verbose_log(string)
|
85
|
+
@out_stream.puts(string) if configuration.verbose?
|
371
86
|
end
|
372
|
-
end
|
373
|
-
|
374
|
-
def list_languages
|
375
|
-
Cli2::LanguageHelpFormatter.list_languages(@out_stream)
|
376
|
-
Kernel.exit
|
377
|
-
end
|
378
87
|
|
379
|
-
|
380
|
-
|
381
|
-
|
88
|
+
def enable_diffing
|
89
|
+
if defined?(::Spec)
|
90
|
+
require 'spec/expectations/differs/default'
|
91
|
+
options = OpenStruct.new(:diff_format => :unified, :context_lines => 3)
|
92
|
+
::Spec::Expectations.differ = ::Spec::Expectations::Differs::Default.new(options)
|
93
|
+
end
|
382
94
|
end
|
383
|
-
|
384
|
-
Kernel.exit
|
95
|
+
|
385
96
|
end
|
386
97
|
end
|
387
98
|
end
|
388
|
-
end
|
389
99
|
|
390
100
|
Cucumber::Cli::Main.step_mother = self
|