Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
23833900
Commit
23833900
authored
Apr 12, 2021
by
Sarah Groff Hennigh-Palermo
Committed by
Andrew Fontaine
Apr 12, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Pipeline Graph: Add Stage Name to Needs/Layers VIew
parent
fb0738b3
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
165 additions
and
57 deletions
+165
-57
app/assets/javascripts/pipelines/components/graph/graph_component.vue
...avascripts/pipelines/components/graph/graph_component.vue
+11
-2
app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue
...scripts/pipelines/components/graph/job_group_dropdown.vue
+13
-15
app/assets/javascripts/pipelines/components/graph/job_item.vue
...ssets/javascripts/pipelines/components/graph/job_item.vue
+54
-23
app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
...pts/pipelines/components/graph/stage_column_component.vue
+22
-6
app/assets/javascripts/pipelines/components/unwrapping_utils.js
...sets/javascripts/pipelines/components/unwrapping_utils.js
+13
-1
app/assets/stylesheets/page_bundles/pipeline.scss
app/assets/stylesheets/page_bundles/pipeline.scss
+13
-1
spec/frontend/pipelines/graph/graph_component_spec.js
spec/frontend/pipelines/graph/graph_component_spec.js
+28
-2
spec/frontend/pipelines/graph/job_group_dropdown_spec.js
spec/frontend/pipelines/graph/job_group_dropdown_spec.js
+6
-2
spec/frontend/pipelines/graph/job_item_spec.js
spec/frontend/pipelines/graph/job_item_spec.js
+2
-2
spec/frontend/pipelines/graph/stage_column_component_spec.js
spec/frontend/pipelines/graph/stage_column_component_spec.js
+3
-3
No files found.
app/assets/javascripts/pipelines/components/graph/graph_component.vue
View file @
23833900
...
...
@@ -54,6 +54,7 @@ export default {
data
()
{
return
{
hoveredJobName
:
''
,
hoveredSourceJobName
:
''
,
highlightedJobs
:
[],
measurements
:
{
width
:
0
,
...
...
@@ -93,6 +94,9 @@ export default {
shouldHideLinks
()
{
return
this
.
isStageView
;
},
shouldShowStageName
()
{
return
!
this
.
isStageView
;
},
// The show downstream check prevents showing redundant linked columns
showDownstreamPipelines
()
{
return
(
...
...
@@ -148,6 +152,9 @@ export default {
setJob
(
jobName
)
{
this
.
hoveredJobName
=
jobName
;
},
setSourceJob
(
jobName
)
{
this
.
hoveredSourceJobName
=
jobName
;
},
slidePipelineContainer
()
{
this
.
$refs
.
mainPipelineContainer
.
scrollBy
({
left
:
ONE_COL_WIDTH
,
...
...
@@ -204,11 +211,13 @@ export default {
<stage-column-component
v-for=
"column in layout"
:key=
"column.id || column.name"
:
titl
e=
"column.name"
:
nam
e=
"column.name"
:groups=
"column.groups"
:action=
"column.status.action"
:highlighted-jobs=
"highlightedJobs"
:show-stage-name=
"shouldShowStageName"
:job-hovered=
"hoveredJobName"
:source-job-hovered=
"hoveredSourceJobName"
:pipeline-expanded=
"pipelineExpanded"
:pipeline-id=
"pipeline.id"
@
refreshPipelineGraph=
"$emit('refreshPipelineGraph')"
...
...
@@ -227,7 +236,7 @@ export default {
:column-title=
"__('Downstream')"
:type=
"$options.pipelineTypeConstants.DOWNSTREAM"
:view-type=
"viewType"
@
downstreamHovered=
"setJob"
@
downstreamHovered=
"set
Source
Job"
@
pipelineExpandToggle=
"togglePipelineExpanded"
@
scrollContainer=
"slidePipelineContainer"
@
error=
"onError"
...
...
app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue
View file @
23833900
<
script
>
import
{
GlTooltipDirective
}
from
'
@gitlab/ui
'
;
import
CiIcon
from
'
~/vue_shared/components/ci_icon.vue
'
;
import
{
reportToSentry
}
from
'
../../utils
'
;
import
JobItem
from
'
./job_item.vue
'
;
...
...
@@ -11,12 +9,8 @@ import JobItem from './job_item.vue';
*
*/
export
default
{
directives
:
{
GlTooltip
:
GlTooltipDirective
,
},
components
:
{
JobItem
,
CiIcon
,
},
props
:
{
group
:
{
...
...
@@ -28,6 +22,11 @@ export default {
required
:
false
,
default
:
-
1
,
},
stageName
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
},
computed
:
{
computedJobId
()
{
...
...
@@ -51,22 +50,21 @@ export default {
<
template
>
<div
:id=
"computedJobId"
class=
"ci-job-dropdown-container dropdown dropright"
>
<button
v-gl-tooltip.hover=
"
{ boundary: 'viewport' }"
:title="tooltipText"
type=
"button"
data-toggle=
"dropdown"
data-display=
"static"
class=
"dropdown-menu-toggle build-content gl-build-content gl-pipeline-job-width! gl-pr-4!"
>
<div
class=
"gl-display-flex gl-align-items-center gl-justify-content-space-between"
>
<span
class=
"gl-display-flex gl-align-items-center gl-min-w-0"
>
<ci-icon
:status=
"group.status"
:size=
"24"
class=
"gl-line-height-0"
/>
<span
class=
"gl-text-truncate mw-70p gl-pl-3"
>
{{
group
.
name
}}
</span>
</span>
<job-item
:dropdown-length=
"group.size"
:group-tooltip=
"tooltipText"
:job=
"group"
:stage-name=
"stageName"
@
pipelineActionRequestComplete=
"pipelineActionRequestComplete"
/>
<
span
class=
"gl-font-weight-100 gl-font-size-lg"
>
{{
group
.
size
}}
</span
>
<
div
class=
"gl-font-weight-100 gl-font-size-lg gl-ml-n4"
>
{{
group
.
size
}}
</div
>
</div>
</button>
...
...
app/assets/javascripts/pipelines/components/graph/job_item.vue
View file @
23833900
...
...
@@ -3,6 +3,7 @@ import { GlTooltipDirective, GlLink } from '@gitlab/ui';
import
delayedJobMixin
from
'
~/jobs/mixins/delayed_job_mixin
'
;
import
{
BV_HIDE_TOOLTIP
}
from
'
~/lib/utils/constants
'
;
import
{
sprintf
}
from
'
~/locale
'
;
import
CiIcon
from
'
~/vue_shared/components/ci_icon.vue
'
;
import
{
reportToSentry
}
from
'
../../utils
'
;
import
ActionComponent
from
'
../jobs_shared/action_component.vue
'
;
import
JobNameComponent
from
'
../jobs_shared/job_name_component.vue
'
;
...
...
@@ -38,6 +39,7 @@ export default {
hoverClass
:
'
gl-shadow-x0-y0-b3-s1-blue-500
'
,
components
:
{
ActionComponent
,
CiIcon
,
JobNameComponent
,
GlLink
,
},
...
...
@@ -65,6 +67,11 @@ export default {
required
:
false
,
default
:
Infinity
,
},
groupTooltip
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
jobHovered
:
{
type
:
String
,
required
:
false
,
...
...
@@ -80,24 +87,47 @@ export default {
required
:
false
,
default
:
-
1
,
},
sourceJobHovered
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
stageName
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
},
computed
:
{
boundary
()
{
return
this
.
dropdownLength
===
1
?
'
viewport
'
:
'
scrollParent
'
;
},
computedJobId
()
{
return
this
.
pipelineId
>
-
1
?
`
${
this
.
job
.
name
}
-
${
this
.
pipelineId
}
`
:
''
;
},
detailsPath
()
{
return
accessValue
(
this
.
dataMethod
,
'
detailsPath
'
,
this
.
status
);
},
hasDetails
()
{
return
accessValue
(
this
.
dataMethod
,
'
hasDetails
'
,
this
.
status
);
},
computedJobId
()
{
return
this
.
pipelineId
>
-
1
?
`
${
this
.
job
.
name
}
-
${
this
.
pipelineId
}
`
:
''
;
nameComponent
()
{
return
this
.
hasDetails
?
'
gl-link
'
:
'
div
'
;
},
showStageName
()
{
return
Boolean
(
this
.
stageName
);
},
status
()
{
return
this
.
job
&&
this
.
job
.
status
?
this
.
job
.
status
:
{};
},
testId
()
{
return
this
.
hasDetails
?
'
job-with-link
'
:
'
job-without-link
'
;
},
tooltipText
()
{
if
(
this
.
groupTooltip
)
{
return
this
.
groupTooltip
;
}
const
textBuilder
=
[];
const
{
name
:
jobName
}
=
this
.
job
;
...
...
@@ -129,7 +159,7 @@ export default {
return
this
.
job
.
status
&&
this
.
job
.
status
.
action
&&
this
.
job
.
status
.
action
.
path
;
},
relatedDownstreamHovered
()
{
return
this
.
job
.
name
===
this
.
j
obHovered
;
return
this
.
job
.
name
===
this
.
sourceJ
obHovered
;
},
relatedDownstreamExpanded
()
{
return
this
.
job
.
name
===
this
.
pipelineExpanded
.
jobName
&&
this
.
pipelineExpanded
.
expanded
;
...
...
@@ -156,44 +186,45 @@ export default {
<
template
>
<div
:id=
"computedJobId"
class=
"ci-job-component gl-display-flex gl-align-items-center gl-justify-content-space-between"
class=
"ci-job-component gl-display-flex gl-align-items-center gl-justify-content-space-between
gl-w-full
"
data-qa-selector=
"job_item_container"
>
<
gl-link
v-if=
"hasDetails
"
<
component
:is=
"nameComponent
"
v-gl-tooltip=
"
{
boundary: 'viewport',
placement: 'bottom',
customClass: 'gl-pointer-events-none',
}"
:href="detailsPath"
:title="tooltipText"
:class="jobClasses"
class="js-pipeline-graph-job-link qa-job-link menu-item gl-text-gray-900 gl-active-text-decoration-none gl-focus-text-decoration-none gl-hover-text-decoration-none"
data-testid="job-with-link"
:href="detailsPath"
class="js-pipeline-graph-job-link qa-job-link menu-item gl-text-gray-900 gl-active-text-decoration-none gl-focus-text-decoration-none gl-hover-text-decoration-none gl-w-full"
:data-testid="testId"
@click.stop="hideTooltips"
@mouseout="hideTooltips"
>
<
job-name-component
:name=
"job.name"
:status=
"job.status"
:icon-size=
"24"
/
>
</gl-link
>
<div
v-else
v-gl-tooltip=
"
{ boundary, placement: 'bottom', customClass: 'gl-pointer-events-none' }
"
:title="tooltipText
"
:class="jobClasses
"
class="js-job-component-tooltip non-details-job-component menu-item"
data-testid="job-without-link"
@mouseout="hideTooltips"
>
<
job-name-component
:name=
"job.name"
:status=
"job.status"
:icon-size=
"24"
/
>
</
div
>
<
div
class=
"ci-job-name-component gl-display-flex gl-align-items-center"
>
<ci-icon
:size=
"24"
:status=
"job.status"
class=
"gl-line-height-0"
/
>
<div
class=
"gl-pl-3 gl-display-flex gl-flex-direction-column gl-w-full"
>
<div
class=
"gl-text-truncate mw-70p gl-line-height-normal"
>
{{
job
.
name
}}
</div>
<div
v-if=
"showStageName
"
data-testid=
"stage-name-in-job
"
class=
"gl-text-truncate mw-70p gl-font-sm gl-text-gray-500 gl-line-height-normal
"
>
{{
stageName
}}
</div>
</div
>
<
/div
>
</
component
>
<action-component
v-if=
"hasAction"
:tooltip-text=
"status.action.title"
:link=
"status.action.path"
:action-icon=
"status.action.icon"
class=
"gl-mr-1"
data-qa-selector=
"action_button"
@
pipelineActionRequestComplete=
"pipelineActionRequestComplete"
/>
...
...
app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
View file @
23833900
...
...
@@ -22,12 +22,12 @@ export default {
type
:
Array
,
required
:
true
,
},
pipelineId
:
{
type
:
Number
,
name
:
{
type
:
String
,
required
:
true
,
},
title
:
{
type
:
String
,
pipelineId
:
{
type
:
Number
,
required
:
true
,
},
action
:
{
...
...
@@ -50,6 +50,16 @@ export default {
required
:
false
,
default
:
()
=>
({}),
},
showStageName
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
sourceJobHovered
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
},
titleClasses
:
[
'
gl-font-weight-bold
'
,
...
...
@@ -75,7 +85,7 @@ export default {
});
},
formattedTitle
()
{
return
capitalize
(
escape
(
this
.
titl
e
));
return
capitalize
(
escape
(
this
.
nam
e
));
},
hasAction
()
{
return
!
isEmpty
(
this
.
action
);
...
...
@@ -145,14 +155,20 @@ export default {
v-if=
"singleJobExists(group)"
:job=
"group.jobs[0]"
:job-hovered=
"jobHovered"
:source-job-hovered=
"sourceJobHovered"
:pipeline-expanded=
"pipelineExpanded"
:pipeline-id=
"pipelineId"
:stage-name=
"showStageName ? group.stageName : ''"
css-class-job-name=
"gl-build-content"
:class=
"
{ 'gl-opacity-3': isFadedOut(group.name) }"
@pipelineActionRequestComplete="$emit('refreshPipelineGraph')"
/>
<div
v-else-if=
"isParallel(group)"
:class=
"
{ 'gl-opacity-3': isFadedOut(group.name) }">
<job-group-dropdown
:group=
"group"
:pipeline-id=
"pipelineId"
/>
<job-group-dropdown
:group=
"group"
:stage-name=
"showStageName ? group.stageName : ''"
:pipeline-id=
"pipelineId"
/>
</div>
</div>
</
template
>
...
...
app/assets/javascripts/pipelines/components/unwrapping_utils.js
View file @
23833900
...
...
@@ -5,7 +5,19 @@ const unwrapGroups = (stages) => {
const
{
groups
:
{
nodes
:
groups
},
}
=
stage
;
return
{
node
:
{
...
stage
,
groups
},
lookup
:
{
stageIdx
:
idx
}
};
/*
Being peformance conscious here means we don't want to spread and copy the
group value just to add one parameter.
*/
/* eslint-disable no-param-reassign */
const
groupsWithStageName
=
groups
.
map
((
group
)
=>
{
group
.
stageName
=
stage
.
name
;
return
group
;
});
/* eslint-enable no-param-reassign */
return
{
node
:
{
...
stage
,
groups
:
groupsWithStageName
},
lookup
:
{
stageIdx
:
idx
}
};
});
};
...
...
app/assets/stylesheets/page_bundles/pipeline.scss
View file @
23833900
...
...
@@ -148,7 +148,19 @@
}
.gl-build-content
{
@include
build-content
();
display
:
inline-block
;
padding
:
8px
10px
9px
;
width
:
100%
;
border
:
1px
solid
var
(
--
border-color
,
$border-color
);
border-radius
:
30px
;
background-color
:
var
(
--
white
,
$white
);
&
:hover
,
&
:focus
{
background-color
:
var
(
--
gray-50
,
$gray-50
);
border
:
1px
solid
$dropdown-toggle-active-border-color
;
color
:
var
(
--
gl-text-color
,
$gl-text-color
);
}
}
.gl-ci-action-icon-container
{
...
...
spec/frontend/pipelines/graph/graph_component_spec.js
View file @
23833900
import
{
mount
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
GRAPHQL
,
STAGE_VIEW
}
from
'
~/pipelines/components/graph/constants
'
;
import
{
GRAPHQL
,
LAYER_VIEW
,
STAGE_VIEW
}
from
'
~/pipelines/components/graph/constants
'
;
import
PipelineGraph
from
'
~/pipelines/components/graph/graph_component.vue
'
;
import
JobItem
from
'
~/pipelines/components/graph/job_item.vue
'
;
import
LinkedPipelinesColumn
from
'
~/pipelines/components/graph/linked_pipelines_column.vue
'
;
import
StageColumnComponent
from
'
~/pipelines/components/graph/stage_column_component.vue
'
;
import
LinksLayer
from
'
~/pipelines/components/graph_shared/links_layer.vue
'
;
import
{
listByLayers
}
from
'
~/pipelines/components/parsing_utils
'
;
import
{
generateResponse
,
mockPipelineResponse
,
...
...
@@ -17,6 +18,7 @@ describe('graph component', () => {
const
findLinkedColumns
=
()
=>
wrapper
.
findAll
(
LinkedPipelinesColumn
);
const
findLinksLayer
=
()
=>
wrapper
.
find
(
LinksLayer
);
const
findStageColumns
=
()
=>
wrapper
.
findAll
(
StageColumnComponent
);
const
findStageNameInJob
=
()
=>
wrapper
.
find
(
'
[data-testid="stage-name-in-job"]
'
);
const
defaultProps
=
{
pipeline
:
generateResponse
(
mockPipelineResponse
,
'
root/fungi-xoxo
'
),
...
...
@@ -82,6 +84,10 @@ describe('graph component', () => {
expect
(
findLinksLayer
().
exists
()).
toBe
(
true
);
});
it
(
'
does not display stage name on the job in default (stage) mode
'
,
()
=>
{
expect
(
findStageNameInJob
().
exists
()).
toBe
(
false
);
});
describe
(
'
when column requests a refresh
'
,
()
=>
{
beforeEach
(()
=>
{
findStageColumns
().
at
(
0
).
vm
.
$emit
(
'
refreshPipelineGraph
'
);
...
...
@@ -93,7 +99,7 @@ describe('graph component', () => {
});
describe
(
'
when links are present
'
,
()
=>
{
beforeEach
(
async
()
=>
{
beforeEach
(()
=>
{
createComponent
({
mountFn
:
mount
,
stubOverride
:
{
'
job-item
'
:
false
},
...
...
@@ -132,4 +138,24 @@ describe('graph component', () => {
expect
(
findLinkedColumns
()).
toHaveLength
(
2
);
});
});
describe
(
'
in layers mode
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
mountFn
:
mount
,
stubOverride
:
{
'
job-item
'
:
false
,
'
job-group-dropdown
'
:
false
,
},
props
:
{
viewType
:
LAYER_VIEW
,
pipelineLayers
:
listByLayers
(
defaultProps
.
pipeline
),
},
});
});
it
(
'
displays the stage name on the job
'
,
()
=>
{
expect
(
findStageNameInJob
().
exists
()).
toBe
(
true
);
});
});
});
spec/frontend/pipelines/graph/job_group_dropdown_spec.js
View file @
23833900
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
shallowMount
,
mount
}
from
'
@vue/test-utils
'
;
import
JobGroupDropdown
from
'
~/pipelines/components/graph/job_group_dropdown.vue
'
;
describe
(
'
job group dropdown component
'
,
()
=>
{
...
...
@@ -65,12 +65,16 @@ describe('job group dropdown component', () => {
let
wrapper
;
const
findButton
=
()
=>
wrapper
.
find
(
'
button
'
);
const
createComponent
=
({
mountFn
=
shallowMount
})
=>
{
wrapper
=
mountFn
(
JobGroupDropdown
,
{
propsData
:
{
group
}
});
};
afterEach
(()
=>
{
wrapper
.
destroy
();
});
beforeEach
(()
=>
{
wrapper
=
shallowMount
(
JobGroupDropdown
,
{
propsData
:
{
group
}
});
createComponent
({
mountFn
:
mount
});
});
it
(
'
renders button with group name and size
'
,
()
=>
{
...
...
spec/frontend/pipelines/graph/job_item_spec.js
View file @
23833900
...
...
@@ -122,7 +122,7 @@ describe('pipeline graph job item', () => {
},
});
expect
(
wrapper
.
find
(
'
.js-job-component-tooltip
'
).
attributes
(
'
title
'
)).
toBe
(
'
test
'
);
expect
(
findJobWithoutLink
(
).
attributes
(
'
title
'
)).
toBe
(
'
test
'
);
});
it
(
'
should not render status label when it is provided
'
,
()
=>
{
...
...
@@ -138,7 +138,7 @@ describe('pipeline graph job item', () => {
},
});
expect
(
wrapper
.
find
(
'
.js-job-component-tooltip
'
).
attributes
(
'
title
'
)).
toBe
(
'
test - success
'
);
expect
(
findJobWithoutLink
(
).
attributes
(
'
title
'
)).
toBe
(
'
test - success
'
);
});
});
...
...
spec/frontend/pipelines/graph/stage_column_component_spec.js
View file @
23833900
...
...
@@ -28,7 +28,7 @@ const mockGroups = Array(4)
});
const
defaultProps
=
{
titl
e
:
'
Fish
'
,
nam
e
:
'
Fish
'
,
groups
:
mockGroups
,
pipelineId
:
159
,
};
...
...
@@ -62,7 +62,7 @@ describe('stage column component', () => {
});
it
(
'
should render provided title
'
,
()
=>
{
expect
(
findStageColumnTitle
().
text
()).
toBe
(
defaultProps
.
titl
e
);
expect
(
findStageColumnTitle
().
text
()).
toBe
(
defaultProps
.
nam
e
);
});
it
(
'
should render the provided groups
'
,
()
=>
{
...
...
@@ -119,7 +119,7 @@ describe('stage column component', () => {
],
},
],
titl
e
:
'
test <img src=x onerror=alert(document.domain)>
'
,
nam
e
:
'
test <img src=x onerror=alert(document.domain)>
'
,
},
});
});
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment