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
c8431dcb
Commit
c8431dcb
authored
Apr 21, 2021
by
Sarah Groff Hennigh-Palermo
Committed by
Andrew Fontaine
Apr 21, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Pipeline Graph: Update graph state selection
parent
a63b410d
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
333 additions
and
162 deletions
+333
-162
app/assets/javascripts/pipelines/components/graph/graph_component.vue
...avascripts/pipelines/components/graph/graph_component.vue
+9
-4
app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
...ts/pipelines/components/graph/graph_component_wrapper.vue
+7
-0
app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue
...cripts/pipelines/components/graph/graph_view_selector.vue
+95
-40
app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue
...ts/pipelines/components/graph/linked_pipelines_column.vue
+5
-0
app/assets/javascripts/pipelines/components/graph_shared/links_layer.vue
...scripts/pipelines/components/graph_shared/links_layer.vue
+4
-44
locale/gitlab.pot
locale/gitlab.pot
+9
-12
spec/frontend/pipelines/graph/graph_component_spec.js
spec/frontend/pipelines/graph/graph_component_spec.js
+1
-0
spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
.../frontend/pipelines/graph/graph_component_wrapper_spec.js
+52
-4
spec/frontend/pipelines/graph/graph_view_selector_spec.js
spec/frontend/pipelines/graph/graph_view_selector_spec.js
+124
-0
spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
.../frontend/pipelines/graph/linked_pipelines_column_spec.js
+1
-0
spec/frontend/pipelines/graph_shared/links_layer_spec.js
spec/frontend/pipelines/graph_shared/links_layer_spec.js
+26
-58
No files found.
app/assets/javascripts/pipelines/components/graph/graph_component.vue
View file @
c8431dcb
...
@@ -25,6 +25,10 @@ export default {
...
@@ -25,6 +25,10 @@ export default {
type
:
Object
,
type
:
Object
,
required
:
true
,
required
:
true
,
},
},
showLinks
:
{
type
:
Boolean
,
required
:
true
,
},
viewType
:
{
viewType
:
{
type
:
String
,
type
:
String
,
required
:
true
,
required
:
true
,
...
@@ -91,8 +95,8 @@ export default {
...
@@ -91,8 +95,8 @@ export default {
collectMetrics
:
true
,
collectMetrics
:
true
,
};
};
},
},
sho
uldHide
Links
()
{
sho
wJob
Links
()
{
return
this
.
isStageView
;
return
!
this
.
isStageView
&&
this
.
showLinks
;
},
},
shouldShowStageName
()
{
shouldShowStageName
()
{
return
!
this
.
isStageView
;
return
!
this
.
isStageView
;
...
@@ -188,6 +192,7 @@ export default {
...
@@ -188,6 +192,7 @@ export default {
:config-paths=
"configPaths"
:config-paths=
"configPaths"
:linked-pipelines=
"upstreamPipelines"
:linked-pipelines=
"upstreamPipelines"
:column-title=
"__('Upstream')"
:column-title=
"__('Upstream')"
:show-links=
"showJobLinks"
:type=
"$options.pipelineTypeConstants.UPSTREAM"
:type=
"$options.pipelineTypeConstants.UPSTREAM"
:view-type=
"viewType"
:view-type=
"viewType"
@
error=
"onError"
@
error=
"onError"
...
@@ -202,9 +207,8 @@ export default {
...
@@ -202,9 +207,8 @@ export default {
:container-measurements=
"measurements"
:container-measurements=
"measurements"
:highlighted-job=
"hoveredJobName"
:highlighted-job=
"hoveredJobName"
:metrics-config=
"metricsConfig"
:metrics-config=
"metricsConfig"
:
never-show-links=
"shouldHide
Links"
:
show-links=
"showJob
Links"
:view-type=
"viewType"
:view-type=
"viewType"
default-link-color=
"gl-stroke-transparent"
@
error=
"onError"
@
error=
"onError"
@
highlightedJobsChange=
"updateHighlightedJobs"
@
highlightedJobsChange=
"updateHighlightedJobs"
>
>
...
@@ -234,6 +238,7 @@ export default {
...
@@ -234,6 +238,7 @@ export default {
:config-paths=
"configPaths"
:config-paths=
"configPaths"
:linked-pipelines=
"downstreamPipelines"
:linked-pipelines=
"downstreamPipelines"
:column-title=
"__('Downstream')"
:column-title=
"__('Downstream')"
:show-links=
"showJobLinks"
:type=
"$options.pipelineTypeConstants.DOWNSTREAM"
:type=
"$options.pipelineTypeConstants.DOWNSTREAM"
:view-type=
"viewType"
:view-type=
"viewType"
@
downstreamHovered=
"setSourceJob"
@
downstreamHovered=
"setSourceJob"
...
...
app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
View file @
c8431dcb
...
@@ -48,6 +48,7 @@ export default {
...
@@ -48,6 +48,7 @@ export default {
pipeline
:
null
,
pipeline
:
null
,
pipelineLayers
:
null
,
pipelineLayers
:
null
,
showAlert
:
false
,
showAlert
:
false
,
showLinks
:
false
,
};
};
},
},
errorTexts
:
{
errorTexts
:
{
...
@@ -182,6 +183,9 @@ export default {
...
@@ -182,6 +183,9 @@ export default {
}
}
},
},
/* eslint-enable @gitlab/require-i18n-strings */
/* eslint-enable @gitlab/require-i18n-strings */
updateShowLinksState
(
val
)
{
this
.
showLinks
=
val
;
},
updateViewType
(
type
)
{
updateViewType
(
type
)
{
this
.
currentViewType
=
type
;
this
.
currentViewType
=
type
;
},
},
...
@@ -202,7 +206,9 @@ export default {
...
@@ -202,7 +206,9 @@ export default {
<graph-view-selector
<graph-view-selector
v-if=
"showGraphViewSelector"
v-if=
"showGraphViewSelector"
:type=
"currentViewType"
:type=
"currentViewType"
:show-links=
"showLinks"
@
updateViewType=
"updateViewType"
@
updateViewType=
"updateViewType"
@
updateShowLinksState=
"updateShowLinksState"
/>
/>
</local-storage-sync>
</local-storage-sync>
<gl-loading-icon
v-if=
"showLoadingIcon"
class=
"gl-mx-auto gl-my-4"
size=
"lg"
/>
<gl-loading-icon
v-if=
"showLoadingIcon"
class=
"gl-mx-auto gl-my-4"
size=
"lg"
/>
...
@@ -211,6 +217,7 @@ export default {
...
@@ -211,6 +217,7 @@ export default {
:config-paths=
"configPaths"
:config-paths=
"configPaths"
:pipeline=
"pipeline"
:pipeline=
"pipeline"
:pipeline-layers=
"getPipelineLayers()"
:pipeline-layers=
"getPipelineLayers()"
:show-links=
"showLinks"
:view-type=
"currentViewType"
:view-type=
"currentViewType"
@
error=
"reportFailure"
@
error=
"reportFailure"
@
refreshPipelineGraph=
"refreshPipelineGraph"
@
refreshPipelineGraph=
"refreshPipelineGraph"
...
...
app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue
View file @
c8431dcb
<
script
>
<
script
>
import
{
Gl
Dropdown
,
GlDropdownItem
,
GlIcon
,
GlSprintf
}
from
'
@gitlab/ui
'
;
import
{
Gl
LoadingIcon
,
GlSegmentedControl
,
GlToggle
}
from
'
@gitlab/ui
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
STAGE_VIEW
,
LAYER_VIEW
}
from
'
./constants
'
;
import
{
STAGE_VIEW
,
LAYER_VIEW
}
from
'
./constants
'
;
export
default
{
export
default
{
name
:
'
GraphViewSelector
'
,
name
:
'
GraphViewSelector
'
,
components
:
{
components
:
{
GlDropdown
,
GlLoadingIcon
,
GlDropdownItem
,
GlSegmentedControl
,
GlIcon
,
GlToggle
,
GlSprintf
,
},
},
props
:
{
props
:
{
showLinks
:
{
type
:
Boolean
,
required
:
true
,
},
type
:
{
type
:
{
type
:
String
,
type
:
String
,
required
:
true
,
required
:
true
,
...
@@ -19,67 +22,119 @@ export default {
...
@@ -19,67 +22,119 @@ export default {
},
},
data
()
{
data
()
{
return
{
return
{
currentViewType
:
STAGE_VIEW
,
currentViewType
:
this
.
type
,
showLinksActive
:
false
,
isToggleLoading
:
false
,
isSwitcherLoading
:
false
,
};
};
},
},
i18n
:
{
i18n
:
{
labelText
:
__
(
'
Order jobs by
'
),
viewLabelText
:
__
(
'
Group jobs by
'
),
linksLabelText
:
__
(
'
Show dependencies
'
),
},
},
views
:
{
views
:
{
[
STAGE_VIEW
]:
{
[
STAGE_VIEW
]:
{
type
:
STAGE_VIEW
,
type
:
STAGE_VIEW
,
text
:
{
text
:
{
primary
:
__
(
'
Stage
'
),
primary
:
__
(
'
Stage
'
),
secondary
:
__
(
'
View the jobs grouped into stages
'
),
},
},
},
},
[
LAYER_VIEW
]:
{
[
LAYER_VIEW
]:
{
type
:
LAYER_VIEW
,
type
:
LAYER_VIEW
,
text
:
{
text
:
{
primary
:
__
(
'
%{codeStart}needs:%{codeEnd} relationships
'
),
primary
:
__
(
'
Job dependencies
'
),
secondary
:
__
(
'
View what jobs are needed for a job to run
'
),
},
},
},
},
},
},
computed
:
{
computed
:
{
currentDropdownText
()
{
showLinksToggle
()
{
return
this
.
$options
.
views
[
this
.
type
].
text
.
primary
;
return
this
.
currentViewType
===
LAYER_VIEW
;
},
viewTypesList
()
{
return
Object
.
keys
(
this
.
$options
.
views
).
map
((
key
)
=>
{
return
{
value
:
key
,
text
:
this
.
$options
.
views
[
key
].
text
.
primary
,
};
});
},
},
watch
:
{
/*
How does this reset the loading? As we note in the methods comment below,
the loader is set to on before the update work is undertaken (in the parent).
Once the work is complete, one of these values will change, since that's the
point of the work. When that happens, the related value will update and we are done.
The bonus for this approach is that it works the same whichever "direction"
the work goes in.
*/
showLinks
()
{
this
.
isToggleLoading
=
false
;
},
type
()
{
this
.
isSwitcherLoading
=
false
;
},
},
},
},
methods
:
{
methods
:
{
itemClick
(
type
)
{
/*
this
.
$emit
(
'
updateViewType
'
,
type
);
In both toggle methods, we use setTimeout so that the loading indicator displays,
then the work is done to update the DOM. The process is:
→ user clicks
→ call stack: set loading to true
→ render: the loading icon appears on the screen
→ callback queue: now do the work to calculate the new view / links
(note: this work is done in the parent after the event is emitted)
setTimeout is how we move the work to the callback queue.
We can't use nextTick because that is called before the render loop.
See https://www.hesselinkwebdesign.nl/2019/nexttick-vs-settimeout-in-vue/ for more details.
*/
toggleView
(
type
)
{
this
.
isSwitcherLoading
=
true
;
setTimeout
(()
=>
{
this
.
$emit
(
'
updateViewType
'
,
type
);
});
},
toggleShowLinksActive
(
val
)
{
this
.
isToggleLoading
=
true
;
setTimeout
(()
=>
{
this
.
$emit
(
'
updateShowLinksState
'
,
val
);
});
},
},
},
},
};
};
</
script
>
</
script
>
<
template
>
<
template
>
<div
class=
"gl-display-flex gl-align-items-center gl-my-4"
>
<div
class=
"gl-relative gl-display-flex gl-align-items-center gl-w-max-content gl-my-4"
>
<span>
{{
$options
.
i18n
.
labelText
}}
</span>
<gl-loading-icon
<gl-dropdown
data-testid=
"pipeline-view-selector"
class=
"gl-ml-4"
>
v-if=
"isSwitcherLoading"
<template
#button-content
>
data-testid=
"switcher-loading-state"
<gl-sprintf
:message=
"currentDropdownText"
>
class=
"gl-absolute gl-w-full gl-bg-white gl-opacity-5 gl-z-index-2"
<template
#code
="
{ content }">
size=
"lg"
<code>
{{
content
}}
</code>
/>
</
template
>
<span
class=
"gl-font-weight-bold"
>
{{
$options
.
i18n
.
viewLabelText
}}
</span>
</gl-sprintf>
<gl-segmented-control
<gl-icon
class=
"gl-px-2"
name=
"angle-down"
:size=
"16"
/>
v-model=
"currentViewType"
</template>
:options=
"viewTypesList"
<gl-dropdown-item
:disabled=
"isSwitcherLoading"
v-for=
"view in $options.views"
data-testid=
"pipeline-view-selector"
:key=
"view.type"
class=
"gl-mx-4"
:secondary-text=
"view.text.secondary"
@
input=
"toggleView"
@
click=
"itemClick(view.type)"
/>
>
<b>
<div
v-if=
"showLinksToggle"
>
<gl-sprintf
:message=
"view.text.primary"
>
<gl-toggle
<
template
#code=
"{ content }"
>
v-model=
"showLinksActive"
<code>
{{
content
}}
</code>
data-testid=
"show-links-toggle"
</
template
>
class=
"gl-mx-4"
</gl-sprintf>
:label=
"$options.i18n.linksLabelText"
</b>
:is-loading=
"isToggleLoading"
</gl-dropdown-item>
label-position=
"left"
</gl-dropdown>
@
change=
"toggleShowLinksActive"
/>
</div>
</div>
</div>
</
template
>
</
template
>
app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue
View file @
c8431dcb
...
@@ -32,6 +32,10 @@ export default {
...
@@ -32,6 +32,10 @@ export default {
type
:
Array
,
type
:
Array
,
required
:
true
,
required
:
true
,
},
},
showLinks
:
{
type
:
Boolean
,
required
:
true
,
},
type
:
{
type
:
{
type
:
String
,
type
:
String
,
required
:
true
,
required
:
true
,
...
@@ -217,6 +221,7 @@ export default {
...
@@ -217,6 +221,7 @@ export default {
:config-paths=
"configPaths"
:config-paths=
"configPaths"
:pipeline=
"currentPipeline"
:pipeline=
"currentPipeline"
:pipeline-layers=
"getPipelineLayers(pipeline.id)"
:pipeline-layers=
"getPipelineLayers(pipeline.id)"
:show-links=
"showLinks"
:is-linked-pipeline=
"true"
:is-linked-pipeline=
"true"
:view-type=
"viewType"
:view-type=
"viewType"
/>
/>
...
...
app/assets/javascripts/pipelines/components/graph_shared/links_layer.vue
View file @
c8431dcb
<
script
>
<
script
>
import
{
GlAlert
}
from
'
@gitlab/ui
'
;
import
{
isEmpty
}
from
'
lodash
'
;
import
{
isEmpty
}
from
'
lodash
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
import
{
...
@@ -19,10 +18,8 @@ import LinksInner from './links_inner.vue';
...
@@ -19,10 +18,8 @@ import LinksInner from './links_inner.vue';
export
default
{
export
default
{
name
:
'
LinksLayer
'
,
name
:
'
LinksLayer
'
,
components
:
{
components
:
{
GlAlert
,
LinksInner
,
LinksInner
,
},
},
MAX_GROUPS
:
200
,
props
:
{
props
:
{
containerMeasurements
:
{
containerMeasurements
:
{
type
:
Object
,
type
:
Object
,
...
@@ -37,10 +34,10 @@ export default {
...
@@ -37,10 +34,10 @@ export default {
required
:
false
,
required
:
false
,
default
:
()
=>
({}),
default
:
()
=>
({}),
},
},
neverS
howLinks
:
{
s
howLinks
:
{
type
:
Boolean
,
type
:
Boolean
,
required
:
false
,
required
:
false
,
default
:
fals
e
,
default
:
tru
e
,
},
},
},
},
data
()
{
data
()
{
...
@@ -67,29 +64,8 @@ export default {
...
@@ -67,29 +64,8 @@ export default {
shouldCollectMetrics
()
{
shouldCollectMetrics
()
{
return
this
.
metricsConfig
.
collectMetrics
&&
this
.
metricsConfig
.
path
;
return
this
.
metricsConfig
.
collectMetrics
&&
this
.
metricsConfig
.
path
;
},
},
showAlert
()
{
/*
This is a hard override that allows us to turn off the links without
needing to remove the component entirely for iteration or based on graph type.
*/
if
(
this
.
neverShowLinks
)
{
return
false
;
}
return
!
this
.
containerZero
&&
!
this
.
showLinkedLayers
&&
!
this
.
alertDismissed
;
},
showLinkedLayers
()
{
showLinkedLayers
()
{
/*
return
this
.
showLinks
&&
!
this
.
containerZero
;
This is a hard override that allows us to turn off the links without
needing to remove the component entirely for iteration or based on graph type.
*/
if
(
this
.
neverShowLinks
)
{
return
false
;
}
return
(
!
this
.
containerZero
&&
(
this
.
showLinksOverride
||
this
.
numGroups
<
this
.
$options
.
MAX_GROUPS
)
);
},
},
},
},
errorCaptured
(
err
,
_vm
,
info
)
{
errorCaptured
(
err
,
_vm
,
info
)
{
...
@@ -103,7 +79,7 @@ export default {
...
@@ -103,7 +79,7 @@ export default {
is closed and functionality is enabled by default.
is closed and functionality is enabled by default.
*/
*/
if
(
this
.
neverS
howLinks
&&
!
isEmpty
(
this
.
pipelineData
))
{
if
(
!
this
.
s
howLinks
&&
!
isEmpty
(
this
.
pipelineData
))
{
window
.
requestAnimationFrame
(()
=>
{
window
.
requestAnimationFrame
(()
=>
{
this
.
prepareLinkData
();
this
.
prepareLinkData
();
});
});
...
@@ -151,13 +127,6 @@ export default {
...
@@ -151,13 +127,6 @@ export default {
reportPerformance
(
this
.
metricsConfig
.
path
,
data
);
reportPerformance
(
this
.
metricsConfig
.
path
,
data
);
});
});
},
},
dismissAlert
()
{
this
.
alertDismissed
=
true
;
},
overrideShowLinks
()
{
this
.
dismissAlert
();
this
.
showLinksOverride
=
true
;
},
prepareLinkData
()
{
prepareLinkData
()
{
this
.
beginPerfMeasure
();
this
.
beginPerfMeasure
();
let
numLinks
;
let
numLinks
;
...
@@ -185,15 +154,6 @@ export default {
...
@@ -185,15 +154,6 @@ export default {
<slot></slot>
<slot></slot>
</links-inner>
</links-inner>
<div
v-else
>
<div
v-else
>
<gl-alert
v-if=
"showAlert"
class=
"gl-ml-4 gl-mb-4"
:primary-button-text=
"$options.i18n.showLinksAnyways"
@
primaryAction=
"overrideShowLinks"
@
dismiss=
"dismissAlert"
>
{{
$options
.
i18n
.
tooManyJobs
}}
</gl-alert>
<div
class=
"gl-display-flex gl-relative"
>
<div
class=
"gl-display-flex gl-relative"
>
<slot></slot>
<slot></slot>
</div>
</div>
...
...
locale/gitlab.pot
View file @
c8431dcb
...
@@ -416,9 +416,6 @@ msgstr ""
...
@@ -416,9 +416,6 @@ msgstr ""
msgid "%{board_target} not found"
msgid "%{board_target} not found"
msgstr ""
msgstr ""
msgid "%{codeStart}needs:%{codeEnd} relationships"
msgstr ""
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
msgstr ""
...
@@ -15217,6 +15214,9 @@ msgstr ""
...
@@ -15217,6 +15214,9 @@ msgstr ""
msgid "Group is required when cluster_type is :group"
msgid "Group is required when cluster_type is :group"
msgstr ""
msgstr ""
msgid "Group jobs by"
msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
msgstr ""
...
@@ -18199,6 +18199,9 @@ msgstr ""
...
@@ -18199,6 +18199,9 @@ msgstr ""
msgid "Job artifacts"
msgid "Job artifacts"
msgstr ""
msgstr ""
msgid "Job dependencies"
msgstr ""
msgid "Job has been erased"
msgid "Job has been erased"
msgstr ""
msgstr ""
...
@@ -22502,9 +22505,6 @@ msgstr ""
...
@@ -22502,9 +22505,6 @@ msgstr ""
msgid "Or you can choose one of the suggested colors below"
msgid "Or you can choose one of the suggested colors below"
msgstr ""
msgstr ""
msgid "Order jobs by"
msgstr ""
msgid "Orphaned member"
msgid "Orphaned member"
msgstr ""
msgstr ""
...
@@ -29048,6 +29048,9 @@ msgstr ""
...
@@ -29048,6 +29048,9 @@ msgstr ""
msgid "Show complete raw log"
msgid "Show complete raw log"
msgstr ""
msgstr ""
msgid "Show dependencies"
msgstr ""
msgid "Show details"
msgid "Show details"
msgstr ""
msgstr ""
...
@@ -34816,9 +34819,6 @@ msgstr ""
...
@@ -34816,9 +34819,6 @@ msgstr ""
msgid "View the documentation"
msgid "View the documentation"
msgstr ""
msgstr ""
msgid "View the jobs grouped into stages"
msgstr ""
msgid "View the latest successful deployment to this environment"
msgid "View the latest successful deployment to this environment"
msgstr ""
msgstr ""
...
@@ -34831,9 +34831,6 @@ msgstr ""
...
@@ -34831,9 +34831,6 @@ msgstr ""
msgid "View users statistics"
msgid "View users statistics"
msgstr ""
msgstr ""
msgid "View what jobs are needed for a job to run"
msgstr ""
msgid "Viewed"
msgid "Viewed"
msgstr ""
msgstr ""
...
...
spec/frontend/pipelines/graph/graph_component_spec.js
View file @
c8431dcb
...
@@ -22,6 +22,7 @@ describe('graph component', () => {
...
@@ -22,6 +22,7 @@ describe('graph component', () => {
const
defaultProps
=
{
const
defaultProps
=
{
pipeline
:
generateResponse
(
mockPipelineResponse
,
'
root/fungi-xoxo
'
),
pipeline
:
generateResponse
(
mockPipelineResponse
,
'
root/fungi-xoxo
'
),
showLinks
:
false
,
viewType
:
STAGE_VIEW
,
viewType
:
STAGE_VIEW
,
configPaths
:
{
configPaths
:
{
metricsPath
:
''
,
metricsPath
:
''
,
...
...
spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
View file @
c8431dcb
...
@@ -15,6 +15,7 @@ import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
...
@@ -15,6 +15,7 @@ import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
import
PipelineGraphWrapper
from
'
~/pipelines/components/graph/graph_component_wrapper.vue
'
;
import
PipelineGraphWrapper
from
'
~/pipelines/components/graph/graph_component_wrapper.vue
'
;
import
GraphViewSelector
from
'
~/pipelines/components/graph/graph_view_selector.vue
'
;
import
GraphViewSelector
from
'
~/pipelines/components/graph/graph_view_selector.vue
'
;
import
StageColumnComponent
from
'
~/pipelines/components/graph/stage_column_component.vue
'
;
import
StageColumnComponent
from
'
~/pipelines/components/graph/stage_column_component.vue
'
;
import
LinksLayer
from
'
~/pipelines/components/graph_shared/links_layer.vue
'
;
import
*
as
parsingUtils
from
'
~/pipelines/components/parsing_utils
'
;
import
*
as
parsingUtils
from
'
~/pipelines/components/parsing_utils
'
;
import
{
mockPipelineResponse
}
from
'
./mock_data
'
;
import
{
mockPipelineResponse
}
from
'
./mock_data
'
;
...
@@ -31,7 +32,9 @@ describe('Pipeline graph wrapper', () => {
...
@@ -31,7 +32,9 @@ describe('Pipeline graph wrapper', () => {
let
wrapper
;
let
wrapper
;
const
getAlert
=
()
=>
wrapper
.
find
(
GlAlert
);
const
getAlert
=
()
=>
wrapper
.
find
(
GlAlert
);
const
getDependenciesToggle
=
()
=>
wrapper
.
find
(
'
[data-testid="show-links-toggle"]
'
);
const
getLoadingIcon
=
()
=>
wrapper
.
find
(
GlLoadingIcon
);
const
getLoadingIcon
=
()
=>
wrapper
.
find
(
GlLoadingIcon
);
const
getLinksLayer
=
()
=>
wrapper
.
findComponent
(
LinksLayer
);
const
getGraph
=
()
=>
wrapper
.
find
(
PipelineGraph
);
const
getGraph
=
()
=>
wrapper
.
find
(
PipelineGraph
);
const
getStageColumnTitle
=
()
=>
wrapper
.
find
(
'
[data-testid="stage-column-title"]
'
);
const
getStageColumnTitle
=
()
=>
wrapper
.
find
(
'
[data-testid="stage-column-title"]
'
);
const
getAllStageColumnGroupsInColumn
=
()
=>
const
getAllStageColumnGroupsInColumn
=
()
=>
...
@@ -59,6 +62,7 @@ describe('Pipeline graph wrapper', () => {
...
@@ -59,6 +62,7 @@ describe('Pipeline graph wrapper', () => {
};
};
const
createComponentWithApollo
=
({
const
createComponentWithApollo
=
({
data
=
{},
getPipelineDetailsHandler
=
jest
.
fn
().
mockResolvedValue
(
mockPipelineResponse
),
getPipelineDetailsHandler
=
jest
.
fn
().
mockResolvedValue
(
mockPipelineResponse
),
mountFn
=
shallowMount
,
mountFn
=
shallowMount
,
provide
=
{},
provide
=
{},
...
@@ -66,7 +70,7 @@ describe('Pipeline graph wrapper', () => {
...
@@ -66,7 +70,7 @@ describe('Pipeline graph wrapper', () => {
const
requestHandlers
=
[[
getPipelineDetails
,
getPipelineDetailsHandler
]];
const
requestHandlers
=
[[
getPipelineDetails
,
getPipelineDetailsHandler
]];
const
apolloProvider
=
createMockApollo
(
requestHandlers
);
const
apolloProvider
=
createMockApollo
(
requestHandlers
);
createComponent
({
apolloProvider
,
provide
,
mountFn
});
createComponent
({
apolloProvider
,
data
,
provide
,
mountFn
});
};
};
afterEach
(()
=>
{
afterEach
(()
=>
{
...
@@ -74,6 +78,15 @@ describe('Pipeline graph wrapper', () => {
...
@@ -74,6 +78,15 @@ describe('Pipeline graph wrapper', () => {
wrapper
=
null
;
wrapper
=
null
;
});
});
beforeAll
(()
=>
{
jest
.
useFakeTimers
();
});
afterAll
(()
=>
{
jest
.
runOnlyPendingTimers
();
jest
.
useRealTimers
();
});
describe
(
'
when data is loading
'
,
()
=>
{
describe
(
'
when data is loading
'
,
()
=>
{
it
(
'
displays the loading icon
'
,
()
=>
{
it
(
'
displays the loading icon
'
,
()
=>
{
createComponentWithApollo
();
createComponentWithApollo
();
...
@@ -282,6 +295,36 @@ describe('Pipeline graph wrapper', () => {
...
@@ -282,6 +295,36 @@ describe('Pipeline graph wrapper', () => {
});
});
});
});
describe
(
'
when pipelineGraphLayersView feature flag is on and layers view is selected
'
,
()
=>
{
beforeEach
(
async
()
=>
{
createComponentWithApollo
({
provide
:
{
glFeatures
:
{
pipelineGraphLayersView
:
true
,
},
},
data
:
{
currentViewType
:
LAYER_VIEW
,
},
mountFn
:
mount
,
});
jest
.
runOnlyPendingTimers
();
await
wrapper
.
vm
.
$nextTick
();
});
it
(
'
sets showLinks to true
'
,
async
()
=>
{
/* This spec uses .props for performance reasons. */
expect
(
getLinksLayer
().
exists
()).
toBe
(
true
);
expect
(
getLinksLayer
().
props
(
'
showLinks
'
)).
toBe
(
false
);
expect
(
getViewSelector
().
props
(
'
type
'
)).
toBe
(
LAYER_VIEW
);
await
getDependenciesToggle
().
trigger
(
'
click
'
);
jest
.
runOnlyPendingTimers
();
await
wrapper
.
vm
.
$nextTick
();
expect
(
wrapper
.
findComponent
(
LinksLayer
).
props
(
'
showLinks
'
)).
toBe
(
true
);
});
});
describe
(
'
when feature flag is on and local storage is set
'
,
()
=>
{
describe
(
'
when feature flag is on and local storage is set
'
,
()
=>
{
beforeEach
(
async
()
=>
{
beforeEach
(
async
()
=>
{
localStorage
.
setItem
(
VIEW_TYPE_KEY
,
LAYER_VIEW
);
localStorage
.
setItem
(
VIEW_TYPE_KEY
,
LAYER_VIEW
);
...
@@ -299,10 +342,15 @@ describe('Pipeline graph wrapper', () => {
...
@@ -299,10 +342,15 @@ describe('Pipeline graph wrapper', () => {
await
wrapper
.
vm
.
$nextTick
();
await
wrapper
.
vm
.
$nextTick
();
});
});
afterEach
(()
=>
{
localStorage
.
clear
();
});
it
(
'
reads the view type from localStorage when available
'
,
()
=>
{
it
(
'
reads the view type from localStorage when available
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
[data-testid="pipeline-view-selector"] code
'
).
text
()).
toContain
(
const
viewSelectorNeedsSegment
=
wrapper
'
needs:
'
,
.
findAll
(
'
[data-testid="pipeline-view-selector"] > label
'
)
);
.
at
(
1
);
expect
(
viewSelectorNeedsSegment
.
classes
()).
toContain
(
'
active
'
);
});
});
});
});
...
...
spec/frontend/pipelines/graph/graph_view_selector_spec.js
0 → 100644
View file @
c8431dcb
import
{
GlLoadingIcon
,
GlSegmentedControl
}
from
'
@gitlab/ui
'
;
import
{
mount
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
LAYER_VIEW
,
STAGE_VIEW
}
from
'
~/pipelines/components/graph/constants
'
;
import
GraphViewSelector
from
'
~/pipelines/components/graph/graph_view_selector.vue
'
;
describe
(
'
the graph view selector component
'
,
()
=>
{
let
wrapper
;
const
findDependenciesToggle
=
()
=>
wrapper
.
find
(
'
[data-testid="show-links-toggle"]
'
);
const
findViewTypeSelector
=
()
=>
wrapper
.
findComponent
(
GlSegmentedControl
);
const
findStageViewLabel
=
()
=>
findViewTypeSelector
().
findAll
(
'
label
'
).
at
(
0
);
const
findLayersViewLabel
=
()
=>
findViewTypeSelector
().
findAll
(
'
label
'
).
at
(
1
);
const
findSwitcherLoader
=
()
=>
wrapper
.
find
(
'
[data-testid="switcher-loading-state"]
'
);
const
findToggleLoader
=
()
=>
findDependenciesToggle
().
find
(
GlLoadingIcon
);
const
defaultProps
=
{
showLinks
:
false
,
type
:
STAGE_VIEW
,
};
const
defaultData
=
{
showLinksActive
:
false
,
isToggleLoading
:
false
,
isSwitcherLoading
:
false
,
};
const
createComponent
=
({
data
=
{},
mountFn
=
shallowMount
,
props
=
{}
}
=
{})
=>
{
wrapper
=
mountFn
(
GraphViewSelector
,
{
propsData
:
{
...
defaultProps
,
...
props
,
},
data
()
{
return
{
...
defaultData
,
...
data
,
};
},
});
};
afterEach
(()
=>
{
wrapper
.
destroy
();
});
describe
(
'
when showing stage view
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
mountFn
:
mount
});
});
it
(
'
shows the Stage view label as active in the selector
'
,
()
=>
{
expect
(
findStageViewLabel
().
classes
()).
toContain
(
'
active
'
);
});
it
(
'
does not show the Job dependencies (links) toggle
'
,
()
=>
{
expect
(
findDependenciesToggle
().
exists
()).
toBe
(
false
);
});
});
describe
(
'
when showing Job dependencies view
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
mountFn
:
mount
,
props
:
{
type
:
LAYER_VIEW
,
},
});
});
it
(
'
shows the Job dependencies view label as active in the selector
'
,
()
=>
{
expect
(
findLayersViewLabel
().
classes
()).
toContain
(
'
active
'
);
});
it
(
'
shows the Job dependencies (links) toggle
'
,
()
=>
{
expect
(
findDependenciesToggle
().
exists
()).
toBe
(
true
);
});
});
describe
(
'
events
'
,
()
=>
{
beforeEach
(()
=>
{
jest
.
useFakeTimers
();
createComponent
({
mountFn
:
mount
,
props
:
{
type
:
LAYER_VIEW
,
},
});
});
it
(
'
shows loading state and emits updateViewType when view type toggled
'
,
async
()
=>
{
expect
(
wrapper
.
emitted
().
updateViewType
).
toBeUndefined
();
expect
(
findSwitcherLoader
().
exists
()).
toBe
(
false
);
await
findStageViewLabel
().
trigger
(
'
click
'
);
/*
Loading happens before the event is emitted or timers are run.
Then we run the timer because the event is emitted in setInterval
which is what gives the loader a chace to show up.
*/
expect
(
findSwitcherLoader
().
exists
()).
toBe
(
true
);
jest
.
runOnlyPendingTimers
();
expect
(
wrapper
.
emitted
().
updateViewType
).
toHaveLength
(
1
);
expect
(
wrapper
.
emitted
().
updateViewType
).
toEqual
([[
STAGE_VIEW
]]);
});
it
(
'
shows loading state and emits updateShowLinks when show links toggle is clicked
'
,
async
()
=>
{
expect
(
wrapper
.
emitted
().
updateShowLinksState
).
toBeUndefined
();
expect
(
findToggleLoader
().
exists
()).
toBe
(
false
);
await
findDependenciesToggle
().
trigger
(
'
click
'
);
/*
Loading happens before the event is emitted or timers are run.
Then we run the timer because the event is emitted in setInterval
which is what gives the loader a chace to show up.
*/
expect
(
findToggleLoader
().
exists
()).
toBe
(
true
);
jest
.
runOnlyPendingTimers
();
expect
(
wrapper
.
emitted
().
updateShowLinksState
).
toHaveLength
(
1
);
expect
(
wrapper
.
emitted
().
updateShowLinksState
).
toEqual
([[
true
]]);
});
});
});
spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
View file @
c8431dcb
...
@@ -26,6 +26,7 @@ describe('Linked Pipelines Column', () => {
...
@@ -26,6 +26,7 @@ describe('Linked Pipelines Column', () => {
const
defaultProps
=
{
const
defaultProps
=
{
columnTitle
:
'
Downstream
'
,
columnTitle
:
'
Downstream
'
,
linkedPipelines
:
processedPipeline
.
downstream
,
linkedPipelines
:
processedPipeline
.
downstream
,
showLinks
:
false
,
type
:
DOWNSTREAM
,
type
:
DOWNSTREAM
,
viewType
:
STAGE_VIEW
,
viewType
:
STAGE_VIEW
,
configPaths
:
{
configPaths
:
{
...
...
spec/frontend/pipelines/graph_shared/links_layer_spec.js
View file @
c8431dcb
import
{
GlAlert
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
fireEvent
,
within
}
from
'
@testing-library/dom
'
;
import
{
mount
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
LinksInner
from
'
~/pipelines/components/graph_shared/links_inner.vue
'
;
import
LinksInner
from
'
~/pipelines/components/graph_shared/links_inner.vue
'
;
import
LinksLayer
from
'
~/pipelines/components/graph_shared/links_layer.vue
'
;
import
LinksLayer
from
'
~/pipelines/components/graph_shared/links_layer.vue
'
;
import
{
generateResponse
,
mockPipelineResponse
}
from
'
../graph/mock_data
'
;
import
{
generateResponse
,
mockPipelineResponse
}
from
'
../graph/mock_data
'
;
...
@@ -8,25 +6,18 @@ import { generateResponse, mockPipelineResponse } from '../graph/mock_data';
...
@@ -8,25 +6,18 @@ import { generateResponse, mockPipelineResponse } from '../graph/mock_data';
describe
(
'
links layer component
'
,
()
=>
{
describe
(
'
links layer component
'
,
()
=>
{
let
wrapper
;
let
wrapper
;
const
withinComponent
=
()
=>
within
(
wrapper
.
element
);
const
findAlert
=
()
=>
wrapper
.
find
(
GlAlert
);
const
findShowAnyways
=
()
=>
withinComponent
().
getByText
(
wrapper
.
vm
.
$options
.
i18n
.
showLinksAnyways
);
const
findLinksInner
=
()
=>
wrapper
.
find
(
LinksInner
);
const
findLinksInner
=
()
=>
wrapper
.
find
(
LinksInner
);
const
pipeline
=
generateResponse
(
mockPipelineResponse
,
'
root/fungi-xoxo
'
);
const
pipeline
=
generateResponse
(
mockPipelineResponse
,
'
root/fungi-xoxo
'
);
const
containerId
=
`pipeline-links-container-
${
pipeline
.
id
}
`
;
const
containerId
=
`pipeline-links-container-
${
pipeline
.
id
}
`
;
const
slotContent
=
"
<div>Ceci n'est pas un graphique</div>
"
;
const
slotContent
=
"
<div>Ceci n'est pas un graphique</div>
"
;
const
tooManyStages
=
Array
(
101
)
.
fill
(
0
)
.
flatMap
(()
=>
pipeline
.
stages
);
const
defaultProps
=
{
const
defaultProps
=
{
containerId
,
containerId
,
containerMeasurements
:
{
width
:
400
,
height
:
400
},
containerMeasurements
:
{
width
:
400
,
height
:
400
},
pipelineId
:
pipeline
.
id
,
pipelineId
:
pipeline
.
id
,
pipelineData
:
pipeline
.
stages
,
pipelineData
:
pipeline
.
stages
,
showLinks
:
false
,
};
};
const
createComponent
=
({
mountFn
=
shallowMount
,
props
=
{}
}
=
{})
=>
{
const
createComponent
=
({
mountFn
=
shallowMount
,
props
=
{}
}
=
{})
=>
{
...
@@ -49,7 +40,7 @@ describe('links layer component', () => {
...
@@ -49,7 +40,7 @@ describe('links layer component', () => {
wrapper
=
null
;
wrapper
=
null
;
});
});
describe
(
'
with
data under max stages
'
,
()
=>
{
describe
(
'
with
show links off
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
createComponent
();
createComponent
();
});
});
...
@@ -58,63 +49,40 @@ describe('links layer component', () => {
...
@@ -58,63 +49,40 @@ describe('links layer component', () => {
expect
(
wrapper
.
html
()).
toContain
(
slotContent
);
expect
(
wrapper
.
html
()).
toContain
(
slotContent
);
});
});
it
(
'
renders the
inner links component
'
,
()
=>
{
it
(
'
does not render
inner links component
'
,
()
=>
{
expect
(
findLinksInner
().
exists
()).
toBe
(
tru
e
);
expect
(
findLinksInner
().
exists
()).
toBe
(
fals
e
);
});
});
});
});
describe
(
'
with more than the max number of stages
'
,
()
=>
{
describe
(
'
with show links on
'
,
()
=>
{
describe
(
'
rendering
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
createComponent
({
createComponent
({
props
:
{
pipelineData
:
tooManyStages
}
});
props
:
{
});
showLinks
:
true
,
},
it
(
'
renders the default slot
'
,
()
=>
{
expect
(
wrapper
.
html
()).
toContain
(
slotContent
);
});
it
(
'
renders the alert component
'
,
()
=>
{
expect
(
findAlert
().
exists
()).
toBe
(
true
);
});
it
(
'
does not render the inner links component
'
,
()
=>
{
expect
(
findLinksInner
().
exists
()).
toBe
(
false
);
});
});
});
});
describe
(
'
with width or height measurement at 0
'
,
()
=>
{
it
(
'
renders the default slot
'
,
()
=>
{
beforeEach
(()
=>
{
expect
(
wrapper
.
html
()).
toContain
(
slotContent
);
createComponent
({
props
:
{
containerMeasurements
:
{
width
:
0
,
height
:
100
}
}
});
});
});
it
(
'
renders the default slot
'
,
()
=>
{
expect
(
wrapper
.
html
()).
toContain
(
slotContent
);
});
it
(
'
does not render the alert component
'
,
()
=>
{
expect
(
findAlert
().
exists
()).
toBe
(
false
);
});
it
(
'
does not render the inner links component
'
,
()
=>
{
it
(
'
renders the inner links component
'
,
()
=>
{
expect
(
findLinksInner
().
exists
()).
toBe
(
false
);
expect
(
findLinksInner
().
exists
()).
toBe
(
true
);
});
});
});
});
describe
(
'
interactions
'
,
()
=>
{
describe
(
'
with width or height measurement at 0
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
createComponent
({
mountFn
:
mount
,
props
:
{
pipelineData
:
tooManyStages
}
});
createComponent
({
props
:
{
containerMeasurements
:
{
width
:
0
,
height
:
100
}
}
});
});
});
it
(
'
renders the disable button
'
,
()
=>
{
it
(
'
renders the default slot
'
,
()
=>
{
expect
(
findShowAnyways
()).
not
.
toBe
(
null
);
expect
(
wrapper
.
html
()).
toContain
(
slotContent
);
});
});
it
(
'
shows links when override is clicked
'
,
async
()
=>
{
it
(
'
does not render the inner links component
'
,
()
=>
{
expect
(
findLinksInner
().
exists
()).
toBe
(
false
);
expect
(
findLinksInner
().
exists
()).
toBe
(
false
);
fireEvent
(
findShowAnyways
(),
new
MouseEvent
(
'
click
'
,
{
bubbles
:
true
}));
await
wrapper
.
vm
.
$nextTick
();
expect
(
findLinksInner
().
exists
()).
toBe
(
true
);
});
});
});
});
});
});
});
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