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
08721092
Commit
08721092
authored
Apr 14, 2021
by
Lee Tickett
Committed by
Paul Slaughter
Apr 14, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Allow Add Comment To Review
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51718
parent
f95046ba
Changes
17
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
397 additions
and
122 deletions
+397
-122
app/assets/javascripts/batch_comments/components/preview_item.vue
...ts/javascripts/batch_comments/components/preview_item.vue
+9
-5
app/assets/javascripts/notes/components/comment_form.vue
app/assets/javascripts/notes/components/comment_form.vue
+78
-50
app/assets/javascripts/notes/components/notes_app.vue
app/assets/javascripts/notes/components/notes_app.vue
+7
-0
app/assets/javascripts/notes/stores/getters.js
app/assets/javascripts/notes/stores/getters.js
+25
-2
changelogs/unreleased/8225-allow-add-discussion-to-review.yml
...gelogs/unreleased/8225-allow-add-discussion-to-review.yml
+5
-0
doc/user/discussions/img/mr_review_new_comment_v13_11.png
doc/user/discussions/img/mr_review_new_comment_v13_11.png
+0
-0
doc/user/discussions/img/review_preview.png
doc/user/discussions/img/review_preview.png
+0
-0
doc/user/discussions/img/review_preview_v13_11.png
doc/user/discussions/img/review_preview_v13_11.png
+0
-0
doc/user/discussions/index.md
doc/user/discussions/index.md
+12
-6
ee/app/assets/stylesheets/components/batch_comments/draft_note.scss
...ets/stylesheets/components/batch_comments/draft_note.scss
+9
-11
locale/gitlab.pot
locale/gitlab.pot
+3
-0
spec/features/merge_request/batch_comments_spec.rb
spec/features/merge_request/batch_comments_spec.rb
+74
-33
spec/frontend/batch_comments/components/preview_item_spec.js
spec/frontend/batch_comments/components/preview_item_spec.js
+12
-0
spec/frontend/notes/components/comment_form_spec.js
spec/frontend/notes/components/comment_form_spec.js
+65
-2
spec/frontend/notes/components/notes_app_spec.js
spec/frontend/notes/components/notes_app_spec.js
+32
-0
spec/frontend/notes/mock_data.js
spec/frontend/notes/mock_data.js
+10
-0
spec/frontend/notes/stores/getters_spec.js
spec/frontend/notes/stores/getters_spec.js
+56
-13
No files found.
app/assets/javascripts/batch_comments/components/preview_item.vue
View file @
08721092
...
...
@@ -41,13 +41,17 @@ export default {
titleText
()
{
const
file
=
this
.
discussion
?
this
.
discussion
.
diff_file
:
this
.
draft
;
if
(
file
)
{
if
(
file
?.
file_path
)
{
return
file
.
file_path
;
}
return
sprintf
(
__
(
"
%{authorsName}'s thread
"
),
{
authorsName
:
this
.
discussion
.
notes
.
find
((
note
)
=>
!
note
.
system
).
author
.
name
,
});
if
(
this
.
discussion
)
{
return
sprintf
(
__
(
"
%{authorsName}'s thread
"
),
{
authorsName
:
this
.
discussion
.
notes
.
find
((
note
)
=>
!
note
.
system
).
author
.
name
,
});
}
return
__
(
'
Your new comment
'
);
},
linePosition
()
{
if
(
this
.
position
?.
position_type
===
IMAGE_DIFF_POSITION_TYPE
)
{
...
...
@@ -94,7 +98,7 @@ export default {
<span
class=
"review-preview-item-header"
>
<gl-icon
class=
"flex-shrink-0"
:name=
"iconName"
/>
<span
class=
"bold text-nowrap gl-align-items-center"
>
<span
class=
"review-preview-item-header-text block-truncated"
>
<span
class=
"review-preview-item-header-text block-truncated
gl-ml-2
"
>
{{
titleText
}}
</span>
<template
v-if=
"showLinePosition"
>
...
...
app/assets/javascripts/notes/components/comment_form.vue
View file @
08721092
...
...
@@ -84,6 +84,7 @@ export default {
'
getNoteableDataByProp
'
,
'
getNotesData
'
,
'
openState
'
,
'
hasDrafts
'
,
]),
...
mapState
([
'
isToggleStateButtonLoading
'
]),
isNoteTypeComment
()
{
...
...
@@ -171,6 +172,9 @@ export default {
endpoint
()
{
return
this
.
getNoteableData
.
create_note_path
;
},
draftEndpoint
()
{
return
this
.
getNotesData
.
draftsPath
;
},
issuableTypeTitle
()
{
return
this
.
noteableType
===
constants
.
MERGE_REQUEST_NOTEABLE_TYPE
?
this
.
$options
.
i18n
.
mergeRequest
...
...
@@ -214,12 +218,15 @@ export default {
this
.
errors
=
[
this
.
$options
.
i18n
.
GENERIC_UNSUBMITTABLE_NETWORK
];
}
},
handleSave
(
withIssueAction
)
{
handleSaveDraft
()
{
this
.
handleSave
({
isDraft
:
true
});
},
handleSave
({
withIssueAction
=
false
,
isDraft
=
false
}
=
{})
{
this
.
errors
=
[];
if
(
this
.
note
.
length
)
{
const
noteData
=
{
endpoint
:
this
.
endpoint
,
endpoint
:
isDraft
?
this
.
draftEndpoint
:
this
.
endpoint
,
data
:
{
note
:
{
noteable_type
:
this
.
noteableType
,
...
...
@@ -229,6 +236,7 @@ export default {
},
merge_request_diff_head_sha
:
this
.
getNoteableData
.
diff_head_sha
,
},
isDraft
,
};
if
(
this
.
noteType
===
constants
.
DISCUSSION
)
{
...
...
@@ -392,62 +400,82 @@ export default {
</markdown-field>
</comment-field-layout>
<div
class=
"note-form-actions"
>
<gl-form-checkbox
v-if=
"confidentialNotesEnabled && canSetConfidential"
v-model=
"noteIsConfidential"
class=
"gl-mb-6"
data-testid=
"confidential-note-checkbox"
>
{{ $options.i18n.confidential }}
<gl-icon
v-gl-tooltip:tooltipcontainer
.
bottom
name=
"question"
:size=
"16"
:title=
"$options.i18n.confidentialVisibility"
class=
"gl-text-gray-500"
/>
</gl-form-checkbox>
<gl-dropdown
split
:text=
"commentButtonTitle"
class=
"gl-mr-3 js-comment-button js-comment-submit-button comment-type-dropdown"
category=
"primary"
variant=
"confirm"
:disabled=
"disableSubmitButton"
data-testid=
"comment-button"
data-qa-selector=
"comment_button"
:data-track-label=
"trackingLabel"
data-track-event=
"click_button"
@
click=
"handleSave()"
>
<gl-dropdown-item
is-check-item
:is-checked=
"isNoteTypeComment"
:selected=
"isNoteTypeComment"
@
click=
"setNoteTypeToComment"
<
template
v-if=
"hasDrafts"
>
<gl-button
:disabled=
"disableSubmitButton"
data-testid=
"add-to-review-button"
type=
"submit"
category=
"primary"
variant=
"success"
@
click.prevent=
"handleSaveDraft()"
>
{{
__
(
'
Add to review
'
)
}}
</gl-button
>
<gl-button
:disabled=
"disableSubmitButton"
data-testid=
"add-comment-now-button"
category=
"secondary"
@
click.prevent=
"handleSave()"
>
{{
__
(
'
Add comment now
'
)
}}
</gl-button
>
</
template
>
<
template
v-else
>
<gl-form-checkbox
v-if=
"confidentialNotesEnabled && canSetConfidential"
v-model=
"noteIsConfidential"
class=
"gl-mb-6"
data-testid=
"confidential-note-checkbox"
>
<strong>
{{ $options.i18n.submitButton.comment }}
</strong>
<p
class=
"gl-m-0"
>
{{ commentDescription }}
</p>
</gl-dropdown-item>
<gl-dropdown-divider
/>
<gl-dropdown-item
is-check-item
:is-checked=
"isNoteTypeDiscussion"
:selected=
"isNoteTypeDiscussion"
data-qa-selector=
"discussion_menu_item"
@
click=
"setNoteTypeToDiscussion"
{{
$options
.
i18n
.
confidential
}}
<gl-icon
v-gl-tooltip:tooltipcontainer
.
bottom
name=
"question"
:size=
"16"
:title=
"$options.i18n.confidentialVisibility"
class=
"gl-text-gray-500"
/>
</gl-form-checkbox>
<gl-dropdown
split
:text=
"commentButtonTitle"
class=
"gl-mr-3 js-comment-button js-comment-submit-button comment-type-dropdown"
category=
"primary"
variant=
"confirm"
:disabled=
"disableSubmitButton"
data-testid=
"comment-button"
data-qa-selector=
"comment_button"
:data-track-label=
"trackingLabel"
data-track-event=
"click_button"
@
click=
"handleSave()"
>
<strong>
{{ $options.i18n.submitButton.startThread }}
</strong>
<p
class=
"gl-m-0"
>
{{ startDiscussionDescription }}
</p>
</gl-dropdown-item>
</gl-dropdown>
<gl-dropdown-item
is-check-item
:is-checked=
"isNoteTypeComment"
:selected=
"isNoteTypeComment"
@
click=
"setNoteTypeToComment"
>
<strong>
{{
$options
.
i18n
.
submitButton
.
comment
}}
</strong>
<p
class=
"gl-m-0"
>
{{
commentDescription
}}
</p>
</gl-dropdown-item>
<gl-dropdown-divider
/>
<gl-dropdown-item
is-check-item
:is-checked=
"isNoteTypeDiscussion"
:selected=
"isNoteTypeDiscussion"
data-qa-selector=
"discussion_menu_item"
@
click=
"setNoteTypeToDiscussion"
>
<strong>
{{
$options
.
i18n
.
submitButton
.
startThread
}}
</strong>
<p
class=
"gl-m-0"
>
{{
startDiscussionDescription
}}
</p>
</gl-dropdown-item>
</gl-dropdown>
</
template
>
<gl-button
v-if=
"canToggleIssueState"
:loading=
"isToggleStateButtonLoading"
:class=
"[actionButtonClassNames, 'btn-comment btn-comment-and-close']"
:disabled=
"isSubmitting"
data-testid=
"close-reopen-button"
@
click=
"handleSave(
true
)"
@
click=
"handleSave(
{ withIssueAction: true }
)"
>
{{ issueActionButtonTitle }}
</gl-button
>
</div>
...
...
app/assets/javascripts/notes/components/notes_app.vue
View file @
08721092
...
...
@@ -3,8 +3,10 @@ import { mapGetters, mapActions } from 'vuex';
import
highlightCurrentUser
from
'
~/behaviors/markdown/highlight_current_user
'
;
import
{
__
}
from
'
~/locale
'
;
import
initUserPopovers
from
'
~/user_popovers
'
;
import
TimelineEntryItem
from
'
~/vue_shared/components/notes/timeline_entry_item.vue
'
;
import
OrderedLayout
from
'
~/vue_shared/components/ordered_layout.vue
'
;
import
glFeatureFlagsMixin
from
'
~/vue_shared/mixins/gl_feature_flags_mixin
'
;
import
draftNote
from
'
../../batch_comments/components/draft_note.vue
'
;
import
{
deprecatedCreateFlash
as
Flash
}
from
'
../../flash
'
;
import
{
getLocationHash
,
doesHashExistInUrl
}
from
'
../../lib/utils/url_utility
'
;
import
placeholderNote
from
'
../../vue_shared/components/notes/placeholder_note.vue
'
;
...
...
@@ -32,6 +34,8 @@ export default {
discussionFilterNote
,
OrderedLayout
,
SidebarSubscription
,
draftNote
,
TimelineEntryItem
,
},
mixins
:
[
glFeatureFlagsMixin
()],
props
:
{
...
...
@@ -276,6 +280,9 @@ export default {
<ul
id=
"notes-list"
class=
"notes main-notes-list timeline"
>
<template
v-for=
"discussion in allDiscussions"
>
<skeleton-loading-container
v-if=
"discussion.isSkeletonNote"
:key=
"discussion.id"
/>
<timeline-entry-item
v-else-if=
"discussion.isDraft"
:key=
"discussion.id"
>
<draft-note
:draft=
"discussion"
/>
</timeline-entry-item>
<template
v-else-if=
"discussion.isPlaceholderNote"
>
<placeholder-system-note
v-if=
"discussion.placeholderType === $options.systemNote"
...
...
app/assets/javascripts/notes/stores/getters.js
View file @
08721092
...
...
@@ -2,7 +2,23 @@ import { flattenDeep, clone } from 'lodash';
import
*
as
constants
from
'
../constants
'
;
import
{
collapseSystemNotes
}
from
'
./collapse_utils
'
;
export
const
discussions
=
(
state
)
=>
{
const
getDraftComments
=
(
state
)
=>
{
if
(
!
state
.
batchComments
)
{
return
[];
}
return
state
.
batchComments
.
drafts
.
filter
((
draft
)
=>
!
draft
.
line_code
&&
!
draft
.
discussion_id
)
.
map
((
x
)
=>
({
...
x
,
// Treat a top-level draft note as individual_note so it's not included in
// expand/collapse threads
individual_note
:
true
,
}))
.
sort
((
a
,
b
)
=>
a
.
id
-
b
.
id
);
};
export
const
discussions
=
(
state
,
getters
,
rootState
)
=>
{
let
discussionsInState
=
clone
(
state
.
discussions
);
// NOTE: not testing bc will be removed when backend is finished.
...
...
@@ -22,11 +38,15 @@ export const discussions = (state) => {
.
sort
((
a
,
b
)
=>
new
Date
(
a
.
created_at
)
-
new
Date
(
b
.
created_at
));
}
discussionsInState
=
collapseSystemNotes
(
discussionsInState
);
discussionsInState
=
discussionsInState
.
concat
(
getDraftComments
(
rootState
));
if
(
state
.
discussionSortOrder
===
constants
.
DESC
)
{
discussionsInState
=
discussionsInState
.
reverse
();
}
return
collapseSystemNotes
(
discussionsInState
)
;
return
discussionsInState
;
};
export
const
convertedDisscussionIds
=
(
state
)
=>
state
.
convertedDisscussionIds
;
...
...
@@ -257,3 +277,6 @@ export const commentsDisabled = (state) => state.commentsDisabled;
export
const
suggestionsCount
=
(
state
,
getters
)
=>
Object
.
values
(
getters
.
notesById
).
filter
((
n
)
=>
n
.
suggestions
.
length
).
length
;
export
const
hasDrafts
=
(
state
,
getters
,
rootState
,
rootGetters
)
=>
Boolean
(
rootGetters
[
'
batchComments/hasDrafts
'
]);
changelogs/unreleased/8225-allow-add-discussion-to-review.yml
0 → 100644
View file @
08721092
---
title
:
Allow Add Comment To Review
merge_request
:
51718
author
:
Lee Tickett @leetickett
type
:
added
doc/user/discussions/img/mr_review_new_comment_v13_11.png
0 → 100644
View file @
08721092
31.4 KB
doc/user/discussions/img/review_preview.png
deleted
100644 → 0
View file @
f95046ba
27 KB
doc/user/discussions/img/review_preview_v13_11.png
0 → 100644
View file @
08721092
27.6 KB
doc/user/discussions/index.md
View file @
08721092
...
...
@@ -334,22 +334,28 @@ comment itself.
![
Unresolve status
](
img/mr_review_unresolve.png
)
### Adding a new comment
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8225) in GitLab 13.10.
If you have a review in progress, you will be presented with the option to
**Add to review**
:
![
New thread
](
img/mr_review_new_comment_v13_11.png
)
### Submitting a review
If you have any comments that have not been submitted, a bar displays at the
bottom of the screen with two buttons:
-
**Discard**
: Discards all comments that have not been submitted.
-
**Finish review**
: Opens a list of comments ready to be submitted for review.
Clicking
**Submit review**
publishes all comments. Any quick actions
submitted are performed at this time.
-
**Pending comments**
: Opens a list of comments ready to be submitted for review.
-
**Submit review**
: Publishes all comments. Any quick actions submitted are performed at this time.
Alternatively, to finish the entire review from a pending comment:
-
Click the
**
Finish
review**
button on the comment.
-
Click the
**
Submit
review**
button on the comment.
-
Use the
`/submit_review`
[
quick action
](
../project/quick_actions.md
)
in the text of non-review comment.
![
Review submission
](
img/review_preview.png
)
![
Review submission
](
img/review_preview
_v13_11
.png
)
Submitting the review sends a single email to every notifiable user of the
merge request with all the comments associated to it.
...
...
ee/app/assets/stylesheets/components/batch_comments/draft_note.scss
View file @
08721092
...
...
@@ -40,20 +40,18 @@ button[disabled] {
vertical-align
:
text-top
;
}
.discussion-body
,
.diff-file
{
.notes
.note
{
&
.draft-note
{
margin
:
0
0
$gl-padding
;
padding
:
0
;
border
:
0
;
.note.draft-note
{
margin
:
0
0
$gl-padding
;
padding
:
0
;
border
:
0
;
&
.is-editing
{
margin-bottom
:
0
;
}
}
&
.is-editing
{
margin-bottom
:
0
;
}
}
.discussion-body
,
.diff-file
{
.notes_holder
{
.notes-content
{
.notes
{
...
...
locale/gitlab.pot
View file @
08721092
...
...
@@ -35998,6 +35998,9 @@ msgstr ""
msgid "Your new SCIM token"
msgstr ""
msgid "Your new comment"
msgstr ""
msgid "Your new personal access token has been created."
msgstr ""
...
...
spec/features/merge_request/batch_comments_spec.rb
View file @
08721092
...
...
@@ -28,7 +28,7 @@ RSpec.describe 'Merge request > Batch comments', :js do
end
it
'adds draft note'
do
write_comment
write_
diff_
comment
expect
(
find
(
'.draft-note-component'
)).
to
have_content
(
'Line is wrong'
)
...
...
@@ -38,7 +38,7 @@ RSpec.describe 'Merge request > Batch comments', :js do
end
it
'publishes review'
do
write_comment
write_
diff_
comment
page
.
within
(
'.review-bar-content'
)
do
click_button
'Submit review'
...
...
@@ -52,7 +52,7 @@ RSpec.describe 'Merge request > Batch comments', :js do
end
it
'publishes single comment'
do
write_comment
write_
diff_
comment
click_button
'Add comment now'
...
...
@@ -64,7 +64,7 @@ RSpec.describe 'Merge request > Batch comments', :js do
end
it
'deletes draft note'
do
write_comment
write_
diff_
comment
accept_alert
{
find
(
'.js-note-delete'
).
click
}
...
...
@@ -74,21 +74,57 @@ RSpec.describe 'Merge request > Batch comments', :js do
end
it
'edits draft note'
do
write_comment
write_
diff_
comment
find
(
'.js-note-edit'
).
click
# make sure comment form is in view
execute_script
(
"window.scrollBy(0, 200)"
)
page
.
within
(
'.js-discussion-note-form'
)
do
fill_in
(
'note_note'
,
with:
'Testing update'
)
click_button
(
'Save comment'
)
write_comment
(
text:
'Testing update'
,
button_text:
'Save comment'
)
expect
(
page
).
to
have_selector
(
'.draft-note-component'
,
text:
'Testing update'
)
end
context
'adding single comment to review'
do
before
do
visit_overview
end
wait_for_requests
it
'at first does not show `Add to review` and `Add comment now` buttons'
do
expect
(
page
).
to
have_no_button
(
'Add to review'
)
expect
(
page
).
to
have_no_button
(
'Add comment now'
)
end
expect
(
page
).
to
have_selector
(
'.draft-note-component'
,
text:
'Testing update'
)
context
'when review has started'
do
before
do
visit_diffs
write_diff_comment
visit_overview
end
it
'can add comment to review'
do
write_comment
(
selector:
'.js-main-target-form'
,
field:
'note-body'
,
text:
'Its a draft comment'
,
button_text:
'Add to review'
)
expect
(
page
).
to
have_selector
(
'.draft-note-component'
,
text:
'Its a draft comment'
)
click_button
(
'Pending comments'
)
expect
(
page
).
to
have_text
(
'2 pending comments'
)
end
it
'can add comment right away'
do
write_comment
(
selector:
'.js-main-target-form'
,
field:
'note-body'
,
text:
'Its a regular comment'
,
button_text:
'Add comment now'
)
expect
(
page
).
to
have_selector
(
'.note:not(.draft-note)'
,
text:
'Its a regular comment'
)
click_button
(
'Pending comments'
)
expect
(
page
).
to
have_text
(
'1 pending comment'
)
end
end
end
context
'in parallel diff'
do
...
...
@@ -197,46 +233,51 @@ RSpec.describe 'Merge request > Batch comments', :js do
wait_for_requests
end
def
write_comment
(
button_text:
'Start a review'
,
text:
'Line is wrong'
)
click_diff_line
(
find
(
"[id='
#{
sample_compare
.
changes
[
0
][
:line_code
]
}
']"
))
page
.
within
(
'.js-discussion-note-form'
)
do
fill_in
(
'note_note'
,
with:
text
)
click_button
(
button_text
)
end
def
visit_overview
visit
project_merge_request_path
(
merge_request
.
project
,
merge_request
)
wait_for_requests
end
def
write_parallel_comment
(
line
,
button_text:
'Start a review'
,
text:
'Line is wrong'
)
def
write_diff_comment
(
**
params
)
click_diff_line
(
find
(
"[id='
#{
sample_compare
.
changes
[
0
][
:line_code
]
}
']"
))
write_comment
(
**
params
)
end
def
write_parallel_comment
(
line
,
**
params
)
find
(
"td[id='
#{
line
}
']"
).
hover
find
(
".is-over button"
).
click
page
.
within
(
"form[data-line-code='
#{
line
}
']"
)
do
fill_in
(
'note_note'
,
with:
text
)
write_comment
(
selector:
"form[data-line-code='
#{
line
}
']"
,
**
params
)
end
def
write_comment
(
selector:
'.js-discussion-note-form'
,
field:
'note_note'
,
button_text:
'Start a review'
,
text:
'Line is wrong'
)
page
.
within
(
selector
)
do
fill_in
(
field
,
with:
text
)
click_button
(
button_text
)
end
wait_for_requests
end
end
def
write_reply_to_discussion
(
button_text:
'Start a review'
,
text:
'Line is wrong'
,
resolve:
false
,
unresolve:
false
)
page
.
within
(
first
(
'.diff-files-holder .discussion-reply-holder'
))
do
find_field
(
'Reply…'
,
match: :first
).
click
def
write_reply_to_discussion
(
button_text:
'Start a review'
,
text:
'Line is wrong'
,
resolve:
false
,
unresolve:
false
)
page
.
within
(
first
(
'.diff-files-holder .discussion-reply-holder'
))
do
find_field
(
'Reply…'
,
match: :first
).
click
fill_in
(
'note_note'
,
with:
text
)
fill_in
(
'note_note'
,
with:
text
)
if
resolve
page
.
check
(
'Resolve thread'
)
end
if
resolve
page
.
check
(
'Resolve thread'
)
end
if
unresolve
page
.
check
(
'Unresolve thread'
)
if
unresolve
page
.
check
(
'Unresolve thread'
)
end
click_button
(
button_text
)
end
click_button
(
button_text
)
wait_for_requests
end
wait_for_requests
end
spec/frontend/batch_comments/components/preview_item_spec.js
View file @
08721092
...
...
@@ -124,4 +124,16 @@ describe('Batch comments draft preview item component', () => {
);
});
});
describe
(
'
for new comment
'
,
()
=>
{
it
(
'
renders title
'
,
()
=>
{
createComponent
(
false
,
{},
(
store
)
=>
{
store
.
state
.
notes
.
discussions
.
push
({});
});
expect
(
vm
.
$el
.
querySelector
(
'
.review-preview-item-header-text
'
).
textContent
).
toContain
(
'
Your new comment
'
,
);
});
});
});
spec/frontend/notes/components/comment_form_spec.js
View file @
08721092
import
{
Gl
Dropdown
,
Gl
Alert
}
from
'
@gitlab/ui
'
;
import
{
GlAlert
}
from
'
@gitlab/ui
'
;
import
{
mount
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
Autosize
from
'
autosize
'
;
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
Vue
,
{
nextTick
}
from
'
vue
'
;
import
Vuex
from
'
vuex
'
;
import
{
extendedWrapper
}
from
'
helpers/vue_test_utils_helper
'
;
import
batchComments
from
'
~/batch_comments/stores/modules/batch_comments
'
;
import
{
refreshUserMergeRequestCounts
}
from
'
~/commons/nav/user_merge_requests
'
;
import
{
deprecatedCreateFlash
as
flash
}
from
'
~/flash
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
...
...
@@ -29,8 +30,10 @@ describe('issue_comment_form component', () => {
const
findCloseReopenButton
=
()
=>
wrapper
.
findByTestId
(
'
close-reopen-button
'
);
const
findTextArea
=
()
=>
wrapper
.
findByTestId
(
'
comment-field
'
);
const
findAddToReviewButton
=
()
=>
wrapper
.
findByTestId
(
'
add-to-review-button
'
);
const
findAddCommentNowButton
=
()
=>
wrapper
.
findByTestId
(
'
add-comment-now-button
'
);
const
findConfidentialNoteCheckbox
=
()
=>
wrapper
.
findByTestId
(
'
confidential-note-checkbox
'
);
const
findCommentGlDropdown
=
()
=>
wrapper
.
find
(
GlDropdown
);
const
findCommentGlDropdown
=
()
=>
wrapper
.
find
ByTestId
(
'
comment-button
'
);
const
findCommentButton
=
()
=>
findCommentGlDropdown
().
find
(
'
button
'
);
const
findErrorAlerts
=
()
=>
wrapper
.
findAllComponents
(
GlAlert
).
wrappers
;
...
...
@@ -582,4 +585,64 @@ describe('issue_comment_form component', () => {
expect
(
findTextArea
().
exists
()).
toBe
(
false
);
});
});
describe
(
'
with batchComments in store
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
registerModule
(
'
batchComments
'
,
batchComments
());
});
describe
(
'
add to review and comment now buttons
'
,
()
=>
{
it
(
'
when no drafts exist, should not render
'
,
()
=>
{
mountComponent
();
expect
(
findCommentGlDropdown
().
exists
()).
toBe
(
true
);
expect
(
findAddToReviewButton
().
exists
()).
toBe
(
false
);
expect
(
findAddCommentNowButton
().
exists
()).
toBe
(
false
);
});
describe
(
'
when drafts exist
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
batchComments
.
drafts
=
[{
note
:
'
A
'
}];
});
it
(
'
should render
'
,
()
=>
{
mountComponent
();
expect
(
findCommentGlDropdown
().
exists
()).
toBe
(
false
);
expect
(
findAddToReviewButton
().
exists
()).
toBe
(
true
);
expect
(
findAddCommentNowButton
().
exists
()).
toBe
(
true
);
});
it
(
'
clicking `add to review`, should call draft endpoint, set `isDraft` true
'
,
()
=>
{
mountComponent
({
mountFunction
:
mount
,
initialData
:
{
note
:
'
a draft note
'
}
});
jest
.
spyOn
(
store
,
'
dispatch
'
).
mockResolvedValue
();
findAddToReviewButton
().
trigger
(
'
click
'
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
saveNote
'
,
expect
.
objectContaining
({
endpoint
:
notesDataMock
.
draftsPath
,
isDraft
:
true
,
}),
);
});
it
(
'
clicking `add comment now`, should call note endpoint, set `isDraft` false
'
,
()
=>
{
mountComponent
({
mountFunction
:
mount
,
initialData
:
{
note
:
'
a comment
'
}
});
jest
.
spyOn
(
store
,
'
dispatch
'
).
mockResolvedValue
();
findAddCommentNowButton
().
trigger
(
'
click
'
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
saveNote
'
,
expect
.
objectContaining
({
endpoint
:
noteableDataMock
.
create_note_path
,
isDraft
:
false
,
}),
);
});
});
});
});
});
spec/frontend/notes/components/notes_app_spec.js
View file @
08721092
...
...
@@ -3,6 +3,8 @@ import AxiosMockAdapter from 'axios-mock-adapter';
import
$
from
'
jquery
'
;
import
Vue
from
'
vue
'
;
import
{
setTestTimeout
}
from
'
helpers/timeout
'
;
import
DraftNote
from
'
~/batch_comments/components/draft_note.vue
'
;
import
batchComments
from
'
~/batch_comments/stores/modules/batch_comments
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
*
as
urlUtility
from
'
~/lib/utils/url_utility
'
;
import
CommentForm
from
'
~/notes/components/comment_form.vue
'
;
...
...
@@ -400,4 +402,34 @@ describe('note_app', () => {
expect
(
getComponentOrder
()).
toStrictEqual
([
TYPE_NOTES_LIST
,
TYPE_COMMENT_FORM
]);
});
});
describe
(
'
when multiple draft types are present
'
,
()
=>
{
beforeEach
(()
=>
{
store
=
createStore
();
store
.
registerModule
(
'
batchComments
'
,
batchComments
());
store
.
state
.
batchComments
.
drafts
=
[
{
line_code
:
1
,
isDraft
:
true
},
{
discussion_id
:
1
,
isDraft
:
true
},
{
note
:
'
A
'
,
isDraft
:
true
},
{
note
:
'
B
'
,
isDraft
:
true
},
];
store
.
state
.
isLoading
=
false
;
wrapper
=
shallowMount
(
NotesApp
,
{
propsData
,
store
,
stubs
:
{
OrderedLayout
,
},
});
});
it
(
'
correctly finds only draft comments
'
,
()
=>
{
const
drafts
=
wrapper
.
findAll
(
DraftNote
).
wrappers
;
expect
(
drafts
.
map
((
x
)
=>
x
.
props
(
'
draft
'
))).
toEqual
([
expect
.
objectContaining
({
note
:
'
A
'
}),
expect
.
objectContaining
({
note
:
'
B
'
}),
]);
});
});
});
spec/frontend/notes/mock_data.js
View file @
08721092
...
...
@@ -6,6 +6,7 @@ export const notesDataMock = {
markdownDocsPath
:
'
/help/user/markdown
'
,
newSessionPath
:
'
/users/sign_in?redirect_to_referer=yes
'
,
notesPath
:
'
/gitlab-org/gitlab-foss/noteable/issue/98/notes
'
,
draftsPath
:
'
/flightjs/flight/-/merge_requests/4/drafts
'
,
quickActionsDocsPath
:
'
/help/user/project/quick_actions
'
,
registerPath
:
'
/users/sign_up?redirect_to_referer=yes
'
,
prerenderedNotesCount
:
1
,
...
...
@@ -1270,3 +1271,12 @@ export const batchSuggestionsInfoMock = [
discussionId
:
'
c003
'
,
},
];
export
const
draftComments
=
[
{
id
:
7
,
note
:
'
test draft note
'
},
{
id
:
9
,
note
:
'
draft note 2
'
},
];
export
const
draftReply
=
{
id
:
8
,
note
:
'
draft reply
'
,
discussion_id
:
1
};
export
const
draftDiffDiscussion
=
{
id
:
6
,
note
:
'
draft diff discussion
'
,
line_code
:
1
};
spec/frontend/notes/stores/getters_spec.js
View file @
08721092
import
{
DESC
}
from
'
~/notes/constants
'
;
import
{
DESC
,
ASC
}
from
'
~/notes/constants
'
;
import
*
as
getters
from
'
~/notes/stores/getters
'
;
import
{
notesDataMock
,
...
...
@@ -12,6 +12,9 @@ import {
discussion3
,
resolvedDiscussion1
,
unresolvableDiscussion
,
draftComments
,
draftReply
,
draftDiffDiscussion
,
}
from
'
../mock_data
'
;
const
discussionWithTwoUnresolvedNotes
=
'
merge_requests/resolved_diff_discussion.json
'
;
...
...
@@ -23,6 +26,8 @@ const createDiscussionNeighborParams = (discussionId, diffOrder, step) => ({
step
,
});
const
asDraftDiscussion
=
(
x
)
=>
({
...
x
,
individual_note
:
true
});
describe
(
'
Getters Notes Store
'
,
()
=>
{
let
state
;
...
...
@@ -61,20 +66,58 @@ describe('Getters Notes Store', () => {
});
describe
(
'
discussions
'
,
()
=>
{
it
(
'
should return all discussions in the store
'
,
()
=>
{
expect
(
getters
.
discussions
(
state
)).
toEqual
([
individualNote
]);
});
let
batchComments
=
null
;
const
getDiscussions
=
()
=>
getters
.
discussions
(
state
,
{},
{
batchComments
});
describe
(
'
without batchComments module
'
,
()
=>
{
it
(
'
should return all discussions in the store
'
,
()
=>
{
expect
(
getDiscussions
()).
toEqual
([
individualNote
]);
});
it
(
'
should transform discussion to individual notes in timeline view
'
,
()
=>
{
state
.
discussions
=
[
discussionMock
];
state
.
isTimelineEnabled
=
true
;
it
(
'
should transform discussion to individual notes in timeline view
'
,
()
=>
{
state
.
discussions
=
[
discussionMock
];
state
.
isTimelineEnabled
=
true
;
const
discussions
=
getDiscussions
();
expect
(
discussions
.
length
).
toEqual
(
discussionMock
.
notes
.
length
);
discussions
.
forEach
((
discussion
)
=>
{
expect
(
discussion
.
individual_note
).
toBe
(
true
);
expect
(
discussion
.
id
).
toBe
(
discussion
.
notes
[
0
].
id
);
expect
(
discussion
.
created_at
).
toBe
(
discussion
.
notes
[
0
].
created_at
);
});
});
});
expect
(
getters
.
discussions
(
state
).
length
).
toEqual
(
discussionMock
.
notes
.
length
);
getters
.
discussions
(
state
).
forEach
((
discussion
)
=>
{
expect
(
discussion
.
individual_note
).
toBe
(
true
);
expect
(
discussion
.
id
).
toBe
(
discussion
.
notes
[
0
].
id
);
expect
(
discussion
.
created_at
).
toBe
(
discussion
.
notes
[
0
].
created_at
);
describe
(
'
with batchComments
'
,
()
=>
{
beforeEach
(()
=>
{
batchComments
=
{
drafts
:
[...
draftComments
,
draftReply
,
draftDiffDiscussion
]
};
});
it
.
each
`
discussionSortOrder | expectation
${
ASC
}
|
${[
individualNote
,
...
draftComments
.
map
(
asDraftDiscussion
)]}
${
DESC
}
|
${[...
draftComments
.
reverse
().
map
(
asDraftDiscussion
),
individualNote
]}
`
(
'
only appends draft comments (discussionSortOrder=$discussionSortOrder)
'
,
({
discussionSortOrder
,
expectation
})
=>
{
state
.
discussionSortOrder
=
discussionSortOrder
;
expect
(
getDiscussions
()).
toEqual
(
expectation
);
},
);
});
});
describe
(
'
hasDrafts
'
,
()
=>
{
it
.
each
`
rootGetters | expected
${{}}
|
$
{
false
}
${{
'
batchComments/hasDrafts
'
:
true
}
} |
${
true
}
${{
'
batchComments/hasDrafts
'
:
false
}
} |
${
false
}
`
(
'
with rootGetters=$rootGetters, returns $expected
'
,
({
rootGetters
,
expected
})
=>
{
expect
(
getters
.
hasDrafts
({},
{},
{},
rootGetters
)).
toBe
(
expected
);
});
});
...
...
@@ -103,7 +146,7 @@ describe('Getters Notes Store', () => {
};
it
(
'
should return a single system note when a description was updated multiple times
'
,
()
=>
{
expect
(
getters
.
discussions
(
stateCollapsedNotes
).
length
).
toEqual
(
1
);
expect
(
getters
.
discussions
(
stateCollapsedNotes
,
{},
{}
).
length
).
toEqual
(
1
);
});
});
...
...
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