lib/cucumber/cli/configuration.rb in square-cucumber-0.3.12.2 vs lib/cucumber/cli/configuration.rb in square-cucumber-0.3.93.1

- old
+ new

@@ -1,200 +1,31 @@ +require 'cucumber/cli/options' + module Cucumber module Cli class YmlLoadError < StandardError; end + class ProfilesNotDefinedError < YmlLoadError; end + class ProfileNotFound < StandardError; end class Configuration - BUILTIN_FORMATS = { - 'html' => 'Cucumber::Formatter::Html', - 'pretty' => 'Cucumber::Formatter::Pretty', - 'profile' => 'Cucumber::Formatter::Profile', - 'progress' => 'Cucumber::Formatter::Progress', - 'rerun' => 'Cucumber::Formatter::Rerun', - 'usage' => 'Cucumber::Formatter::Usage', - 'junit' => 'Cucumber::Formatter::Junit', - 'tag_cloud' => 'Cucumber::Formatter::TagCloud', - 'steps' => 'Cucumber::Formatter::Steps' - } - DRB_FLAG = '--drb' - PROFILE_SHORT_FLAG = '-p' - PROFILE_LONG_FLAG = '--profile' - - attr_reader :paths attr_reader :options def initialize(out_stream = STDOUT, error_stream = STDERR) @out_stream = out_stream @error_stream = error_stream - - @paths = [] - @options = default_options + @options = Options.new(@out_stream, @error_stream, :default_profile => 'default') end def parse!(args) - args.concat(%w{--profile default}) if args.empty? @args = args - expand_profiles_into_args - return if parse_drb + @options.parse!(args) + raise("You can't use both --strict and --wip") if strict? && wip? - @args.each do |arg| - if arg =~ /^(\w+)=(.*)$/ - ENV[$1] = $2 - @args.delete(arg) - end - end + return @args.replace(@options.expanded_args_without_drb) if drb? - @args.extend(::OptionParser::Arguable) - - @args.options do |opts| - opts.banner = ["Usage: cucumber [options] [ [FILE|DIR|URL][:LINE[:LINE]*] ]+", "", - "Examples:", - "cucumber examples/i18n/en/features", - "cucumber --language it examples/i18n/it/features/somma.feature:6:98:113", - "cucumber -s -i http://rubyurl.com/eeCl", "", "", - ].join("\n") - opts.on("-r LIBRARY|DIR", "--require LIBRARY|DIR", - "Require files before executing the features. If this", - "option is not specified, all *.rb files that are", - "siblings or below the features will be loaded auto-", - "matically. Automatic loading is disabled when this", - "option is specified, and all loading becomes explicit.", - "Files under directories named \"support\" are always", - "loaded first.", - "This option can be specified multiple times.") do |v| - @options[:require] ||= [] - @options[:require] << v - end - opts.on("-l LANG", "--language LANG", - "Specify language for features (Default: #{@options[:lang]})", - %{Run with "--language help" to see all languages}, - %{Run with "--language LANG help" to list keywords for LANG}) do |v| - if v == 'help' - list_languages_and_exit - elsif args==['help'] - list_keywords_and_exit(v) - else - @options[:lang] = v - end - end - opts.on("-f FORMAT", "--format FORMAT", - "How to format features (Default: pretty)", - "Available formats: #{BUILTIN_FORMATS.keys.sort.join(", ")}", - "FORMAT can also be the fully qualified class name of", - "your own custom formatter. If the class isn't loaded,", - "Cucumber will attempt to require a file with a relative", - "file name that is the underscore name of the class name.", - "Example: --format Foo::BarZap -> Cucumber will look for", - "foo/bar_zap.rb. You can place the file with this relative", - "path underneath your features/support directory or anywhere", - "on Ruby's LOAD_PATH, for example in a Ruby gem.") do |v| - @options[:formats] << [v, @out_stream] - @active_format = v - end - opts.on("-o", "--out [FILE|DIR]", - "Write output to a file/directory instead of STDOUT. This option", - "applies to the previously specified --format, or the", - "default format if no format is specified. Check the specific", - "formatter's docs to see whether to pass a file or a dir.") do |v| - @options[:formats] << ['pretty', nil] if @options[:formats].empty? - @options[:formats][-1][1] = v - end - opts.on("-t TAGS", "--tags TAGS", - "Only execute the features or scenarios with the specified tags.", - "TAGS must be comma-separated without spaces. Prefix tags with ~ to", - "exclude features or scenarios having that tag. Tags can be specified", - "with or without the @ prefix.") do |v| - @options[:include_tags], @options[:exclude_tags] = *parse_tags(v) - end - opts.on("-n NAME", "--name NAME", - "Only execute the feature elements which match part of the given name.", - "If this option is given more than once, it will match against all the", - "given names.") do |v| - @options[:name_regexps] << /#{v}/ - end - opts.on("-e", "--exclude PATTERN", "Don't run feature files or require ruby files matching PATTERN") do |v| - @options[:excludes] << Regexp.new(v) - end - opts.on(PROFILE_SHORT_FLAG, "#{PROFILE_LONG_FLAG} PROFILE", "Pull commandline arguments from cucumber.yml.") do |v| - # Processing of this is done previsouly so that the DRb flag can be detected within profiles. - end - opts.on("-c", "--[no-]color", - "Whether or not to use ANSI color in the output. Cucumber decides", - "based on your platform and the output destination if not specified.") do |v| - Term::ANSIColor.coloring = v - end - opts.on("-d", "--dry-run", "Invokes formatters without executing the steps.", - "This also omits the loading of your support/env.rb file if it exists.", - "Implies --quiet.") do - @options[:dry_run] = true - @quiet = true - end - opts.on("-a", "--autoformat DIRECTORY", - "Reformats (pretty prints) feature files and write them to DIRECTORY.", - "Be careful if you choose to overwrite the originals.", - "Implies --dry-run --formatter pretty.") do |directory| - @options[:autoformat] = directory - Term::ANSIColor.coloring = false - @options[:dry_run] = true - @quiet = true - end - opts.on("-m", "--no-multiline", - "Don't print multiline strings and tables under steps.") do - @options[:no_multiline] = true - end - opts.on("-s", "--no-source", - "Don't print the file and line of the step definition with the steps.") do - @options[:source] = false - end - opts.on("-i", "--no-snippets", "Don't print snippets for pending steps.") do - @options[:snippets] = false - end - opts.on("-q", "--quiet", "Alias for --no-snippets --no-source.") do - @quiet = true - end - opts.on("-b", "--backtrace", "Show full backtrace for all errors.") do - Exception.cucumber_full_backtrace = true - end - opts.on("-S", "--strict", "Fail if there are any undefined steps.") do - @options[:strict] = true - end - opts.on("-w", "--wip", "Fail if there are any passing scenarios.") do - @options[:wip] = true - end - opts.on("-v", "--verbose", "Show the files and features loaded.") do - @options[:verbose] = true - end - opts.on("-g", "--guess", "Guess best match for Ambiguous steps.") do - @options[:guess] = true - end - opts.on("-x", "--expand", "Expand Scenario Outline Tables in output.") do - @options[:expand] = true - end - opts.on("--no-diff", "Disable diff output on failing expectations.") do - @options[:diff_enabled] = false - end - opts.on(DRB_FLAG, "Run features against a DRb server. (i.e. with the spork gem)") do - # Processing of this is done previsouly in order to short circuit args from being lost. - end - opts.on_tail("--version", "Show version.") do - @out_stream.puts VERSION::STRING - Kernel.exit - end - opts.on_tail("-h", "--help", "You're looking at it.") do - @out_stream.puts opts.help - Kernel.exit - end - end.parse! - + set_environment_variables arrange_formats - - @options[:snippets] = true if !@quiet && @options[:snippets].nil? - @options[:source] = true if !@quiet && @options[:source].nil? - - raise("You can't use both --strict and --wip") if @options[:strict] && @options[:wip] - - # Whatever is left after option parsing is the FILE arguments - @paths += @args end def verbose? @options[:verbose] end @@ -214,22 +45,15 @@ def diff_enabled? @options[:diff_enabled] end def drb? - @drb + @options[:drb] end - def parse_tags(tag_string) - tag_names = tag_string.split(",") - excludes, includes = tag_names.partition{|tag| tag =~ /^~/} - excludes = excludes.map{|tag| tag[1..-1]} - - # Strip @ - includes = includes.map{|tag| Ast::Tags.strip_prefix(tag)} - excludes = excludes.map{|tag| Ast::Tags.strip_prefix(tag)} - [includes, excludes] + def paths + @options[:paths] end def build_formatter_broadcaster(step_mother) return Formatter::Pretty.new(step_mother, nil, @options) if @options[:autoformat] formatters = @options[:formats].map do |format_and_out| @@ -258,19 +82,19 @@ broadcaster.options = @options return broadcaster end def formatter_class(format) - if(builtin = BUILTIN_FORMATS[format]) - constantize(builtin) + if(builtin = Options::BUILTIN_FORMATS[format]) + constantize(builtin[0]) else constantize(format) end end def files_to_require - requires = @options[:require] || feature_dirs + requires = @options[:require].empty? ? require_dirs : @options[:require] files = requires.map do |path| path = path.gsub(/\\/, '/') # In case we're on windows. Globs don't work with backslashes. path = path.gsub(/\/$/, '') # Strip trailing slash. File.directory?(path) ? Dir["#{path}/**/*.rb"] : path end.flatten.uniq @@ -281,21 +105,27 @@ files.reject! {|f| f =~ %r{/support/env.rb} } if @options[:dry_run] files end def feature_files - potential_feature_files = @paths.map do |path| + potential_feature_files = @options[:paths].map do |path| path = path.gsub(/\\/, '/') # In case we're on windows. Globs don't work with backslashes. path = path.chomp('/') File.directory?(path) ? Dir["#{path}/**/*.feature"] : path end.flatten.uniq remove_excluded_files_from(potential_feature_files) potential_feature_files end - protected + private + def set_environment_variables + @options[:env_vars].each do |var, value| + ENV[var] = value + end + end + def arrange_formats @options[:formats] << ['pretty', @out_stream] if @options[:formats].empty? @options[:formats] = @options[:formats].sort_by{|f| f[1] == @out_stream ? -1 : 1} if @options[:formats].length > 1 && @options[:formats][1][1] == @out_stream raise "All but one formatter must use --out, only one can print to STDOUT" @@ -305,13 +135,17 @@ def remove_excluded_files_from(files) files.reject! {|path| @options[:excludes].detect {|pattern| path =~ pattern } } end def feature_dirs - @paths.map { |f| File.directory?(f) ? f : File.dirname(f) }.uniq + paths.map { |f| File.directory?(f) ? f : File.dirname(f) }.uniq end + def require_dirs + feature_dirs + Dir['vendor/{gems,plugins}/*/cucumber'] + end + def constantize(camel_cased_word) begin names = camel_cased_word.split('::') names.shift if names.empty? || names.first.empty? @@ -333,93 +167,9 @@ gsub(/([a-z\d])([A-Z])/,'\1_\2'). tr("-", "_"). downcase end - def expand_profiles_into_args - while (profile_index = @args.index(PROFILE_SHORT_FLAG) || @args.index(PROFILE_LONG_FLAG)) do - @args.delete_at(profile_index) - @args[profile_index] = args_from_profile(@args[profile_index]) - @args.flatten! - end - end - - def args_from_profile(profile) - unless cucumber_yml.has_key?(profile) - raise(<<-END_OF_ERROR) -Could not find profile: '#{profile}' - -Defined profiles in cucumber.yml: - * #{cucumber_yml.keys.join("\n * ")} - END_OF_ERROR - end - - args_from_yml = cucumber_yml[profile] || '' - - case(args_from_yml) - when String - raise "The '#{profile}' profile in cucumber.yml was blank. Please define the command line arguments for the '#{profile}' profile in cucumber.yml.\n" if args_from_yml =~ /^\s*$/ - args_from_yml = args_from_yml.split(' ') - when Array - raise "The '#{profile}' profile in cucumber.yml was empty. Please define the command line arguments for the '#{profile}' profile in cucumber.yml.\n" if args_from_yml.empty? - else - raise "The '#{profile}' profile in cucumber.yml was a #{args_from_yml.class}. It must be a String or Array" - end - args_from_yml - end - - def cucumber_yml - return @cucumber_yml if @cucumber_yml - unless File.exist?('cucumber.yml') - raise(YmlLoadError,"cucumber.yml was not found. Please refer to cucumber's documentation 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") - end - - require 'yaml' - begin - @cucumber_yml = YAML::load(IO.read('cucumber.yml')) - rescue StandardError => e - raise(YmlLoadError,"cucumber.yml was found, but could not be parsed. Please refer to cucumber's documentation on correct profile usage.\n") - end - - if @cucumber_yml.nil? || !@cucumber_yml.is_a?(Hash) - raise(YmlLoadError,"cucumber.yml was found, but was blank or malformed. Please refer to cucumber's documentation on correct profile usage.\n") - end - - return @cucumber_yml - end - - # TODO: Move to Language - def list_keywords_and_exit(lang) - unless Cucumber::LANGUAGES[lang] - raise("No language with key #{lang}") - end - LanguageHelpFormatter.list_keywords(@out_stream, lang) - Kernel.exit - end - - def list_languages_and_exit - LanguageHelpFormatter.list_languages(@out_stream) - Kernel.exit - end - - def parse_drb - @drb = @args.delete(DRB_FLAG) ? true : false - end - - def default_options - { - :strict => false, - :require => nil, - :lang => nil, - :dry_run => false, - :formats => [], - :excludes => [], - :include_tags => [], - :exclude_tags => [], - :name_regexps => [], - :diff_enabled => true - } - end end end end