Commit 9722651d authored by drew's avatar drew Committed by Nathan Friend

Renderable TestSuite errors

- Do not raise exceptions for malformed JUnit XML
- Add an existential error field to TestSuite
parent 1d478913
...@@ -21,7 +21,8 @@ export default { ...@@ -21,7 +21,8 @@ export default {
return this.selectedSuite.total_count > 0; return this.selectedSuite.total_count > 0;
}, },
showTests() { showTests() {
return this.testReports.total_count > 0; const { test_suites: testSuites = [] } = this.testReports;
return testSuites.length > 0;
}, },
}, },
methods: { methods: {
......
<script> <script>
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import store from '~/pipelines/stores/test_reports'; import store from '~/pipelines/stores/test_reports';
import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue'; import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
export default { export default {
name: 'TestsSummaryTable', name: 'TestsSummaryTable',
components: { components: {
GlIcon,
SmartVirtualList, SmartVirtualList,
}, },
directives: {
GlTooltip: GlTooltipDirective,
},
store, store,
props: { props: {
heading: { heading: {
...@@ -75,7 +80,10 @@ export default { ...@@ -75,7 +80,10 @@ export default {
v-for="(testSuite, index) in getTestSuites" v-for="(testSuite, index) in getTestSuites"
:key="index" :key="index"
role="row" role="row"
class="gl-responsive-table-row gl-responsive-table-row-clickable test-reports-summary-row rounded cursor-pointer js-suite-row" class="gl-responsive-table-row test-reports-summary-row rounded js-suite-row"
:class="{
'gl-responsive-table-row-clickable cursor-pointer': !testSuite.suite_error,
}"
@click="tableRowClick(testSuite)" @click="tableRowClick(testSuite)"
> >
<div class="table-section section-25"> <div class="table-section section-25">
...@@ -84,6 +92,14 @@ export default { ...@@ -84,6 +92,14 @@ export default {
</div> </div>
<div class="table-mobile-content underline cgray pl-3"> <div class="table-mobile-content underline cgray pl-3">
{{ testSuite.name }} {{ testSuite.name }}
<gl-icon
v-if="testSuite.suite_error"
ref="suiteErrorIcon"
v-gl-tooltip
name="error"
:title="testSuite.suite_error"
class="vertical-align-middle"
/>
</div> </div>
</div> </div>
......
<script> <script>
import { mapActions, mapGetters, mapState } from 'vuex'; import { mapActions, mapGetters, mapState } from 'vuex';
import { s__ } from '~/locale'; import { sprintf, s__ } from '~/locale';
import { componentNames } from './issue_body'; import { componentNames } from './issue_body';
import ReportSection from './report_section.vue'; import ReportSection from './report_section.vue';
import SummaryRow from './summary_row.vue'; import SummaryRow from './summary_row.vue';
...@@ -52,8 +52,17 @@ export default { ...@@ -52,8 +52,17 @@ export default {
methods: { methods: {
...mapActions(['setEndpoint', 'fetchReports']), ...mapActions(['setEndpoint', 'fetchReports']),
reportText(report) { reportText(report) {
const summary = report.summary || {}; const { name, summary } = report || {};
return reportTextBuilder(report.name, summary);
if (report.status === 'error') {
return sprintf(s__('Reports|An error occurred while loading %{name} results'), { name });
}
if (!report.name) {
return s__('Reports|An error occured while loading report');
}
return reportTextBuilder(name, summary);
}, },
getReportIcon(report) { getReportIcon(report) {
return statusIcon(report.status); return statusIcon(report.status);
......
...@@ -8,8 +8,7 @@ export default { ...@@ -8,8 +8,7 @@ export default {
state.isLoading = true; state.isLoading = true;
}, },
[types.RECEIVE_REPORTS_SUCCESS](state, response) { [types.RECEIVE_REPORTS_SUCCESS](state, response) {
// Make sure to clean previous state in case it was an error state.hasError = response.suites.some(suite => suite.status === 'error');
state.hasError = false;
state.isLoading = false; state.isLoading = false;
......
...@@ -169,19 +169,9 @@ class Projects::PipelinesController < Projects::ApplicationController ...@@ -169,19 +169,9 @@ class Projects::PipelinesController < Projects::ApplicationController
end end
format.json do format.json do
if pipeline_test_report == :error render json: TestReportSerializer
render json: { status: :error_parsing_report } .new(current_user: @current_user)
else .represent(pipeline_test_report, project: project)
test_reports = if params[:scope] == "with_attachment"
pipeline_test_report.with_attachment!
else
pipeline_test_report
end
render json: TestReportSerializer
.new(current_user: @current_user)
.represent(test_reports, project: project)
end
end end
end end
end end
...@@ -189,11 +179,7 @@ class Projects::PipelinesController < Projects::ApplicationController ...@@ -189,11 +179,7 @@ class Projects::PipelinesController < Projects::ApplicationController
def test_reports_count def test_reports_count
return unless Feature.enabled?(:junit_pipeline_view, project) return unless Feature.enabled?(:junit_pipeline_view, project)
begin render json: { total_count: pipeline.test_reports_count }.to_json
render json: { total_count: pipeline.test_reports_count }.to_json
rescue Gitlab::Ci::Parsers::ParserError
render json: { total_count: 0 }.to_json
end
end end
private private
...@@ -269,9 +255,9 @@ class Projects::PipelinesController < Projects::ApplicationController ...@@ -269,9 +255,9 @@ class Projects::PipelinesController < Projects::ApplicationController
def pipeline_test_report def pipeline_test_report
strong_memoize(:pipeline_test_report) do strong_memoize(:pipeline_test_report) do
@pipeline.test_reports @pipeline.test_reports.tap do |reports|
rescue Gitlab::Ci::Parsers::ParserError reports.with_attachment! if params[:scope] == 'with_attachment'
:error end
end end
end end
end end
......
...@@ -9,8 +9,9 @@ class TestSuiteEntity < Grape::Entity ...@@ -9,8 +9,9 @@ class TestSuiteEntity < Grape::Entity
expose :failed_count expose :failed_count
expose :skipped_count expose :skipped_count
expose :error_count expose :error_count
expose :suite_error
expose :test_cases, using: TestCaseEntity do |test_suite| expose :test_cases, using: TestCaseEntity do |test_suite|
test_suite.test_cases.values.flat_map(&:values) test_suite.suite_error ? [] : test_suite.test_cases.values.flat_map(&:values)
end end
end end
---
title: Render TestReport parsing errors back to pipeline test summary
merge_request: 24188
author:
type: fixed
...@@ -15,10 +15,10 @@ module Gitlab ...@@ -15,10 +15,10 @@ module Gitlab
test_case = create_test_case(test_case, args) test_case = create_test_case(test_case, args)
test_suite.add_test_case(test_case) test_suite.add_test_case(test_case)
end end
rescue Nokogiri::XML::SyntaxError rescue Nokogiri::XML::SyntaxError => e
raise JunitParserError, "XML parsing failed" test_suite.set_suite_error("JUnit XML parsing failed: #{e}")
rescue rescue StandardError => e
raise JunitParserError, "JUnit parsing failed" test_suite.set_suite_error("JUnit data parsing failed: #{e}")
end end
private private
......
...@@ -42,6 +42,12 @@ module Gitlab ...@@ -42,6 +42,12 @@ module Gitlab
self self
end end
def suite_errors
test_suites.each_with_object({}) do |(name, suite), errors|
errors[suite.name] = suite.suite_error if suite.suite_error
end
end
TestCase::STATUS_TYPES.each do |status_type| TestCase::STATUS_TYPES.each do |status_type|
define_method("#{status_type}_count") do define_method("#{status_type}_count") do
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
......
...@@ -7,6 +7,7 @@ module Gitlab ...@@ -7,6 +7,7 @@ module Gitlab
attr_reader :name attr_reader :name
attr_reader :test_cases attr_reader :test_cases
attr_reader :total_time attr_reader :total_time
attr_reader :suite_error
def initialize(name = nil) def initialize(name = nil)
@name = name @name = name
...@@ -25,12 +26,16 @@ module Gitlab ...@@ -25,12 +26,16 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def total_count def total_count
return 0 if suite_error
test_cases.values.sum(&:count) test_cases.values.sum(&:count)
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def total_status def total_status
if failed_count > 0 || error_count > 0 if suite_error
TestCase::STATUS_ERROR
elsif failed_count > 0 || error_count > 0
TestCase::STATUS_FAILED TestCase::STATUS_FAILED
else else
TestCase::STATUS_SUCCESS TestCase::STATUS_SUCCESS
...@@ -49,14 +54,22 @@ module Gitlab ...@@ -49,14 +54,22 @@ module Gitlab
TestCase::STATUS_TYPES.each do |status_type| TestCase::STATUS_TYPES.each do |status_type|
define_method("#{status_type}") do define_method("#{status_type}") do
test_cases[status_type] || {} return {} if suite_error || test_cases[status_type].nil?
test_cases[status_type]
end end
define_method("#{status_type}_count") do define_method("#{status_type}_count") do
test_cases[status_type]&.length.to_i return 0 if suite_error || test_cases[status_type].nil?
test_cases[status_type].length
end end
end end
def set_suite_error(msg)
@suite_error = msg
end
private private
def existing_key?(test_case) def existing_key?(test_case)
......
...@@ -17183,6 +17183,12 @@ msgstr "" ...@@ -17183,6 +17183,12 @@ msgstr ""
msgid "Reports|Actions" msgid "Reports|Actions"
msgstr "" msgstr ""
msgid "Reports|An error occured while loading report"
msgstr ""
msgid "Reports|An error occurred while loading %{name} results"
msgstr ""
msgid "Reports|Class" msgid "Reports|Class"
msgstr "" msgstr ""
......
...@@ -752,8 +752,6 @@ describe Projects::PipelinesController do ...@@ -752,8 +752,6 @@ describe Projects::PipelinesController do
end end
context 'when pipeline does not have a test report' do context 'when pipeline does not have a test report' do
let(:pipeline) { create(:ci_pipeline, project: project) }
it 'renders an empty test report' do it 'renders an empty test report' do
get_test_report_json get_test_report_json
...@@ -763,7 +761,11 @@ describe Projects::PipelinesController do ...@@ -763,7 +761,11 @@ describe Projects::PipelinesController do
end end
context 'when pipeline has a test report' do context 'when pipeline has a test report' do
let(:pipeline) { create(:ci_pipeline, :with_test_reports, project: project) } before do
create(:ci_build, name: 'rspec', pipeline: pipeline).tap do |build|
create(:ci_job_artifact, :junit, job: build)
end
end
it 'renders the test report' do it 'renders the test report' do
get_test_report_json get_test_report_json
...@@ -773,19 +775,22 @@ describe Projects::PipelinesController do ...@@ -773,19 +775,22 @@ describe Projects::PipelinesController do
end end
end end
context 'when pipeline has corrupt test reports' do context 'when pipeline has a corrupt test report artifact' do
let(:pipeline) { create(:ci_pipeline, project: project) }
before do before do
job = create(:ci_build, pipeline: pipeline) create(:ci_build, name: 'rspec', pipeline: pipeline).tap do |build|
create(:ci_job_artifact, :junit_with_corrupted_data, job: job, project: project) create(:ci_job_artifact, :junit_with_corrupted_data, job: build)
end end
it 'renders the test reports' do
get_test_report_json get_test_report_json
end
it 'renders the test reports' do
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(json_response['status']).to eq('error_parsing_report') expect(json_response['test_suites'].count).to eq(1)
end
it 'returns a suite_error on the suite with corrupted XML' do
expect(json_response['test_suites'].first['suite_error']).to eq('JUnit XML parsing failed: 1:1: FATAL: Document is empty')
end end
end end
......
...@@ -314,6 +314,12 @@ FactoryBot.define do ...@@ -314,6 +314,12 @@ FactoryBot.define do
end end
end end
trait :broken_test_reports do
after(:build) do |build|
build.job_artifacts << create(:ci_job_artifact, :junit_with_corrupted_data, job: build)
end
end
trait :coverage_reports do trait :coverage_reports do
after(:build) do |build| after(:build) do |build|
build.job_artifacts << create(:ci_job_artifact, :cobertura, job: build) build.job_artifacts << create(:ci_job_artifact, :cobertura, job: build)
......
...@@ -75,6 +75,14 @@ FactoryBot.define do ...@@ -75,6 +75,14 @@ FactoryBot.define do
end end
end end
trait :with_broken_test_reports do
status { :success }
after(:build) do |pipeline, _evaluator|
pipeline.builds << build(:ci_build, :broken_test_reports, pipeline: pipeline, project: pipeline.project)
end
end
trait :with_coverage_reports do trait :with_coverage_reports do
status { :success } status { :success }
......
...@@ -37,11 +37,47 @@ describe('Test reports summary table', () => { ...@@ -37,11 +37,47 @@ describe('Test reports summary table', () => {
describe('when test reports are supplied', () => { describe('when test reports are supplied', () => {
beforeEach(() => createComponent()); beforeEach(() => createComponent());
const findErrorIcon = () => wrapper.find({ ref: 'suiteErrorIcon' });
it('renders the correct number of rows', () => { it('renders the correct number of rows', () => {
expect(noSuitesToShow().exists()).toBe(false); expect(noSuitesToShow().exists()).toBe(false);
expect(allSuitesRows().length).toBe(testReports.test_suites.length); expect(allSuitesRows().length).toBe(testReports.test_suites.length);
}); });
describe('when there is a suite error', () => {
beforeEach(() => {
createComponent({
test_suites: [
{
...testReports.test_suites[0],
suite_error: 'Suite Error',
},
],
});
});
it('renders error icon', () => {
expect(findErrorIcon().exists()).toBe(true);
expect(findErrorIcon().attributes('title')).toEqual('Suite Error');
});
});
describe('when there is not a suite error', () => {
beforeEach(() => {
createComponent({
test_suites: [
{
...testReports.test_suites[0],
suite_error: null,
},
],
});
});
it('does not render error icon', () => {
expect(findErrorIcon().exists()).toBe(false);
});
});
}); });
describe('when there are no test suites', () => { describe('when there are no test suites', () => {
......
...@@ -4,6 +4,7 @@ import axios from '~/lib/utils/axios_utils'; ...@@ -4,6 +4,7 @@ import axios from '~/lib/utils/axios_utils';
import state from '~/reports/store/state'; import state from '~/reports/store/state';
import component from '~/reports/components/grouped_test_reports_app.vue'; import component from '~/reports/components/grouped_test_reports_app.vue';
import mountComponent from '../../helpers/vue_mount_component_helper'; import mountComponent from '../../helpers/vue_mount_component_helper';
import { failedReport } from '../mock_data/mock_data';
import newFailedTestReports from '../mock_data/new_failures_report.json'; import newFailedTestReports from '../mock_data/new_failures_report.json';
import newErrorsTestReports from '../mock_data/new_errors_report.json'; import newErrorsTestReports from '../mock_data/new_errors_report.json';
import successTestReports from '../mock_data/no_failures_report.json'; import successTestReports from '../mock_data/no_failures_report.json';
...@@ -199,6 +200,26 @@ describe('Grouped Test Reports App', () => { ...@@ -199,6 +200,26 @@ describe('Grouped Test Reports App', () => {
}); });
}); });
describe('with a report that failed to load', () => {
beforeEach(() => {
mock.onGet('test_results.json').reply(200, failedReport, {});
vm = mountComponent(Component, {
endpoint: 'test_results.json',
});
});
it('renders an error status for the report', done => {
setTimeout(() => {
const { name } = failedReport.suites[0];
expect(vm.$el.querySelector('.report-block-list-issue').textContent).toContain(
`An error occurred while loading ${name} results`,
);
done();
});
});
});
describe('with error', () => { describe('with error', () => {
beforeEach(() => { beforeEach(() => {
mock.onGet('test_results.json').reply(500, {}, {}); mock.onGet('test_results.json').reply(500, {}, {});
......
// eslint-disable-next-line import/prefer-default-export
export const issue = { export const issue = {
result: 'failure', result: 'failure',
name: 'Test#sum when a is 1 and b is 2 returns summary', name: 'Test#sum when a is 1 and b is 2 returns summary',
...@@ -6,3 +5,20 @@ export const issue = { ...@@ -6,3 +5,20 @@ export const issue = {
system_output: system_output:
"Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in \u003ctop (required)\u003e'", "Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in \u003ctop (required)\u003e'",
}; };
export const failedReport = {
summary: { total: 11, resolved: 0, errored: 2, failed: 0 },
suites: [
{
name: 'rspec:pg',
status: 'error',
summary: { total: 0, resolved: 0, errored: 0, failed: 0 },
new_failures: [],
resolved_failures: [],
existing_failures: [],
new_errors: [],
resolved_errors: [],
existing_errors: [],
},
],
};
...@@ -215,8 +215,64 @@ describe Gitlab::Ci::Parsers::Test::Junit do ...@@ -215,8 +215,64 @@ describe Gitlab::Ci::Parsers::Test::Junit do
context 'when data is not JUnit style XML' do context 'when data is not JUnit style XML' do
let(:junit) { { testsuite: 'abc' }.to_json } let(:junit) { { testsuite: 'abc' }.to_json }
it 'raises an error' do it 'attaches an error to the TestSuite object' do
expect { subject }.to raise_error(described_class::JunitParserError) expect { subject }.not_to raise_error
expect(test_cases).to be_empty
end
end
context 'when data is malformed JUnit XML' do
let(:junit) do
<<-EOF.strip_heredoc
<testsuite>
<testcase classname='Calculator' name='sumTest1' time='0.01'></testcase>
<testcase classname='Calculator' name='sumTest2' time='0.02'></testcase
</testsuite>
EOF
end
it 'attaches an error to the TestSuite object' do
expect { subject }.not_to raise_error
expect(test_suite.suite_error).to eq("JUnit XML parsing failed: 4:1: FATAL: expected '>'")
end
it 'returns 0 tests cases' do
subject
expect(test_cases).to be_empty
expect(test_suite.total_count).to eq(0)
expect(test_suite.success_count).to eq(0)
expect(test_suite.error_count).to eq(0)
end
it 'returns a failure status' do
subject
expect(test_suite.total_status).to eq(Gitlab::Ci::Reports::TestCase::STATUS_ERROR)
end
end
context 'when data is not XML' do
let(:junit) { double(:random_trash) }
it 'attaches an error to the TestSuite object' do
expect { subject }.not_to raise_error
expect(test_suite.suite_error).to eq('JUnit data parsing failed: no implicit conversion of RSpec::Mocks::Double into String')
end
it 'returns 0 tests cases' do
subject
expect(test_cases).to be_empty
expect(test_suite.total_count).to eq(0)
expect(test_suite.success_count).to eq(0)
expect(test_suite.error_count).to eq(0)
end
it 'returns a failure status' do
subject
expect(test_suite.total_status).to eq(Gitlab::Ci::Reports::TestCase::STATUS_ERROR)
end end
end end
......
...@@ -141,6 +141,29 @@ describe Gitlab::Ci::Reports::TestReports do ...@@ -141,6 +141,29 @@ describe Gitlab::Ci::Reports::TestReports do
end end
end end
describe '#suite_errors' do
subject { test_reports.suite_errors }
context 'when a suite has normal spec errors or failures' do
before do
test_reports.get_suite('junit').add_test_case(create_test_case_java_success)
test_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
test_reports.get_suite('junit').add_test_case(create_test_case_java_error)
end
it { is_expected.to be_empty }
end
context 'when there is an error test case' do
before do
test_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
test_reports.get_suite('junit').set_suite_error('Existential parsing error')
end
it { is_expected.to eq({ 'junit' => 'Existential parsing error' }) }
end
end
Gitlab::Ci::Reports::TestCase::STATUS_TYPES.each do |status_type| Gitlab::Ci::Reports::TestCase::STATUS_TYPES.each do |status_type|
describe "##{status_type}_count" do describe "##{status_type}_count" do
subject { test_reports.public_send("#{status_type}_count") } subject { test_reports.public_send("#{status_type}_count") }
......
...@@ -114,6 +114,31 @@ describe Gitlab::Ci::Reports::TestSuite do ...@@ -114,6 +114,31 @@ describe Gitlab::Ci::Reports::TestSuite do
end end
end end
describe '#set_suite_error' do
let(:set_suite_error) { test_suite.set_suite_error('message') }
context 'when @suite_error is nil' do
it 'returns message' do
expect(set_suite_error).to eq('message')
end
it 'sets the new message' do
set_suite_error
expect(test_suite.suite_error).to eq('message')
end
end
context 'when a suite_error has already been set' do
before do
test_suite.set_suite_error('old message')
end
it 'overwrites the existing message' do
expect { set_suite_error }.to change(test_suite, :suite_error).from('old message').to('message')
end
end
end
Gitlab::Ci::Reports::TestCase::STATUS_TYPES.each do |status_type| Gitlab::Ci::Reports::TestCase::STATUS_TYPES.each do |status_type|
describe "##{status_type}" do describe "##{status_type}" do
subject { test_suite.public_send("#{status_type}") } subject { test_suite.public_send("#{status_type}") }
......
...@@ -3812,8 +3812,13 @@ describe Ci::Build do ...@@ -3812,8 +3812,13 @@ describe Ci::Build do
create(:ci_job_artifact, :junit_with_corrupted_data, job: build, project: build.project) create(:ci_job_artifact, :junit_with_corrupted_data, job: build, project: build.project)
end end
it 'raises an error' do it 'returns no test data and includes a suite_error message' do
expect { subject }.to raise_error(Gitlab::Ci::Parsers::Test::Junit::JunitParserError) expect { subject }.not_to raise_error
expect(test_reports.get_suite(build.name).total_count).to eq(0)
expect(test_reports.get_suite(build.name).success_count).to eq(0)
expect(test_reports.get_suite(build.name).failed_count).to eq(0)
expect(test_reports.get_suite(build.name).suite_error).to eq('JUnit XML parsing failed: 1:1: FATAL: Document is empty')
end end
end end
end end
......
...@@ -3,27 +3,65 @@ ...@@ -3,27 +3,65 @@
require 'spec_helper' require 'spec_helper'
describe TestSuiteEntity do describe TestSuiteEntity do
let(:pipeline) { create(:ci_pipeline, :with_test_reports) } let(:pipeline) { create(:ci_pipeline, :with_test_reports) }
let(:entity) { described_class.new(pipeline.test_reports.test_suites.each_value.first) } let(:test_suite) { pipeline.test_reports.test_suites.each_value.first }
let(:entity) { described_class.new(test_suite) }
describe '#as_json' do describe '#as_json' do
subject(:as_json) { entity.as_json } subject(:as_json) { entity.as_json }
it 'contains the suite name' do it 'contains the suite name' do
expect(as_json).to include(:name) expect(as_json[:name]).to be_present
end end
it 'contains the total time' do it 'contains the total time' do
expect(as_json).to include(:total_time) expect(as_json[:total_time]).to be_present
end end
it 'contains the counts' do it 'contains the counts' do
expect(as_json).to include(:total_count, :success_count, :failed_count, :skipped_count, :error_count) expect(as_json[:total_count]).to eq(4)
expect(as_json[:success_count]).to eq(2)
expect(as_json[:failed_count]).to eq(2)
expect(as_json[:skipped_count]).to eq(0)
expect(as_json[:error_count]).to eq(0)
end end
it 'contains the test cases' do it 'contains the test cases' do
expect(as_json).to include(:test_cases)
expect(as_json[:test_cases].count).to eq(4) expect(as_json[:test_cases].count).to eq(4)
end end
it 'contains an empty error message' do
expect(as_json[:suite_error]).to be_nil
end
context 'with a suite error' do
before do
test_suite.set_suite_error('a really bad error')
end
it 'contains the suite name' do
expect(as_json[:name]).to be_present
end
it 'contains the total time' do
expect(as_json[:total_time]).to be_present
end
it 'returns all the counts as 0' do
expect(as_json[:total_count]).to eq(0)
expect(as_json[:success_count]).to eq(0)
expect(as_json[:failed_count]).to eq(0)
expect(as_json[:skipped_count]).to eq(0)
expect(as_json[:error_count]).to eq(0)
end
it 'returns no test cases' do
expect(as_json[:test_cases]).to be_empty
end
it 'returns a suite error' do
expect(as_json[:suite_error]).to eq('a really bad error')
end
end
end end
end end
...@@ -38,9 +38,10 @@ describe Ci::CompareTestReportsService do ...@@ -38,9 +38,10 @@ describe Ci::CompareTestReportsService do
create(:ci_job_artifact, :junit_with_corrupted_data, job: build, project: project) create(:ci_job_artifact, :junit_with_corrupted_data, job: build, project: project)
end end
it 'returns status and error message' do it 'returns a parsed TestReports success status and failure on the individual suite' do
expect(subject[:status]).to eq(:error) expect(subject[:status]).to eq(:parsed)
expect(subject[:status_reason]).to include('XML parsing failed') expect(subject.dig(:data, 'status')).to eq('success')
expect(subject.dig(:data, 'suites', 0, 'status') ).to eq('error')
end end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment