Commit b28833cc authored by Frédéric Caplette's avatar Frédéric Caplette Committed by Nicolò Maria Mezzopera

Add tag to schedule pipeline

On the pipeline tab page,
if the pipeline was triggered by
a schedule, we add a tag that
indicates that origin. There is
also a tooltip on hover and
clicking on it links to the
schedule pipeline page.
parent 2507dfd2
......@@ -40,6 +40,7 @@ document.addEventListener(
props: {
store: this.store,
endpoint: this.dataset.endpoint,
pipelineScheduleUrl: this.dataset.pipelineScheduleUrl,
helpPagePath: this.dataset.helpPagePath,
emptyStateSvgPath: this.dataset.emptyStateSvgPath,
errorStateSvgPath: this.dataset.errorStateSvgPath,
......
<script>
import { GlLink, GlTooltipDirective } from '@gitlab/ui';
import { escape } from 'lodash';
import { SCHEDULE_ORIGIN } from '../../constants';
import { __, sprintf } from '~/locale';
import popover from '~/vue_shared/directives/popover';
......@@ -27,6 +28,10 @@ export default {
type: Object,
required: true,
},
pipelineScheduleUrl: {
type: String,
required: true,
},
autoDevopsHelpPath: {
type: String,
required: true,
......@@ -36,6 +41,9 @@ export default {
user() {
return this.pipeline.user;
},
isScheduled() {
return this.pipeline.source === SCHEDULE_ORIGIN;
},
popoverOptions() {
return {
html: true,
......@@ -61,16 +69,28 @@ export default {
<gl-link
:href="pipeline.path"
class="js-pipeline-url-link js-onboarding-pipeline-item"
data-testid="pipeline-url-link"
data-qa-selector="pipeline_url_link"
>
<span class="pipeline-id">#{{ pipeline.id }}</span>
</gl-link>
<div class="label-container">
<gl-link v-if="isScheduled" :href="pipelineScheduleUrl" target="__blank">
<span
v-gl-tooltip
:title="__('This pipeline was triggered by a schedule.')"
class="badge badge-info"
data-testid="pipeline-url-scheduled"
>
{{ __('Scheduled') }}
</span>
</gl-link>
<span
v-if="pipeline.flags.latest"
v-gl-tooltip
:title="__('Latest pipeline for the most recent commit on this branch')"
class="js-pipeline-url-latest badge badge-success"
data-testid="pipeline-url-latest"
>
{{ __('latest') }}
</span>
......@@ -79,6 +99,7 @@ export default {
v-gl-tooltip
:title="pipeline.yaml_errors"
class="js-pipeline-url-yaml badge badge-danger"
data-testid="pipeline-url-yaml"
>
{{ __('yaml invalid') }}
</span>
......@@ -87,6 +108,7 @@ export default {
v-gl-tooltip
:title="pipeline.failure_reason"
class="js-pipeline-url-failure badge badge-danger"
data-testid="pipeline-url-failure"
>
{{ __('error') }}
</span>
......@@ -95,10 +117,15 @@ export default {
v-popover="popoverOptions"
tabindex="0"
class="js-pipeline-url-autodevops badge badge-info autodevops-badge"
data-testid="pipeline-url-autodevops"
role="button"
>{{ __('Auto DevOps') }}</gl-link
>
<span v-if="pipeline.flags.stuck" class="js-pipeline-url-stuck badge badge-warning">
<span
v-if="pipeline.flags.stuck"
class="js-pipeline-url-stuck badge badge-warning"
data-testid="pipeline-url-stuck"
>
{{ __('stuck') }}
</span>
<span
......@@ -110,6 +137,7 @@ export default {
)
"
class="js-pipeline-url-detached badge badge-info"
data-testid="pipeline-url-detached"
>
{{ __('detached') }}
</span>
......
......@@ -42,6 +42,11 @@ export default {
type: String,
required: true,
},
pipelineScheduleUrl: {
type: String,
required: false,
default: '',
},
helpPagePath: {
type: String,
required: true,
......@@ -340,6 +345,7 @@ export default {
<div v-else-if="stateToRender === $options.stateMap.tableList" class="table-holder">
<pipelines-table-component
:pipelines="state.pipelines"
:pipeline-schedule-url="pipelineScheduleUrl"
:update-graph-dropdown="updateGraphDropdown"
:auto-devops-help-path="autoDevopsPath"
:view-type="viewType"
......
......@@ -22,6 +22,11 @@ export default {
type: Array,
required: true,
},
pipelineScheduleUrl: {
type: String,
required: false,
default: '',
},
updateGraphDropdown: {
type: Boolean,
required: false,
......@@ -91,6 +96,7 @@ export default {
v-for="model in pipelines"
:key="model.id"
:pipeline="model"
:pipeline-schedule-url="pipelineScheduleUrl"
:update-graph-dropdown="updateGraphDropdown"
:auto-devops-help-path="autoDevopsHelpPath"
:view-type="viewType"
......
......@@ -35,6 +35,11 @@ export default {
type: Object,
required: true,
},
pipelineScheduleUrl: {
type: String,
required: false,
default: '',
},
updateGraphDropdown: {
type: Boolean,
required: false,
......@@ -274,7 +279,11 @@ export default {
</div>
</div>
<pipeline-url :pipeline="pipeline" :auto-devops-help-path="autoDevopsHelpPath" />
<pipeline-url
:pipeline="pipeline"
:pipeline-schedule-url="pipelineScheduleUrl"
:auto-devops-help-path="autoDevopsHelpPath"
/>
<pipeline-triggerer :pipeline="pipeline" />
<div class="table-section section-wrap section-20">
......
......@@ -7,6 +7,7 @@ export const FILTER_PIPELINES_SEARCH_DELAY = 200;
export const ANY_TRIGGER_AUTHOR = 'Any';
export const SUPPORTED_FILTER_PARAMETERS = ['username', 'ref', 'status'];
export const FILTER_TAG_IDENTIFIER = 'tag';
export const SCHEDULE_ORIGIN = 'schedule';
export const TestStatus = {
FAILED: 'failed',
......
......@@ -7,6 +7,7 @@
params: params.to_json,
"help-page-path" => help_page_path('ci/quick_start/README'),
"help-auto-devops-path" => help_page_path('topics/autodevops/index.md'),
"pipeline-schedule-url" => pipeline_schedules_path(@project),
"empty-state-svg-path" => image_path('illustrations/pipelines_empty.svg'),
"error-state-svg-path" => image_path('illustrations/pipelines_failed.svg'),
"no-pipelines-svg-path" => image_path('illustrations/pipelines_pending.svg'),
......
---
title: Provide a label for 'Scheduled Pipeline' in the pipelines overview page
merge_request: 35554
author:
type: added
......@@ -23587,6 +23587,9 @@ msgstr ""
msgid "This pipeline was triggered by a parent pipeline"
msgstr ""
msgid "This pipeline was triggered by a schedule."
msgstr ""
msgid "This project"
msgstr ""
......
......@@ -8,101 +8,133 @@ $.fn.popover = () => {};
describe('Pipeline Url Component', () => {
let wrapper;
const findPipelineUrlLink = () => wrapper.find('[data-testid="pipeline-url-link"]');
const findScheduledTag = () => wrapper.find('[data-testid="pipeline-url-scheduled"]');
const findLatestTag = () => wrapper.find('[data-testid="pipeline-url-latest"]');
const findYamlTag = () => wrapper.find('[data-testid="pipeline-url-yaml"]');
const findFailureTag = () => wrapper.find('[data-testid="pipeline-url-failure"]');
const findAutoDevopsTag = () => wrapper.find('[data-testid="pipeline-url-autodevops"]');
const findStuckTag = () => wrapper.find('[data-testid="pipeline-url-stuck"]');
const findDetachedTag = () => wrapper.find('[data-testid="pipeline-url-detached"]');
const defaultProps = {
pipeline: {
id: 1,
path: 'foo',
flags: {},
},
autoDevopsHelpPath: 'foo',
pipelineScheduleUrl: 'foo',
};
const createComponent = props => {
wrapper = shallowMount(PipelineUrlComponent, {
propsData: props,
propsData: { ...defaultProps, ...props },
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('should render a table cell', () => {
createComponent();
expect(wrapper.attributes('class')).toContain('table-section');
});
it('should render a link the provided path and id', () => {
createComponent();
expect(findPipelineUrlLink().attributes('href')).toBe('foo');
expect(findPipelineUrlLink().text()).toBe('#1');
});
it('should render the stuck tag when flag is provided', () => {
createComponent({
pipeline: {
id: 1,
path: 'foo',
flags: {},
flags: {
stuck: true,
},
},
autoDevopsHelpPath: 'foo',
});
expect(wrapper.attributes('class')).toContain('table-section');
expect(findStuckTag().text()).toContain('stuck');
});
it('should render a link the provided path and id', () => {
it('should render latest tag when flag is provided', () => {
createComponent({
pipeline: {
id: 1,
path: 'foo',
flags: {},
flags: {
latest: true,
},
},
autoDevopsHelpPath: 'foo',
});
expect(wrapper.find('.js-pipeline-url-link').attributes('href')).toBe('foo');
expect(wrapper.find('.js-pipeline-url-link span').text()).toBe('#1');
expect(findLatestTag().text()).toContain('latest');
});
it('should render latest, yaml invalid, merge request, and stuck flags when provided', () => {
it('should render a yaml badge when it is invalid', () => {
createComponent({
pipeline: {
id: 1,
path: 'foo',
flags: {
latest: true,
yaml_errors: true,
stuck: true,
merge_request_pipeline: true,
detached_merge_request_pipeline: true,
},
},
autoDevopsHelpPath: 'foo',
});
expect(wrapper.find('.js-pipeline-url-latest').text()).toContain('latest');
expect(wrapper.find('.js-pipeline-url-yaml').text()).toContain('yaml invalid');
expect(findYamlTag().text()).toContain('yaml invalid');
});
expect(wrapper.find('.js-pipeline-url-stuck').text()).toContain('stuck');
it('should render an autodevops badge when flag is provided', () => {
createComponent({
pipeline: {
flags: {
auto_devops: true,
},
},
});
expect(wrapper.find('.js-pipeline-url-detached').text()).toContain('detached');
expect(trimText(findAutoDevopsTag().text())).toBe('Auto DevOps');
});
it('should render a badge for autodevops', () => {
it('should render a detached badge when flag is provided', () => {
createComponent({
pipeline: {
id: 1,
path: 'foo',
flags: {
latest: true,
yaml_errors: true,
stuck: true,
auto_devops: true,
detached_merge_request_pipeline: true,
},
},
autoDevopsHelpPath: 'foo',
});
expect(trimText(wrapper.find('.js-pipeline-url-autodevops').text())).toEqual('Auto DevOps');
expect(findDetachedTag().text()).toContain('detached');
});
it('should render error badge when pipeline has a failure reason set', () => {
createComponent({
pipeline: {
id: 1,
path: 'foo',
flags: {
failure_reason: true,
},
failure_reason: 'some reason',
},
autoDevopsHelpPath: 'foo',
});
expect(wrapper.find('.js-pipeline-url-failure').text()).toContain('error');
expect(wrapper.find('.js-pipeline-url-failure').attributes('title')).toContain('some reason');
expect(findFailureTag().text()).toContain('error');
expect(findFailureTag().attributes('title')).toContain('some reason');
});
it('should render scheduled badge when pipeline was triggered by a schedule', () => {
createComponent({
pipeline: {
flags: {},
source: 'schedule',
},
});
expect(findScheduledTag().exists()).toBe(true);
expect(findScheduledTag().text()).toContain('Scheduled');
});
});
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