BigW Consortium Gitlab
Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
gitlab-ce
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Forest Godfrey
gitlab-ce
Commits
780eead7
Unverified
Commit
780eead7
authored
May 18, 2018
by
Filipa Lacerda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Backport of Resolve "Dropdown actions in mini pipeline graph in mr widget don't work"
parent
82fbbe45
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
132 additions
and
117 deletions
+132
-117
action_component.vue
...vascripts/pipelines/components/graph/action_component.vue
+28
-22
dropdown_job_component.vue
...pts/pipelines/components/graph/dropdown_job_component.vue
+6
-7
graph_component.vue
...avascripts/pipelines/components/graph/graph_component.vue
+5
-6
job_component.vue
.../javascripts/pipelines/components/graph/job_component.vue
+6
-6
stage_column_component.vue
...pts/pipelines/components/graph/stage_column_component.vue
+6
-7
pipelines_table_row.vue
.../javascripts/pipelines/components/pipelines_table_row.vue
+3
-0
stage.vue
app/assets/javascripts/pipelines/components/stage.vue
+19
-0
constants.js
app/assets/javascripts/pipelines/constants.js
+1
-1
pipelines.js
app/assets/javascripts/pipelines/mixins/pipelines.js
+2
-0
pipeline_details_bundle.js
app/assets/javascripts/pipelines/pipeline_details_bundle.js
+8
-22
action_component_spec.js
spec/javascripts/pipelines/graph/action_component_spec.js
+21
-46
stage_spec.js
spec/javascripts/pipelines/stage_spec.js
+27
-0
No files found.
app/assets/javascripts/pipelines/components/graph/action_component.vue
View file @
780eead7
<
script
>
import
$
from
'jquery'
;
import
tooltip
from
'../../../vue_shared/directives/tooltip'
;
import
Icon
from
'../../../vue_shared/components/icon.vue'
;
import
{
dasherize
}
from
'../../../lib/utils/text_utility'
;
import
eventHub
from
'../../event_hub'
;
import
axios
from
'~/lib/utils/axios_utils'
;
import
{
dasherize
}
from
'~/lib/utils/text_utility'
;
import
{
__
}
from
'~/locale'
;
import
createFlash
from
'~/flash'
;
import
tooltip
from
'~/vue_shared/directives/tooltip'
;
import
Icon
from
'~/vue_shared/components/icon.vue'
;
/**
* Renders either a cancel, retry or play icon pointing to the given path.
* Renders either a cancel, retry or play icon button and handles the post request
*
* Used in:
* - mr widget mini pipeline graph: `mr_widget_pipeline.vue`
* - pipelines table
* - pipelines table in merge request page
* - pipelines table in commit page
* - pipelines detail page in big gra
*/
export
default
{
components
:
{
...
...
@@ -31,17 +41,10 @@ export default {
type
:
String
,
required
:
true
,
},
requestFinishedFor
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
},
data
()
{
return
{
isDisabled
:
false
,
linkRequested
:
''
,
};
},
...
...
@@ -51,19 +54,22 @@ export default {
return
`
${
actionIconDash
}
js-icon-
${
actionIconDash
}
`
;
},
},
watch
:
{
requestFinishedFor
()
{
if
(
this
.
requestFinishedFor
===
this
.
linkRequested
)
{
this
.
isDisabled
=
false
;
}
},
},
methods
:
{
onClickAction
()
{
$
(
this
.
$el
).
tooltip
(
'hide'
);
eventHub
.
$emit
(
'postAction'
,
this
.
link
);
this
.
linkRequested
=
this
.
link
;
this
.
isDisabled
=
true
;
axios
.
post
(
`
${
this
.
link
}
.json`
)
.
then
(()
=>
{
this
.
isDisabled
=
false
;
this
.
$emit
(
'pipelineActionRequestComplete'
);
})
.
catch
(()
=>
{
this
.
isDisabled
=
false
;
createFlash
(
__
(
'An error occurred while making the request.'
));
});
},
},
};
...
...
@@ -80,6 +86,6 @@ btn-transparent ci-action-icon-container ci-action-icon-wrapper"
data-container=
"body"
:disabled=
"isDisabled"
>
<icon
:name=
"actionIcon"
/>
<icon
:name=
"actionIcon"
/>
</button>
</
template
>
app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue
View file @
780eead7
...
...
@@ -42,11 +42,6 @@ export default {
type
:
Object
,
required
:
true
,
},
requestFinishedFor
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
},
computed
:
{
...
...
@@ -76,11 +71,15 @@ export default {
e
.
stopPropagation
();
});
},
pipelineActionRequestComplete
()
{
this
.
$emit
(
'pipelineActionRequestComplete'
);
},
},
};
</
script
>
<
template
>
<div
class=
"ci-job-dropdown-container"
>
<div
class=
"ci-job-dropdown-container
dropdown
"
>
<button
v-tooltip
type=
"button"
...
...
@@ -110,7 +109,7 @@ export default {
<job-component
:job=
"item"
css-class-job-name=
"mini-pipeline-graph-dropdown-item"
:request-finished-for=
"requestFinishedFor
"
@
pipelineActionRequestComplete=
"pipelineActionRequestComplete
"
/>
</li>
</ul>
...
...
app/assets/javascripts/pipelines/components/graph/graph_component.vue
View file @
780eead7
...
...
@@ -16,11 +16,6 @@ export default {
type
:
Object
,
required
:
true
,
},
requestFinishedFor
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
},
computed
:
{
...
...
@@ -51,6 +46,10 @@ export default {
return
className
;
},
refreshPipelineGraph
()
{
this
.
$emit
(
'refreshPipelineGraph'
);
},
},
};
</
script
>
...
...
@@ -74,7 +73,7 @@ export default {
:key=
"stage.name"
:stage-connector-class=
"stageConnectorClass(index, stage)"
:is-first-column=
"isFirstColumn(index)"
:request-finished-for=
"requestFinishedFor
"
@
refreshPipelineGraph=
"refreshPipelineGraph
"
/>
</ul>
</div>
...
...
app/assets/javascripts/pipelines/components/graph/job_component.vue
View file @
780eead7
...
...
@@ -46,11 +46,6 @@ export default {
required
:
false
,
default
:
''
,
},
requestFinishedFor
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
},
computed
:
{
status
()
{
...
...
@@ -84,6 +79,11 @@ export default {
return
this
.
job
.
status
&&
this
.
job
.
status
.
action
&&
this
.
job
.
status
.
action
.
path
;
},
},
methods
:
{
pipelineActionRequestComplete
()
{
this
.
$emit
(
'pipelineActionRequestComplete'
);
},
},
};
</
script
>
<
template
>
...
...
@@ -126,7 +126,7 @@ export default {
:tooltip-text=
"status.action.title"
:link=
"status.action.path"
:action-icon=
"status.action.icon"
:request-finished-for=
"requestFinishedFor
"
@
pipelineActionRequestComplete=
"pipelineActionRequestComplete
"
/>
</div>
</
template
>
app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
View file @
780eead7
...
...
@@ -29,12 +29,6 @@ export default {
required
:
false
,
default
:
''
,
},
requestFinishedFor
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
},
methods
:
{
...
...
@@ -49,6 +43,10 @@ export default {
buildConnnectorClass
(
index
)
{
return
index
===
0
&&
!
this
.
isFirstColumn
?
'left-connector'
:
''
;
},
pipelineActionRequestComplete
()
{
this
.
$emit
(
'refreshPipelineGraph'
);
},
},
};
</
script
>
...
...
@@ -75,12 +73,13 @@ export default {
v-if=
"job.size === 1"
:job=
"job"
css-class-job-name=
"build-content"
@
pipelineActionRequestComplete=
"pipelineActionRequestComplete"
/>
<dropdown-job-component
v-if=
"job.size > 1"
:job=
"job"
:request-finished-for=
"requestFinishedFor
"
@
pipelineActionRequestComplete=
"pipelineActionRequestComplete
"
/>
</li>
...
...
app/assets/javascripts/pipelines/components/pipelines_table_row.vue
View file @
780eead7
...
...
@@ -8,6 +8,7 @@
import
pipelineUrl
from
'./pipeline_url.vue'
;
import
pipelinesTimeago
from
'./time_ago.vue'
;
import
commitComponent
from
'../../vue_shared/components/commit.vue'
;
import
{
PIPELINES_TABLE
}
from
'../constants'
;
/**
* Pipeline table row.
...
...
@@ -44,6 +45,7 @@
required
:
true
,
},
},
pipelinesTable
:
PIPELINES_TABLE
,
computed
:
{
/**
* If provided, returns the commit tag.
...
...
@@ -272,6 +274,7 @@
v-for=
"(stage, index) in pipeline.details.stages"
:key=
"index"
>
<pipeline-stage
:type=
"$options.pipelinesTable"
:stage=
"stage"
:update-dropdown=
"updateGraphDropdown"
/>
...
...
app/assets/javascripts/pipelines/components/stage.vue
View file @
780eead7
...
...
@@ -21,6 +21,7 @@ import Icon from '../../vue_shared/components/icon.vue';
import
LoadingIcon
from
'../../vue_shared/components/loading_icon.vue'
;
import
JobComponent
from
'./graph/job_component.vue'
;
import
tooltip
from
'../../vue_shared/directives/tooltip'
;
import
{
PIPELINES_TABLE
}
from
'../constants'
;
export
default
{
components
:
{
...
...
@@ -44,6 +45,12 @@ export default {
required
:
false
,
default
:
false
,
},
type
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
},
data
()
{
...
...
@@ -133,6 +140,16 @@ export default {
isDropdownOpen
()
{
return
this
.
$el
.
classList
.
contains
(
'open'
);
},
pipelineActionRequestComplete
()
{
if
(
this
.
type
===
PIPELINES_TABLE
)
{
// warn the table to update
eventHub
.
$emit
(
'refreshPipelinesTable'
);
}
else
{
// close the dropdown in mr widget
$
(
this
.
$refs
.
dropdown
).
dropdown
(
'toggle'
);
}
},
},
};
</
script
>
...
...
@@ -151,6 +168,7 @@ export default {
id=
"stageDropdown"
aria-haspopup=
"true"
aria-expanded=
"false"
ref=
"dropdown"
>
<span
...
...
@@ -188,6 +206,7 @@ export default {
<job-component
:job=
"job"
css-class-job-name=
"mini-pipeline-graph-dropdown-item"
@
pipelineActionRequestComplete=
"pipelineActionRequestComplete"
/>
</li>
</ul>
...
...
app/assets/javascripts/pipelines/constants.js
View file @
780eead7
// eslint-disable-next-line import/prefer-default-export
export
const
CANCEL_REQUEST
=
'CANCEL_REQUEST'
;
export
const
PIPELINES_TABLE
=
'PIPELINES_TABLE'
;
app/assets/javascripts/pipelines/mixins/pipelines.js
View file @
780eead7
...
...
@@ -54,10 +54,12 @@ export default {
eventHub
.
$on
(
'postAction'
,
this
.
postAction
);
eventHub
.
$on
(
'clickedDropdown'
,
this
.
updateTable
);
eventHub
.
$on
(
'refreshPipelinesTable'
,
this
.
fetchPipelines
);
},
beforeDestroy
()
{
eventHub
.
$off
(
'postAction'
,
this
.
postAction
);
eventHub
.
$off
(
'clickedDropdown'
,
this
.
updateTable
);
eventHub
.
$off
(
'refreshPipelinesTable'
,
this
.
fetchPipelines
);
},
destroyed
()
{
this
.
poll
.
stop
();
...
...
app/assets/javascripts/pipelines/pipeline_details_bundle.js
View file @
780eead7
...
...
@@ -25,30 +25,14 @@ export default () => {
data
()
{
return
{
mediator
,
requestFinishedFor
:
null
,
};
},
created
()
{
eventHub
.
$on
(
'postAction'
,
this
.
postAction
);
},
beforeDestroy
()
{
eventHub
.
$off
(
'postAction'
,
this
.
postAction
);
},
methods
:
{
postAction
(
action
)
{
// Click was made, reset this variable
this
.
requestFinishedFor
=
null
;
this
.
mediator
.
service
.
postAction
(
action
)
.
then
(()
=>
{
this
.
mediator
.
refreshPipeline
();
this
.
requestFinishedFor
=
action
;
})
.
catch
(()
=>
{
this
.
requestFinishedFor
=
action
;
Flash
(
__
(
'An error occurred while making the request.'
));
});
requestRefreshPipelineGraph
()
{
// When an action is clicked
// (wether in the dropdown or in the main nodes, we refresh the big graph)
this
.
mediator
.
refreshPipeline
()
.
catch
(()
=>
Flash
(
__
(
'An error occurred while making the request.'
)));
},
},
render
(
createElement
)
{
...
...
@@ -56,7 +40,9 @@ export default () => {
props
:
{
isLoading
:
this
.
mediator
.
state
.
isLoading
,
pipeline
:
this
.
mediator
.
store
.
state
.
pipeline
,
requestFinishedFor
:
this
.
requestFinishedFor
,
},
on
:
{
refreshPipelineGraph
:
this
.
requestRefreshPipelineGraph
,
},
});
},
...
...
spec/javascripts/pipelines/graph/action_component_spec.js
View file @
780eead7
import
Vue
from
'vue'
;
import
actionComponent
from
'~/pipelines/components/graph/action_component.vue'
;
import
eventHub
from
'~/pipelines/event_hub'
;
import
MockAdapter
from
'axios-mock-adapter'
;
import
axios
from
'~/lib/utils/axios_utils'
;
import
mountComponent
from
'../../helpers/vue_mount_component_helper'
;
describe
(
'pipeline graph action component'
,
()
=>
{
let
component
;
let
mock
;
beforeEach
(
done
=>
{
const
ActionComponent
=
Vue
.
extend
(
actionComponent
);
mock
=
new
MockAdapter
(
axios
);
mock
.
onPost
(
'foo.json'
).
reply
(
200
);
component
=
mountComponent
(
ActionComponent
,
{
tooltipText
:
'bar'
,
link
:
'foo'
,
...
...
@@ -18,15 +24,10 @@ describe('pipeline graph action component', () => {
});
afterEach
(()
=>
{
mock
.
restore
();
component
.
$destroy
();
});
it
(
'should emit an event with the provided link'
,
()
=>
{
eventHub
.
$on
(
'postAction'
,
link
=>
{
expect
(
link
).
toEqual
(
'foo'
);
});
});
it
(
'should render the provided title as a bootstrap tooltip'
,
()
=>
{
expect
(
component
.
$el
.
getAttribute
(
'data-original-title'
)).
toEqual
(
'bar'
);
});
...
...
@@ -34,55 +35,29 @@ describe('pipeline graph action component', () => {
it
(
'should update bootstrap tooltip when title changes'
,
done
=>
{
component
.
tooltipText
=
'changed'
;
setTimeout
(()
=>
{
expect
(
component
.
$el
.
getAttribute
(
'data-original-title'
)).
toBe
(
'changed'
);
done
();
});
});
it
(
'should render an svg'
,
()
=>
{
expect
(
component
.
$el
.
querySelector
(
'.ci-action-icon-wrapper'
)).
toBeDefined
();
expect
(
component
.
$el
.
querySelector
(
'svg'
)).
toBeDefined
();
});
it
(
'disables the button when clicked'
,
done
=>
{
component
.
$el
.
click
();
component
.
$nextTick
(()
=>
{
expect
(
component
.
$el
.
getAttribute
(
'disabled'
)).
toEqual
(
'disabled'
);
done
();
});
});
it
(
're-enabled the button when `requestFinishedFor` matches `linkRequested`'
,
done
=>
{
component
.
$el
.
click
();
component
.
$nextTick
()
component
.
$nextTick
()
.
then
(()
=>
{
expect
(
component
.
$el
.
getAttribute
(
'disabled'
)).
toEqual
(
'disabled'
);
component
.
requestFinishedFor
=
'foo'
;
})
.
then
(()
=>
{
expect
(
component
.
$el
.
getAttribute
(
'disabled'
)).
toBeNull
();
expect
(
component
.
$el
.
getAttribute
(
'data-original-title'
)).
toBe
(
'changed'
);
})
.
then
(
done
)
.
catch
(
done
.
fail
);
});
it
(
'does not re-enable the button when `requestFinishedFor` does not matches `linkRequested`'
,
done
=>
{
component
.
$el
.
click
();
it
(
'should render an svg'
,
()
=>
{
expect
(
component
.
$el
.
querySelector
(
'.ci-action-icon-wrapper'
)).
toBeDefined
();
expect
(
component
.
$el
.
querySelector
(
'svg'
)).
toBeDefined
();
});
component
.
$nextTick
()
describe
(
'on click'
,
()
=>
{
it
(
'emits `pipelineActionRequestComplete` after a successfull request'
,
done
=>
{
spyOn
(
component
,
'$emit'
);
component
.
$el
.
click
();
component
.
$nextTick
()
.
then
(()
=>
{
expect
(
component
.
$el
.
getAttribute
(
'disabled'
)).
toEqual
(
'disabled'
);
component
.
requestFinishedFor
=
'bar'
;
})
.
then
(()
=>
{
expect
(
component
.
$el
.
getAttribute
(
'disabled'
)).
toEqual
(
'disabled'
);
expect
(
component
.
$emit
).
toHaveBeenCalledWith
(
'pipelineActionRequestComplete'
);
})
.
then
(
done
)
.
catch
(
done
.
fail
);
});
});
});
spec/javascripts/pipelines/stage_spec.js
View file @
780eead7
...
...
@@ -102,4 +102,31 @@ describe('Pipelines stage component', () => {
});
});
});
describe
(
'pipelineActionRequestComplete'
,
()
=>
{
beforeEach
(()
=>
{
mock
.
onGet
(
'path.json'
).
reply
(
200
,
stageReply
);
mock
.
onPost
(
`
${
stageReply
.
latest_statuses
[
0
].
status
.
action
.
path
}
.json`
).
reply
(
200
);
});
describe
(
'within pipeline table'
,
()
=>
{
it
(
'emits `refreshPipelinesTable` event when `pipelineActionRequestComplete` is triggered'
,
done
=>
{
spyOn
(
eventHub
,
'$emit'
);
component
.
type
=
'PIPELINES_TABLE'
;
component
.
$el
.
querySelector
(
'button'
).
click
();
setTimeout
(()
=>
{
component
.
$el
.
querySelector
(
'.js-ci-action'
).
click
();
component
.
$nextTick
()
.
then
(()
=>
{
expect
(
eventHub
.
$emit
).
toHaveBeenCalledWith
(
'refreshPipelinesTable'
);
})
.
then
(
done
)
.
catch
(
done
.
fail
);
},
0
);
});
});
});
});
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