require'multi_json'require'base64'require'cucumber/formatter/io'require'cucumber/formatter/hook_query_visitor'moduleCucumbermoduleFormatter# The formatter used for <tt>--format json</tt>classJsonincludeIodefinitialize(runtime,io,_options)@runtime=runtime@io=ensure_io(io)@feature_hashes=[]enddefbefore_test_case(test_case)builder=Builder.new(test_case)unlesssame_feature_as_previous_test_case?(test_case.feature)@feature_hash=builder.feature_hash@feature_hashes<<@feature_hashend@test_case_hash=builder.test_case_hashifbuilder.background?feature_elements<<builder.background_hash@element_hash=builder.background_hashelsefeature_elements<<@test_case_hash@element_hash=@test_case_hashend@any_step_failed=falseenddefbefore_test_step(test_step)returnifinternal_hook?(test_step)hook_query=HookQueryVisitor.new(test_step)ifhook_query.hook?@step_or_hook_hash={}hooks_of_type(hook_query)<<@step_or_hook_hashreturnendiffirst_step_after_background?(test_step)feature_elements<<@test_case_hash@element_hash=@test_case_hashend@step_or_hook_hash=create_step_hash(test_step.source.last)steps<<@step_or_hook_hash@step_hash=@step_or_hook_hashenddefafter_test_step(test_step,result)returnifinternal_hook?(test_step)add_match_and_result(test_step,result)@any_step_failed=trueifresult.failed?enddefafter_test_case(test_case,result)add_failed_around_hook(result)ifresult.failed?&&!@any_step_failedenddefdone@io.write(MultiJson.dump(@feature_hashes,pretty: true))enddefputs(message)test_step_output<<messageenddefembed(src,mime_type,_label)ifFile.file?(src)content=File.open(src,'rb'){|f|f.read}data=encode64(content)elseifmime_type=~/;base64$/mime_type=mime_type[0..-8]data=srcelsedata=encode64(src)endendtest_step_embeddings<<{mime_type: mime_type,data: data}endprivatedefsame_feature_as_previous_test_case?(feature)current_feature[:uri]==feature.file&¤t_feature[:line]==feature.location.lineenddeffirst_step_after_background?(test_step)test_step.source[1].name!=@element_hash[:name]enddefinternal_hook?(test_step)test_step.source.last.location.file.include?('lib/cucumber/')enddefcurrent_feature@feature_hash||={}enddeffeature_elements@feature_hash[:elements]||=[]enddefsteps@element_hash[:steps]||=[]enddefhooks_of_type(hook_query)casehook_query.typewhen:beforereturnbefore_hookswhen:afterreturnafter_hookswhen:after_stepreturnafter_step_hookselsefail'Unkown hook type '+hook_query.type.to_sendenddefbefore_hooks@element_hash[:before]||=[]enddefafter_hooks@element_hash[:after]||=[]enddefaround_hooks@element_hash[:around]||=[]enddefafter_step_hooks@step_hash[:after]||=[]enddeftest_step_output@step_or_hook_hash[:output]||=[]enddeftest_step_embeddings@step_or_hook_hash[:embeddings]||=[]enddefcreate_step_hash(step_source)step_hash={keyword: step_source.keyword,name: step_source.name,line: step_source.location.line}step_hash[:comments]=Formatter.create_comments_array(step_source.comments)unlessstep_source.comments.empty?step_hash[:doc_string]=create_doc_string_hash(step_source.multiline_arg)ifstep_source.multiline_arg.doc_string?step_hashenddefcreate_doc_string_hash(doc_string)content_type=doc_string.content_type?doc_string.content_type:""{value: doc_string.content,content_type: content_type,line: doc_string.location.line}enddefadd_match_and_result(test_step,result)@step_or_hook_hash[:match]=create_match_hash(test_step,result)@step_or_hook_hash[:result]=create_result_hash(result)enddefadd_failed_around_hook(result)@step_or_hook_hash={}around_hooks<<@step_or_hook_hash@step_or_hook_hash[:match]={location: "unknown_hook_location:1"}@step_or_hook_hash[:result]=create_result_hash(result)enddefcreate_match_hash(test_step,result){location: test_step.action_location}enddefcreate_result_hash(result)result_hash={status: result.to_sym}result_hash[:error_message]=create_error_message(result)ifresult.failed?||result.pending?result.duration.tap{|duration|result_hash[:duration]=duration.nanoseconds}result_hashenddefcreate_error_message(result)message_element=result.failed??result.exception:resultmessage="#{message_element.message} (#{message_element.class})"([message]+message_element.backtrace).join("\n")enddefencode64(data)# strip newlines from the encoded dataBase64.encode64(data).gsub(/\n/,'')endclassBuilderattr_reader:feature_hash,:background_hash,:test_case_hashdefinitialize(test_case)@background_hash=niltest_case.describe_source_to(self)test_case.feature.background.describe_to(self)enddefbackground?@background_hash!=nilenddeffeature(feature)@feature_hash={uri: feature.file,id: create_id(feature),keyword: feature.keyword,name: feature.name,description: feature.description,line: feature.location.line}unlessfeature.tags.empty?@feature_hash[:tags]=create_tags_array(feature.tags)if@test_case_hash[:tags]@test_case_hash[:tags]=@feature_hash[:tags]+@test_case_hash[:tags]else@test_case_hash[:tags]=@feature_hash[:tags]endend@feature_hash[:comments]=Formatter.create_comments_array(feature.comments)unlessfeature.comments.empty?@test_case_hash[:id].insert(0,@feature_hash[:id]+';')enddefbackground(background)@background_hash={keyword: background.keyword,name: background.name,description: background.description,line: background.location.line,type: 'background'}@background_hash[:comments]=Formatter.create_comments_array(background.comments)unlessbackground.comments.empty?enddefscenario(scenario)@test_case_hash={id: create_id(scenario),keyword: scenario.keyword,name: scenario.name,description: scenario.description,line: scenario.location.line,type: 'scenario'}@test_case_hash[:tags]=create_tags_array(scenario.tags)unlessscenario.tags.empty?@test_case_hash[:comments]=Formatter.create_comments_array(scenario.comments)unlessscenario.comments.empty?enddefscenario_outline(scenario)@test_case_hash={id: create_id(scenario)+';'+@example_id,keyword: scenario.keyword,name: scenario.name,description: scenario.description,line: @row.location.line,type: 'scenario'}tags=[]tags+=create_tags_array(scenario.tags)unlessscenario.tags.empty?tags+=@examples_table_tagsif@examples_table_tags@test_case_hash[:tags]=tagsunlesstags.empty?comments=[]comments+=Formatter.create_comments_array(scenario.comments)unlessscenario.comments.empty?comments+=@examples_table_commentsif@examples_table_commentscomments+=@row_commentsif@row_comments@test_case_hash[:comments]=commentsunlesscomments.empty?enddefexamples_table(examples_table)# the json file have traditionally used the header row as row 1,# wheras cucumber-ruby-core used the first example row as row 1.@example_id=create_id(examples_table)+";#{@row.number+1}"@examples_table_tags=create_tags_array(examples_table.tags)unlessexamples_table.tags.empty?@examples_table_comments=Formatter.create_comments_array(examples_table.comments)unlessexamples_table.comments.empty?enddefexamples_table_row(row)@row=row@row_comments=Formatter.create_comments_array(row.comments)unlessrow.comments.empty?endprivatedefcreate_id(element)element.name.downcase.gsub(/ /,'-')enddefcreate_tags_array(tags)tags_array=[]tags.each{|tag|tags_array<<{name: tag.name,line: tag.location.line}}tags_arrayendendenddefself.create_comments_array(comments)comments_array=[]comments.each{|comment|comments_array<<{value: comment.to_s.strip,line: comment.location.line}}comments_arrayendendend