diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-18 11:18:50 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-18 11:18:50 +0000 |
commit | 8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781 (patch) | |
tree | a77e7fe7a93de11213032ed4ab1f33a3db51b738 /spec/frontend/notes/components | |
parent | 00b35af3db1abfe813a778f643dad221aad51fca (diff) | |
download | gitlab-ce-8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781.tar.gz |
Add latest changes from gitlab-org/gitlab@13-1-stable-ee
Diffstat (limited to 'spec/frontend/notes/components')
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); |