Commit ab0eab66 authored by Mark Florian's avatar Mark Florian

Merge branch '235202-test-report-docs-link' into 'master'

Add test report empty state

See merge request gitlab-org/gitlab!59812
parents b2e48b5b 83f5ba73
<script>
import { GlEmptyState } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { s__ } from '~/locale';
export const i18n = {
noTestsButton: s__('TestReports|Learn more about pipeline test reports'),
noTestsDescription: s__('TestReports|No test cases were found in the test report.'),
noTestsTitle: s__('TestReports|There are no tests to display'),
noReportsButton: s__('TestReports|Learn how to upload pipeline test reports'),
noReportsDescription: s__(
'TestReports|You can configure your job to use unit test reports, and GitLab displays a report here and in the related merge request.',
),
noReportsTitle: s__('TestReports|There are no test reports for this pipeline'),
};
export default {
i18n,
components: {
GlEmptyState,
},
inject: {
emptyStateImagePath: {
default: '',
},
hasTestReport: {
default: false,
},
},
computed: {
emptyStateText() {
if (this.hasTestReport) {
return {
button: this.$options.i18n.noTestsButton,
description: this.$options.i18n.noTestsDescription,
title: this.$options.i18n.noTestsTitle,
};
}
return {
button: this.$options.i18n.noReportsButton,
description: this.$options.i18n.noReportsDescription,
title: this.$options.i18n.noReportsTitle,
};
},
testReportDocPath() {
return helpPagePath('ci/unit_test_reports');
},
},
};
</script>
<template>
<gl-empty-state
:title="emptyStateText.title"
:description="emptyStateText.description"
:svg-path="emptyStateImagePath"
:primary-button-link="testReportDocPath"
:primary-button-text="emptyStateText.button"
/>
</template>
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import { mapActions, mapGetters, mapState } from 'vuex';
import EmptyState from './empty_state.vue';
import TestSuiteTable from './test_suite_table.vue';
import TestSummary from './test_summary.vue';
import TestSummaryTable from './test_summary_table.vue';
......@@ -8,11 +9,17 @@ import TestSummaryTable from './test_summary_table.vue';
export default {
name: 'TestReports',
components: {
EmptyState,
GlLoadingIcon,
TestSuiteTable,
TestSummary,
TestSummaryTable,
},
inject: {
hasTestReport: {
default: false,
},
},
computed: {
...mapState(['isLoading', 'selectedSuiteIndex', 'testReports']),
...mapGetters(['getSelectedSuite']),
......@@ -25,7 +32,9 @@ export default {
},
},
created() {
if (this.hasTestReport) {
this.fetchSummary();
}
},
methods: {
...mapActions([
......@@ -83,11 +92,5 @@ export default {
</transition>
</div>
<div v-else>
<div class="row gl-mt-3">
<div class="col-12">
<p data-testid="no-tests-to-show">{{ s__('TestReports|There are no tests to show.') }}</p>
</div>
</div>
</div>
<empty-state v-else />
</template>
import Vue from 'vue';
import { deprecatedCreateFlash as Flash } from '~/flash';
import { parseBoolean } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
import Translate from '~/vue_shared/translate';
import PipelineGraphLegacy from './components/graph/graph_component_legacy.vue';
......@@ -63,7 +64,8 @@ const createLegacyPipelinesDetailApp = (mediator) => {
const createTestDetails = () => {
const el = document.querySelector(SELECTORS.PIPELINE_TESTS);
const { blobPath, summaryEndpoint, suiteEndpoint } = el?.dataset || {};
const { blobPath, emptyStateImagePath, hasTestReport, summaryEndpoint, suiteEndpoint } =
el?.dataset || {};
const testReportsStore = createTestReportsStore({
blobPath,
summaryEndpoint,
......@@ -76,6 +78,10 @@ const createTestDetails = () => {
components: {
TestReports,
},
provide: {
emptyStateImagePath,
hasTestReport: parseBoolean(hasTestReport),
},
store: testReportsStore,
render(createElement) {
return createElement('test-reports');
......
......@@ -83,5 +83,7 @@
#js-tab-tests.tab-pane
#js-pipeline-tests-detail{ data: { summary_endpoint: summary_project_pipeline_tests_path(@project, @pipeline, format: :json),
suite_endpoint: project_pipeline_test_path(@project, @pipeline, suite_name: 'suite', format: :json),
blob_path: project_blob_path(@project, @pipeline.sha) } }
blob_path: project_blob_path(@project, @pipeline.sha),
has_test_report: @pipeline.has_reports?(Ci::JobArtifact.test_reports).to_s,
empty_state_image_path: image_path('illustrations/empty-state/empty-test-cases-lg.svg') } }
= render_if_exists "projects/pipelines/tabs_content", pipeline: @pipeline, project: @project
---
title: Add link to documentation in empty pipeline test reports
merge_request: 59812
author:
type: added
......@@ -31175,16 +31175,28 @@ msgstr ""
msgid "TestReports|Jobs"
msgstr ""
msgid "TestReports|Learn how to upload pipeline test reports"
msgstr ""
msgid "TestReports|Learn more about pipeline test reports"
msgstr ""
msgid "TestReports|No test cases were found in the test report."
msgstr ""
msgid "TestReports|Tests"
msgstr ""
msgid "TestReports|There are no test cases to display."
msgstr ""
msgid "TestReports|There are no test reports for this pipeline"
msgstr ""
msgid "TestReports|There are no test suites to show."
msgstr ""
msgid "TestReports|There are no tests to show."
msgid "TestReports|There are no tests to display"
msgstr ""
msgid "TestReports|There was an error fetching the summary."
......@@ -31193,6 +31205,9 @@ msgstr ""
msgid "TestReports|There was an error fetching the test suite."
msgstr ""
msgid "TestReports|You can configure your job to use unit test reports, and GitLab displays a report here and in the related merge request."
msgstr ""
msgid "Tests"
msgstr ""
......
import { GlEmptyState } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import EmptyState, { i18n } from '~/pipelines/components/test_reports/empty_state.vue';
describe('Test report empty state', () => {
let wrapper;
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
const createComponent = ({ hasTestReport = true } = {}) => {
wrapper = shallowMount(EmptyState, {
provide: {
emptyStateImagePath: '/image/path',
hasTestReport,
},
stubs: {
GlEmptyState,
},
});
};
describe('when pipeline has a test report', () => {
it('should render empty test report message', () => {
createComponent();
expect(findEmptyState().props()).toMatchObject({
primaryButtonText: i18n.noTestsButton,
description: i18n.noTestsDescription,
title: i18n.noTestsTitle,
});
});
});
describe('when pipeline does not have a test report', () => {
it('should render no test report message', () => {
createComponent({ hasTestReport: false });
expect(findEmptyState().props()).toMatchObject({
primaryButtonText: i18n.noReportsButton,
description: i18n.noReportsDescription,
title: i18n.noReportsTitle,
});
});
});
});
......@@ -2,6 +2,8 @@ import { GlLoadingIcon } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { getJSONFixture } from 'helpers/fixtures';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import EmptyState from '~/pipelines/components/test_reports/empty_state.vue';
import TestReports from '~/pipelines/components/test_reports/test_reports.vue';
import TestSummary from '~/pipelines/components/test_reports/test_summary.vue';
import TestSummaryTable from '~/pipelines/components/test_reports/test_summary_table.vue';
......@@ -16,11 +18,11 @@ describe('Test reports app', () => {
const testReports = getJSONFixture('pipelines/test_report.json');
const loadingSpinner = () => wrapper.find(GlLoadingIcon);
const testsDetail = () => wrapper.find('[data-testid="tests-detail"]');
const noTestsToShow = () => wrapper.find('[data-testid="no-tests-to-show"]');
const testSummary = () => wrapper.find(TestSummary);
const testSummaryTable = () => wrapper.find(TestSummaryTable);
const loadingSpinner = () => wrapper.findComponent(GlLoadingIcon);
const testsDetail = () => wrapper.findByTestId('tests-detail');
const emptyState = () => wrapper.findComponent(EmptyState);
const testSummary = () => wrapper.findComponent(TestSummary);
const testSummaryTable = () => wrapper.findComponent(TestSummaryTable);
const actionSpies = {
fetchTestSuite: jest.fn(),
......@@ -29,7 +31,7 @@ describe('Test reports app', () => {
removeSelectedSuiteIndex: jest.fn(),
};
const createComponent = (state = {}) => {
const createComponent = ({ state = {}, hasTestReport = true } = {}) => {
store = new Vuex.Store({
state: {
isLoading: false,
......@@ -41,10 +43,15 @@ describe('Test reports app', () => {
getters,
});
wrapper = shallowMount(TestReports, {
wrapper = extendedWrapper(
shallowMount(TestReports, {
store,
localVue,
});
provide: {
hasTestReport,
},
}),
);
};
afterEach(() => {
......@@ -52,33 +59,34 @@ describe('Test reports app', () => {
});
describe('when component is created', () => {
beforeEach(() => {
it('should call fetchSummary when pipeline has test report', () => {
createComponent();
});
it('should call fetchSummary', () => {
expect(actionSpies.fetchSummary).toHaveBeenCalled();
});
it('should not call fetchSummary when pipeline does not have test report', () => {
createComponent({ state: {}, hasTestReport: false });
expect(actionSpies.fetchSummary).not.toHaveBeenCalled();
});
});
describe('when loading', () => {
beforeEach(() => createComponent({ isLoading: true }));
beforeEach(() => createComponent({ state: { isLoading: true } }));
it('shows the loading spinner', () => {
expect(noTestsToShow().exists()).toBe(false);
expect(emptyState().exists()).toBe(false);
expect(testsDetail().exists()).toBe(false);
expect(loadingSpinner().exists()).toBe(true);
});
});
describe('when the api returns no data', () => {
beforeEach(() => createComponent({ testReports: {} }));
it('displays that there are no tests to show', () => {
const noTests = noTestsToShow();
it('displays empty state component', () => {
createComponent({ state: { testReports: {} } });
expect(noTests.exists()).toBe(true);
expect(noTests.text()).toBe('There are no tests to show.');
expect(emptyState().exists()).toBe(true);
});
});
......@@ -97,7 +105,7 @@ describe('Test reports app', () => {
describe('when a suite is clicked', () => {
beforeEach(() => {
createComponent({ hasFullReport: true });
createComponent({ state: { hasFullReport: true } });
testSummaryTable().vm.$emit('row-click', 0);
});
......@@ -109,7 +117,7 @@ describe('Test reports app', () => {
describe('when clicking back to summary', () => {
beforeEach(() => {
createComponent({ selectedSuiteIndex: 0 });
createComponent({ state: { selectedSuiteIndex: 0 } });
testSummary().vm.$emit('on-back-click');
});
......
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