Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/notes/components')
-rw-r--r--spec/frontend/notes/components/diff_with_note_spec.js9
-rw-r--r--spec/frontend/notes/components/discussion_reply_placeholder_spec.js2
-rw-r--r--spec/frontend/notes/components/multiline_comment_utils_spec.js49
-rw-r--r--spec/frontend/notes/components/note_actions_spec.js60
-rw-r--r--spec/frontend/notes/components/note_form_spec.js54
-rw-r--r--spec/frontend/notes/components/noteable_note_spec.js53
6 files changed, 218 insertions, 9 deletions
diff --git a/spec/frontend/notes/components/diff_with_note_spec.js b/spec/frontend/notes/components/diff_with_note_spec.js
index d6d42e1988d..6480af015db 100644
--- a/spec/frontend/notes/components/diff_with_note_spec.js
+++ b/spec/frontend/notes/components/diff_with_note_spec.js
@@ -1,4 +1,4 @@
-import { mount } from '@vue/test-utils';
+import { shallowMount } from '@vue/test-utils';
import DiffWithNote from '~/notes/components/diff_with_note.vue';
import { createStore } from '~/mr_notes/stores';
@@ -37,7 +37,7 @@ describe('diff_with_note', () => {
beforeEach(() => {
const diffDiscussion = getJSONFixture(discussionFixture)[0];
- wrapper = mount(DiffWithNote, {
+ wrapper = shallowMount(DiffWithNote, {
propsData: {
discussion: diffDiscussion,
},
@@ -76,7 +76,10 @@ describe('diff_with_note', () => {
describe('image diff', () => {
beforeEach(() => {
const imageDiscussion = getJSONFixture(imageDiscussionFixture)[0];
- wrapper = mount(DiffWithNote, { propsData: { discussion: imageDiscussion }, store });
+ wrapper = shallowMount(DiffWithNote, {
+ propsData: { discussion: imageDiscussion, diffFile: {} },
+ store,
+ });
});
it('shows image diff', () => {
diff --git a/spec/frontend/notes/components/discussion_reply_placeholder_spec.js b/spec/frontend/notes/components/discussion_reply_placeholder_spec.js
index a881e44a007..b7b7ec08867 100644
--- a/spec/frontend/notes/components/discussion_reply_placeholder_spec.js
+++ b/spec/frontend/notes/components/discussion_reply_placeholder_spec.js
@@ -20,7 +20,7 @@ describe('ReplyPlaceholder', () => {
wrapper.destroy();
});
- it('emits onClick even on button click', () => {
+ it('emits onClick event on button click', () => {
findButton().trigger('click');
return wrapper.vm.$nextTick().then(() => {
diff --git a/spec/frontend/notes/components/multiline_comment_utils_spec.js b/spec/frontend/notes/components/multiline_comment_utils_spec.js
new file mode 100644
index 00000000000..261bfb106e7
--- /dev/null
+++ b/spec/frontend/notes/components/multiline_comment_utils_spec.js
@@ -0,0 +1,49 @@
+import {
+ getSymbol,
+ getStartLineNumber,
+ getEndLineNumber,
+} from '~/notes/components/multiline_comment_utils';
+
+describe('Multiline comment utilities', () => {
+ describe('getStartLineNumber', () => {
+ it.each`
+ lineCode | type | result
+ ${'abcdef_1_1'} | ${'old'} | ${'-1'}
+ ${'abcdef_1_1'} | ${'new'} | ${'+1'}
+ ${'abcdef_1_1'} | ${null} | ${'1'}
+ ${'abcdef'} | ${'new'} | ${''}
+ ${'abcdef'} | ${'old'} | ${''}
+ ${'abcdef'} | ${null} | ${''}
+ `('returns line number', ({ lineCode, type, result }) => {
+ expect(getStartLineNumber({ start_line_code: lineCode, start_line_type: type })).toEqual(
+ result,
+ );
+ });
+ });
+ describe('getEndLineNumber', () => {
+ it.each`
+ lineCode | type | result
+ ${'abcdef_1_1'} | ${'old'} | ${'-1'}
+ ${'abcdef_1_1'} | ${'new'} | ${'+1'}
+ ${'abcdef_1_1'} | ${null} | ${'1'}
+ ${'abcdef'} | ${'new'} | ${''}
+ ${'abcdef'} | ${'old'} | ${''}
+ ${'abcdef'} | ${null} | ${''}
+ `('returns line number', ({ lineCode, type, result }) => {
+ expect(getEndLineNumber({ end_line_code: lineCode, end_line_type: type })).toEqual(result);
+ });
+ });
+ describe('getSymbol', () => {
+ it.each`
+ type | result
+ ${'new'} | ${'+'}
+ ${'old'} | ${'-'}
+ ${'unused'} | ${''}
+ ${''} | ${''}
+ ${null} | ${''}
+ ${undefined} | ${''}
+ `('`$type` returns `$result`', ({ type, result }) => {
+ expect(getSymbol(type)).toEqual(result);
+ });
+ });
+});
diff --git a/spec/frontend/notes/components/note_actions_spec.js b/spec/frontend/notes/components/note_actions_spec.js
index 5d13f587ca7..220ac22d8eb 100644
--- a/spec/frontend/notes/components/note_actions_spec.js
+++ b/spec/frontend/notes/components/note_actions_spec.js
@@ -4,26 +4,33 @@ import { TEST_HOST } from 'spec/test_constants';
import createStore from '~/notes/stores';
import noteActions from '~/notes/components/note_actions.vue';
import { userDataMock } from '../mock_data';
+import AxiosMockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
describe('noteActions', () => {
let wrapper;
let store;
let props;
+ let actions;
+ let axiosMock;
- const shallowMountNoteActions = propsData => {
+ const shallowMountNoteActions = (propsData, computed) => {
const localVue = createLocalVue();
return shallowMount(localVue.extend(noteActions), {
store,
propsData,
localVue,
+ computed,
});
};
beforeEach(() => {
store = createStore();
+
props = {
accessLevel: 'Maintainer',
- authorId: 26,
+ authorId: 1,
+ author: userDataMock,
canDelete: true,
canEdit: true,
canAwardEmoji: true,
@@ -33,10 +40,17 @@ describe('noteActions', () => {
reportAbusePath: `${TEST_HOST}/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_539&user_id=26`,
showReply: false,
};
+
+ actions = {
+ updateAssignees: jest.fn(),
+ };
+
+ axiosMock = new AxiosMockAdapter(axios);
});
afterEach(() => {
wrapper.destroy();
+ axiosMock.restore();
});
describe('user is logged in', () => {
@@ -76,6 +90,14 @@ describe('noteActions', () => {
it('should not show copy link action when `noteUrl` prop is empty', done => {
wrapper.setProps({
...props,
+ author: {
+ avatar_url: 'mock_path',
+ id: 26,
+ name: 'Example Maintainer',
+ path: '/ExampleMaintainer',
+ state: 'active',
+ username: 'ExampleMaintainer',
+ },
noteUrl: '',
});
@@ -104,6 +126,25 @@ describe('noteActions', () => {
})
.catch(done.fail);
});
+
+ it('should be possible to assign or unassign the comment author', () => {
+ wrapper = shallowMountNoteActions(props, {
+ targetType: () => 'issue',
+ });
+
+ const assignUserButton = wrapper.find('[data-testid="assign-user"]');
+ expect(assignUserButton.exists()).toBe(true);
+
+ assignUserButton.trigger('click');
+ axiosMock.onPut(`${TEST_HOST}/api/v4/projects/group/project/issues/1`).reply(() => {
+ expect(actions.updateAssignees).toHaveBeenCalled();
+ });
+ });
+
+ it('should not be possible to assign or unassign the comment author in a merge request', () => {
+ const assignUserButton = wrapper.find('[data-testid="assign-user"]');
+ expect(assignUserButton.exists()).toBe(false);
+ });
});
});
@@ -157,4 +198,19 @@ describe('noteActions', () => {
expect(replyButton.exists()).toBe(false);
});
});
+
+ describe('Draft notes', () => {
+ beforeEach(() => {
+ store.dispatch('setUserData', userDataMock);
+
+ wrapper = shallowMountNoteActions({ ...props, canResolve: true, isDraft: true });
+ });
+
+ it('should render the right resolve button title', () => {
+ const resolveButton = wrapper.find({ ref: 'resolveButton' });
+
+ expect(resolveButton.exists()).toBe(true);
+ expect(resolveButton.attributes('title')).toBe('Thread stays unresolved');
+ });
+ });
});
diff --git a/spec/frontend/notes/components/note_form_spec.js b/spec/frontend/notes/components/note_form_spec.js
index 8270c148fb5..15802841c57 100644
--- a/spec/frontend/notes/components/note_form_spec.js
+++ b/spec/frontend/notes/components/note_form_spec.js
@@ -1,8 +1,9 @@
import { shallowMount, createLocalVue } from '@vue/test-utils';
import createStore from '~/notes/stores';
import NoteForm from '~/notes/components/note_form.vue';
+import batchComments from '~/batch_comments/stores/modules/batch_comments';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
-import { noteableDataMock, notesDataMock } from '../mock_data';
+import { noteableDataMock, notesDataMock, discussionMock } from '../mock_data';
import { getDraft, updateDraft } from '~/lib/utils/autosave';
@@ -245,4 +246,55 @@ describe('issue_note_form component', () => {
expect(updateDraft).toHaveBeenCalledWith(dummyAutosaveKey, dummyContent);
});
});
+
+ describe('with batch comments', () => {
+ beforeEach(() => {
+ store.registerModule('batchComments', batchComments());
+
+ wrapper = createComponentWrapper();
+ wrapper.setProps({
+ ...props,
+ noteId: '',
+ discussion: { ...discussionMock, for_commit: false },
+ });
+ });
+
+ it('should be possible to cancel', () => {
+ jest.spyOn(wrapper.vm, 'cancelHandler');
+
+ return wrapper.vm.$nextTick().then(() => {
+ const cancelButton = wrapper.find('[data-testid="cancelBatchCommentsEnabled"]');
+ cancelButton.trigger('click');
+
+ expect(wrapper.vm.cancelHandler).toHaveBeenCalledWith(true);
+ });
+ });
+
+ it('shows resolve checkbox', () => {
+ expect(wrapper.find('.js-resolve-checkbox').exists()).toBe(true);
+ });
+
+ it('hides actions for commits', () => {
+ wrapper.setProps({ discussion: { for_commit: true } });
+
+ return wrapper.vm.$nextTick(() => {
+ expect(wrapper.find('.note-form-actions').text()).not.toContain('Start a review');
+ });
+ });
+
+ describe('on enter', () => {
+ it('should start review or add to review when cmd+enter is pressed', () => {
+ const textarea = wrapper.find('textarea');
+
+ jest.spyOn(wrapper.vm, 'handleAddToReview');
+
+ textarea.setValue('Foo');
+ textarea.trigger('keydown.enter', { metaKey: true });
+
+ return wrapper.vm.$nextTick(() => {
+ expect(wrapper.vm.handleAddToReview).toHaveBeenCalled();
+ });
+ });
+ });
+ });
});
diff --git a/spec/frontend/notes/components/noteable_note_spec.js b/spec/frontend/notes/components/noteable_note_spec.js
index 0d67b1d87a9..aa3eaa97e20 100644
--- a/spec/frontend/notes/components/noteable_note_spec.js
+++ b/spec/frontend/notes/components/noteable_note_spec.js
@@ -1,5 +1,5 @@
import { escape } from 'lodash';
-import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { mount, createLocalVue } from '@vue/test-utils';
import createStore from '~/notes/stores';
import issueNote from '~/notes/components/noteable_note.vue';
import NoteHeader from '~/notes/components/note_header.vue';
@@ -8,9 +8,19 @@ import NoteActions from '~/notes/components/note_actions.vue';
import NoteBody from '~/notes/components/note_body.vue';
import { noteableDataMock, notesDataMock, note } from '../mock_data';
+jest.mock('~/vue_shared/mixins/gl_feature_flags_mixin', () => () => ({
+ inject: {
+ glFeatures: {
+ from: 'glFeatures',
+ default: () => ({ multilineComments: true }),
+ },
+ },
+}));
+
describe('issue_note', () => {
let store;
let wrapper;
+ const findMultilineComment = () => wrapper.find('[data-testid="multiline-comment"]');
beforeEach(() => {
store = createStore();
@@ -18,12 +28,13 @@ describe('issue_note', () => {
store.dispatch('setNotesData', notesDataMock);
const localVue = createLocalVue();
- wrapper = shallowMount(localVue.extend(issueNote), {
+ wrapper = mount(localVue.extend(issueNote), {
store,
propsData: {
note,
},
localVue,
+ stubs: ['note-header', 'user-avatar-link', 'note-actions', 'note-body'],
});
});
@@ -31,6 +42,44 @@ describe('issue_note', () => {
wrapper.destroy();
});
+ describe('mutiline comments', () => {
+ it('should render if has multiline comment', () => {
+ const position = {
+ line_range: {
+ start_line_code: 'abc_1_1',
+ end_line_code: 'abc_2_2',
+ },
+ };
+ wrapper.setProps({
+ note: { ...note, position },
+ });
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findMultilineComment().text()).toEqual('Comment on lines 1 to 2');
+ });
+ });
+
+ it('should not render if has single line comment', () => {
+ const position = {
+ line_range: {
+ start_line_code: 'abc_1_1',
+ end_line_code: 'abc_1_1',
+ },
+ };
+ wrapper.setProps({
+ note: { ...note, position },
+ });
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findMultilineComment().exists()).toBe(false);
+ });
+ });
+
+ it('should not render if `line_range` is unavailable', () => {
+ expect(findMultilineComment().exists()).toBe(false);
+ });
+ });
+
it('should render user information', () => {
const { author } = note;
const avatar = wrapper.find(UserAvatarLink);