# frozen_string_literal: true require 'spec_helper' require 'cucumber/formatter/spec_helper' require 'cucumber/formatter/junit' require 'nokogiri' module Cucumber module Formatter class TestDoubleJunitFormatter < ::Cucumber::Formatter::Junit attr_reader :written_files def initialize(config) super config.on_event :test_step_started, &method(:on_test_step_started) end def on_test_step_started(_event) Interceptor::Pipe.unwrap! :stdout @fake_io = $stdout = StringIO.new $stdout.sync = true @interceptedout = Interceptor::Pipe.wrap(:stdout) end def on_test_step_finished(event) super $stdout = STDOUT @fake_io.close end def write_file(feature_filename, data) @written_files ||= {} @written_files[feature_filename] = data end end describe Junit do extend SpecHelperDsl include SpecHelper context 'With --junit,fileattribute=true option' do before(:each) do allow(File).to receive(:directory?) { true } @formatter = TestDoubleJunitFormatter.new( actual_runtime.configuration.with_options( out_stream: '', formats: [['junit', { 'fileattribute' => 'true' }]] ) ) end describe 'includes the file' do before(:each) do run_defined_feature @doc = Nokogiri.XML(@formatter.written_files.values.first) end define_steps do Given(/a passing scenario/) do Kernel.puts 'foo' end end define_feature <<-FEATURE Feature: One passing feature Scenario: Passing Given a passing scenario FEATURE it 'will contain the file attribute' do expect(@doc.xpath('//testsuite/testcase/@file').size).to equal 1 expect(@doc.xpath('//testsuite/testcase/@file').first.value).to eq('spec.feature') end end end context 'With --junit,fileattribute=different option' do before(:each) do allow(File).to receive(:directory?) { true } @formatter = TestDoubleJunitFormatter.new( actual_runtime.configuration.with_options( out_stream: '', formats: [['junit', { 'fileattribute' => 'different' }]] ) ) end describe 'includes the file' do before(:each) do run_defined_feature @doc = Nokogiri.XML(@formatter.written_files.values.first) end define_steps do Given(/a passing scenario/) do Kernel.puts 'foo' end end define_feature <<-FEATURE Feature: One passing feature Scenario: Passing Given a passing scenario FEATURE it 'will not contain the file attribute' do expect(@doc.xpath('//testsuite/testcase/@file').size).to equal 0 end end end context 'With --junit no fileattribute option' do before(:each) do allow(File).to receive(:directory?) { true } @formatter = TestDoubleJunitFormatter.new(actual_runtime.configuration.with_options(out_stream: '')) end describe 'includes the file' do before(:each) do run_defined_feature @doc = Nokogiri.XML(@formatter.written_files.values.first) end define_steps do Given(/a passing scenario/) do Kernel.puts 'foo' end end define_feature <<-FEATURE Feature: One passing scenario Scenario: Passing Given a passing scenario FEATURE it 'will not contain the file attribute' do expect(@doc.xpath('//testsuite/testcase/@file').size).to equal 0 end end end context 'With no options' do before(:each) do allow(File).to receive(:directory?) { true } @formatter = TestDoubleJunitFormatter.new(actual_runtime.configuration.with_options(out_stream: '')) end describe 'is able to strip control chars from cdata' do before(:each) do run_defined_feature @doc = Nokogiri.XML(@formatter.written_files.values.first) end define_steps do Given(/a passing ctrl scenario/) do Kernel.puts "boo\b\cx\e\a\f boo " end end define_feature <<-FEATURE Feature: One passing scenario, one failing scenario Scenario: Passing Given a passing ctrl scenario FEATURE it { expect(@doc.xpath('//testsuite/testcase/system-out').first.content).to match(/\s+boo boo\s+/) } end describe 'a feature with no name' do define_feature <<-FEATURE Feature: Scenario: Passing Given a passing scenario FEATURE it 'raises an exception' do expect { run_defined_feature }.to raise_error(Junit::UnNamedFeatureError) end end describe 'given a single feature' do before(:each) do run_defined_feature @doc = Nokogiri.XML(@formatter.written_files.values.first) end describe 'with a single scenario' do define_feature <<-FEATURE Feature: One passing scenario, one failing scenario Scenario: Passing Given a passing scenario FEATURE it { expect(@doc.to_s).to match(/One passing scenario, one failing scenario/) } it 'has not a root system-out node' do expect(@doc.xpath('//testsuite/system-out').size).to eq 0 end it 'has not a root system-err node' do expect(@doc.xpath('//testsuite/system-err').size).to eq 0 end it 'has a system-out node under ' do expect(@doc.xpath('//testcase/system-out').size).to eq 1 end it 'has a system-err node under ' do expect(@doc.xpath('//testcase/system-err').size).to eq 1 end end describe 'with a scenario in a subdirectory' do define_feature %( Feature: One passing scenario, one failing scenario Scenario: Passing Given a passing scenario ), File.join('features', 'some', 'path', 'spec.feature') it 'writes the filename with absolute path' do expect(@formatter.written_files.keys.first).to eq File.absolute_path('TEST-features-some-path-spec.xml') end end describe 'with a scenario outline table' do define_steps do Given(/.*/) {} end define_feature <<-FEATURE Feature: Eat things when hungry Scenario Outline: Eat things Given And stuff: | foo | | bar | Examples: Good | Things | | Cucumber | | Whisky | Examples: Evil | Things | | Big Mac | FEATURE it { expect(@doc.to_s).to match(/Eat things when hungry/) } it { expect(@doc.to_s).to match(/Cucumber/) } it { expect(@doc.to_s).to match(/Whisky/) } it { expect(@doc.to_s).to match(/Big Mac/) } it { expect(@doc.to_s).not_to match(/Things/) } it { expect(@doc.to_s).not_to match(/Good|Evil/) } it { expect(@doc.to_s).not_to match(/type="skipped"/) } end describe 'scenario with skipped test in junit report' do define_feature <<-FEATURE Feature: junit report with skipped test Scenario Outline: skip a test and junit report of the same Given a scenario Examples: | skip | | undefined | | still undefined | FEATURE it { expect(@doc.to_s).to match(/skipped="2"/) } end describe 'with a regular data table scenario' do define_steps do Given(/the following items on a shortlist/) { |table| } When(/I go.*/) {} Then(/I should have visited at least/) { |table| } end define_feature <<-FEATURE Feature: Shortlist Scenario: Procure items Given the following items on a shortlist: | item | | milk | | cookies | When I get some.. Then I'll eat 'em FEATURE # these type of tables shouldn't crash (or generate test cases) it { expect(@doc.to_s).not_to match(/milk/) } it { expect(@doc.to_s).not_to match(/cookies/) } end context 'with failing hooks' do describe 'with a failing before hook' do define_steps do Before do raise 'Before hook failed' end Given(/a passing step/) do end end define_feature <<-FEATURE Feature: One passing scenario Scenario: Passing Given a passing step FEATURE it { expect(@doc.to_s).to match(/Before hook at spec\/cucumber\/formatter\/junit_spec.rb:(\d+)/) } end describe 'with a failing after hook' do define_steps do After do raise 'After hook failed' end Given(/a passing step/) do end end define_feature <<-FEATURE Feature: One passing scenario Scenario: Passing Given a passing step FEATURE it { expect(@doc.to_s).to match(/After hook at spec\/cucumber\/formatter\/junit_spec.rb:(\d+)/) } end describe 'with a failing after step hook' do define_steps do AfterStep do raise 'AfterStep hook failed' end Given(/a passing step/) do end end define_feature <<-FEATURE Feature: One passing scenario Scenario: Passing Given a passing step FEATURE it { expect(@doc.to_s).to match(/AfterStep hook at spec\/cucumber\/formatter\/junit_spec.rb:(\d+)/) } end describe 'with a failing around hook' do define_steps do Around do |_scenario, block| block.call raise 'Around hook failed' end Given(/a passing step/) do end end define_feature <<-FEATURE Feature: One passing scenario Scenario: Passing Given a passing step FEATURE it { expect(@doc.to_s).to match(/Around hook\n\nMessage:/) } end end end end context 'In --expand mode' do let(:runtime) { Runtime.new(expand: true) } before(:each) do allow(File).to receive(:directory?) { true } @formatter = TestDoubleJunitFormatter.new(actual_runtime.configuration.with_options(out_stream: '', expand: true)) end describe 'given a single feature' do before(:each) do run_defined_feature @doc = Nokogiri.XML(@formatter.written_files.values.first) end describe 'with a scenario outline table' do define_steps do Given(/.*/) {} end define_feature <<-FEATURE Feature: Eat things when hungry Scenario Outline: Eat things Given And stuff: | foo | | bar | Examples: Good | Things | | Cucumber | | Whisky | Examples: Evil | Things | | Big Mac | FEATURE it { expect(@doc.to_s).to match(/Eat things when hungry/) } it { expect(@doc.to_s).to match(/Cucumber/) } it { expect(@doc.to_s).to match(/Whisky/) } it { expect(@doc.to_s).to match(/Big Mac/) } it { expect(@doc.to_s).not_to match(/Things/) } it { expect(@doc.to_s).not_to match(/Good|Evil/) } it { expect(@doc.to_s).not_to match(/type="skipped"/) } end end end end end end