class CucumberJunitToJson::App

The Cucumber Junit to Json app class that handles std inputs and command line args

Constants

Error

Attributes

feature_parser[R]
junit_parser[R]
stderr[R]
stdin[R]
stdout[R]

Public Class Methods

new(options = {}) click to toggle source
# File lib/cucumber_junit_to_json/app.rb, line 22
def initialize(options = {})
  @stdin  = options[:stdin] || STDIN
  @stdout = options[:stdout] || STDOUT
  @stderr = options[:stderr] || STDERR
  @feature_id = 1
  @scenario_id = 1
end

Public Instance Methods

run(*args) click to toggle source
# File lib/cucumber_junit_to_json/app.rb, line 32
def run(*args)
  options = parse_args(args)
  @junit_parser = CucumberJunitToJson::Parsers::JunitParser.new(options.junit_dir)
  @feature_parser = CucumberJunitToJson::Parsers::FeatureParser.new(options.feature_dir)
  output_file = options.output_file || 'cucumber.json'
  features = []
  unless File.file?(output_file)
    output_directory = File.dirname(output_file)
    FileUtils.mkdir_p(output_directory) unless File.directory?(output_directory)
    FileUtils.touch(output_file)
    raise Error, "Could not create output file #{output_file}" unless File.file?(output_file)
  end
  # if we are dealing with a directory of xml files
  if File.directory?(junit_parser.path_to_junit)
    Find.find(junit_parser.path_to_junit) do |path_to_file|
      next unless File.file?(path_to_file) && File.extname(path_to_file) == '.xml'
      features.push(convert_to_json(path_to_file))
    end
    # if we are dealing with just a single xml file
  elsif File.exist?(junit_parser.path_to_junit)
    features.push(convert_to_json(junit_parser.path_to_junit))
  end
  open(output_file, 'w') { |f| f.write(features.to_json) }
  0
rescue Error, OptionParser::ParseError => error
  stderr.puts error.message
  1
end

Private Instance Methods

convert_to_json(file) click to toggle source
# File lib/cucumber_junit_to_json/app.rb, line 63
def convert_to_json(file)
  file_content = @junit_parser.read(file)

  return if file_content.empty?

  document = Nokogiri::XML::Document.parse(file_content)
  testsuite = document.at_css('testsuite')
  feature_uri = Pathname.new(File.join(@feature_parser.path_to_features, @junit_parser.path_to_file(testsuite['name']))).realpath.to_s
  feature = CucumberJunitToJson::Models::Feature.new
  feature.keyword = 'Feature'
  feature.name = @junit_parser.feature_name(testsuite['name'])
  feature.uri = feature_uri
  feature.id = @feature_id
  feature.tags, feature.line = @feature_parser.tags_and_line_number_matching(feature_uri, "#{feature.keyword}:")

  testcases = testsuite.css('testcase')
  scenarios = []
  testcases.each do |testcase|
    scenario = CucumberJunitToJson::Models::Scenario.new
    failing_step = nil
    failure_message = nil
    if testcase['status'] == 'failed'
      failure = testcase.css('failure')
      # Sometimes <failure> node come as <error>
      failure ||= testcase.css('error')
      if failure && !failure.nil?
        # Gracefully rescue issues with failure node not being properly formatted
        begin
          failure_info = failure.text.split(failure.attribute('message')).first
          failure_message = failure.attribute('message')
          failing_step = failure_info.split('Failing step:').last.split('...').first.strip
        rescue StandardError => e
          puts "Rescuing Error with failure node #{failure}\n#{e.message}\n"
        end
      end
    end
    # Removing scenario outline added blob gives you the actual scenario name
    use_similar_matcher = false
    use_similar_matcher = true if testcase['name'].include?('-- @')
    scenario.name = testcase['name'].split('-- @').first.strip
    scenario.tags, scenario.line = @feature_parser.tags_and_line_number_matching(feature_uri, scenario.name, use_similar_matcher)
    scenario_line_text = @feature_parser.text_and_line_number_matching(feature_uri, scenario.name).first
    scenario.keyword = scenario_line_text.split(':').first.strip
    scenario.type = 'scenario'
    scenario.uri = feature_uri
    scenario.id = @scenario_id
    scenario_output = get_string_between(testcase.at_css('system-out').text, '@scenario.begin', '@scenario.end')
    scenario.steps = CucumberJunitToJson::Models::Step.get_steps_for(scenario.name, scenario_output, feature_uri, failing_step, failure_message)
    scenarios.push(scenario)
    @scenario_id += 1
  end
  feature.elements = scenarios
  builder = CucumberJunitToJson::FeatureJsonBuilder.new(feature)
  @feature_id += 1
  JSON.parse(builder.feature_json)
end
get_string_between(str, start_at, end_at) click to toggle source
# File lib/cucumber_junit_to_json/app.rb, line 120
def get_string_between(str, start_at, end_at)
  regex = /#{start_at}(.*?)#{end_at}/m
  str[regex, 1]
end
parse_args(args) click to toggle source
# File lib/cucumber_junit_to_json/app.rb, line 125
def parse_args(args)
  options = OpenStruct.new
  options.junit_dir = nil
  options.feature_dir = nil
  options.output_file = nil
  parser = OptionParser.new do |p|
    p.banner = "USAGE: #{$PROGRAM_NAME} --junit-dir JUNITDIR --feature-dir FEATUREDIR"
    p.separator ''
    p.separator 'Specific options:'

    p.on '-j', '--junit-dir JUNITDIR', 'Provide a path to junit .xml files.' do |junit_dir|
      options.junit_dir = junit_dir
    end

    p.on '-f', '--feature-dir FEATUREDIR', 'Provide a path to .feature files.' do |feature_dir|
      options.feature_dir = feature_dir
    end

    p.on '-o', '--output [OUTPUT]', 'Provide a path to output json file.' do |output|
      options.output_file = output
    end

    p.on_tail('-h', '--help', 'Show this message') do
      puts p
      exit
    end

    # Another typical switch to print the version.
    p.on_tail('--version', 'Show version') do
      puts CucumberJunitToJson::VERSION
      exit
    end
  end

  parser.parse!(args)
  raise Error, parser.banner unless options.junit_dir && options.feature_dir
  options
end