Class: Cucumber::Cli::Options

Inherits:
Object
  • Object
show all
Defined in:
lib/cucumber/cli/options.rb

Constant Summary collapse

CUCUMBER_PUBLISH_URL =
ENV['CUCUMBER_PUBLISH_URL'] || 'https://messages.cucumber.io/api/reports -X GET'
INDENT =
' ' * 53
BUILTIN_FORMATS =
{
  'pretty' => ['Cucumber::Formatter::Pretty', 'Prints the feature as is - in colours.'],
  'progress' => ['Cucumber::Formatter::Progress', 'Prints one character per scenario.'],
  'rerun' => ['Cucumber::Formatter::Rerun', 'Prints failing files with line numbers.'],
  'usage' => ['Cucumber::Formatter::Usage', "Prints where step definitions are used.\n" \
    "#{INDENT}The slowest step definitions (with duration) are\n" \
    "#{INDENT}listed first. If --dry-run is used the duration\n" \
    "#{INDENT}is not shown, and step definitions are sorted by\n" \
    "#{INDENT}filename instead."],
  'stepdefs' => ['Cucumber::Formatter::Stepdefs', "Prints All step definitions with their locations. Same as\n" \
    "#{INDENT}the usage formatter, except that steps are not printed."],
  'junit' => ['Cucumber::Formatter::Junit', "Generates a report similar to Ant+JUnit. Use\n" \
    "#{INDENT}junit,fileattribute=true to include a file attribute."],
  'json' => ['Cucumber::Formatter::Json', "Prints the feature as JSON.\n" \
    "#{INDENT}The JSON format is in maintenance mode.\n" \
    "#{INDENT}Please consider using the message formatter\n"\
    "#{INDENT}with the standalone json-formatter\n" \
    "#{INDENT}(https://github.com/cucumber/cucumber/tree/master/json-formatter)."],
  'message' => ['Cucumber::Formatter::Message', 'Prints each message in NDJSON form, which can then be consumed by other tools.'],
  'html' => ['Cucumber::Formatter::HTML', 'Outputs HTML report'],
  'summary' => ['Cucumber::Formatter::Summary', 'Summary output of feature and scenarios']
}.freeze
FORMAT_HELP_MSG =
[
  'Use --format rerun --out rerun.txt to write out failing',
  'features. You can rerun them with cucumber @rerun.txt.',
  '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."
].freeze
FORMAT_HELP =
(BUILTIN_FORMATS.keys.sort.map do |key|
  "  #{key}#{' ' * (max - key.length)} : #{BUILTIN_FORMATS[key][1]}"
end) + FORMAT_HELP_MSG
PROFILE_SHORT_FLAG =
'-p'
NO_PROFILE_SHORT_FLAG =
'-P'
PROFILE_LONG_FLAG =
'--profile'
NO_PROFILE_LONG_FLAG =
'--no-profile'
FAIL_FAST_FLAG =
'--fail-fast'
RETRY_FLAG =
'--retry'
RETRY_TOTAL_FLAG =
'--retry-total'
OPTIONS_WITH_ARGS =
[
  '-r', '--require', '--i18n-keywords', '-f', '--format', '-o',
  '--out', '-t', '--tags', '-n', '--name', '-e', '--exclude',
  PROFILE_SHORT_FLAG, PROFILE_LONG_FLAG, RETRY_FLAG, RETRY_TOTAL_FLAG,
  '-l', '--lines', '--port', '-I', '--snippet-type'
].freeze
ORDER_TYPES =
%w[defined random].freeze
TAG_LIMIT_MATCHER =
/(?<tag_name>@\w+):(?<limit>\d+)/x.freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(out_stream = $stdout, error_stream = $stderr, options = {}) ⇒ Options

Returns a new instance of Options.



73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/cucumber/cli/options.rb', line 73

def initialize(out_stream = $stdout, error_stream = $stderr, options = {})
  @out_stream   = out_stream
  @error_stream = error_stream

  @default_profile = options[:default_profile]
  @profiles = options[:profiles] || []
  @overridden_paths = []
  @options = default_options.merge(options)
  @profile_loader = options[:profile_loader]
  @options[:skip_profile_information] = options[:skip_profile_information]

  @disable_profile_loading = nil
end

Class Method Details

.parse(args, out_stream, error_stream, options = {}) ⇒ Object



69
70
71
# File 'lib/cucumber/cli/options.rb', line 69

def self.parse(args, out_stream, error_stream, options = {})
  new(out_stream, error_stream, options).parse!(args)
end

Instance Method Details

#[](key) ⇒ Object



87
88
89
# File 'lib/cucumber/cli/options.rb', line 87

def [](key)
  @options[key]
end

#[]=(key, value) ⇒ Object



91
92
93
# File 'lib/cucumber/cli/options.rb', line 91

def []=(key, value)
  @options[key] = value
end

#check_formatter_stream_conflictsObject



182
183
184
185
186
187
# File 'lib/cucumber/cli/options.rb', line 182

def check_formatter_stream_conflicts
  streams = @options[:formats].uniq.map { |(_, _, stream)| stream }
  return if streams == streams.uniq

  raise 'All but one formatter must use --out, only one can print to each stream (or STDOUT)'
end

#custom_profilesObject



174
175
176
# File 'lib/cucumber/cli/options.rb', line 174

def custom_profiles
  @profiles - [@default_profile]
end

#filtersObject



178
179
180
# File 'lib/cucumber/cli/options.rb', line 178

def filters
  @options[:filters] ||= []
end

#parse!(args) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/cucumber/cli/options.rb', line 95

