class ReportPortal::Cucumber::JSONSlurper

Helper class to parse and post test launch data generated by Cucumber's JSON formatter Supports ONLY reports generated with -x switch (i.e. expanded outlines)

Public Class Methods

new(filename) click to toggle source
# File lib/report_portal/cucumber/json_slurper.rb, line 31
def initialize(filename)
  @json = JSON.parse(File.read(filename))
  fail "Cannot process #{filename} because it was generated without -x/--expand Cucumber option!" unless expanded?
  calculate_start_time
end

Public Instance Methods

run() click to toggle source
# File lib/report_portal/cucumber/json_slurper.rb, line 37
def run
  ReportPortal.start_launch('', get_time)

  @json.each do |feature|
    current_feature = ReportPortal::TestItem.new("Feature: #{feature['name']}",
                                                 :TEST,
                                                 nil,
                                                 get_time,
                                                 feature['uri'],
                                                 nil,
                                                 tags(feature))
    current_feature_node = Tree::TreeNode.new(SecureRandom.hex, current_feature)
    current_feature.id = ReportPortal.start_item(current_feature_node)
    current_element_name = nil
    current_outline_row = 0

    feature['elements'].each do |element|
      type = element['type'] == 'background' ? :BEFORE_CLASS : :STEP

      element_name = "#{element['keyword']}: #{element['name']}"
      if element['keyword'] == 'Scenario Outline'
        if element['name'] == current_element_name
          current_outline_row += 1
        else
          current_element_name = element['name']
          current_outline_row = 1
        end
        element_name << " [#{current_outline_row}]"
      end

      ReportPortal.current_scenario = ReportPortal::TestItem.new(element_name,
                                                                 type,
                                                                 nil,
                                                                 get_time,
                                                                 "#{feature['uri']}:#{element['line']}",
                                                                 nil,
                                                                 tags(element))
      current_scenario_node = Tree::TreeNode.new(SecureRandom.hex, ReportPortal.current_scenario)
      ReportPortal.current_scenario.id = ReportPortal.start_item(current_scenario_node)

      statuses = report_hooks(element, 'before')
      forced_issue = nil
      element['steps'].each do |step|
        name = decorate("#{step['keyword']}#{step['name']}")
        if step['rows']
          name << step['rows'].reduce("\n") { |acc, row| acc << decorate("| #{row['cells'].join(' | ')} |") << "\n" }
        end
        if step['doc_string']
          name << %(\n"""\n#{step['doc_string']['value']}\n""")
        end

        ReportPortal.send_log(:passed, name, get_time)
        step['output'].each { |o| ReportPortal.send_log(:passed, o, get_time) } unless step['output'].nil?
        error = step['result']['error_message']
        ReportPortal.send_log(:failed, error, get_time) if error
        (step['embeddings'] || []).each do |embedding|
          ReportPortal.send_file(:failed, embedding['data'], 'Embedding', get_time, embedding['mime_type'])
        end
        statuses << step['result']['status']
        forced_issue ||= case step['result']['status']
                         when 'pending'
                           error
                         when 'undefined'
                           "Undefined step #{step['name']} at #{step['match']['location']}"
                         else
                           nil
                         end

        ReportPortal.send_log(step['result']['status'].to_sym,
                              "STEP #{step['result']['status'].upcase}",
                              get_time(step['result']['duration'].to_i / 1_000_000))
      end
      statuses += report_hooks(element, 'after')

      status = if statuses.any? { |s| %w(failed undefined pending).include? s }
                 :failed
               elsif statuses.all? { |s| s == 'passed' }
                 :passed
               else
                 :skipped
               end

      ReportPortal.finish_item(ReportPortal.current_scenario, status, get_time, forced_issue)
      ReportPortal.current_scenario = nil
    end

    ReportPortal.finish_item(current_feature, nil, get_time)
  end
  ReportPortal.finish_item(root, nil, get_time)
  ReportPortal.finish_launch(get_time)
end

Private Instance Methods

calculate_start_time() click to toggle source
# File lib/report_portal/cucumber/json_slurper.rb, line 148
def calculate_start_time
  duration_nanos = 0
  @json.each do |f|
    f['elements'].each do |e|
      items = e['steps'] + (e['before'] || []) + (e['after'] || [])
      items.each do |s|
        duration_nanos += s['result']['duration'].to_i
      end
    end
  end
  @now = (Time.now.to_f * 1000).to_i - (duration_nanos / 1_000_000)
end
decorate(str) click to toggle source
# File lib/report_portal/cucumber/json_slurper.rb, line 135
def decorate(str)
  sep = '-' * 25
  "#{sep}#{str}#{sep}"
end
expanded?() click to toggle source
# File lib/report_portal/cucumber/json_slurper.rb, line 140
def expanded?
  bad_item = @json.find do |f|
    so = f['elements'].find { |e| e['keyword'] == 'Scenario Outline' }
    so ? so.key?('examples') : false
  end
  bad_item.nil?
end
get_time(offset = 0) click to toggle source

update current time and return

# File lib/report_portal/cucumber/json_slurper.rb, line 162
def get_time(offset = 0)
  @now += offset + 1
end
report_hooks(element, tag) click to toggle source

report before/after hooks and return array of their statuses

# File lib/report_portal/cucumber/json_slurper.rb, line 167
def report_hooks(element, tag)
  (element[tag] || []).map do |hook|
    ReportPortal.send_log(hook['result']['status'].to_sym,
                          "HOOK #{hook['match']['location']} #{hook['result']['status'].upcase}",
                          get_time(hook['result']['duration'].to_i / 1_000_000))
    hook['result']['status']
  end
end
tags(item) click to toggle source
# File lib/report_portal/cucumber/json_slurper.rb, line 131
def tags(item)
  item['tags'].nil? ? [] : item['tags'].map { |h| h['name'] }
end