Commit bbe504d8 authored by Miranda Fluharty's avatar Miranda Fluharty Committed by Enrique Alcántara

Project quality summary: add test runs empty state

Display empty state when loading has finished but no tests were run
Link to docs about adding test coverage results to a merge request

Changelog: changed
parent a2204ab2
......@@ -11,7 +11,13 @@ const apolloProvider = new VueApollo({
});
const mountPipelineChartsApp = (el) => {
const { projectPath, failedPipelinesLink, coverageChartPath, defaultBranch } = el.dataset;
const {
projectPath,
failedPipelinesLink,
coverageChartPath,
defaultBranch,
testRunsEmptyStateImagePath,
} = el.dataset;
const shouldRenderDoraCharts = parseBoolean(el.dataset.shouldRenderDoraCharts);
const shouldRenderQualitySummary = parseBoolean(el.dataset.shouldRenderQualitySummary);
......@@ -30,6 +36,7 @@ const mountPipelineChartsApp = (el) => {
shouldRenderQualitySummary,
coverageChartPath,
defaultBranch,
testRunsEmptyStateImagePath,
},
render: (createElement) => createElement(ProjectPipelinesCharts, {}),
});
......
......@@ -5,4 +5,5 @@
should_render_quality_summary: should_render_quality_summary.to_s,
failed_pipelines_link: project_pipelines_path(@project, page: '1', scope: 'all', status: 'failed'),
coverage_chart_path: charts_project_graph_path(@project, @project.default_branch),
test_runs_empty_state_image_path: image_path('illustrations/pipeline.svg'),
default_branch: @project.default_branch } }
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11 36L61 36" stroke="#6E49CB" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="61" cy="36" r="9" fill="#4BEACC" stroke="#6E49CB" stroke-width="4" stroke-linecap="round"/>
<path d="M58.0416 35.5742C57.6518 35.183 57.0186 35.1819 56.6274 35.5718C56.2362 35.9616 56.2351 36.5948 56.625 36.986L58.0416 35.5742ZM59.4902 38.4444L58.7818 39.1503C58.9695 39.3386 59.2244 39.4444 59.4902 39.4444C59.756 39.4444 60.0109 39.3386 60.1985 39.1503L59.4902 38.4444ZM65.375 33.9559C65.7648 33.5647 65.7637 32.9315 65.3725 32.5417C64.9813 32.1518 64.3482 32.1529 63.9583 32.5441L65.375 33.9559ZM56.625 36.986L58.7818 39.1503L60.1985 37.7386L58.0416 35.5742L56.625 36.986ZM60.1985 39.1503L65.375 33.9559L63.9583 32.5441L58.7818 37.7386L60.1985 39.1503Z" fill="#6E49CB"/>
<circle cx="36" cy="36" r="9" fill="#4BEACC" stroke="#6E49CB" stroke-width="4" stroke-linecap="round"/>
<path d="M33.0416 35.5742C32.6518 35.183 32.0186 35.1819 31.6274 35.5718C31.2362 35.9616 31.2351 36.5948 31.625 36.986L33.0416 35.5742ZM34.4902 38.4444L33.7818 39.1503C33.9695 39.3386 34.2244 39.4444 34.4902 39.4444C34.756 39.4444 35.0109 39.3386 35.1985 39.1503L34.4902 38.4444ZM40.375 33.9559C40.7648 33.5647 40.7637 32.9315 40.3725 32.5417C39.9813 32.1518 39.3482 32.1529 38.9583 32.5441L40.375 33.9559ZM31.625 36.986L33.7818 39.1503L35.1985 37.7386L33.0416 35.5742L31.625 36.986ZM35.1985 39.1503L40.375 33.9559L38.9583 32.5441L33.7818 37.7386L35.1985 39.1503Z" fill="#6E49CB"/>
<circle cx="11" cy="36" r="9" fill="#4BEACC" stroke="#6E49CB" stroke-width="4" stroke-linecap="round"/>
<path d="M8.04164 35.5742C7.6518 35.183 7.01863 35.1819 6.62743 35.5718C6.23623 35.9616 6.23513 36.5948 6.62498 36.986L8.04164 35.5742ZM9.49018 38.4444L8.78184 39.1503C8.96948 39.3386 9.22436 39.4444 9.49018 39.4444C9.75599 39.4444 10.0109 39.3386 10.1985 39.1503L9.49018 38.4444ZM15.375 33.9559C15.7648 33.5647 15.7637 32.9315 15.3725 32.5417C14.9813 32.1518 14.3482 32.1529 13.9583 32.5441L15.375 33.9559ZM6.62498 36.986L8.78184 39.1503L10.1985 37.7386L8.04164 35.5742L6.62498 36.986ZM10.1985 39.1503L15.375 33.9559L13.9583 32.5441L8.78184 37.7386L10.1985 39.1503Z" fill="#6E49CB"/>
</svg>
......@@ -2,11 +2,12 @@
import { GlSkeletonLoader, GlCard, GlLink, GlIcon, GlPopover } from '@gitlab/ui';
import { GlSingleStat } from '@gitlab/ui/dist/charts';
import createFlash from '~/flash';
import { s__ } from '~/locale';
import { percent, percentHundred } from '~/lib/utils/unit_format';
import { helpPagePath } from '~/helpers/help_page_helper';
import TestRunsEmptyState from './components/test_runs_empty_state.vue';
import getProjectQuality from './graphql/queries/get_project_quality.query.graphql';
import { formatStat } from './utils';
import { i18n } from './constants';
export default {
components: {
......@@ -16,6 +17,7 @@ export default {
GlIcon,
GlPopover,
GlSingleStat,
TestRunsEmptyState,
},
inject: {
projectPath: {
......@@ -57,6 +59,9 @@ export default {
},
},
computed: {
hasTestRunsData() {
return Boolean(this.projectQuality?.testReportSummary?.total.count);
},
testSuccessPercentage() {
return formatStat(
this.projectQuality?.testReportSummary.total.success /
......@@ -85,32 +90,7 @@ export default {
return `${this.projectQuality?.pipelinePath}/test_report`;
},
},
i18n: {
testRuns: {
title: s__('ProjectQualitySummary|Test runs'),
popoverBody: s__(
'ProjectQualitySummary|The percentage of tests that succeed, fail, or are skipped.',
),
learnMoreLink: s__('ProjectQualitySummary|Learn more about test reports'),
fullReportLink: s__('ProjectQualitySummary|See full report'),
successLabel: s__('ProjectQualitySummary|Success'),
failureLabel: s__('ProjectQualitySummary|Failure'),
skippedLabel: s__('ProjectQualitySummary|Skipped'),
},
coverage: {
title: s__('ProjectQualitySummary|Test coverage'),
popoverBody: s__(
'ProjectQualitySummary|Measure of how much of your code is covered by tests.',
),
learnMoreLink: s__('ProjectQualitySummary|Learn more about test coverage'),
fullReportLink: s__('ProjectQualitySummary|See project Code Coverage Statistics'),
coverageLabel: s__('ProjectQualitySummary|Coverage'),
},
subHeader: s__('ProjectQualitySummary|Latest pipeline results'),
fetchError: s__(
'ProjectQualitySummary|An error occurred while trying to fetch project quality statistics',
),
},
i18n,
testRunsHelpPath: helpPagePath('ci/unit_test_reports'),
coverageHelpPath: helpPagePath('ci/pipelines/settings', {
anchor: 'add-test-coverage-results-to-a-merge-request',
......@@ -119,10 +99,10 @@ export default {
</script>
<template>
<div>
<gl-card class="gl-mt-6">
<gl-card v-if="$apollo.queries.projectQuality.loading || hasTestRunsData" class="gl-mt-6">
<template #header>
<div class="gl-display-flex gl-justify-content-space-between gl-align-items-baseline">
<h4 class="gl-m-2">{{ $options.i18n.testRuns.title }}</h4>
<div class="gl-display-flex gl-justify-content-space-between gl-align-items-center">
<h5 class="gl-font-lg gl-m-2">{{ $options.i18n.testRuns.title }}</h5>
<gl-icon
id="test-runs-question-icon"
name="question-o"
......@@ -152,7 +132,7 @@ export default {
</template>
<template #default>
<gl-skeleton-loader v-if="$apollo.queries.projectQuality.loading" />
<div v-else-if="projectQuality.testReportSummary" class="row gl-ml-2">
<div v-else class="row gl-ml-2">
<gl-single-stat
class="col-sm-6 col-md-4"
data-testid="test-runs-stat"
......@@ -183,10 +163,15 @@ export default {
</div>
</template>
</gl-card>
<template v-else>
<test-runs-empty-state />
<hr />
</template>
<gl-card class="gl-mt-6">
<template #header>
<div class="gl-display-flex gl-justify-content-space-between gl-align-items-baseline">
<h4 class="gl-m-2">{{ $options.i18n.coverage.title }}</h4>
<div class="gl-display-flex gl-justify-content-space-between gl-align-items-center">
<h5 class="gl-font-lg gl-m-2">{{ $options.i18n.coverage.title }}</h5>
<gl-icon
id="coverage-question-icon"
name="question-o"
......
<script>
import { GlEmptyState, GlLink, GlIcon } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { i18n } from '../constants';
export default {
components: {
GlEmptyState,
GlLink,
GlIcon,
},
inject: ['testRunsEmptyStateImagePath'],
testRunsHelpPath: helpPagePath('ci/pipelines/settings', {
anchor: 'add-test-coverage-results-to-a-merge-request',
}),
i18n,
};
</script>
<template>
<div>
<gl-empty-state compact :svg-path="testRunsEmptyStateImagePath" class="gl-align-items-center">
<template #title>
<h5 class="gl-display-block gl-font-lg gl-my-4">
{{ $options.i18n.testRuns.title }}
</h5>
</template>
<template #description>
{{ $options.i18n.testRuns.emptyStateDescription }}
</template>
<template #actions>
<gl-link
:href="$options.testRunsHelpPath"
target="_blank"
rel="noopener noreferrer"
:aria-label="$options.i18n.testRuns.emptyStateLinkLabel"
>{{ $options.i18n.testRuns.emptyStateLink }}
<gl-icon name="external-link" />
</gl-link>
</template>
</gl-empty-state>
</div>
</template>
import { s__ } from '~/locale';
export const i18n = {
testRuns: {
title: s__('ProjectQualitySummary|Test runs'),
popoverBody: s__(
'ProjectQualitySummary|The percentage of tests that succeed, fail, or are skipped.',
),
learnMoreLink: s__('ProjectQualitySummary|Learn more about test reports'),
fullReportLink: s__('ProjectQualitySummary|See full report'),
successLabel: s__('ProjectQualitySummary|Success'),
failureLabel: s__('ProjectQualitySummary|Failure'),
skippedLabel: s__('ProjectQualitySummary|Skipped'),
emptyStateDescription: s__(
'ProjectQualitySummary|Get insight into the overall percentage of tests in your project that succeed, fail and are skipped.',
),
emptyStateLink: s__('ProjectQualitySummary|Set up test runs'),
emptyStateLinkLabel: s__('ProjectQualitySummary|Set up test runs (opens in a new tab)'),
},
coverage: {
title: s__('ProjectQualitySummary|Test coverage'),
popoverBody: s__('ProjectQualitySummary|Measure of how much of your code is covered by tests.'),
learnMoreLink: s__('ProjectQualitySummary|Learn more about test coverage'),
fullReportLink: s__('ProjectQualitySummary|See project Code Coverage Statistics'),
coverageLabel: s__('ProjectQualitySummary|Coverage'),
},
subHeader: s__('ProjectQualitySummary|Latest pipeline results'),
fetchError: s__(
'ProjectQualitySummary|An error occurred while trying to fetch project quality statistics',
),
};
import { GlSkeletonLoader } from '@gitlab/ui';
import { GlSkeletonLoader, GlEmptyState } from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import mockProjectQualityResponse from 'test_fixtures/graphql/project_quality_summary/graphql/queries/get_project_quality.query.graphql.json';
......@@ -9,6 +9,7 @@ import { mountExtended } from 'helpers/vue_test_utils_helper';
import ProjectQualitySummary from 'ee/project_quality_summary/app.vue';
import getProjectQuality from 'ee/project_quality_summary/graphql/queries/get_project_quality.query.graphql';
import { i18n } from 'ee/project_quality_summary/constants';
jest.mock('~/flash');
......@@ -36,6 +37,7 @@ describe('Project quality summary app component', () => {
projectPath: 'project-path',
coverageChartPath,
defaultBranch: 'main',
testRunsEmptyStateImagePath: 'image/path',
},
});
};
......@@ -104,4 +106,20 @@ describe('Project quality summary app component', () => {
});
});
});
describe('without data', () => {
beforeEach(async () => {
createComponent(jest.fn().mockResolvedValue([]));
await waitForPromises();
});
it('shows a test runs empty state', () => {
const emptyState = wrapper.findComponent(GlEmptyState);
expect(emptyState.exists()).toBe(true);
expect(emptyState.text()).toContain(i18n.testRuns.title);
expect(emptyState.text()).toContain(i18n.testRuns.emptyStateDescription);
expect(emptyState.text()).toContain(i18n.testRuns.emptyStateLink);
});
});
});
......@@ -28487,6 +28487,9 @@ msgstr ""
msgid "ProjectQualitySummary|Failure"
msgstr ""
msgid "ProjectQualitySummary|Get insight into the overall percentage of tests in your project that succeed, fail and are skipped."
msgstr ""
msgid "ProjectQualitySummary|Latest pipeline results"
msgstr ""
......@@ -28505,6 +28508,12 @@ msgstr ""
msgid "ProjectQualitySummary|See project Code Coverage Statistics"
msgstr ""
msgid "ProjectQualitySummary|Set up test runs"
msgstr ""
msgid "ProjectQualitySummary|Set up test runs (opens in a new tab)"
msgstr ""
msgid "ProjectQualitySummary|Skipped"
msgstr ""
......
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