def parse!(args)
  @args = args
  @expanded_args = @args.dup

  @args.extend(::OptionParser::Arguable)

  @args.options do |opts|
    opts.banner = banner
    opts.on('--publish', 'Publish a report to https://reports.cucumber.io') do
      set_option :publish_enabled, true
    end
    opts.on('--publish-quiet', 'Don\'t print information banner about publishing reports') { set_option :publish_quiet }
    opts.on('-r LIBRARY|DIR', '--require LIBRARY|DIR', *require_files_msg) { |lib| require_files(lib) }

    opts.on('-j DIR', '--jars DIR', 'Load all the jars under DIR') { |jars| load_jars(jars) } if Cucumber::JRUBY

    opts.on("#{RETRY_FLAG} ATTEMPTS", *retry_msg) { |v| set_option :retry, v.to_i }
    opts.on("#{RETRY_TOTAL_FLAG} TESTS", *retry_total_msg) { |v| set_option :retry_total, v.to_i }
    opts.on('--i18n-languages', *i18n_languages_msg) { list_languages_and_exit }
    opts.on('--i18n-keywords LANG', *i18n_keywords_msg) { |lang| language lang }
    opts.on(FAIL_FAST_FLAG, 'Exit immediately following the first failing scenario') { set_option :fail_fast }
    opts.on('-f FORMAT', '--format FORMAT', *format_msg, *FORMAT_HELP) do |v|
      add_option :formats, [*parse_formats(v), @out_stream]
    end
    opts.on('--init', *init_msg) { initialize_project }
    opts.on('-o', '--out [FILE|DIR|URL]', *out_msg) { |v| out_stream v }
    opts.on('-t TAG_EXPRESSION', '--tags TAG_EXPRESSION', *tags_msg) { |v| add_tag(v) }
    opts.on('-n NAME', '--name NAME', *name_msg) { |v| add_option(:name_regexps, /#{v}/) }
    opts.on('-e', '--exclude PATTERN', *exclude_msg) { |v| add_option :excludes, Regexp.new(v) }
    opts.on(PROFILE_SHORT_FLAG, "#{PROFILE_LONG_FLAG} PROFILE", *profile_short_flag_msg) { |v| add_profile v }
    opts.on(NO_PROFILE_SHORT_FLAG, NO_PROFILE_LONG_FLAG, *no_profile_short_flag_msg) { |_v| disable_profile_loading }
    opts.on('-c', '--[no-]color', *color_msg) { |v| color v }
    opts.on('-d', '--dry-run', *dry_run_msg) { set_dry_run_and_duration }
    opts.on('-m', '--no-multiline', "Don't print multiline strings and tables under steps.") { set_option :no_multiline }
    opts.on('-s', '--no-source', "Don't print the file and line of the step definition with the steps.") { set_option :source, false }
    opts.on('-i', '--no-snippets', "Don't print snippets for pending steps.") { set_option :snippets, false }
    opts.on('-I', '--snippet-type TYPE', *snippet_type_msg) { |v| set_option :snippet_type, v.to_sym }
    opts.on('-q', '--quiet', 'Alias for --no-snippets --no-source --no-duration --publish-quiet.') { shut_up }
    opts.on('--no-duration', "Don't print the duration at the end of the summary") { set_option :duration, false }
    opts.on('-b', '--backtrace', 'Show full backtrace for all errors.') { Cucumber.use_full_backtrace = true }
    opts.on('-S', '--[no-]strict', *strict_msg) { |setting| set_strict(setting) }
    opts.on('--[no-]strict-undefined', 'Fail if there are any undefined results.') { |setting| set_strict(setting, :undefined) }
    opts.on('--[no-]strict-pending', 'Fail if there are any pending results.') { |setting| set_strict(setting, :pending) }
    opts.on('--[no-]strict-flaky', 'Fail if there are any flaky results.') { |setting| set_strict(setting, :flaky) }
    opts.on('-w', '--wip', 'Fail if there are any passing scenarios.') { set_option :wip }
    opts.on('-v', '--verbose', 'Show the files and features loaded.') { set_option :verbose }
    opts.on('-g', '--guess', 'Guess best match for Ambiguous steps.') { set_option :guess }
    opts.on('-l', '--lines LINES', *lines_msg) { |lines| set_option :lines, lines }
    opts.on('-x', '--expand', 'Expand Scenario Outline Tables in output.') { set_option :expand }

    opts.on('--order TYPE[:SEED]', 'Run examples in the specified order. Available types:',
            *<<~TEXT.split("\n")) do |order|
                [defined]     Run scenarios in the order they were defined (default).
                [random]      Shuffle scenarios before running.
              Specify SEED to reproduce the shuffling from a previous run.
                e.g. --order random:5738
            TEXT
      @options[:order], @options[:seed] = *order.split(':')
      raise "'#{@options[:order]}' is not a recognised order type. Please use one of #{ORDER_TYPES.join(', ')}." unless ORDER_TYPES.include?(@options[:order])
    end

    opts.on_tail('--version', 'Show version.') { exit_ok(Cucumber::VERSION) }
    opts.on_tail('-h', '--help', "You're looking at it.") { exit_ok(opts.help) }
  end.parse!

  process_publish_options

  @args.map! { |a| "#{a}:#{@options[:lines]}" } if @options[:lines]

  extract_environment_variables
  @options[:paths] = @args.dup # whatver is left over

  check_formatter_stream_conflicts

  merge_profiles

  self
end

#to_hashObject



189
190
191
# File 'lib/cucumber/cli/options.rb', line 189

def to_hash
  Hash(@options)
end