class Cukerail::Sender

Attributes

failed_step[R]
testrail_api_client[R]

Public Class Methods

new(runtime, io, options) click to toggle source
# File lib/cukerail.rb, line 10
def initialize(runtime, io, options)
  if %w(BASE_URL USER PASSWORD).map{|e| ENV["TESTRAIL_#{e}"]}.any?{|e| e=='' || !e}
    raise 'You need to setup Testrail environment parameters see https://bbcworldwide.atlassian.net/wiki/display/BAR/Installing+and+Running+Cukerail' 
  end
  @testrail_api_client = TestRail::APIClient.new(ENV['TESTRAIL_BASE_URL'],ENV['TESTRAIL_USER'],ENV['TESTRAIL_PASSWORD'],ENV['TESTRAIL_PROXY_URL'],ENV['TESTRAIL_PROXY_PORT'])
end

Public Instance Methods

add_case_to_test_run(testcase_id,run_id) click to toggle source
# File lib/cukerail.rb, line 244
def add_case_to_test_run(testcase_id,run_id)
  run = get_run(run_id)
  unless run['include_all']
    case_ids = get_tests_in_a_run(run_id).map{|h| h['case_id']} + [testcase_id]
    update_run(run_id,{'case_ids'=>case_ids})
  end
end
after_test_case(test_case,result) click to toggle source
# File lib/cukerail.rb, line 17
def after_test_case(test_case,result)
  #guard clause
  return false unless test_case.tags.any?{|tag| tag.name =~/project/} && test_case.tags.any?{|tag| tag.name=~/suite/} 
  feature = test_case.feature
  #      ap feature.methods - Object.methods
  #      ap feature.tags
  @id = get_id(test_case)
  raise 'No id found' unless @id
  send_steps(test_case,@id)
  if ENV['UPDATE_SOURCE'] && !test_case.source.any?{|h| h.is_a?(Cucumber::Core::Ast::ScenarioOutline)}
    update_source_file(test_case,@id)
  end
  if ENV['TESTRUN']
    send_result(test_case,result,@id,ENV['TESTRUN'].to_i) 
  end
rescue StandardError => e
  puts "#{e.message} in #{extract_title(test_case)}"
end
after_test_step(step,result) click to toggle source
# File lib/cukerail.rb, line 45
def after_test_step(step,result)
  unless result.passed?
    # only the first non-passed step
    failed_step[:step]   ||= step
    failed_step[:result] ||= result
  end
end
all_tags(test_case) click to toggle source
# File lib/cukerail.rb, line 180
def all_tags(test_case)
  test_case.tags + test_case.feature.tags
end
before_test_case(*args) click to toggle source
# File lib/cukerail.rb, line 53
def before_test_case(*args)
  @test_steps =[]
  @scenario_tags = []
  @failed_step = {}
end
create_new_case(project_id,suite_id,sub_section_id,test_case) click to toggle source
# File lib/cukerail.rb, line 108
def create_new_case(project_id,suite_id,sub_section_id,test_case)
  testrail_api_client.send_post("add_case/#{sub_section_id || suite_id}",test_case_data(test_case))
end
defects(test_case) click to toggle source
# File lib/cukerail.rb, line 184
def defects(test_case)
  all_tags(test_case).select{|tag| tag.name =~/(?:jira|defect)_/}.map{|ticket| /(?:jira|defect)_(\w+-\d+)$/.match(ticket.name)[1]}.uniq.join(",")
end
extract_title(test_case) click to toggle source
# File lib/cukerail.rb, line 157
def extract_title(test_case)
  requirements_tags = all_tags(test_case).select{|tag| tag.name =~ /req_\w+/}.map{|tag| /req_(\w+)/.match(tag.name)[1]}.join(', ')
  if test_case.source.last.is_a?(Cucumber::Core::Ast::ExamplesTable::Row)
    title  = test_case.source.select{|s| s.is_a?(Cucumber::Core::Ast::ScenarioOutline)}.first.name
    if ENV['OLD_STYLE_OUTLINE_NAMES'] 
      title += ' :: ' + test_case.source.last.send(:data).map{|key,value| "#{key}='#{value}'"}.join(', ')
    else
      title += " " + test_case.source.last.send(:data).map{|key,value| "#{key}=#{value}"}.join(', ')
    end
  else
    title = test_case.source.last.name
  end
  [requirements_tags,title].compact.join(' ').strip
end
get_id(test_case) click to toggle source
# File lib/cukerail.rb, line 59
def get_id(test_case)
  #      ap test_case.methods - Object.methods
  tagged_id = test_case.tags.detect{|tag| tag.name =~/testcase/}
  if tagged_id
    result = /testcase_(\d+)/.match(tagged_id.name)[1]
  else
    tags = test_case.tags
    project_id = /\d+/.match(tags.select{|tag| tag.name =~/project/}.last.name)[0] 
    suite_id = /\d+/.match(tags.select{|tag| tag.name =~/suite/}.last.name)[0] 
    section_id = /\d+/.match(tags.select{|tag| tag.name =~/sub_section/}.last.name)[0] 
    title = extract_title(test_case)
    found_case = testrail_api_client.send_get("get_cases/#{project_id}&suite_id=#{suite_id}&section_id=#{section_id}").select{|c| c['title'] == title}.first
    if found_case
      result= found_case['id']
    else
      result = create_new_case(project_id,suite_id,section_id,test_case)['id']
    end
  end
  return result
end
get_run(run_id) click to toggle source
# File lib/cukerail.rb, line 172
def get_run(run_id)
  testrail_api_client.send_get("get_run/#{run_id}")
end
get_tests_in_a_run(run_id) click to toggle source
# File lib/cukerail.rb, line 176
def get_tests_in_a_run(run_id)
  testrail_api_client.send_get("get_tests/#{run_id}")
end
refs(test_case) click to toggle source
# File lib/cukerail.rb, line 188
def refs(test_case)
  all_tags(test_case).select{|tag| tag.name =~/(?:jira|ref)_/}.map{|ticket| /(?:jira|ref)_(\w+-\d+)$/.match(ticket.name)[1]}.uniq.join(",")
end
remove_case_from_test_run(testcase,run_id) click to toggle source
# File lib/cukerail.rb, line 235
def remove_case_from_test_run(testcase,run_id)
  testcase_id = get_id(testcase)
  run = get_run(run_id)
  unless run['include_all']
    case_ids = get_tests_in_a_run(run_id).map{|h| h['case_id']} - [testcase_id]
    update_run(run_id,{'case_ids'=>case_ids})
  end
end
send_result(test_case,result,id,testrun) click to toggle source
# File lib/cukerail.rb, line 112
    def send_result(test_case,result,id,testrun)
      testrail_status = case 
                        when result.passed?
                          {id:1,comment: 'passed'}
                        when result.failed?
                          {id:5,comment: 'failed'}
                        when result.undefined?
                          {id:7,comment: 'undefined step'}
                        when result.pending?
                          {id:6,comment: 'pending step'}
                        end
      unless result.passed?
        # the before step can fail. So we have to check
        if @failed_step[:step].source.last.is_a?(Cucumber::Hooks::BeforeHook)
          failed_step = 'failed in the before hook'
          location = 'before hook'
        elsif @failed_step[:step].source.last.is_a?(Cucumber::Hooks::AfterHook)
          failed_step = 'failed in the after hook'
          location = 'after hook'
        else
          failed_step = "#{@failed_step[:step].source.last.keyword}#{@failed_step[:step].source.last.name}"
          location=@failed_step[:step].source.last.file_colon_line
        end
        failure_message = <<-FAILURE
        Error message: #{testrail_status[:comment]} #{result.exception.message}
        Step: #{failed_step}
        Location: #{location}
        FAILURE
      else
        failure_message = nil
      end
      #only send defects if the test is not passed
      report_on_result =  {status_id:testrail_status[:id],comment:failure_message,defects:testrail_status[:id]==1 ? '' : defects(test_case)}
      begin
        testrail_api_client.send_post("add_result_for_case/#{testrun}/#{id}",report_on_result)
      rescue => e
        if e.message =~ /No \(active\) test found for the run\/case combination/
          add_case_to_test_run(id,testrun)
          retry
        else
          puts "#{e.message} testrun=#{testrun} test case id=#{id}"
        end
      end
    end
send_steps(test_case,id) click to toggle source
# File lib/cukerail.rb, line 104
def send_steps(test_case,id)
  testrail_api_client.send_post("update_case/#{id}",test_case_data(test_case))
end
step_name(keyword, step_match, status, source_indent, background, file_colon_line) click to toggle source
# File lib/cukerail.rb, line 40
def step_name(keyword, step_match, status, source_indent, background, file_colon_line)
  step_name = step_match.format_args(lambda{|param| "*#{param}*"})
  @test_steps << "#{keyword}#{step_name}"
end
tag_name(tag_name) click to toggle source
# File lib/cukerail.rb, line 36
def tag_name(tag_name)
  @scenario_tags << tag_name
end
test_case_data(test_case) click to toggle source
# File lib/cukerail.rb, line 200
def test_case_data(test_case)
  steps_as_string = test_case.test_steps.map{|step| step.source.last}
  .select{|step| step.is_a?(Cucumber::Core::Ast::Step)}
  .reject{|step| step.is_a?(Cucumber::Hooks::BeforeHook)}.map do | g_step |
    str = g_step.send(:keyword)+g_step.send(:name)
    str += g_step.multiline_arg.raw.map{|l|"\n| #{l.join(' | ')} |"}.join if g_step.multiline_arg.data_table?
    str
  end.join("\n")

  {'title'=>extract_title(test_case),
          'type_id'=>type_id(test_case),
          'custom_steps'=>steps_as_string,
          'refs'=>refs(test_case)
  }
end
type_id(test_case) click to toggle source
# File lib/cukerail.rb, line 192
def type_id(test_case)
  type_ids = [1]
  type_ids << 7  if test_case.tags.any?{|tag| tag.name =~/manual/}
  type_ids << 13 if test_case.tags.any?{|tag| tag.name =~/on_hold/}
  #get the highest precedence type found in the tags. E.g. if it's @on_hold and @manual it selects 13 for on hold
  ([13,7,1] & type_ids).first
end
update_plan(plan_id,run_id,case_ids) click to toggle source
# File lib/cukerail.rb, line 229
def update_plan(plan_id,run_id,case_ids)
  test_plan = testrail_api_client.send_get("get_plan/#{plan_id}")
  entry_id = test_plan['entries'].detect{|e| e['runs'].any?{|r| r['id']==run_id}}['id']
  testrail_api_client.send_post("update_plan_entry/#{plan_id}/#{entry_id}",case_ids)
end
update_run(run_id,case_ids) click to toggle source
# File lib/cukerail.rb, line 216
def update_run(run_id,case_ids)
  run = get_run(run_id)
  begin
    if run['plan_id']
      update_plan(run['plan_id'],run_id,case_ids)
    else
      testrail_api_client.send_post("update_run/#{run_id}",case_ids)
    end
  rescue => e
    puts "#{e.message} testrun=#{run_id} test case ids=#{case_ids}"
  end
end
update_source_file(scenario, external_reference) click to toggle source
# File lib/cukerail.rb, line 80
def update_source_file(scenario, external_reference)
  #this could be done on one line with sed. But the format is different on Mac OS and GNU Linux version and it definitely won't work on a Windows machine
  # byebug
  path = scenario.location.file
  # ap path
  lines = IO.readlines(path)
  lines[scenario.location.line-2].gsub!(/ @testcase_\d*/," @testcase_#{external_reference}") 
  lines[scenario.location.line-2].gsub!(/\n/," @testcase_#{external_reference}") unless lines[scenario.location.line-2] =~ /testcase/ 
  temp_file = Tempfile.new('foo')
  begin
    File.open(path, 'r') do |file|
      lines.each do |line|
        # puts line
        temp_file.puts line
      end
    end
    temp_file.close
    FileUtils.mv(temp_file.path, path)
  ensure
    temp_file.close
    temp_file.unlink
  end
end