class Cucumber::StepMother
This is the meaty part of Cucumber that ties everything together.
def after(scenario) #:nodoc:
def after(scenario) #:nodoc: @current_scenario = nil return if options[:dry_run] @programming_languages.each do |programming_language| programming_language.after(scenario) end end
def after_configuration(configuration) #:nodoc
def after_configuration(configuration) #:nodoc @programming_languages.each do |programming_language| programming_language.after_configuration(configuration) end end
def after_step #:nodoc:
def after_step #:nodoc: return if options[:dry_run] @programming_languages.each do |programming_language| programming_language.execute_after_step(@current_scenario) end end
def announce(msg)
nicer, and in all outputs (in case you use several formatters)
This is an alternative to using Kernel#puts - it will display
Output +announcement+ alongside the formatted output.
def announce(msg) msg.respond_to?(:join) ? @visitor.announce(msg.join("\n")) : @visitor.announce(msg.to_s) end
def ask(question, timeout_seconds)
that makes a sound before invoking #ask.
If that doesn't issue a beep, you can shell out to something else
ask("#{7.chr}How many cukes are in the external system?")
just prepend ASCII character 7 to the question:
If you want a beep to happen (to grab the manual tester's attention),
the result is added to the output using #announce.
An operator (manual tester) can then enter a line of text and hit
Suspends execution and prompts +question+ to the console (STDOUT).
def ask(question, timeout_seconds) STDOUT.puts(question) STDOUT.flush announce(question) if(Cucumber::JRUBY) answer = jruby_gets(timeout_seconds) else answer = mri_gets(timeout_seconds) end if(answer) announce(answer) answer else raise("Waited for input for #{timeout_seconds} seconds, then timed out.") end end
def before(scenario) #:nodoc:
def before(scenario) #:nodoc: return if options[:dry_run] || @current_scenario @current_scenario = scenario @programming_languages.each do |programming_language| programming_language.before(scenario) end end
def before_and_after(scenario, skip_hooks=false) #:nodoc:
def before_and_after(scenario, skip_hooks=false) #:nodoc: before(scenario) unless skip_hooks yield scenario after(scenario) unless skip_hooks scenario_visited(scenario) end
def best_matches(step_name, step_matches) #:nodoc:
def best_matches(step_name, step_matches) #:nodoc: no_groups = step_matches.select {|step_match| step_match.args.length == 0} max_arg_length = step_matches.map {|step_match| step_match.args.length }.max top_groups = step_matches.select {|step_match| step_match.args.length == max_arg_length } if no_groups.any? longest_regexp_length = no_groups.map {|step_match| step_match.text_length }.max no_groups.select {|step_match| step_match.text_length == longest_regexp_length } elsif top_groups.any? shortest_capture_length = top_groups.map {|step_match| step_match.args.inject(0) {|sum, c| sum + c.to_s.length } }.min top_groups.select {|step_match| step_match.args.inject(0) {|sum, c| sum + c.to_s.length } == shortest_capture_length } else top_groups end end
def embed(file, mime_type)
not be ignored, depending on what kind of formatter(s) are active.
Embed +file+ of MIME type +mime_type+ into the output. This may or may
def embed(file, mime_type) @visitor.embed(file, mime_type) end
def initialize
def initialize @unsupported_programming_languages = [] @programming_languages = [] @language_map = {} @current_scenario = nil end
def invoke(step_name, multiline_argument=nil)
def invoke(step_name, multiline_argument=nil) begin step_match(step_name).invoke(multiline_argument) rescue Exception => e e.nested! if Undefined === e raise e end end
def invoke_steps(steps_text, natural_language)
Then I should not be thirsty
Given I have 8 cukes in my belly
invoke(%Q{
Invokes a series of steps +steps_text+. Example:
def invoke_steps(steps_text, natural_language) ored_keywords = natural_language.step_keywords.map{|kw| Regexp.escape(kw)}.join("|") # TODO Gherkin: # This a bit hacky and fragile. When we move to Gherkin we should replace this entire method body # with a call to the parser - parsing the body of a scenario. We may need to put the parser/policy in the # appropriate state (the same state it's in after parsing a Scenario: line). steps_text.strip.split(/(?=^\s*(?:#{ored_keywords}))/).map { |step| step.strip }.each do |step| output = step.match(/^\s*(#{ored_keywords})([^\n]+)(\n.*)?$/m) action, step_name, table_or_string = output[1], output[2], output[3] if table_or_string.to_s.strip =~ /^\|/ table_or_string = table(table_or_string) elsif table_or_string.to_s.strip =~ /^"""/ table_or_string = py_string(table_or_string.gsub(/^\n/, "")) end args = [step_name, table_or_string].compact invoke(*args) end end
def jruby_gets(timeout_seconds)
def jruby_gets(timeout_seconds) answer = nil t = java.lang.Thread.new do answer = STDIN.gets end t.start t.join(timeout_seconds * 1000) answer end
def load_code_file(step_def_file)
def load_code_file(step_def_file) if programming_language = programming_language_for(step_def_file) log.debug(" * #{step_def_file}\n") programming_language.load_code_file(step_def_file) else log.debug(" * #{step_def_file} [NOT SUPPORTED]\n") end end
def load_code_files(step_def_files)
def load_code_files(step_def_files) log.debug("Code:\n") step_def_files.each do |step_def_file| load_code_file(step_def_file) end log.debug("\n") end
def load_plain_text_features(feature_files)
def load_plain_text_features(feature_files) features = Ast::Features.new start = Time.new log.debug("Features:\n") feature_files.each do |f| feature_file = FeatureFile.new(f) feature = feature_file.parse(self, options) if feature features.add_feature(feature) log.debug(" * #{f}\n") end end duration = Time.now - start log.debug("Parsing feature files took #{format_duration(duration)}\n\n") features end
def load_programming_language(ext)
twice will return the same instance.
Instances are cached, so calling with the same argument
Loads and registers programming language implementation.
def load_programming_language(ext) return @language_map[ext] if @language_map[ext] programming_language_class = constantize("Cucumber::#{ext.capitalize}Support::#{ext.capitalize}Language") programming_language = programming_language_class.new(self) @programming_languages << programming_language @language_map[ext] = programming_language programming_language end
def log
def log @log ||= Logger.new(STDOUT) end
def max_step_definition_length #:nodoc:
def max_step_definition_length #:nodoc: @max_step_definition_length ||= step_definitions.map{|step_definition| step_definition.text_length}.max end
def mri_gets(timeout_seconds)
def mri_gets(timeout_seconds) begin Timeout.timeout(timeout_seconds) do STDIN.gets end rescue Timeout::Error => e nil end end
def options
def options @options ||= {} end
def programming_language_for(step_def_file) #:nodoc:
def programming_language_for(step_def_file) #:nodoc: if ext = File.extname(step_def_file)[1..-1] return nil if @unsupported_programming_languages.index(ext) begin load_programming_language(ext) rescue LoadError => e log.debug("Failed to load '#{ext}' programming language for file #{step_def_file}: #{e.message}\n") @unsupported_programming_languages << ext nil end else nil end end
def py_string(string_with_triple_quotes, file=nil, line_offset=0)
Is retured as: " hello\nworld"
"""
world
hello
"""
Returns a regular String for +string_with_triple_quotes+. Example:
def py_string(string_with_triple_quotes, file=nil, line_offset=0) Ast::PyString.parse(string_with_triple_quotes) end
def scenario_visited(scenario) #:nodoc:
def scenario_visited(scenario) #:nodoc: scenarios << scenario unless scenarios.index(scenario) end
def scenarios(status = nil) #:nodoc:
def scenarios(status = nil) #:nodoc: @scenarios ||= [] if(status) @scenarios.select{|scenario| scenario.status == status} else @scenarios end end
def snippet_text(step_keyword, step_name, multiline_arg_class) #:nodoc:
def snippet_text(step_keyword, step_name, multiline_arg_class) #:nodoc: load_programming_language('rb') if unknown_programming_language? @programming_languages.map do |programming_language| programming_language.snippet_text(step_keyword, step_name, multiline_arg_class) end.join("\n") end
def step_match(step_name, name_to_report=nil) #:nodoc:
def step_match(step_name, name_to_report=nil) #:nodoc: matches = @programming_languages.map do |programming_language| programming_language.step_matches(step_name, name_to_report).to_a end.flatten raise Undefined.new(step_name) if matches.empty? matches = best_matches(step_name, matches) if matches.size > 1 && options[:guess] raise Ambiguous.new(step_name, matches, options[:guess]) if matches.size > 1 matches[0] end
def step_visited(step) #:nodoc:
def step_visited(step) #:nodoc: steps << step unless steps.index(step) end
def steps(status = nil) #:nodoc:
def steps(status = nil) #:nodoc: @steps ||= [] if(status) @steps.select{|step| step.status == status} else @steps end end
def table(text_or_table, file=nil, line_offset=0)
])
%w{ CUC-101 Peeler 22 }
%w{ INT-100 Taxi 114 },
%w{ account description amount },
table([
or a 2D Array:
})
| CUC-101 | Peeler | 22 |
| INT-100 | Taxi | 114 |
| account | description | amount |
table(%{
be a String:
Returns a Cucumber::Ast::Table for +text_or_table+, which can either
def table(text_or_table, file=nil, line_offset=0) if Array === text_or_table Ast::Table.new(text_or_table) else Ast::Table.parse(text_or_table) end end
def unknown_programming_language?
def unknown_programming_language? @programming_languages.empty? end
def unmatched_step_definitions
def unmatched_step_definitions @programming_languages.map do |programming_language| programming_language.unmatched_step_definitions end.flatten end