Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-04-14 21:09:09 +00:00
parent 844e3ef899
commit e30d680b42
18 changed files with 410 additions and 119 deletions

View File

@ -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">

View File

@ -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
>
<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"
<gl-button
:disabled="disableSubmitButton"
data-testid="add-comment-now-button"
category="secondary"
@click.prevent="handleSave()"
>{{ __('Add comment now') }}</gl-button
>
<strong>{{ $options.i18n.submitButton.startThread }}</strong>
<p class="gl-m-0">{{ startDiscussionDescription }}</p>
</gl-dropdown-item>
</gl-dropdown>
</template>
<template v-else>
<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"
>
<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>

View File

@ -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"

View File

@ -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']);

View File

@ -0,0 +1,9 @@
.gl-badge.feature-highlight-badge {
background-color: $purple-light;
color: $purple;
&,
&.sm {
padding: 0.25rem;
}
}

View File

@ -0,0 +1,5 @@
---
title: Allow Add Comment To Review
merge_request: 51718
author: Lee Tickett @leetickett
type: added

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -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.

View File

@ -108,6 +108,7 @@ The following table depicts the various user permission levels in a project.
| Upload [Design Management](project/issues/design_management.md) files | | | ✓ | ✓ | ✓ |
| Create/edit [releases](project/releases/index.md)| | | ✓ | ✓ | ✓ |
| Delete [releases](project/releases/index.md)| | | | ✓ | ✓ |
| Manage merge approval rules (project settings) | | | | ✓ | ✓ |
| Create new merge request | | | ✓ | ✓ | ✓ |
| Create new branches | | | ✓ | ✓ | ✓ |
| Push to non-protected branches | | | ✓ | ✓ | ✓ |
@ -300,6 +301,7 @@ group.
| View Value Stream analytics | ✓ | ✓ | ✓ | ✓ | ✓ |
| View Billing **(FREE SAAS)** | | | | | ✓ (4) |
| View Usage Quotas **(FREE SAAS)** | | | | | ✓ (4) |
| Manage [group push rules](group/index.md#group-push-rules) **(PREMIUM)** | | | | ✓ | ✓ |
| View 2FA status of members | | | | | ✓ |
| Filter members by 2FA status | | | | | ✓ |
| Administer project compliance frameworks | | | | | ✓ |

View File

@ -23591,7 +23591,7 @@ msgstr ""
msgid "Please enter a valid number"
msgstr ""
msgid "Please enter or upload a license."
msgid "Please enter or upload a valid license."
msgstr ""
msgid "Please fill in a descriptive name for your group."
@ -31872,6 +31872,9 @@ msgstr ""
msgid "This epic does not exist or you don't have sufficient permission."
msgstr ""
msgid "This feature is part of your GitLab Ultimate trial."
msgstr ""
msgid "This feature requires local storage to be enabled"
msgstr ""
@ -36303,6 +36306,9 @@ msgstr ""
msgid "Your new SCIM token"
msgstr ""
msgid "Your new comment"
msgstr ""
msgid "Your new personal access token has been created."
msgstr ""

View File

@ -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,23 +74,59 @@ 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')
end
wait_for_requests
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
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
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
before do
find('.js-show-diff-settings').click
@ -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
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
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)
if resolve
page.check('Resolve thread')
end
if unresolve
page.check('Unresolve thread')
end
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
fill_in('note_note', with: text)
if resolve
page.check('Resolve thread')
end
if unresolve
page.check('Unresolve thread')
end
click_button(button_text)
end
wait_for_requests
end

View File

@ -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',
);
});
});
});

View File

@ -1,10 +1,11 @@
import { GlDropdown, GlAlert } 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.findByTestId('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,
}),
);
});
});
});
});
});

View File

@ -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' }),
]);
});
});
});

View File

@ -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 };

View File

@ -1,4 +1,4 @@
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;
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);
});
});
});
it('should transform discussion to individual notes in timeline view', () => {
state.discussions = [discussionMock];
state.isTimelineEnabled = true;
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);
});
});