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
d68436d9
Commit
d68436d9
authored
Nov 14, 2017
by
Clement Ho
Committed by
Filipa Lacerda
Nov 14, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Delete epic
parent
cef76488
Changes
23
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
353 additions
and
43 deletions
+353
-43
app/assets/javascripts/issue_show/components/app.vue
app/assets/javascripts/issue_show/components/app.vue
+14
-3
app/assets/javascripts/issue_show/components/edit_actions.vue
...assets/javascripts/issue_show/components/edit_actions.vue
+9
-1
app/assets/javascripts/issue_show/components/form.vue
app/assets/javascripts/issue_show/components/form.vue
+7
-1
app/assets/javascripts/vue_shared/components/loading_button.vue
...sets/javascripts/vue_shared/components/loading_button.vue
+6
-1
app/assets/stylesheets/framework/blocks.scss
app/assets/stylesheets/framework/blocks.scss
+4
-0
app/services/users/migrate_to_ghost_user_service.rb
app/services/users/migrate_to_ghost_user_service.rb
+1
-0
changelogs/unreleased-ee/delete-epic.yml
changelogs/unreleased-ee/delete-epic.yml
+5
-0
ee/app/assets/javascripts/epics/epic_show/components/epic_header.vue
...ts/javascripts/epics/epic_show/components/epic_header.vue
+33
-7
ee/app/assets/javascripts/epics/epic_show/components/epic_show_app.vue
.../javascripts/epics/epic_show/components/epic_show_app.vue
+29
-7
ee/app/assets/javascripts/epics/epic_show/epic_show_bundle.js
...pp/assets/javascripts/epics/epic_show/epic_show_bundle.js
+1
-5
ee/app/policies/ee/group_policy.rb
ee/app/policies/ee/group_policy.rb
+3
-1
ee/app/services/ee/users/migrate_to_ghost_user_service.rb
ee/app/services/ee/users/migrate_to_ghost_user_service.rb
+17
-0
spec/ee/spec/controllers/groups/epics_controller_spec.rb
spec/ee/spec/controllers/groups/epics_controller_spec.rb
+23
-2
spec/ee/spec/features/epics/delete_epic_spec.rb
spec/ee/spec/features/epics/delete_epic_spec.rb
+39
-0
spec/ee/spec/policies/epic_policy_spec.rb
spec/ee/spec/policies/epic_policy_spec.rb
+24
-0
spec/ee/spec/services/ee/users/migrate_to_ghost_user_service_spec.rb
...c/services/ee/users/migrate_to_ghost_user_service_spec.rb
+22
-0
spec/javascripts/epics/epic_show/components/epic_header_spec.js
...avascripts/epics/epic_show/components/epic_header_spec.js
+38
-0
spec/javascripts/epics/epic_show/components/epic_show_app_spec.js
...ascripts/epics/epic_show/components/epic_show_app_spec.js
+13
-0
spec/javascripts/epics/epic_show/mock_data.js
spec/javascripts/epics/epic_show/mock_data.js
+1
-1
spec/javascripts/issue_show/components/app_spec.js
spec/javascripts/issue_show/components/app_spec.js
+37
-14
spec/javascripts/issue_show/components/edit_actions_spec.js
spec/javascripts/issue_show/components/edit_actions_spec.js
+9
-0
spec/javascripts/vue_shared/components/loading_button_spec.js
.../javascripts/vue_shared/components/loading_button_spec.js
+17
-0
spec/policies/group_policy_spec.rb
spec/policies/group_policy_spec.rb
+1
-0
No files found.
app/assets/javascripts/issue_show/components/app.vue
View file @
d68436d9
...
@@ -29,6 +29,11 @@ export default {
...
@@ -29,6 +29,11 @@ export default {
required
:
false
,
required
:
false
,
default
:
false
,
default
:
false
,
},
},
showDeleteButton
:
{
type
:
Boolean
,
required
:
false
,
default
:
true
,
},
issuableRef
:
{
issuableRef
:
{
type
:
String
,
type
:
String
,
required
:
true
,
required
:
true
,
...
@@ -92,6 +97,11 @@ export default {
...
@@ -92,6 +97,11 @@ export default {
type
:
String
,
type
:
String
,
required
:
true
,
required
:
true
,
},
},
issuableType
:
{
type
:
String
,
required
:
false
,
default
:
'
issue
'
,
},
},
},
data
()
{
data
()
{
const
store
=
new
Store
({
const
store
=
new
Store
({
...
@@ -157,21 +167,21 @@ export default {
...
@@ -157,21 +167,21 @@ export default {
})
})
.
catch
(()
=>
{
.
catch
(()
=>
{
eventHub
.
$emit
(
'
close.form
'
);
eventHub
.
$emit
(
'
close.form
'
);
window
.
Flash
(
'
Error updating issue
'
);
window
.
Flash
(
`Error updating
${
this
.
issuableType
}
`
);
});
});
},
},
deleteIssuable
()
{
deleteIssuable
()
{
this
.
service
.
deleteIssuable
()
this
.
service
.
deleteIssuable
()
.
then
(
res
=>
res
.
json
())
.
then
(
res
=>
res
.
json
())
.
then
((
data
)
=>
{
.
then
((
data
)
=>
{
// Stop the poll so we don't get 404's with the issue not existing
// Stop the poll so we don't get 404's with the issu
abl
e not existing
this
.
poll
.
stop
();
this
.
poll
.
stop
();
gl
.
utils
.
visitUrl
(
data
.
web_url
);
gl
.
utils
.
visitUrl
(
data
.
web_url
);
})
})
.
catch
(()
=>
{
.
catch
(()
=>
{
eventHub
.
$emit
(
'
close.form
'
);
eventHub
.
$emit
(
'
close.form
'
);
window
.
Flash
(
'
Error deleting issue
'
);
window
.
Flash
(
`Error deleting
${
this
.
issuableType
}
`
);
});
});
},
},
},
},
...
@@ -223,6 +233,7 @@ export default {
...
@@ -223,6 +233,7 @@ export default {
:markdown-preview-path=
"markdownPreviewPath"
:markdown-preview-path=
"markdownPreviewPath"
:project-path=
"projectPath"
:project-path=
"projectPath"
:project-namespace=
"projectNamespace"
:project-namespace=
"projectNamespace"
:show-delete-button=
"showDeleteButton"
/>
/>
<div
v-else
>
<div
v-else
>
<title-component
<title-component
...
...
app/assets/javascripts/issue_show/components/edit_actions.vue
View file @
d68436d9
...
@@ -13,6 +13,11 @@
...
@@ -13,6 +13,11 @@
type
:
Object
,
type
:
Object
,
required
:
true
,
required
:
true
,
},
},
showDeleteButton
:
{
type
:
Boolean
,
required
:
false
,
default
:
true
,
},
},
},
data
()
{
data
()
{
return
{
return
{
...
@@ -23,6 +28,9 @@
...
@@ -23,6 +28,9 @@
isSubmitEnabled
()
{
isSubmitEnabled
()
{
return
this
.
formState
.
title
.
trim
()
!==
''
;
return
this
.
formState
.
title
.
trim
()
!==
''
;
},
},
shouldShowDeleteButton
()
{
return
this
.
canDestroy
&&
this
.
showDeleteButton
;
},
},
},
methods
:
{
methods
:
{
closeForm
()
{
closeForm
()
{
...
@@ -62,7 +70,7 @@
...
@@ -62,7 +70,7 @@
Cancel
Cancel
</button>
</button>
<button
<button
v-if=
"
canDestroy
"
v-if=
"
shouldShowDeleteButton
"
class=
"btn btn-danger pull-right append-right-default"
class=
"btn btn-danger pull-right append-right-default"
:class=
"
{ disabled: deleteLoading }"
:class=
"
{ disabled: deleteLoading }"
type="button"
type="button"
...
...
app/assets/javascripts/issue_show/components/form.vue
View file @
d68436d9
...
@@ -36,6 +36,11 @@
...
@@ -36,6 +36,11 @@
type
:
String
,
type
:
String
,
required
:
true
,
required
:
true
,
},
},
showDeleteButton
:
{
type
:
Boolean
,
required
:
false
,
default
:
true
,
},
},
},
components
:
{
components
:
{
lockedWarning
,
lockedWarning
,
...
@@ -81,6 +86,7 @@
...
@@ -81,6 +86,7 @@
:markdown-docs-path=
"markdownDocsPath"
/>
:markdown-docs-path=
"markdownDocsPath"
/>
<edit-actions
<edit-actions
:form-state=
"formState"
:form-state=
"formState"
:can-destroy=
"canDestroy"
/>
:can-destroy=
"canDestroy"
:show-delete-button=
"showDeleteButton"
/>
</form>
</form>
</
template
>
</
template
>
app/assets/javascripts/vue_shared/components/loading_button.vue
View file @
d68436d9
...
@@ -35,6 +35,11 @@ export default {
...
@@ -35,6 +35,11 @@ export default {
type
:
String
,
type
:
String
,
required
:
false
,
required
:
false
,
},
},
containerClass
:
{
type
:
String
,
required
:
false
,
default
:
'
btn btn-align-content
'
,
},
},
},
components
:
{
components
:
{
loadingIcon
,
loadingIcon
,
...
@@ -49,9 +54,9 @@ export default {
...
@@ -49,9 +54,9 @@ export default {
<
template
>
<
template
>
<button
<button
class=
"btn btn-align-content"
@
click=
"onClick"
@
click=
"onClick"
type=
"button"
type=
"button"
:class=
"containerClass"
:disabled=
"loading || disabled"
:disabled=
"loading || disabled"
>
>
<transition
name=
"fade"
>
<transition
name=
"fade"
>
...
...
app/assets/stylesheets/framework/blocks.scss
View file @
d68436d9
...
@@ -353,3 +353,7 @@
...
@@ -353,3 +353,7 @@
display
:
-
webkit-flex
;
display
:
-
webkit-flex
;
display
:
flex
;
display
:
flex
;
}
}
.flex-right
{
margin-left
:
auto
;
}
app/services/users/migrate_to_ghost_user_service.rb
View file @
d68436d9
...
@@ -6,6 +6,7 @@
...
@@ -6,6 +6,7 @@
# when the user is destroyed.
# when the user is destroyed.
module
Users
module
Users
class
MigrateToGhostUserService
class
MigrateToGhostUserService
prepend
EE
::
Users
::
MigrateToGhostUserService
extend
ActiveSupport
::
Concern
extend
ActiveSupport
::
Concern
attr_reader
:ghost_user
,
:user
attr_reader
:ghost_user
,
:user
...
...
changelogs/unreleased-ee/delete-epic.yml
0 → 100644
View file @
d68436d9
---
title
:
Add delete epic button
merge_request
:
author
:
type
:
added
ee/app/assets/javascripts/epics/epic_show/components/epic_header.vue
View file @
d68436d9
...
@@ -2,6 +2,8 @@
...
@@ -2,6 +2,8 @@
import
userAvatarLink
from
'
~/vue_shared/components/user_avatar/user_avatar_link.vue
'
;
import
userAvatarLink
from
'
~/vue_shared/components/user_avatar/user_avatar_link.vue
'
;
import
timeagoTooltip
from
'
~/vue_shared/components/time_ago_tooltip.vue
'
;
import
timeagoTooltip
from
'
~/vue_shared/components/time_ago_tooltip.vue
'
;
import
tooltip
from
'
~/vue_shared/directives/tooltip
'
;
import
tooltip
from
'
~/vue_shared/directives/tooltip
'
;
import
loadingButton
from
'
~/vue_shared/components/loading_button.vue
'
;
import
{
s__
}
from
'
~/locale
'
;
export
default
{
export
default
{
name
:
'
epicHeader
'
,
name
:
'
epicHeader
'
,
...
@@ -15,6 +17,16 @@
...
@@ -15,6 +17,16 @@
type
:
String
,
type
:
String
,
required
:
true
,
required
:
true
,
},
},
canDelete
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
},
data
()
{
return
{
deleteLoading
:
false
,
};
},
},
directives
:
{
directives
:
{
tooltip
,
tooltip
,
...
@@ -22,6 +34,15 @@
...
@@ -22,6 +34,15 @@
components
:
{
components
:
{
userAvatarLink
,
userAvatarLink
,
timeagoTooltip
,
timeagoTooltip
,
loadingButton
,
},
methods
:
{
deleteEpic
()
{
if
(
confirm
(
s__
(
'
Epic will be removed! Are you sure?
'
)))
{
// eslint-disable-line no-alert
this
.
deleteLoading
=
true
;
this
.
$emit
(
'
deleteEpic
'
);
}
},
},
},
};
};
</
script
>
</
script
>
...
@@ -29,21 +50,26 @@
...
@@ -29,21 +50,26 @@
<
template
>
<
template
>
<div
class=
"detail-page-header"
>
<div
class=
"detail-page-header"
>
<div
class=
"issuable-meta"
>
<div
class=
"issuable-meta"
>
Opened
{{
s__
(
'
Opened
'
)
}}
<timeagoTooltip
<timeago-tooltip
:time=
"created"
/>
:time=
"created"
{{
s__
(
'
by
'
)
}}
/>
by
<strong>
<strong>
<user-avatar-link
<user-avatar-link
:link-href=
"author.url"
:link-href=
"author.url"
:img-src=
"author.src"
:img-src=
"author.src"
:img-size=
"24"
:img-size=
"24"
:tooltip
T
ext=
"author.username"
:tooltip
-t
ext=
"author.username"
:username=
"author.name"
:username=
"author.name"
img
CssC
lasses=
"avatar-inline"
img
-css-c
lasses=
"avatar-inline"
/>
/>
</strong>
</strong>
</div>
</div>
<loading-button
v-if=
"canDelete"
:loading=
"deleteLoading"
@
click=
"deleteEpic"
:label=
"s__('Delete')"
container-class=
"btn btn-remove btn-inverted flex-right"
/>
</div>
</div>
</
template
>
</
template
>
ee/app/assets/javascripts/epics/epic_show/components/epic_show_app.vue
View file @
d68436d9
<
script
>
<
script
>
import
issuableApp
from
'
~/issue_show/components/app.vue
'
;
import
issuableApp
from
'
~/issue_show/components/app.vue
'
;
import
issuableAppEventHub
from
'
~/issue_show/event_hub
'
;
import
epicHeader
from
'
./epic_header.vue
'
;
import
epicHeader
from
'
./epic_header.vue
'
;
import
epicSidebar
from
'
../../sidebar/components/sidebar_app.vue
'
;
import
epicSidebar
from
'
../../sidebar/components/sidebar_app.vue
'
;
...
@@ -65,16 +66,23 @@
...
@@ -65,16 +66,23 @@
required
:
false
,
required
:
false
,
},
},
},
},
data
()
{
return
{
// Epics specific configuration
issuableRef
:
''
,
projectPath
:
this
.
groupPath
,
projectNamespace
:
''
,
};
},
components
:
{
components
:
{
epicHeader
,
epicHeader
,
epicSidebar
,
epicSidebar
,
issuableApp
,
issuableApp
,
},
},
created
()
{
methods
:
{
// Epics specific configuration
deleteEpic
()
{
this
.
issuableRef
=
''
;
issuableAppEventHub
.
$emit
(
'
delete.issuable
'
);
this
.
projectPath
=
this
.
groupPath
;
},
this
.
projectNamespace
=
''
;
},
},
};
};
</
script
>
</
script
>
...
@@ -84,6 +92,8 @@
...
@@ -84,6 +92,8 @@
<epic-header
<epic-header
:author=
"author"
:author=
"author"
:created=
"created"
:created=
"created"
:canDelete=
"canDestroy"
@
deleteEpic=
"deleteEpic"
/>
/>
<div
class=
"issuable-details content-block"
>
<div
class=
"issuable-details content-block"
>
<div
class=
"detail-page-description"
>
<div
class=
"detail-page-description"
>
...
@@ -105,9 +115,21 @@
...
@@ -105,9 +115,21 @@
</div>
</div>
<epic-sidebar
<epic-sidebar
:endpoint=
"endpoint"
:endpoint=
"endpoint"
:issuable-ref=
"issuableRef"
:initial-title-html=
"initialTitleHtml"
:initial-title-text=
"initialTitleText"
:initial-description-html=
"initialDescriptionHtml"
:initial-description-text=
"initialDescriptionText"
:markdown-preview-path=
"markdownPreviewPath"
:markdown-docs-path=
"markdownDocsPath"
:project-path=
"projectPath"
:project-namespace=
"projectNamespace"
:show-inline-edit-button=
"true"
:show-delete-button=
"false"
issuable-type=
"epic"
:editable=
"canUpdate"
:editable=
"canUpdate"
:initial
StartD
ate=
"startDate"
:initial
-start-d
ate=
"startDate"
:initial
EndD
ate=
"endDate"
:initial
-end-d
ate=
"endDate"
/>
/>
</div>
</div>
</div>
</div>
...
...
ee/app/assets/javascripts/epics/epic_show/epic_show_bundle.js
View file @
d68436d9
...
@@ -6,11 +6,7 @@ document.addEventListener('DOMContentLoaded', () => {
...
@@ -6,11 +6,7 @@ document.addEventListener('DOMContentLoaded', () => {
const
metaData
=
JSON
.
parse
(
el
.
dataset
.
meta
);
const
metaData
=
JSON
.
parse
(
el
.
dataset
.
meta
);
const
initialData
=
JSON
.
parse
(
el
.
dataset
.
initial
);
const
initialData
=
JSON
.
parse
(
el
.
dataset
.
initial
);
const
props
=
Object
.
assign
({},
initialData
,
metaData
,
{
const
props
=
Object
.
assign
({},
initialData
,
metaData
);
// Current iteration does not enable users
// to delete epics
canDestroy
:
false
,
});
// Convert backend casing to match frontend style guide
// Convert backend casing to match frontend style guide
props
.
startDate
=
props
.
start_date
;
props
.
startDate
=
props
.
start_date
;
...
...
ee/app/policies/ee/group_policy.rb
View file @
d68436d9
...
@@ -27,13 +27,15 @@ module EE
...
@@ -27,13 +27,15 @@ module EE
enable
:create_epic
enable
:create_epic
enable
:admin_epic
enable
:admin_epic
enable
:update_epic
enable
:update_epic
enable
:destroy_epic
end
end
rule
{
owner
}.
enable
:destroy_epic
rule
{
auditor
}.
policy
do
rule
{
auditor
}.
policy
do
enable
:read_group
enable
:read_group
enable
:read_epic
enable
:read_epic
end
end
rule
{
admin
}.
enable
:read_epic
rule
{
admin
}.
enable
:read_epic
rule
{
has_projects
}.
enable
:read_epic
rule
{
has_projects
}.
enable
:read_epic
...
...
ee/app/services/ee/users/migrate_to_ghost_user_service.rb
0 → 100644
View file @
d68436d9
module
EE
module
Users
module
MigrateToGhostUserService
private
def
migrate_records
migrate_epics
super
end
def
migrate_epics
user
.
epics
.
update_all
(
author_id:
ghost_user
.
id
)
::
Epic
.
where
(
last_edited_by_id:
user
.
id
).
update_all
(
last_edited_by_id:
ghost_user
.
id
)
end
end
end
end
spec/ee/spec/controllers/groups/epics_controller_spec.rb
View file @
d68436d9
...
@@ -91,7 +91,7 @@ describe Groups::EpicsController do
...
@@ -91,7 +91,7 @@ describe Groups::EpicsController do
describe
'PUT #update'
do
describe
'PUT #update'
do
before
do
before
do
group
.
add_
user
(
user
,
:develop
er
)
group
.
add_
developer
(
us
er
)
put
:update
,
group_id:
group
,
id:
epic
.
to_param
,
epic:
{
title:
'New title'
},
format: :json
put
:update
,
group_id:
group
,
id:
epic
.
to_param
,
epic:
{
title:
'New title'
},
format: :json
end
end
...
@@ -107,7 +107,7 @@ describe Groups::EpicsController do
...
@@ -107,7 +107,7 @@ describe Groups::EpicsController do
describe
'GET #realtime_changes'
do
describe
'GET #realtime_changes'
do
subject
{
get
:realtime_changes
,
group_id:
group
,
id:
epic
.
to_param
}
subject
{
get
:realtime_changes
,
group_id:
group
,
id:
epic
.
to_param
}
it
'returns epic'
do
it
'returns epic'
do
group
.
add_
user
(
user
,
:develop
er
)
group
.
add_
developer
(
us
er
)
subject
subject
expect
(
response
.
content_type
).
to
eq
'application/json'
expect
(
response
.
content_type
).
to
eq
'application/json'
...
@@ -122,4 +122,25 @@ describe Groups::EpicsController do
...
@@ -122,4 +122,25 @@ describe Groups::EpicsController do
end
end
end
end
end
end
describe
"DELETE #destroy"
do
before
do
sign_in
(
user
)
end
it
"rejects a developer to destroy an epic"
do
group
.
add_developer
(
user
)
delete
:destroy
,
group_id:
group
,
id:
epic
.
to_param
expect
(
response
).
to
have_gitlab_http_status
(
404
)
end
it
"deletes the epic"
do
group
.
add_owner
(
user
)
delete
:destroy
,
group_id:
group
,
id:
epic
.
to_param
expect
(
response
).
to
have_gitlab_http_status
(
302
)
expect
(
controller
).
to
set_flash
[
:notice
].
to
(
/The epic was successfully deleted\./
)
end
end
end
end
spec/ee/spec/features/epics/delete_epic_spec.rb
0 → 100644
View file @
d68436d9
require
'spec_helper'
feature
'Delete Epic'
,
:js
do
let
(
:user
)
{
create
(
:user
)
}
let
(
:group
)
{
create
(
:group
,
:public
)
}
let
(
:epic
)
{
create
(
:epic
,
group:
group
)
}
let!
(
:epic2
)
{
create
(
:epic
,
group:
group
)
}
before
do
sign_in
(
user
)
end
context
'when user who is not a group member displays the epic'
do
it
'does not show the Delete button'
do
visit
group_epic_path
(
group
,
epic
)
expect
(
page
).
not_to
have_selector
(
'.detail-page-header button'
)
end
end
context
'when user with owner access displays the epic'
do
before
do
group
.
add_owner
(
user
)
visit
group_epic_path
(
group
,
epic
)
wait_for_requests
end
it
'deletes the issue and redirect to epic list'
do
page
.
accept_alert
'Epic will be removed! Are you sure?'
do
find
(
'.detail-page-header button'
).
click
end
wait_for_requests
expect
(
find
(
'.issuable-list'
)).
not_to
have_content
(
epic
.
title
)
expect
(
find
(
'.issuable-list'
)).
to
have_content
(
epic2
.
title
)
end
end
end
spec/ee/spec/policies/epic_policy_spec.rb
View file @
d68436d9
...
@@ -32,6 +32,14 @@ describe EpicPolicy do
...
@@ -32,6 +32,14 @@ describe EpicPolicy do
it
'reporter group member can manage epics'
do
it
'reporter group member can manage epics'
do
group
.
add_reporter
(
user
)
group
.
add_reporter
(
user
)
expect
(
permissions
(
user
,
group
)).
to
be_disallowed
(
:destroy_epic
)
expect
(
permissions
(
user
,
group
))
.
to
be_allowed
(
:read_epic
,
:update_epic
,
:admin_epic
,
:create_epic
)
end
it
'only group owner can destroy epics'
do
group
.
add_owner
(
user
)
expect
(
permissions
(
user
,
group
))
expect
(
permissions
(
user
,
group
))
.
to
be_allowed
(
:read_epic
,
:update_epic
,
:destroy_epic
,
:admin_epic
,
:create_epic
)
.
to
be_allowed
(
:read_epic
,
:update_epic
,
:destroy_epic
,
:admin_epic
,
:create_epic
)
end
end
...
@@ -60,6 +68,14 @@ describe EpicPolicy do
...
@@ -60,6 +68,14 @@ describe EpicPolicy do
it
'reporter group member can manage epics'
do
it
'reporter group member can manage epics'
do
group
.
add_reporter
(
user
)
group
.
add_reporter
(
user
)
expect
(
permissions
(
user
,
group
)).
to
be_disallowed
(
:destroy_epic
)
expect
(
permissions
(
user
,
group
))
.
to
be_allowed
(
:read_epic
,
:update_epic
,
:admin_epic
,
:create_epic
)
end
it
'only group owner can destroy epics'
do
group
.
add_owner
(
user
)
expect
(
permissions
(
user
,
group
))
expect
(
permissions
(
user
,
group
))
.
to
be_allowed
(
:read_epic
,
:update_epic
,
:destroy_epic
,
:admin_epic
,
:create_epic
)
.
to
be_allowed
(
:read_epic
,
:update_epic
,
:destroy_epic
,
:admin_epic
,
:create_epic
)
end
end
...
@@ -88,6 +104,14 @@ describe EpicPolicy do
...
@@ -88,6 +104,14 @@ describe EpicPolicy do
it
'reporter group member can manage epics'
do
it
'reporter group member can manage epics'
do
group
.
add_reporter
(
user
)
group
.
add_reporter
(
user
)
expect
(
permissions
(
user
,
group
)).
to
be_disallowed
(
:destroy_epic
)
expect
(
permissions
(
user
,
group
))
.
to
be_allowed
(
:read_epic
,
:update_epic
,
:admin_epic
,
:create_epic
)
end
it
'only group owner can destroy epics'
do
group
.
add_owner
(
user
)
expect
(
permissions
(
user
,
group
))
expect
(
permissions
(
user
,
group
))
.
to
be_allowed
(
:read_epic
,
:update_epic
,
:destroy_epic
,
:admin_epic
,
:create_epic
)
.
to
be_allowed
(
:read_epic
,
:update_epic
,
:destroy_epic
,
:admin_epic
,
:create_epic
)
end
end
...
...
spec/ee/spec/services/ee/users/migrate_to_ghost_user_service_spec.rb
0 → 100644
View file @
d68436d9
require
'spec_helper'
describe
Users
::
MigrateToGhostUserService
do
context
'epics'
do
let!
(
:user
)
{
create
(
:user
)
}
let
(
:service
)
{
described_class
.
new
(
user
)
}
context
'deleted user is present as both author and edited_user'
do
include_examples
"migrating a deleted user's associated records to the ghost user"
,
Epic
,
[
:author
,
:last_edited_by
]
do
let
(
:created_record
)
do
create
(
:epic
,
group:
create
(
:group
),
author:
user
,
last_edited_by:
user
)
end
end
end
context
'deleted user is present only as edited_user'
do
include_examples
"migrating a deleted user's associated records to the ghost user"
,
Epic
,
[
:last_edited_by
]
do
let
(
:created_record
)
{
create
(
:epic
,
group:
create
(
:group
),
author:
create
(
:user
),
last_edited_by:
user
)
}
end
end
end
end
spec/javascripts/epics/epic_show/components/epic_header_spec.js
View file @
d68436d9
...
@@ -31,4 +31,42 @@ describe('epicHeader', () => {
...
@@ -31,4 +31,42 @@ describe('epicHeader', () => {
it
(
'
should render username tooltip
'
,
()
=>
{
it
(
'
should render username tooltip
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.user-avatar-link span
'
).
dataset
.
originalTitle
).
toEqual
(
author
.
username
);
expect
(
vm
.
$el
.
querySelector
(
'
.user-avatar-link span
'
).
dataset
.
originalTitle
).
toEqual
(
author
.
username
);
});
});
describe
(
'
canDelete
'
,
()
=>
{
it
(
'
should not show loading button by default
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.btn-remove
'
)).
toBeNull
();
});
it
(
'
should show loading button if canDelete
'
,
(
done
)
=>
{
vm
.
canDelete
=
true
;
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.btn-remove
'
)).
toBeDefined
();
done
();
});
});
});
describe
(
'
delete epic
'
,
()
=>
{
let
deleteEpic
;
beforeEach
((
done
)
=>
{
deleteEpic
=
jasmine
.
createSpy
();
spyOn
(
window
,
'
confirm
'
).
and
.
returnValue
(
true
);
vm
.
canDelete
=
true
;
vm
.
$on
(
'
deleteEpic
'
,
deleteEpic
);
Vue
.
nextTick
(()
=>
{
vm
.
$el
.
querySelector
(
'
.btn-remove
'
).
click
();
done
();
});
});
it
(
'
should set deleteLoading
'
,
()
=>
{
expect
(
vm
.
deleteLoading
).
toEqual
(
true
);
});
it
(
'
should emit deleteEpic event
'
,
()
=>
{
expect
(
deleteEpic
).
toHaveBeenCalled
();
});
});
});
});
spec/javascripts/epics/epic_show/components/epic_show_app_spec.js
View file @
d68436d9
...
@@ -3,6 +3,8 @@ import epicShowApp from 'ee/epics/epic_show/components/epic_show_app.vue';
...
@@ -3,6 +3,8 @@ import epicShowApp from 'ee/epics/epic_show/components/epic_show_app.vue';
import
epicHeader
from
'
ee/epics/epic_show/components/epic_header.vue
'
;
import
epicHeader
from
'
ee/epics/epic_show/components/epic_header.vue
'
;
import
epicSidebar
from
'
ee/epics/sidebar/components/sidebar_app.vue
'
;
import
epicSidebar
from
'
ee/epics/sidebar/components/sidebar_app.vue
'
;
import
issuableApp
from
'
~/issue_show/components/app.vue
'
;
import
issuableApp
from
'
~/issue_show/components/app.vue
'
;
import
issuableAppEventHub
from
'
~/issue_show/event_hub
'
;
import
'
~/lib/utils/url_utility
'
;
import
mountComponent
from
'
../../../helpers/vue_mount_component_helper
'
;
import
mountComponent
from
'
../../../helpers/vue_mount_component_helper
'
;
import
{
props
}
from
'
../mock_data
'
;
import
{
props
}
from
'
../mock_data
'
;
import
issueShowData
from
'
../../../issue_show/mock_data
'
;
import
issueShowData
from
'
../../../issue_show/mock_data
'
;
...
@@ -43,6 +45,7 @@ describe('EpicShowApp', () => {
...
@@ -43,6 +45,7 @@ describe('EpicShowApp', () => {
headerVm
=
mountComponent
(
EpicHeader
,
{
headerVm
=
mountComponent
(
EpicHeader
,
{
author
,
author
,
created
,
created
,
canDelete
:
canDestroy
,
});
});
const
IssuableApp
=
Vue
.
extend
(
issuableApp
);
const
IssuableApp
=
Vue
.
extend
(
issuableApp
);
...
@@ -86,4 +89,14 @@ describe('EpicShowApp', () => {
...
@@ -86,4 +89,14 @@ describe('EpicShowApp', () => {
it
(
'
should render epic-sidebar
'
,
()
=>
{
it
(
'
should render epic-sidebar
'
,
()
=>
{
expect
(
vm
.
$el
.
innerHTML
.
indexOf
(
sidebarVm
.
$el
.
innerHTML
)
!==
-
1
).
toEqual
(
true
);
expect
(
vm
.
$el
.
innerHTML
.
indexOf
(
sidebarVm
.
$el
.
innerHTML
)
!==
-
1
).
toEqual
(
true
);
});
});
it
(
'
should emit delete.issuable when epic is deleted
'
,
()
=>
{
const
deleteIssuable
=
jasmine
.
createSpy
();
issuableAppEventHub
.
$on
(
'
delete.issuable
'
,
deleteIssuable
);
spyOn
(
window
,
'
confirm
'
).
and
.
returnValue
(
true
);
spyOn
(
gl
.
utils
,
'
visitUrl
'
).
and
.
callFake
(()
=>
{});
vm
.
$el
.
querySelector
(
'
.detail-page-header .btn-remove
'
).
click
();
expect
(
deleteIssuable
).
toHaveBeenCalled
();
});
});
});
spec/javascripts/epics/epic_show/mock_data.js
View file @
d68436d9
export
const
contentProps
=
{
export
const
contentProps
=
{
endpoint
:
''
,
endpoint
:
''
,
canUpdate
:
true
,
canUpdate
:
true
,
canDestroy
:
fals
e
,
canDestroy
:
tru
e
,
markdownPreviewPath
:
''
,
markdownPreviewPath
:
''
,
markdownDocsPath
:
''
,
markdownDocsPath
:
''
,
groupPath
:
''
,
groupPath
:
''
,
...
...
spec/javascripts/issue_show/components/app_spec.js
View file @
d68436d9
...
@@ -223,23 +223,46 @@ describe('Issuable output', () => {
...
@@ -223,23 +223,46 @@ describe('Issuable output', () => {
});
});
});
});
it
(
'
closes form on error
'
,
(
done
)
=>
{
describe
(
'
error when updating
'
,
()
=>
{
spyOn
(
window
,
'
Flash
'
).
and
.
callThrough
();
beforeEach
(()
=>
{
spyOn
(
vm
.
service
,
'
updateIssuable
'
).
and
.
callFake
(()
=>
new
Promise
((
resolve
,
reject
)
=>
{
spyOn
(
window
,
'
Flash
'
).
and
.
callThrough
();
reject
();
spyOn
(
vm
.
service
,
'
updateIssuable
'
).
and
.
callFake
(()
=>
new
Promise
((
resolve
,
reject
)
=>
{
}));
reject
();
}));
});
vm
.
updateIssuable
();
it
(
'
closes form on error
'
,
(
done
)
=>
{
vm
.
updateIssuable
();
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
expect
(
eventHub
.
$emit
,
eventHub
.
$emit
,
).
toHaveBeenCalledWith
(
'
close.form
'
);
).
toHaveBeenCalledWith
(
'
close.form
'
);
expect
(
expect
(
window
.
Flash
,
window
.
Flash
,
).
toHaveBeenCalledWith
(
'
Error updating issue
'
);
).
toHaveBeenCalledWith
(
'
Error updating issue
'
);
done
();
done
();
});
});
it
(
'
returns the correct error message for issuableType
'
,
(
done
)
=>
{
vm
.
issuableType
=
'
merge request
'
;
Vue
.
nextTick
(()
=>
{
vm
.
updateIssuable
();
setTimeout
(()
=>
{
expect
(
eventHub
.
$emit
,
).
toHaveBeenCalledWith
(
'
close.form
'
);
expect
(
window
.
Flash
,
).
toHaveBeenCalledWith
(
'
Error updating merge request
'
);
done
();
});
});
});
});
});
});
});
});
...
...
spec/javascripts/issue_show/components/edit_actions_spec.js
View file @
d68436d9
...
@@ -61,6 +61,15 @@ describe('Edit Actions components', () => {
...
@@ -61,6 +61,15 @@ describe('Edit Actions components', () => {
});
});
});
});
it
(
'
should not show delete button if showDeleteButton is false
'
,
(
done
)
=>
{
vm
.
showDeleteButton
=
false
;
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.btn-danger
'
)).
toBeNull
();
done
();
});
});
describe
(
'
updateIssuable
'
,
()
=>
{
describe
(
'
updateIssuable
'
,
()
=>
{
it
(
'
sends update.issauble event when clicking save button
'
,
()
=>
{
it
(
'
sends update.issauble event when clicking save button
'
,
()
=>
{
vm
.
$el
.
querySelector
(
'
.btn-save
'
).
click
();
vm
.
$el
.
querySelector
(
'
.btn-save
'
).
click
();
...
...
spec/javascripts/vue_shared/components/loading_button_spec.js
View file @
d68436d9
...
@@ -66,6 +66,23 @@ describe('LoadingButton', function () {
...
@@ -66,6 +66,23 @@ describe('LoadingButton', function () {
});
});
});
});
describe
(
'
container class
'
,
()
=>
{
it
(
'
should default to btn btn-align-content
'
,
()
=>
{
vm
=
mountComponent
(
LoadingButton
,
{});
expect
(
vm
.
$el
.
classList
.
contains
(
'
btn
'
)).
toEqual
(
true
);
expect
(
vm
.
$el
.
classList
.
contains
(
'
btn-align-content
'
)).
toEqual
(
true
);
});
it
(
'
should be configurable through props
'
,
()
=>
{
vm
=
mountComponent
(
LoadingButton
,
{
containerClass
:
'
test-class
'
,
});
expect
(
vm
.
$el
.
classList
.
contains
(
'
btn
'
)).
toEqual
(
false
);
expect
(
vm
.
$el
.
classList
.
contains
(
'
btn-align-content
'
)).
toEqual
(
false
);
expect
(
vm
.
$el
.
classList
.
contains
(
'
test-class
'
)).
toEqual
(
true
);
});
});
describe
(
'
click callback prop
'
,
()
=>
{
describe
(
'
click callback prop
'
,
()
=>
{
it
(
'
calls given callback when normal
'
,
()
=>
{
it
(
'
calls given callback when normal
'
,
()
=>
{
vm
=
mountComponent
(
LoadingButton
,
{
vm
=
mountComponent
(
LoadingButton
,
{
...
...
spec/policies/group_policy_spec.rb
View file @
d68436d9
...
@@ -26,6 +26,7 @@ describe GroupPolicy do
...
@@ -26,6 +26,7 @@ describe GroupPolicy do
:admin_namespace
,
:admin_namespace
,
:admin_group_member
,
:admin_group_member
,
:change_visibility_level
,
:change_visibility_level
,
:destroy_epic
,
(
Gitlab
::
Database
.
postgresql?
?
:create_subgroup
:
nil
)
(
Gitlab
::
Database
.
postgresql?
?
:create_subgroup
:
nil
)
].
compact
].
compact
end
end
...
...
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