Commit 9065fa7d authored by Jose Ivan Vargas's avatar Jose Ivan Vargas

Merge branch '15399-show-multiple-jobs-for-coverage' into 'master'

Show multiple builds for coverage

See merge request gitlab-org/gitlab!41217
parents be791863 cf093daa
<script> <script>
/* eslint-disable vue/require-default-prop, vue/no-v-html */ /* eslint-disable vue/require-default-prop, vue/no-v-html */
import { GlIcon, GlLink, GlLoadingIcon, GlSprintf, GlTooltipDirective } from '@gitlab/ui'; import {
GlIcon,
GlLink,
GlLoadingIcon,
GlSprintf,
GlTooltip,
GlTooltipDirective,
} from '@gitlab/ui';
import mrWidgetPipelineMixin from 'ee_else_ce/vue_merge_request_widget/mixins/mr_widget_pipeline'; import mrWidgetPipelineMixin from 'ee_else_ce/vue_merge_request_widget/mixins/mr_widget_pipeline';
import { s__ } from '~/locale'; import { s__, n__ } from '~/locale';
import PipelineStage from '~/pipelines/components/pipelines_list/stage.vue'; import PipelineStage from '~/pipelines/components/pipelines_list/stage.vue';
import CiIcon from '~/vue_shared/components/ci_icon.vue'; import CiIcon from '~/vue_shared/components/ci_icon.vue';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
...@@ -15,6 +22,7 @@ export default { ...@@ -15,6 +22,7 @@ export default {
GlLoadingIcon, GlLoadingIcon,
GlIcon, GlIcon,
GlSprintf, GlSprintf,
GlTooltip,
PipelineStage, PipelineStage,
TooltipOnTruncate, TooltipOnTruncate,
LinkedPipelinesMiniList: () => LinkedPipelinesMiniList: () =>
...@@ -33,6 +41,11 @@ export default { ...@@ -33,6 +41,11 @@ export default {
type: String, type: String,
required: false, required: false,
}, },
buildsWithCoverage: {
type: Array,
required: false,
default: () => [],
},
// This prop needs to be camelCase, html attributes are case insensive // This prop needs to be camelCase, html attributes are case insensive
// https://vuejs.org/v2/guide/components.html#camelCase-vs-kebab-case // https://vuejs.org/v2/guide/components.html#camelCase-vs-kebab-case
hasCi: { hasCi: {
...@@ -100,6 +113,16 @@ export default { ...@@ -100,6 +113,16 @@ export default {
} }
return ''; return '';
}, },
pipelineCoverageJobNumberText() {
return n__('from %d job', 'from %d jobs', this.buildsWithCoverage.length);
},
pipelineCoverageTooltipDescription() {
return n__(
'Coverage value for this pipeline was calculated by the coverage value of %d job.',
'Coverage value for this pipeline was calculated by averaging the resulting coverage values of %d jobs.',
this.buildsWithCoverage.length,
);
},
}, },
errorText: s__( errorText: s__(
'Pipeline|Could not retrieve the pipeline status. For troubleshooting steps, read the %{linkStart}documentation%{linkEnd}.', 'Pipeline|Could not retrieve the pipeline status. For troubleshooting steps, read the %{linkStart}documentation%{linkEnd}.',
...@@ -139,7 +162,7 @@ export default { ...@@ -139,7 +162,7 @@ export default {
> >
<gl-icon <gl-icon
name="question" name="question"
:small="12" :size="12"
tabindex="0" tabindex="0"
role="text" role="text"
:aria-label="__('Link to go to GitLab pipeline documentation')" :aria-label="__('Link to go to GitLab pipeline documentation')"
...@@ -189,14 +212,30 @@ export default { ...@@ -189,14 +212,30 @@ export default {
</div> </div>
<div v-if="pipeline.coverage" class="coverage" data-testid="pipeline-coverage"> <div v-if="pipeline.coverage" class="coverage" data-testid="pipeline-coverage">
{{ s__('Pipeline|Coverage') }} {{ pipeline.coverage }}% {{ s__('Pipeline|Coverage') }} {{ pipeline.coverage }}%
<span <span
v-if="pipelineCoverageDelta" v-if="pipelineCoverageDelta"
:class="coverageDeltaClass" :class="coverageDeltaClass"
data-testid="pipeline-coverage-delta" data-testid="pipeline-coverage-delta"
>({{ pipelineCoverageDelta }}%)</span
> >
({{ pipelineCoverageDelta }}%)
{{ pipelineCoverageJobNumberText }}
<span ref="pipelineCoverageQuestion">
<gl-icon name="question" :size="12" />
</span> </span>
<gl-tooltip
:target="() => $refs.pipelineCoverageQuestion"
data-testid="pipeline-coverage-tooltip"
>
{{ pipelineCoverageTooltipDescription }}
<div
v-for="(build, index) in buildsWithCoverage"
:key="`${build.name}-${index}`"
class="gl-mt-3 gl-text-left gl-px-4"
>
{{ build.name }} ({{ build.coverage }}%)
</div>
</gl-tooltip>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -77,6 +77,7 @@ export default { ...@@ -77,6 +77,7 @@ export default {
<mr-widget-pipeline <mr-widget-pipeline
:pipeline="pipeline" :pipeline="pipeline"
:pipeline-coverage-delta="mr.pipelineCoverageDelta" :pipeline-coverage-delta="mr.pipelineCoverageDelta"
:builds-with-coverage="mr.buildsWithCoverage"
:ci-status="mr.ciStatus" :ci-status="mr.ciStatus"
:has-ci="mr.hasCI" :has-ci="mr.hasCI"
:pipeline-must-succeed="mr.onlyAllowMergeIfPipelineSucceeds" :pipeline-must-succeed="mr.onlyAllowMergeIfPipelineSucceeds"
......
...@@ -52,6 +52,7 @@ export default class MergeRequestStore { ...@@ -52,6 +52,7 @@ export default class MergeRequestStore {
this.divergedCommitsCount = data.diverged_commits_count; this.divergedCommitsCount = data.diverged_commits_count;
this.pipeline = data.pipeline || {}; this.pipeline = data.pipeline || {};
this.pipelineCoverageDelta = data.pipeline_coverage_delta; this.pipelineCoverageDelta = data.pipeline_coverage_delta;
this.buildsWithCoverage = data.builds_with_coverage;
this.mergePipeline = data.merge_pipeline || {}; this.mergePipeline = data.merge_pipeline || {};
this.deployments = this.deployments || data.deployments || []; this.deployments = this.deployments || data.deployments || [];
this.postMergeDeployments = this.postMergeDeployments || []; this.postMergeDeployments = this.postMergeDeployments || [];
......
---
title: Show multiple jobs contributing to code coverage
merge_request: 41217
author:
type: added
...@@ -7102,6 +7102,11 @@ msgstr "" ...@@ -7102,6 +7102,11 @@ msgstr ""
msgid "Coverage Fuzzing" msgid "Coverage Fuzzing"
msgstr "" msgstr ""
msgid "Coverage value for this pipeline was calculated by the coverage value of %d job."
msgid_plural "Coverage value for this pipeline was calculated by averaging the resulting coverage values of %d jobs."
msgstr[0] ""
msgstr[1] ""
msgid "Create" msgid "Create"
msgstr "" msgstr ""
...@@ -29512,6 +29517,11 @@ msgstr "" ...@@ -29512,6 +29517,11 @@ msgstr ""
msgid "from" msgid "from"
msgstr "" msgstr ""
msgid "from %d job"
msgid_plural "from %d jobs"
msgstr[0] ""
msgstr[1] ""
msgid "group" msgid "group"
msgstr "" msgstr ""
......
...@@ -29,6 +29,8 @@ describe('MRWidgetPipeline', () => { ...@@ -29,6 +29,8 @@ describe('MRWidgetPipeline', () => {
const findAllPipelineStages = () => wrapper.findAll(PipelineStage); const findAllPipelineStages = () => wrapper.findAll(PipelineStage);
const findPipelineCoverage = () => wrapper.find('[data-testid="pipeline-coverage"]'); const findPipelineCoverage = () => wrapper.find('[data-testid="pipeline-coverage"]');
const findPipelineCoverageDelta = () => wrapper.find('[data-testid="pipeline-coverage-delta"]'); const findPipelineCoverageDelta = () => wrapper.find('[data-testid="pipeline-coverage-delta"]');
const findPipelineCoverageTooltipText = () =>
wrapper.find('[data-testid="pipeline-coverage-tooltip"]').text();
const findMonitoringPipelineMessage = () => const findMonitoringPipelineMessage = () =>
wrapper.find('[data-testid="monitoring-pipeline-message"]'); wrapper.find('[data-testid="monitoring-pipeline-message"]');
const findLoadingIcon = () => wrapper.find(GlLoadingIcon); const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
...@@ -140,6 +142,7 @@ describe('MRWidgetPipeline', () => { ...@@ -140,6 +142,7 @@ describe('MRWidgetPipeline', () => {
createWrapper( createWrapper(
{ {
pipelineCoverageDelta: mockData.pipelineCoverageDelta, pipelineCoverageDelta: mockData.pipelineCoverageDelta,
buildsWithCoverage: mockData.buildsWithCoverage,
}, },
mount, mount,
); );
...@@ -178,6 +181,22 @@ describe('MRWidgetPipeline', () => { ...@@ -178,6 +181,22 @@ describe('MRWidgetPipeline', () => {
expect(findPipelineCoverageDelta().exists()).toBe(true); expect(findPipelineCoverageDelta().exists()).toBe(true);
expect(findPipelineCoverageDelta().text()).toBe(`(${mockData.pipelineCoverageDelta}%)`); expect(findPipelineCoverageDelta().text()).toBe(`(${mockData.pipelineCoverageDelta}%)`);
}); });
it('should render tooltip for jobs contributing to code coverage', () => {
const tooltipText = findPipelineCoverageTooltipText();
const expectedDescription = `Coverage value for this pipeline was calculated by averaging the resulting coverage values of ${mockData.buildsWithCoverage.length} jobs.`;
expect(tooltipText).toContain(expectedDescription);
});
it.each(mockData.buildsWithCoverage)(
'should have name and coverage for build %s listed in tooltip',
build => {
const tooltipText = findPipelineCoverageTooltipText();
expect(tooltipText).toContain(`${build.name} (${build.coverage}%)`);
},
);
}); });
describe('without commit path', () => { describe('without commit path', () => {
......
...@@ -193,6 +193,7 @@ export default { ...@@ -193,6 +193,7 @@ export default {
updated_at: '2017-04-07T15:28:44.800Z', updated_at: '2017-04-07T15:28:44.800Z',
}, },
pipelineCoverageDelta: '15.25', pipelineCoverageDelta: '15.25',
buildsWithCoverage: [{ name: 'karma', coverage: '40.2' }, { name: 'rspec', coverage: '80.4' }],
work_in_progress: false, work_in_progress: false,
source_branch_exists: false, source_branch_exists: false,
mergeable_discussions_state: true, mergeable_discussions_state: true,
......
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