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
4b1d1a7e
Commit
4b1d1a7e
authored
Apr 03, 2018
by
Jan Provaznik
Committed by
Fatih Acet
Apr 03, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[EE] Epic comment thread
parent
d0a8a2db
Changes
49
Hide whitespace changes
Inline
Side-by-side
Showing
49 changed files
with
619 additions
and
118 deletions
+619
-118
app/assets/javascripts/awards_handler.js
app/assets/javascripts/awards_handler.js
+2
-2
app/assets/javascripts/lib/utils/common_utils.js
app/assets/javascripts/lib/utils/common_utils.js
+1
-0
app/assets/javascripts/notes/components/comment_form.vue
app/assets/javascripts/notes/components/comment_form.vue
+5
-1
app/assets/javascripts/notes/components/notes_app.vue
app/assets/javascripts/notes/components/notes_app.vue
+5
-1
app/assets/javascripts/notes/constants.js
app/assets/javascripts/notes/constants.js
+1
-0
app/assets/javascripts/notes/index.js
app/assets/javascripts/notes/index.js
+4
-1
app/assets/javascripts/notes/mixins/noteable.js
app/assets/javascripts/notes/mixins/noteable.js
+2
-0
app/controllers/concerns/issuable_actions.rb
app/controllers/concerns/issuable_actions.rb
+5
-1
app/controllers/concerns/notes_actions.rb
app/controllers/concerns/notes_actions.rb
+1
-1
app/controllers/projects/discussions_controller.rb
app/controllers/projects/discussions_controller.rb
+1
-1
app/finders/notes_finder.rb
app/finders/notes_finder.rb
+2
-0
app/helpers/award_emoji_helper.rb
app/helpers/award_emoji_helper.rb
+2
-0
app/helpers/notes_helper.rb
app/helpers/notes_helper.rb
+11
-9
app/models/note.rb
app/models/note.rb
+5
-2
app/serializers/discussion_entity.rb
app/serializers/discussion_entity.rb
+4
-2
app/serializers/note_entity.rb
app/serializers/note_entity.rb
+0
-30
app/serializers/note_serializer.rb
app/serializers/note_serializer.rb
+0
-3
app/serializers/project_note_entity.rb
app/serializers/project_note_entity.rb
+25
-0
app/serializers/project_note_serializer.rb
app/serializers/project_note_serializer.rb
+3
-0
config/routes/group.rb
config/routes/group.rb
+6
-1
doc/user/group/epics/index.md
doc/user/group/epics/index.md
+17
-0
ee/app/assets/javascripts/pages/groups/epics/show/index.js
ee/app/assets/javascripts/pages/groups/epics/show/index.js
+1
-0
ee/app/controllers/groups/epics/notes_controller.rb
ee/app/controllers/groups/epics/notes_controller.rb
+40
-0
ee/app/controllers/groups/epics_controller.rb
ee/app/controllers/groups/epics_controller.rb
+9
-2
ee/app/finders/ee/notes_finder.rb
ee/app/finders/ee/notes_finder.rb
+15
-0
ee/app/helpers/ee/award_emoji_helper.rb
ee/app/helpers/ee/award_emoji_helper.rb
+16
-0
ee/app/helpers/ee/notes_helper.rb
ee/app/helpers/ee/notes_helper.rb
+19
-0
ee/app/models/ee/epic.rb
ee/app/models/ee/epic.rb
+5
-0
ee/app/models/ee/note.rb
ee/app/models/ee/note.rb
+16
-1
ee/app/serializers/epic_entity.rb
ee/app/serializers/epic_entity.rb
+14
-0
ee/app/serializers/epic_note_entity.rb
ee/app/serializers/epic_note_entity.rb
+9
-0
ee/app/serializers/epic_note_serializer.rb
ee/app/serializers/epic_note_serializer.rb
+3
-0
ee/app/views/groups/epics/_discussion.html.haml
ee/app/views/groups/epics/_discussion.html.haml
+6
-0
ee/app/views/groups/epics/show.html.haml
ee/app/views/groups/epics/show.html.haml
+8
-0
ee/changelogs/unreleased/3889-epic-comment-thread.yml
ee/changelogs/unreleased/3889-epic-comment-thread.yml
+5
-0
ee/lib/ee/gitlab/etag_caching/router.rb
ee/lib/ee/gitlab/etag_caching/router.rb
+24
-0
ee/spec/controllers/groups/epics/notes_controller_spec.rb
ee/spec/controllers/groups/epics/notes_controller_spec.rb
+173
-0
ee/spec/features/discussion_comments/epic_spec.rb
ee/spec/features/discussion_comments/epic_spec.rb
+16
-0
ee/spec/features/epics/update_epic_spec.rb
ee/spec/features/epics/update_epic_spec.rb
+3
-3
ee/spec/fixtures/api/schemas/entities/epic.json
ee/spec/fixtures/api/schemas/entities/epic.json
+6
-1
ee/spec/lib/ee/gitlab/etag_caching/router_spec.rb
ee/spec/lib/ee/gitlab/etag_caching/router_spec.rb
+20
-0
ee/spec/serializers/epic_note_entity_spec.rb
ee/spec/serializers/epic_note_entity_spec.rb
+19
-0
lib/gitlab/etag_caching/router.rb
lib/gitlab/etag_caching/router.rb
+2
-0
spec/controllers/projects/issues_controller_spec.rb
spec/controllers/projects/issues_controller_spec.rb
+1
-1
spec/serializers/discussion_entity_spec.rb
spec/serializers/discussion_entity_spec.rb
+1
-1
spec/serializers/note_entity_spec.rb
spec/serializers/note_entity_spec.rb
+1
-49
spec/serializers/project_note_entity_spec.rb
spec/serializers/project_note_entity_spec.rb
+29
-0
spec/support/features/discussion_comments_shared_example.rb
spec/support/features/discussion_comments_shared_example.rb
+14
-5
spec/support/shared_examples/serializers/note_entity_examples.rb
...pport/shared_examples/serializers/note_entity_examples.rb
+42
-0
No files found.
app/assets/javascripts/awards_handler.js
View file @
4b1d1a7e
...
...
@@ -4,7 +4,7 @@ import $ from 'jquery';
import
_
from
'
underscore
'
;
import
Cookies
from
'
js-cookie
'
;
import
{
__
}
from
'
./locale
'
;
import
{
isInIssuePage
,
isInMRPage
,
hasVueMRDiscussionsCookie
,
updateTooltipTitle
}
from
'
./lib/utils/common_utils
'
;
import
{
isInIssuePage
,
isInMRPage
,
isInEpicPage
,
hasVueMRDiscussionsCookie
,
updateTooltipTitle
}
from
'
./lib/utils/common_utils
'
;
import
flash
from
'
./flash
'
;
import
axios
from
'
./lib/utils/axios_utils
'
;
...
...
@@ -300,7 +300,7 @@ class AwardsHandler {
}
isInVueNoteablePage
()
{
return
isInIssuePage
()
||
this
.
isVueMRDiscussions
();
return
isInIssuePage
()
||
isInEpicPage
()
||
this
.
isVueMRDiscussions
();
}
getVotesBlock
()
{
...
...
app/assets/javascripts/lib/utils/common_utils.js
View file @
4b1d1a7e
...
...
@@ -33,6 +33,7 @@ export const checkPageAndAction = (page, action) => {
export
const
isInIssuePage
=
()
=>
checkPageAndAction
(
'
issues
'
,
'
show
'
);
export
const
isInMRPage
=
()
=>
checkPageAndAction
(
'
merge_requests
'
,
'
show
'
);
export
const
isInEpicPage
=
()
=>
checkPageAndAction
(
'
epics
'
,
'
show
'
);
export
const
isInNoteablePage
=
()
=>
isInIssuePage
()
||
isInMRPage
();
export
const
hasVueMRDiscussionsCookie
=
()
=>
Cookies
.
get
(
'
vue_mr_discussions
'
);
...
...
app/assets/javascripts/notes/components/comment_form.vue
View file @
4b1d1a7e
...
...
@@ -99,6 +99,10 @@ export default {
'
js-note-target-reopen
'
:
!
this
.
isOpen
,
};
},
supportQuickActions
()
{
// Disable quick actions support for Epics
return
this
.
noteableType
!==
constants
.
EPIC_NOTEABLE_TYPE
;
},
markdownDocsPath
()
{
return
this
.
getNotesData
.
markdownDocsPath
;
},
...
...
@@ -355,7 +359,7 @@ Please check your network connection and try again.`;
name=
"note[note]"
class=
"note-textarea js-vue-comment-form
js-gfm-input js-autosize markdown-area js-vue-textarea"
data-supports-quick-actions=
"true
"
:data-supports-quick-actions=
"supportQuickActions
"
aria-label=
"Description"
v-model=
"note"
ref=
"textarea"
...
...
app/assets/javascripts/notes/components/notes_app.vue
View file @
4b1d1a7e
...
...
@@ -50,7 +50,11 @@ export default {
...
mapGetters
([
'
notes
'
,
'
getNotesDataByProp
'
,
'
discussionCount
'
]),
noteableType
()
{
// FIXME -- @fatihacet Get this from JSON data.
const
{
ISSUE_NOTEABLE_TYPE
,
MERGE_REQUEST_NOTEABLE_TYPE
}
=
constants
;
const
{
ISSUE_NOTEABLE_TYPE
,
MERGE_REQUEST_NOTEABLE_TYPE
,
EPIC_NOTEABLE_TYPE
}
=
constants
;
if
(
this
.
noteableData
.
noteableType
===
EPIC_NOTEABLE_TYPE
)
{
return
EPIC_NOTEABLE_TYPE
;
}
return
this
.
noteableData
.
merge_params
?
MERGE_REQUEST_NOTEABLE_TYPE
...
...
app/assets/javascripts/notes/constants.js
View file @
4b1d1a7e
...
...
@@ -10,6 +10,7 @@ export const CLOSED = 'closed';
export
const
EMOJI_THUMBSUP
=
'
thumbsup
'
;
export
const
EMOJI_THUMBSDOWN
=
'
thumbsdown
'
;
export
const
ISSUE_NOTEABLE_TYPE
=
'
issue
'
;
export
const
EPIC_NOTEABLE_TYPE
=
'
epic
'
;
export
const
MERGE_REQUEST_NOTEABLE_TYPE
=
'
merge_request
'
;
export
const
UNRESOLVE_NOTE_METHOD_NAME
=
'
delete
'
;
export
const
RESOLVE_NOTE_METHOD_NAME
=
'
post
'
;
app/assets/javascripts/notes/index.js
View file @
4b1d1a7e
...
...
@@ -12,8 +12,11 @@ document.addEventListener(
data
()
{
const
notesDataset
=
document
.
getElementById
(
'
js-vue-notes
'
).
dataset
;
const
parsedUserData
=
JSON
.
parse
(
notesDataset
.
currentUserData
);
const
noteableData
=
JSON
.
parse
(
notesDataset
.
noteableData
);
let
currentUserData
=
{};
noteableData
.
noteableType
=
notesDataset
.
noteableType
;
if
(
parsedUserData
)
{
currentUserData
=
{
id
:
parsedUserData
.
id
,
...
...
@@ -25,7 +28,7 @@ document.addEventListener(
}
return
{
noteableData
:
JSON
.
parse
(
notesDataset
.
noteableData
)
,
noteableData
,
currentUserData
,
notesData
:
JSON
.
parse
(
notesDataset
.
notesData
),
};
...
...
app/assets/javascripts/notes/mixins/noteable.js
View file @
4b1d1a7e
...
...
@@ -14,6 +14,8 @@ export default {
return
constants
.
MERGE_REQUEST_NOTEABLE_TYPE
;
case
'
Issue
'
:
return
constants
.
ISSUE_NOTEABLE_TYPE
;
case
'
Epic
'
:
return
constants
.
EPIC_NOTEABLE_TYPE
;
default
:
return
''
;
}
...
...
app/controllers/concerns/issuable_actions.rb
View file @
4b1d1a7e
...
...
@@ -88,11 +88,15 @@ module IssuableActions
discussions
=
Discussion
.
build_collection
(
notes
,
issuable
)
render
json:
DiscussionSerializer
.
new
(
project:
project
,
noteable:
issuable
,
current_user:
current_user
)
.
represent
(
discussions
,
context:
self
)
render
json:
discussion_serializer
.
represent
(
discussions
,
context:
self
)
end
private
def
discussion_serializer
DiscussionSerializer
.
new
(
project:
project
,
noteable:
issuable
,
current_user:
current_user
,
note_entity:
ProjectNoteEntity
)
end
def
recaptcha_check_if_spammable
(
should_redirect
=
true
,
&
block
)
return
yield
unless
issuable
.
is_a?
Spammable
...
...
app/controllers/concerns/notes_actions.rb
View file @
4b1d1a7e
...
...
@@ -212,7 +212,7 @@ module NotesActions
end
def
note_serializer
NoteSerializer
.
new
(
project:
project
,
noteable:
noteable
,
current_user:
current_user
)
Project
NoteSerializer
.
new
(
project:
project
,
noteable:
noteable
,
current_user:
current_user
)
end
def
note_project
...
...
app/controllers/projects/discussions_controller.rb
View file @
4b1d1a7e
...
...
@@ -43,7 +43,7 @@ class Projects::DiscussionsController < Projects::ApplicationController
def
render_json_with_discussions_serializer
render
json:
DiscussionSerializer
.
new
(
project:
project
,
noteable:
discussion
.
noteable
,
current_user:
current_user
)
DiscussionSerializer
.
new
(
project:
project
,
noteable:
discussion
.
noteable
,
current_user:
current_user
,
note_entity:
ProjectNoteEntity
)
.
represent
(
discussion
,
context:
self
)
end
...
...
app/finders/notes_finder.rb
View file @
4b1d1a7e
class
NotesFinder
prepend
EE
::
NotesFinder
FETCH_OVERLAP
=
5
.
seconds
# Used to filter Notes
...
...
app/helpers/award_emoji_helper.rb
View file @
4b1d1a7e
module
AwardEmojiHelper
prepend
EE
::
AwardEmojiHelper
def
toggle_award_url
(
awardable
)
return
url_for
([
:toggle_award_emoji
,
awardable
])
unless
@project
||
awardable
.
is_a?
(
Note
)
...
...
app/helpers/notes_helper.rb
View file @
4b1d1a7e
module
NotesHelper
prepend
EE
::
NotesHelper
def
note_target_fields
(
note
)
if
note
.
noteable
hidden_field_tag
(
:target_type
,
note
.
noteable
.
class
.
name
.
underscore
)
+
...
...
@@ -151,16 +153,17 @@ module NotesHelper
}
end
def
notes_data
(
issuable
)
discussions_path
=
if
issuable
.
is_a?
(
Issue
)
discussions_project_issue_path
(
@project
,
issuable
,
format: :json
)
else
discussions_project_merge_request_path
(
@project
,
issuable
,
format: :json
)
end
def
discussions_path
(
issuable
)
if
issuable
.
is_a?
(
Issue
)
discussions_project_issue_path
(
@project
,
issuable
,
format: :json
)
else
discussions_project_merge_request_path
(
@project
,
issuable
,
format: :json
)
end
end
def
notes_data
(
issuable
)
{
discussionsPath:
discussions_path
,
discussionsPath:
discussions_path
(
issuable
)
,
registerPath:
new_session_path
(
:user
,
redirect_to_referer:
'yes'
,
anchor:
'register-pane'
),
newSessionPath:
new_session_path
(
:user
,
redirect_to_referer:
'yes'
),
markdownDocsPath:
help_page_path
(
'user/markdown'
),
...
...
@@ -170,7 +173,6 @@ module NotesHelper
notesPath:
notes_url
,
totalNotes:
issuable
.
discussions
.
length
,
lastFetchedAt:
Time
.
now
.
to_i
}.
to_json
end
...
...
app/models/note.rb
View file @
4b1d1a7e
...
...
@@ -387,12 +387,15 @@ class Note < ActiveRecord::Base
def
expire_etag_cache
return
unless
noteable
&
.
discussions_rendered_on_frontend?
key
=
Gitlab
::
Routing
.
url_helpers
.
project_noteable_notes_path
(
Gitlab
::
EtagCaching
::
Store
.
new
.
touch
(
etag_key
)
end
def
etag_key
Gitlab
::
Routing
.
url_helpers
.
project_noteable_notes_path
(
project
,
target_type:
noteable_type
.
underscore
,
target_id:
noteable_id
)
Gitlab
::
EtagCaching
::
Store
.
new
.
touch
(
key
)
end
def
touch
(
*
args
)
...
...
app/serializers/discussion_entity.rb
View file @
4b1d1a7e
...
...
@@ -4,7 +4,9 @@ class DiscussionEntity < Grape::Entity
expose
:id
,
:reply_id
expose
:expanded?
,
as: :expanded
expose
:notes
,
using:
NoteEntity
expose
:notes
do
|
discussion
,
opts
|
request
.
note_entity
.
represent
(
discussion
.
notes
,
opts
)
end
expose
:individual_note?
,
as: :individual_note
expose
:resolvable?
,
as: :resolvable
...
...
@@ -12,7 +14,7 @@ class DiscussionEntity < Grape::Entity
expose
:resolve_path
,
if:
->
(
d
,
_
)
{
d
.
resolvable?
}
do
|
discussion
|
resolve_project_merge_request_discussion_path
(
discussion
.
project
,
discussion
.
noteable
,
discussion
.
id
)
end
expose
:resolve_with_issue_path
do
|
discussion
|
expose
:resolve_with_issue_path
,
if:
->
(
d
,
_
)
{
d
.
resolvable?
}
do
|
discussion
|
new_project_issue_path
(
discussion
.
project
,
merge_request_to_resolve_discussions_of:
discussion
.
noteable
.
iid
,
discussion_to_resolve:
discussion
.
id
)
end
...
...
app/serializers/note_entity.rb
View file @
4b1d1a7e
...
...
@@ -5,10 +5,6 @@ class NoteEntity < API::Entities::Note
expose
:author
,
using:
NoteUserEntity
expose
:human_access
do
|
note
|
note
.
project
.
team
.
human_max_access
(
note
.
author_id
)
end
unexpose
:note
,
as: :body
expose
:note
...
...
@@ -37,36 +33,10 @@ class NoteEntity < API::Entities::Note
expose
:emoji_awardable?
,
as: :emoji_awardable
expose
:award_emoji
,
if:
->
(
note
,
_
)
{
note
.
emoji_awardable?
},
using:
AwardEmojiEntity
expose
:toggle_award_path
,
if:
->
(
note
,
_
)
{
note
.
emoji_awardable?
}
do
|
note
|
if
note
.
for_personal_snippet?
toggle_award_emoji_snippet_note_path
(
note
.
noteable
,
note
)
else
toggle_award_emoji_project_note_path
(
note
.
project
,
note
.
id
)
end
end
expose
:report_abuse_path
do
|
note
|
new_abuse_report_path
(
user_id:
note
.
author
.
id
,
ref_url:
Gitlab
::
UrlBuilder
.
build
(
note
))
end
expose
:path
do
|
note
|
if
note
.
for_personal_snippet?
snippet_note_path
(
note
.
noteable
,
note
)
else
project_note_path
(
note
.
project
,
note
)
end
end
expose
:resolve_path
,
if:
->
(
note
,
_
)
{
note
.
part_of_discussion?
&&
note
.
resolvable?
}
do
|
note
|
resolve_project_merge_request_discussion_path
(
note
.
project
,
note
.
noteable
,
note
.
discussion_id
)
end
expose
:resolve_with_issue_path
,
if:
->
(
note
,
_
)
{
note
.
part_of_discussion?
&&
note
.
resolvable?
}
do
|
note
|
new_project_issue_path
(
note
.
project
,
merge_request_to_resolve_discussions_of:
note
.
noteable
.
iid
,
discussion_to_resolve:
note
.
discussion_id
)
end
expose
:attachment
,
using:
NoteAttachmentEntity
,
if:
->
(
note
,
_
)
{
note
.
attachment?
}
expose
:delete_attachment_path
,
if:
->
(
note
,
_
)
{
note
.
attachment?
}
do
|
note
|
delete_attachment_project_note_path
(
note
.
project
,
note
)
end
end
app/serializers/note_serializer.rb
deleted
100644 → 0
View file @
d0a8a2db
class
NoteSerializer
<
BaseSerializer
entity
NoteEntity
end
app/serializers/project_note_entity.rb
0 → 100644
View file @
4b1d1a7e
class
ProjectNoteEntity
<
NoteEntity
expose
:human_access
do
|
note
|
note
.
project
.
team
.
human_max_access
(
note
.
author_id
)
end
expose
:toggle_award_path
,
if:
->
(
note
,
_
)
{
note
.
emoji_awardable?
}
do
|
note
|
toggle_award_emoji_project_note_path
(
note
.
project
,
note
.
id
)
end
expose
:path
do
|
note
|
project_note_path
(
note
.
project
,
note
)
end
expose
:resolve_path
,
if:
->
(
note
,
_
)
{
note
.
part_of_discussion?
&&
note
.
resolvable?
}
do
|
note
|
resolve_project_merge_request_discussion_path
(
note
.
project
,
note
.
noteable
,
note
.
discussion_id
)
end
expose
:resolve_with_issue_path
,
if:
->
(
note
,
_
)
{
note
.
part_of_discussion?
&&
note
.
resolvable?
}
do
|
note
|
new_project_issue_path
(
note
.
project
,
merge_request_to_resolve_discussions_of:
note
.
noteable
.
iid
,
discussion_to_resolve:
note
.
discussion_id
)
end
expose
:delete_attachment_path
,
if:
->
(
note
,
_
)
{
note
.
attachment?
}
do
|
note
|
delete_attachment_project_note_path
(
note
.
project
,
note
)
end
end
app/serializers/project_note_serializer.rb
0 → 100644
View file @
4b1d1a7e
class
ProjectNoteSerializer
<
BaseSerializer
entity
ProjectNoteEntity
end
config/routes/group.rb
View file @
4b1d1a7e
...
...
@@ -82,12 +82,17 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
end
resources
:billings
,
only:
[
:index
]
resources
:epics
do
resources
:epics
,
concerns: :awardable
,
constraints:
{
id:
/\d+/
}
do
member
do
get
:discussions
,
format: :json
get
:realtime_changes
end
resources
:epic_issues
,
only:
[
:index
,
:create
,
:destroy
,
:update
],
as:
'issues'
,
path:
'issues'
scope
module: :epics
do
resources
:notes
,
only:
[
:index
,
:create
,
:destroy
,
:update
],
concerns: :awardable
,
constraints:
{
id:
/\d+/
}
end
end
# On CE only index and show are needed
...
...
doc/user/group/epics/index.md
View file @
4b1d1a7e
...
...
@@ -97,3 +97,20 @@ You may also consult the [group permissions table][permissions].
[
ee
]:
https://about.gitlab.com/products/
[
permissions
]:
../../permissions.md#group-members-permissions
## Thread
-
Comments: collaborate on that epic by posting comments in its thread.
These text fields also fully support
[
GitLab Flavored Markdown
](
../../markdown.md#gitlab-flavored-markdown-gfm
)
.
## Comment, or start a discussion
Once you wrote your comment, you can either:
-
Click "Comment" and your comment will be published.
-
Click "Start discussion": start a thread within that epic's thread to discuss specific points.
## Award emoji
-
You can
[
award an emoji
](
../../award_emojis.md
)
to that epic or its comments.
ee/app/assets/javascripts/pages/groups/epics/show/index.js
View file @
4b1d1a7e
import
ZenMode
from
'
~/zen_mode
'
;
import
initEpicShow
from
'
ee/epics/epic_show/epic_show_bundle
'
;
import
'
~/notes/index
'
;
document
.
addEventListener
(
'
DOMContentLoaded
'
,
()
=>
{
new
ZenMode
();
// eslint-disable-line no-new
...
...
ee/app/controllers/groups/epics/notes_controller.rb
0 → 100644
View file @
4b1d1a7e
class
Groups::Epics::NotesController
<
Groups
::
ApplicationController
include
NotesActions
include
NotesHelper
include
ToggleAwardEmoji
before_action
:epic
before_action
:authorize_create_note!
,
only:
[
:create
]
private
def
project
nil
end
def
note
@note
||=
noteable
.
notes
.
find
(
params
[
:id
])
end
alias_method
:awardable
,
:note
def
epic
@epic
||=
@group
.
epics
.
find_by
(
iid:
params
[
:epic_id
])
return
render_404
unless
can?
(
current_user
,
:read_epic
,
@epic
)
@epic
end
alias_method
:noteable
,
:epic
def
finder_params
params
.
merge
(
last_fetched_at:
last_fetched_at
,
target_id:
epic
.
id
,
target_type:
'epic'
,
group_id:
@group
.
id
)
end
def
authorize_create_note!
access_denied!
unless
can?
(
current_user
,
:create_note
,
noteable
)
end
def
note_serializer
EpicNoteSerializer
.
new
(
project:
nil
,
noteable:
noteable
,
current_user:
current_user
)
end
end
ee/app/controllers/groups/epics_controller.rb
View file @
4b1d1a7e
class
Groups::EpicsController
<
Groups
::
ApplicationController
include
IssuableActions
include
IssuableCollections
include
ToggleAwardEmoji
include
RendersNotes
before_action
:check_epics_available!
before_action
:epic
,
except:
[
:index
,
:create
]
...
...
@@ -23,7 +25,7 @@ class Groups::EpicsController < Groups::ApplicationController
end
def
create
@epic
=
Epics
::
CreateService
.
new
(
@group
,
current_user
,
epic_params
).
execute
@epic
=
::
Epics
::
CreateService
.
new
(
@group
,
current_user
,
epic_params
).
execute
if
@epic
.
persisted?
render
json:
{
...
...
@@ -48,6 +50,7 @@ class Groups::EpicsController < Groups::ApplicationController
@epic
end
alias_method
:issuable
,
:epic
alias_method
:awardable
,
:epic
def
epic_params
params
.
require
(
:epic
).
permit
(
*
epic_params_attributes
)
...
...
@@ -67,8 +70,12 @@ class Groups::EpicsController < Groups::ApplicationController
EpicSerializer
.
new
(
current_user:
current_user
)
end
def
discussion_serializer
DiscussionSerializer
.
new
(
project:
nil
,
noteable:
issuable
,
current_user:
current_user
,
note_entity:
EpicNoteEntity
)
end
def
update_service
Epics
::
UpdateService
.
new
(
@group
,
current_user
,
epic_params
)
::
Epics
::
UpdateService
.
new
(
@group
,
current_user
,
epic_params
)
end
def
finder_type
...
...
ee/app/finders/ee/notes_finder.rb
0 → 100644
View file @
4b1d1a7e
module
EE
module
NotesFinder
extend
ActiveSupport
::
Concern
extend
::
Gitlab
::
Utils
::
Override
override
:noteables_for_type
def
noteables_for_type
(
noteable_type
)
if
noteable_type
==
"epic"
return
EpicsFinder
.
new
(
@current_user
,
group_id:
@params
[
:group_id
])
# rubocop:disable Gitlab/ModuleWithInstanceVariables
end
super
end
end
end
ee/app/helpers/ee/award_emoji_helper.rb
0 → 100644
View file @
4b1d1a7e
module
EE
module
AwardEmojiHelper
extend
::
Gitlab
::
Utils
::
Override
override
:toggle_award_url
def
toggle_award_url
(
awardable
)
if
awardable
.
is_a?
(
Note
)
&&
awardable
.
for_epic?
return
toggle_award_emoji_group_epic_note_path
(
awardable
.
noteable
.
group
,
awardable
.
noteable
,
awardable
)
elsif
awardable
.
is_a?
(
Epic
)
return
toggle_award_emoji_group_epic_path
(
awardable
.
group
,
awardable
)
end
super
end
end
end
ee/app/helpers/ee/notes_helper.rb
0 → 100644
View file @
4b1d1a7e
module
EE
module
NotesHelper
extend
::
Gitlab
::
Utils
::
Override
override
:notes_url
def
notes_url
(
params
=
{})
return
group_epic_notes_path
(
@epic
.
group
,
@epic
)
if
@epic
.
is_a?
(
Epic
)
super
end
override
:discussions_path
def
discussions_path
(
issuable
)
return
discussions_group_epic_path
(
issuable
.
group
,
issuable
,
format: :json
)
if
issuable
.
is_a?
(
Epic
)
super
end
end
end
ee/app/models/ee/epic.rb
View file @
4b1d1a7e
...
...
@@ -7,6 +7,7 @@ module EE
include
Issuable
include
Noteable
include
Referable
include
Awardable
belongs_to
:assignee
,
class_name:
"User"
belongs_to
:group
...
...
@@ -115,5 +116,9 @@ module EE
def
mentionable_params
{
group:
group
}
end
def
discussions_rendered_on_frontend?
true
end
end
end
ee/app/models/ee/note.rb
View file @
4b1d1a7e
...
...
@@ -7,7 +7,6 @@ module EE
include
ObjectStorage
::
BackgroundMove
end
override
:for_project_noteable?
def
for_epic?
noteable
.
is_a?
(
Epic
)
end
...
...
@@ -21,5 +20,21 @@ module EE
def
can_create_todo?
!
for_epic?
&&
super
end
override
:etag_key
def
etag_key
if
for_epic?
return
::
Gitlab
::
Routing
.
url_helpers
.
group_epic_notes_path
(
noteable
.
group
,
noteable
)
end
super
end
override
:banzai_render_context
def
banzai_render_context
(
field
)
return
super
unless
for_epic?
super
.
merge
(
group:
noteable
.
group
)
end
end
end
ee/app/serializers/epic_entity.rb
View file @
4b1d1a7e
...
...
@@ -12,4 +12,18 @@ class EpicEntity < IssuableEntity
group_epic_path
(
epic
.
group
,
epic
)
end
expose
:labels
,
using:
LabelEntity
expose
:current_user
do
expose
:can_create_note
do
|
epic
|
can?
(
request
.
current_user
,
:create_note
,
epic
)
end
end
expose
:create_note_path
do
|
epic
|
group_epic_notes_path
(
epic
.
group
,
epic
)
end
expose
:preview_note_path
do
|
epic
|
preview_markdown_path
(
epic
.
group
,
quick_actions_target_type:
'Epic'
,
quick_actions_target_id:
epic
.
id
)
end
end
ee/app/serializers/epic_note_entity.rb
0 → 100644
View file @
4b1d1a7e
class
EpicNoteEntity
<
NoteEntity
expose
:toggle_award_path
,
if:
->
(
note
,
_
)
{
note
.
emoji_awardable?
}
do
|
note
|
toggle_award_emoji_group_epic_note_path
(
note
.
noteable
.
group
,
note
.
noteable
,
note
)
end
expose
:path
do
|
note
|
group_epic_note_path
(
note
.
noteable
.
group
,
note
.
noteable
,
note
)
end
end
ee/app/serializers/epic_note_serializer.rb
0 → 100644
View file @
4b1d1a7e
class
EpicNoteSerializer
<
BaseSerializer
entity
EpicNoteEntity
end
ee/app/views/groups/epics/_discussion.html.haml
0 → 100644
View file @
4b1d1a7e
-
@gfm_form
=
true
%section
.js-vue-notes-event
#js-vue-notes
{
data:
{
notes_data:
notes_data
(
@epic
),
noteable_data:
EpicSerializer
.
new
(
current_user:
current_user
).
represent
(
@epic
).
to_json
,
current_user_data:
UserSerializer
.
new
.
represent
(
current_user
,
only_path:
true
).
to_json
,
noteable_type:
'epic'
}
}
ee/app/views/groups/epics/show.html.haml
View file @
4b1d1a7e
...
...
@@ -13,3 +13,11 @@
-
page_card_attributes
@epic
.
card_attributes
#epic-show-app
{
data:
epic_show_app_data
(
@epic
,
author_icon:
avatar_icon_for_user
(
@epic
.
author
),
initial:
issuable_initial_data
(
@epic
))
}
.content-block.emoji-block
.row
.col-sm-8.js-noteable-awards
=
render
'award_emoji/awards_block'
,
awardable:
@epic
,
inline:
true
%section
.issuable-discussion
=
render
'discussion'
ee/changelogs/unreleased/3889-epic-comment-thread.yml
0 → 100644
View file @
4b1d1a7e
---
title
:
Add comment thread to Epics
merge_request
:
author
:
type
:
added
ee/lib/ee/gitlab/etag_caching/router.rb
0 → 100644
View file @
4b1d1a7e
module
EE
module
Gitlab
module
EtagCaching
module
Router
module
ClassMethods
def
match
(
path
)
epic_route
=
::
Gitlab
::
EtagCaching
::
Router
::
Route
.
new
(
%r(^/groups/
#{
::
Gitlab
::
PathRegex
.
full_namespace_route_regex
}
/-/epics/
\d
+/notes
\z
)
,
'epic_notes'
)
return
epic_route
if
epic_route
.
regexp
.
match
(
path
)
super
end
end
def
self
.
prepended
(
base
)
base
.
singleton_class
.
prepend
ClassMethods
end
end
end
end
end
ee/spec/controllers/groups/epics/notes_controller_spec.rb
0 → 100644
View file @
4b1d1a7e
require
'spec_helper'
describe
Groups
::
Epics
::
NotesController
do
let
(
:user
)
{
create
(
:user
)
}
let
(
:group
)
{
create
(
:group
)
}
let
(
:epic
)
{
create
(
:epic
,
group:
group
)
}
let
(
:note
)
{
create
(
:note
,
noteable:
epic
)
}
let
(
:parsed_response
)
{
JSON
.
parse
(
response
.
body
).
with_indifferent_access
}
before
do
stub_licensed_features
(
epics:
true
)
end
describe
'GET index'
do
let
(
:request_params
)
do
{
group_id:
group
,
epic_id:
epic
.
iid
,
format:
'json'
}
end
let
(
:note_json
)
{
parsed_response
[
:notes
].
first
}
before
do
group
.
add_developer
(
user
)
sign_in
(
user
)
note
end
it
'responds with array of notes'
do
get
:index
,
request_params
expect
(
parsed_response
[
:notes
]).
to
be_an
Array
expect
(
parsed_response
[
:notes
].
count
).
to
eq
(
1
)
end
context
'with cross-reference system note that is not visible to the current user'
,
:request_store
do
it
"does not return any note"
do
expect_any_instance_of
(
Note
).
to
receive
(
:cross_reference_not_visible_for?
).
and_return
(
true
)
get
:index
,
request_params
expect
(
parsed_response
[
:notes
].
count
).
to
eq
(
0
)
end
end
end
describe
'POST create'
do
let
(
:request_params
)
do
{
note:
{
note:
'some note'
,
noteable_id:
epic
.
id
,
noteable_type:
'Epic'
},
group_id:
group
,
epic_id:
epic
.
iid
,
format:
'json'
}
end
before
do
sign_in
(
user
)
group
.
add_developer
(
user
)
end
it
"returns status 302 for html"
do
post
:create
,
request_params
.
merge
(
format: :html
)
expect
(
response
).
to
have_gitlab_http_status
(
302
)
end
it
"returns status 200 for json"
do
post
:create
,
request_params
expect
(
response
).
to
have_gitlab_http_status
(
200
)
expect
(
parsed_response
[
:id
]).
not_to
be_nil
end
end
describe
'PUT update'
do
let
(
:request_params
)
do
{
note:
{
note:
'updated note'
,
noteable_id:
epic
.
id
,
noteable_type:
'Epic'
},
group_id:
group
,
epic_id:
epic
.
iid
,
id:
note
.
id
,
format:
'json'
}
end
before
do
sign_in
(
note
.
author
)
end
it
"updates the note"
do
expect
{
put
:update
,
request_params
}.
to
change
{
note
.
reload
.
note
}
end
end
describe
'DELETE destroy'
do
let
(
:request_params
)
do
{
group_id:
group
,
epic_id:
epic
.
iid
,
id:
note
.
id
,
format:
'js'
}
end
before
do
group
.
add_developer
(
user
)
end
context
'user is the author of a note'
do
before
do
sign_in
(
note
.
author
)
end
it
"returns status 200"
do
delete
:destroy
,
request_params
expect
(
response
).
to
have_gitlab_http_status
(
200
)
end
it
"deletes the note"
do
expect
{
delete
:destroy
,
request_params
}.
to
change
{
Note
.
count
}.
from
(
1
).
to
(
0
)
end
end
context
'user is not the author of the note'
do
before
do
sign_in
(
user
)
end
it
"returns status 404"
do
delete
:destroy
,
request_params
expect
(
response
).
to
have_gitlab_http_status
(
404
)
end
end
end
describe
'POST toggle_award_emoji'
do
let
(
:request_params
)
do
{
group_id:
group
,
epic_id:
epic
,
id:
note
.
id
}
end
before
do
group
.
add_developer
(
user
)
sign_in
(
user
)
end
it
"toggles the award emoji"
do
expect
do
post
(
:toggle_award_emoji
,
request_params
.
merge
(
name:
"thumbsup"
))
end
.
to
change
{
note
.
award_emoji
.
count
}.
by
(
1
)
expect
(
response
).
to
have_gitlab_http_status
(
200
)
end
it
"removes the already awarded emoji"
do
post
(
:toggle_award_emoji
,
request_params
.
merge
(
name:
"thumbsup"
))
expect
do
post
(
:toggle_award_emoji
,
request_params
.
merge
(
name:
"thumbsup"
))
end
.
to
change
{
AwardEmoji
.
count
}.
by
(
-
1
)
expect
(
response
).
to
have_gitlab_http_status
(
200
)
end
end
end
ee/spec/features/discussion_comments/epic_spec.rb
0 → 100644
View file @
4b1d1a7e
require
'spec_helper'
describe
'Discussion Comments Epic'
,
:js
do
let
(
:user
)
{
create
(
:user
)
}
let
(
:epic
)
{
create
(
:epic
)
}
before
do
stub_licensed_features
(
epics:
true
)
epic
.
group
.
add_master
(
user
)
sign_in
(
user
)
visit
group_epic_path
(
epic
.
group
,
epic
)
end
it_behaves_like
'discussion comments'
,
'epic'
end
ee/spec/features/epics/update_epic_spec.rb
View file @
4b1d1a7e
...
...
@@ -46,7 +46,7 @@ feature 'Update Epic', :js do
fill_in
'issuable-title'
,
with:
'New epic title'
fill_in
'issue-description'
,
with:
'New epic description'
click_link
(
'Preview'
)
page
.
within
(
'.detail-page-description'
)
{
click_link
(
'Preview'
)
}
expect
(
find
(
'.md-preview'
)).
to
have_content
(
'New epic description'
)
click_button
'Save changes'
...
...
@@ -56,7 +56,7 @@ feature 'Update Epic', :js do
end
it
'edits full screen'
do
find
(
'.js-zen-enter'
).
click
page
.
within
(
'.detail-page-description'
)
{
find
(
'.js-zen-enter'
).
click
}
expect
(
page
).
to
have_selector
(
'.div-dropzone-wrapper.fullscreen'
)
end
...
...
@@ -68,7 +68,7 @@ feature 'Update Epic', :js do
expect
(
page
.
find_field
(
"issue-description"
).
value
).
to
have_content
(
'banana_sample'
)
click_link
(
'Preview'
)
page
.
within
(
'.detail-page-description'
)
{
click_link
(
'Preview'
)
}
wait_for_requests
within
(
'.md-preview'
)
do
...
...
ee/spec/fixtures/api/schemas/entities/epic.json
View file @
4b1d1a7e
...
...
@@ -49,7 +49,12 @@
"avatar_url"
:
{
"type"
:
"uri"
}
}
}
}
},
"current_user"
:
{
"can_create_note"
:
{
"type"
:
"boolean"
}
},
"create_note_path"
:
{
"type"
:
"string"
},
"preview_note_path"
:
{
"type"
:
"string"
}
},
"additionalProperties"
:
false
}
ee/spec/lib/ee/gitlab/etag_caching/router_spec.rb
0 → 100644
View file @
4b1d1a7e
require
'spec_helper'
describe
Gitlab
::
EtagCaching
::
Router
do
it
'matches epic notes endpoint'
do
result
=
described_class
.
match
(
'/groups/my-group/and-subgroup/-/epics/1/notes'
)
expect
(
result
).
to
be_present
expect
(
result
.
name
).
to
eq
'epic_notes'
end
it
'does not match invalid epic notes endpoint'
do
result
=
described_class
.
match
(
'/groups/my-group/-/and-subgroup/-/epics/1/notes'
)
expect
(
result
).
to
be_blank
end
end
ee/spec/serializers/epic_note_entity_spec.rb
0 → 100644
View file @
4b1d1a7e
require
'spec_helper'
describe
EpicNoteEntity
do
include
Gitlab
::
Routing
let
(
:request
)
{
double
(
'request'
,
current_user:
user
,
noteable:
note
.
noteable
)
}
let
(
:entity
)
{
described_class
.
new
(
note
,
request:
request
)
}
let
(
:epic
)
{
create
(
:epic
,
author:
user
)
}
let
(
:note
)
{
create
(
:note
,
noteable:
epic
,
author:
user
)
}
let
(
:user
)
{
create
(
:user
)
}
subject
{
entity
.
as_json
}
it_behaves_like
'note entity'
it
'exposes epic-specific elements'
do
expect
(
subject
).
to
include
(
:toggle_award_path
,
:path
)
end
end
lib/gitlab/etag_caching/router.rb
View file @
4b1d1a7e
module
Gitlab
module
EtagCaching
class
Router
prepend
EE
::
Gitlab
::
EtagCaching
::
Router
Route
=
Struct
.
new
(
:regexp
,
:name
)
# We enable an ETag for every request matching the regex.
# To match a regex the path needs to match the following:
...
...
spec/controllers/projects/issues_controller_spec.rb
View file @
4b1d1a7e
...
...
@@ -974,7 +974,7 @@ describe Projects::IssuesController do
it
'returns discussion json'
do
get
:discussions
,
namespace_id:
project
.
namespace
,
project_id:
project
,
id:
issue
.
iid
expect
(
json_response
.
first
.
keys
).
to
match_array
(
%w[id reply_id expanded notes diff_discussion individual_note resolvable resolve
_with_issue_path resolve
d]
)
expect
(
json_response
.
first
.
keys
).
to
match_array
(
%w[id reply_id expanded notes diff_discussion individual_note resolvable resolved]
)
end
it
'filters notes that the user should not see'
do
...
...
spec/serializers/discussion_entity_spec.rb
View file @
4b1d1a7e
...
...
@@ -6,7 +6,7 @@ describe DiscussionEntity do
let
(
:user
)
{
create
(
:user
)
}
let
(
:note
)
{
create
(
:discussion_note_on_merge_request
)
}
let
(
:discussion
)
{
note
.
discussion
}
let
(
:request
)
{
double
(
'request'
)
}
let
(
:request
)
{
double
(
'request'
,
note_entity:
ProjectNoteEntity
)
}
let
(
:controller
)
{
double
(
'controller'
)
}
let
(
:entity
)
{
described_class
.
new
(
discussion
,
request:
request
,
context:
controller
)
}
...
...
spec/serializers/note_entity_spec.rb
View file @
4b1d1a7e
...
...
@@ -10,53 +10,5 @@ describe NoteEntity do
let
(
:user
)
{
create
(
:user
)
}
subject
{
entity
.
as_json
}
context
'basic note'
do
it
'exposes correct elements'
do
expect
(
subject
).
to
include
(
:type
,
:author
,
:human_access
,
:note
,
:note_html
,
:current_user
,
:discussion_id
,
:emoji_awardable
,
:award_emoji
,
:toggle_award_path
,
:report_abuse_path
,
:path
,
:attachment
)
end
it
'does not expose elements for specific notes cases'
do
expect
(
subject
).
not_to
include
(
:last_edited_by
,
:last_edited_at
,
:system_note_icon_name
)
end
it
'exposes author correctly'
do
expect
(
subject
[
:author
]).
to
include
(
:id
,
:name
,
:username
,
:state
,
:avatar_url
,
:path
)
end
it
'does not expose web_url for author'
do
expect
(
subject
[
:author
]).
not_to
include
(
:web_url
)
end
end
context
'when note was edited'
do
before
do
note
.
update
(
updated_at:
1
.
minute
.
from_now
,
updated_by:
user
)
end
it
'exposes last_edited_at and last_edited_by elements'
do
expect
(
subject
).
to
include
(
:last_edited_at
,
:last_edited_by
)
end
end
context
'when note is a system note'
do
before
do
note
.
update
(
system:
true
)
end
it
'exposes system_note_icon_name element'
do
expect
(
subject
).
to
include
(
:system_note_icon_name
)
end
end
context
'when note is part of resolvable discussion'
do
before
do
allow
(
note
).
to
receive
(
:part_of_discussion?
).
and_return
(
true
)
allow
(
note
).
to
receive
(
:resolvable?
).
and_return
(
true
)
end
it
'exposes paths to resolve note'
do
expect
(
subject
).
to
include
(
:resolve_path
,
:resolve_with_issue_path
)
end
end
it_behaves_like
'note entity'
end
spec/serializers/project_note_entity_spec.rb
0 → 100644
View file @
4b1d1a7e
require
'spec_helper'
describe
ProjectNoteEntity
do
include
Gitlab
::
Routing
let
(
:request
)
{
double
(
'request'
,
current_user:
user
,
noteable:
note
.
noteable
)
}
let
(
:entity
)
{
described_class
.
new
(
note
,
request:
request
)
}
let
(
:note
)
{
create
(
:note
)
}
let
(
:user
)
{
create
(
:user
)
}
subject
{
entity
.
as_json
}
it_behaves_like
'note entity'
it
'exposes project-specific elements'
do
expect
(
subject
).
to
include
(
:human_access
,
:toggle_award_path
,
:path
)
end
context
'when note is part of resolvable discussion'
do
before
do
allow
(
note
).
to
receive
(
:part_of_discussion?
).
and_return
(
true
)
allow
(
note
).
to
receive
(
:resolvable?
).
and_return
(
true
)
end
it
'exposes paths to resolve note'
do
expect
(
subject
).
to
include
(
:resolve_path
,
:resolve_with_issue_path
)
end
end
end
spec/support/features/discussion_comments_shared_example.rb
View file @
4b1d1a7e
...
...
@@ -81,7 +81,10 @@ shared_examples 'discussion comments' do |resource_name|
# on issues page, the menu closes when clicking anywhere, on other pages it will
# remain open if clicking divider or menu padding, but should not change button action
if
resource_name
==
'issue'
#
# if dropdown menu is not toggled (and also not present),
# it's "issue-type" dropdown
if
first
(
menu_selector
).
nil?
expect
(
find
(
dropdown_selector
)).
to
have_content
'Comment'
find
(
toggle_selector
).
click
...
...
@@ -107,8 +110,10 @@ shared_examples 'discussion comments' do |resource_name|
end
it
'updates the submit button text and closes the dropdown'
do
button
=
find
(
submit_selector
)
# on issues page, the submit input is a <button>, on other pages it is <input>
if
resource_name
==
'issue
'
if
button
.
tag_name
==
'button
'
expect
(
find
(
submit_selector
)).
to
have_content
'Start discussion'
else
expect
(
find
(
submit_selector
).
value
).
to
eq
'Start discussion'
...
...
@@ -132,6 +137,8 @@ shared_examples 'discussion comments' do |resource_name|
describe
'creating a discussion'
do
before
do
find
(
submit_selector
).
click
wait_for_requests
find
(
comments_selector
,
match: :first
)
end
...
...
@@ -197,11 +204,13 @@ shared_examples 'discussion comments' do |resource_name|
end
it
'updates the submit button text and closes the dropdown'
do
button
=
find
(
submit_selector
)
# on issues page, the submit input is a <button>, on other pages it is <input>
if
resource_name
==
'issue
'
expect
(
find
(
submit_selector
)
).
to
have_content
'Comment'
if
button
.
tag_name
==
'button
'
expect
(
button
).
to
have_content
'Comment'
else
expect
(
find
(
submit_selector
)
.
value
).
to
eq
'Comment'
expect
(
button
.
value
).
to
eq
'Comment'
end
expect
(
page
).
not_to
have_selector
menu_selector
...
...
spec/support/shared_examples/serializers/note_entity_examples.rb
0 → 100644
View file @
4b1d1a7e
shared_examples
'note entity'
do
subject
{
entity
.
as_json
}
context
'basic note'
do
it
'exposes correct elements'
do
expect
(
subject
).
to
include
(
:type
,
:author
,
:note
,
:note_html
,
:current_user
,
:discussion_id
,
:emoji_awardable
,
:award_emoji
,
:report_abuse_path
,
:attachment
)
end
it
'does not expose elements for specific notes cases'
do
expect
(
subject
).
not_to
include
(
:last_edited_by
,
:last_edited_at
,
:system_note_icon_name
)
end
it
'exposes author correctly'
do
expect
(
subject
[
:author
]).
to
include
(
:id
,
:name
,
:username
,
:state
,
:avatar_url
,
:path
)
end
it
'does not expose web_url for author'
do
expect
(
subject
[
:author
]).
not_to
include
(
:web_url
)
end
end
context
'when note was edited'
do
before
do
note
.
update
(
updated_at:
1
.
minute
.
from_now
,
updated_by:
user
)
end
it
'exposes last_edited_at and last_edited_by elements'
do
expect
(
subject
).
to
include
(
:last_edited_at
,
:last_edited_by
)
end
end
context
'when note is a system note'
do
before
do
note
.
update
(
system:
true
)
end
it
'exposes system_note_icon_name element'
do
expect
(
subject
).
to
include
(
:system_note_icon_name
)
end
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