require'cucumber/constantize'require'cucumber/core_ext/instance_exec'require'cucumber/language_support/language_methods'require'cucumber/formatter/duration'require'cucumber/cli/options'require'timeout'moduleCucumber# Raised when there is no matching StepDefinition for a step.classUndefined<StandardErrorattr_reader:step_namedefinitialize(step_name)super%{Undefined step: "#{step_name}"}@step_name=step_nameenddefnested!@nested=trueenddefnested?@nestedendend# Raised when a StepDefinition's block invokes World#pendingclassPending<StandardErrorend# Raised when a step matches 2 or more StepDefinitionsclassAmbiguous<StandardErrordefinitialize(step_name,step_definitions,used_guess)message="Ambiguous match of \"#{step_name}\":\n\n"message<<step_definitions.map{|sd|sd.backtrace_line}.join("\n")message<<"\n\n"message<<"You can run again with --guess to make Cucumber be more smart about it\n"unlessused_guesssuper(message)endend# This is the meaty part of Cucumber that ties everything together.classStepMotherincludeConstantizeincludeFormatter::Durationattr_writer:options,:visitor,:logdefinitialize@unsupported_programming_languages=[]@programming_languages=[]@language_map={}@current_scenario=nilenddefload_plain_text_features(feature_files)features=Ast::Features.newstart=Time.newlog.debug("Features:\n")feature_files.eachdo|f|feature_file=FeatureFile.new(f)feature=feature_file.parse(self,options)iffeaturefeatures.add_feature(feature)log.debug(" * #{f}\n")endendduration=Time.now-startlog.debug("Parsing feature files took #{format_duration(duration)}\n\n")featuresenddefload_code_files(step_def_files)log.debug("Code:\n")step_def_files.eachdo|step_def_file|load_code_file(step_def_file)endlog.debug("\n")enddefload_code_file(step_def_file)ifprogramming_language=programming_language_for(step_def_file)log.debug(" * #{step_def_file}\n")programming_language.load_code_file(step_def_file)elselog.debug(" * #{step_def_file} [NOT SUPPORTED]\n")endend# Loads and registers programming language implementation.# Instances are cached, so calling with the same argument# twice will return the same instance.#defload_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_languageprogramming_languageend# Returns the options passed on the command line.defoptions@options||=Cli::Options.newenddefstep_visited(step)#:nodoc:steps<<stepunlesssteps.index(step)enddefsteps(status=nil)#:nodoc:@steps||=[]if(status)@steps.select{|step|step.status==status}else@stepsendend# Output +announcement+ alongside the formatted output.# This is an alternative to using Kernel#puts - it will display# nicer, and in all outputs (in case you use several formatters)#defannounce(msg)msg.respond_to?(:join)?@visitor.announce(msg.join("\n")):@visitor.announce(msg.to_s)end# Suspends execution and prompts +question+ to the console (STDOUT).# An operator (manual tester) can then enter a line of text and hit# <ENTER>. The entered text is returned, and both +question+ and# the result is added to the output using #announce.## If you want a beep to happen (to grab the manual tester's attention),# just prepend ASCII character 7 to the question:## ask("#{7.chr}How many cukes are in the external system?")## If that doesn't issue a beep, you can shell out to something else# that makes a sound before invoking #ask.#defask(question,timeout_seconds)STDOUT.puts(question)STDOUT.flushannounce(question)if(Cucumber::JRUBY)answer=jruby_gets(timeout_seconds)elseanswer=mri_gets(timeout_seconds)endif(answer)announce(answer)answerelseraise("Waited for input for #{timeout_seconds} seconds, then timed out.")endend# Embed +file+ of MIME type +mime_type+ into the output. This may or may# not be ignored, depending on what kind of formatter(s) are active.#defembed(file,mime_type)@visitor.embed(file,mime_type)enddefscenarios(status=nil)#:nodoc:@scenarios||=[]if(status)@scenarios.select{|scenario|scenario.status==status}else@scenariosendenddefinvoke(step_name,multiline_argument=nil)beginstep_match(step_name).invoke(multiline_argument)rescueException=>ee.nested!ifUndefined===eraiseeendend# Invokes a series of steps +steps_text+. Example:## invoke(%Q{# Given I have 8 cukes in my belly# Then I should not be thirsty# })definvoke_steps(steps_text,i18n)lexer=i18n.lexer(Gherkin::Parser::Parser.new(StepInvoker.new(self),true,'steps'))lexer.scan(steps_text)endclassStepInvokerdefinitialize(step_mother)@step_mother=step_motherenddefstep(keyword,name,line)invoke@name=nameenddefpy_string(string,line)@multiline=Ast::PyString.new(string)enddefrow(row,line)@rows||=[]@rows<<rowenddefeofinvokeendprivatedefinvokeif@name@multiline=Ast::Table.new(@rows)if@multiline.nil?&&@rows@step_mother.invoke(*[@name,@multiline].compact)@name=nil@multiline=nil@rows=nilendendend# Returns a Cucumber::Ast::Table for +text_or_table+, which can either# be a String:## table(%{# | account | description | amount |# | INT-100 | Taxi | 114 |# | CUC-101 | Peeler | 22 |# })## or a 2D Array:## table([# %w{ account description amount },# %w{ INT-100 Taxi 114 },# %w{ CUC-101 Peeler 22 }# ])#deftable(text_or_table,file=nil,line_offset=0)ifArray===text_or_tableAst::Table.new(text_or_table)elseAst::Table.parse(text_or_table)endend# Returns a regular String for +string_with_triple_quotes+. Example:## """# hello# world# """## Is retured as: " hello\nworld"#defpy_string(string_with_triple_quotes,file=nil,line_offset=0)Ast::PyString.parse(string_with_triple_quotes)enddefstep_match(step_name,name_to_report=nil)#:nodoc:matches=@programming_languages.mapdo|programming_language|programming_language.step_matches(step_name,name_to_report).to_aend.flattenraiseUndefined.new(step_name)ifmatches.empty?matches=best_matches(step_name,matches)ifmatches.size>1&&options[:guess]raiseAmbiguous.new(step_name,matches,options[:guess])ifmatches.size>1matches[0]enddefbest_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}.maxtop_groups=step_matches.select{|step_match|step_match.args.length==max_arg_length}ifno_groups.any?longest_regexp_length=no_groups.map{|step_match|step_match.text_length}.maxno_groups.select{|step_match|step_match.text_length==longest_regexp_length}elsiftop_groups.any?shortest_capture_length=top_groups.map{|step_match|step_match.args.inject(0){|sum,c|sum+c.to_s.length}}.mintop_groups.select{|step_match|step_match.args.inject(0){|sum,c|sum+c.to_s.length}==shortest_capture_length}elsetop_groupsendenddefunmatched_step_definitions@programming_languages.mapdo|programming_language|programming_language.unmatched_step_definitionsend.flattenenddefsnippet_text(step_keyword,step_name,multiline_arg_class)#:nodoc:load_programming_language('rb')ifunknown_programming_language?@programming_languages.mapdo|programming_language|programming_language.snippet_text(step_keyword,step_name,multiline_arg_class)end.join("\n")enddefunknown_programming_language?@programming_languages.empty?enddefbefore_and_after(scenario,skip_hooks=false)#:nodoc:before(scenario)unlessskip_hooksyieldscenarioafter(scenario)unlessskip_hooksscenario_visited(scenario)enddefbefore(scenario)#:nodoc:returnifoptions[:dry_run]||@current_scenario@current_scenario=scenario@programming_languages.eachdo|programming_language|programming_language.before(scenario)endenddefafter(scenario)#:nodoc:@current_scenario=nilreturnifoptions[:dry_run]@programming_languages.eachdo|programming_language|programming_language.after(scenario)endenddefafter_step#:nodoc:returnifoptions[:dry_run]@programming_languages.eachdo|programming_language|programming_language.execute_after_step(@current_scenario)endenddefafter_configuration(configuration)#:nodoc@programming_languages.eachdo|programming_language|programming_language.after_configuration(configuration)endendprivatedefprogramming_language_for(step_def_file)#:nodoc:ifext=File.extname(step_def_file)[1..-1]returnnilif@unsupported_programming_languages.index(ext)beginload_programming_language(ext)rescueLoadError=>elog.debug("Failed to load '#{ext}' programming language for file #{step_def_file}: #{e.message}\n")@unsupported_programming_languages<<extnilendelsenilendenddefmax_step_definition_length#:nodoc:@max_step_definition_length||=step_definitions.map{|step_definition|step_definition.text_length}.maxenddefscenario_visited(scenario)#:nodoc:scenarios<<scenariounlessscenarios.index(scenario)enddeflog@log||=Logger.new(STDOUT)enddefmri_gets(timeout_seconds)beginTimeout.timeout(timeout_seconds)doSTDIN.getsendrescueTimeout::Error=>enilendenddefjruby_gets(timeout_seconds)answer=nilt=java.lang.Thread.newdoanswer=STDIN.getsendt.startt.join(timeout_seconds*1000)answerendendend