diff options
Diffstat (limited to 'spec/frontend/notes')
-rw-r--r-- | spec/frontend/notes/components/comment_form_spec.js | 443 | ||||
-rw-r--r-- | spec/frontend/notes/components/multiline_comment_utils_spec.js | 13 | ||||
-rw-r--r-- | spec/frontend/notes/components/note_header_spec.js | 37 | ||||
-rw-r--r-- | spec/frontend/notes/mixins/discussion_navigation_spec.js | 5 | ||||
-rw-r--r-- | spec/frontend/notes/stores/actions_spec.js | 68 | ||||
-rw-r--r-- | spec/frontend/notes/stores/mutation_spec.js | 46 |
6 files changed, 341 insertions, 271 deletions
diff --git a/spec/frontend/notes/components/comment_form_spec.js b/spec/frontend/notes/components/comment_form_spec.js index 59fa7b372ed..fca1beca999 100644 --- a/spec/frontend/notes/components/comment_form_spec.js +++ b/spec/frontend/notes/components/comment_form_spec.js @@ -1,18 +1,20 @@ -import $ from 'jquery'; -import { mount } from '@vue/test-utils'; +import { nextTick } from 'vue'; +import { mount, shallowMount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; import Autosize from 'autosize'; -import { trimText } from 'helpers/text_helper'; +import { deprecatedCreateFlash as flash } from '~/flash'; import axios from '~/lib/utils/axios_utils'; import createStore from '~/notes/stores'; import CommentForm from '~/notes/components/comment_form.vue'; import * as constants from '~/notes/constants'; +import eventHub from '~/notes/event_hub'; import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests'; -import { keyboardDownEvent } from '../../issue_show/helpers'; +import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; import { loggedOutnoteableData, notesDataMock, userDataMock, noteableDataMock } from '../mock_data'; jest.mock('autosize'); jest.mock('~/commons/nav/user_merge_requests'); +jest.mock('~/flash'); jest.mock('~/gl_form'); describe('issue_comment_form component', () => { @@ -20,17 +22,33 @@ describe('issue_comment_form component', () => { let wrapper; let axiosMock; - const setupStore = (userData, noteableData) => { - store.dispatch('setUserData', userData); + const findCloseReopenButton = () => wrapper.find('[data-testid="close-reopen-button"]'); + + const findCommentButton = () => wrapper.find('[data-testid="comment-button"]'); + + const findTextArea = () => wrapper.find('[data-testid="comment-field"]'); + + const mountComponent = ({ + initialData = {}, + noteableType = 'Issue', + noteableData = noteableDataMock, + notesData = notesDataMock, + userData = userDataMock, + mountFunction = shallowMount, + } = {}) => { store.dispatch('setNoteableData', noteableData); - store.dispatch('setNotesData', notesDataMock); - }; + store.dispatch('setNotesData', notesData); + store.dispatch('setUserData', userData); - const mountComponent = (noteableType = 'issue') => { - wrapper = mount(CommentForm, { + wrapper = mountFunction(CommentForm, { propsData: { noteableType, }, + data() { + return { + ...initialData, + }; + }, store, }); }; @@ -46,168 +64,157 @@ describe('issue_comment_form component', () => { }); describe('user is logged in', () => { - beforeEach(() => { - setupStore(userDataMock, noteableDataMock); - - mountComponent(); - }); + describe('avatar', () => { + it('should render user avatar with link', () => { + mountComponent({ mountFunction: mount }); - it('should render user avatar with link', () => { - expect(wrapper.find('.timeline-icon .user-avatar-link').attributes('href')).toEqual( - userDataMock.path, - ); + expect(wrapper.find(UserAvatarLink).attributes('href')).toBe(userDataMock.path); + }); }); describe('handleSave', () => { it('should request to save note when note is entered', () => { - wrapper.vm.note = 'hello world'; - jest.spyOn(wrapper.vm, 'saveNote').mockReturnValue(new Promise(() => {})); + mountComponent({ mountFunction: mount, initialData: { note: 'hello world' } }); + + jest.spyOn(wrapper.vm, 'saveNote').mockResolvedValue(); jest.spyOn(wrapper.vm, 'resizeTextarea'); jest.spyOn(wrapper.vm, 'stopPolling'); - wrapper.vm.handleSave(); + findCloseReopenButton().trigger('click'); - expect(wrapper.vm.isSubmitting).toEqual(true); - expect(wrapper.vm.note).toEqual(''); + expect(wrapper.vm.isSubmitting).toBe(true); + expect(wrapper.vm.note).toBe(''); expect(wrapper.vm.saveNote).toHaveBeenCalled(); expect(wrapper.vm.stopPolling).toHaveBeenCalled(); expect(wrapper.vm.resizeTextarea).toHaveBeenCalled(); }); it('should toggle issue state when no note', () => { + mountComponent({ mountFunction: mount }); + jest.spyOn(wrapper.vm, 'toggleIssueState'); - wrapper.vm.handleSave(); + findCloseReopenButton().trigger('click'); expect(wrapper.vm.toggleIssueState).toHaveBeenCalled(); }); - it('should disable action button while submitting', done => { + it('should disable action button while submitting', async () => { + mountComponent({ mountFunction: mount, initialData: { note: 'hello world' } }); + const saveNotePromise = Promise.resolve(); - wrapper.vm.note = 'hello world'; + jest.spyOn(wrapper.vm, 'saveNote').mockReturnValue(saveNotePromise); jest.spyOn(wrapper.vm, 'stopPolling'); - const actionButton = wrapper.find('.js-action-button'); - - wrapper.vm.handleSave(); - - wrapper.vm - .$nextTick() - .then(() => { - expect(actionButton.vm.disabled).toBeTruthy(); - }) - .then(saveNotePromise) - .then(wrapper.vm.$nextTick) - .then(() => { - expect(actionButton.vm.disabled).toBeFalsy(); - }) - .then(done) - .catch(done.fail); + const actionButton = findCloseReopenButton(); + + await actionButton.trigger('click'); + + expect(actionButton.props('disabled')).toBe(true); + + await saveNotePromise; + + await nextTick(); + + expect(actionButton.props('disabled')).toBe(false); }); }); describe('textarea', () => { - it('should render textarea with placeholder', () => { - expect(wrapper.find('.js-main-target-form textarea').attributes('placeholder')).toEqual( - 'Write a comment or drag your files here…', - ); - }); + describe('general', () => { + it('should render textarea with placeholder', () => { + mountComponent({ mountFunction: mount }); - it('should make textarea disabled while requesting', done => { - const $submitButton = $(wrapper.find('.js-comment-submit-button').element); - wrapper.vm.note = 'hello world'; - jest.spyOn(wrapper.vm, 'stopPolling'); - jest.spyOn(wrapper.vm, 'saveNote').mockReturnValue(new Promise(() => {})); + expect(findTextArea().attributes('placeholder')).toBe( + 'Write a comment or drag your files here…', + ); + }); - wrapper.vm.$nextTick(() => { - // Wait for wrapper.vm.note change triggered. It should enable $submitButton. - $submitButton.trigger('click'); + it('should make textarea disabled while requesting', async () => { + mountComponent({ mountFunction: mount }); - wrapper.vm.$nextTick(() => { - // Wait for wrapper.isSubmitting triggered. It should disable textarea. - expect(wrapper.find('.js-main-target-form textarea').attributes('disabled')).toBe( - 'disabled', - ); - done(); - }); + jest.spyOn(wrapper.vm, 'stopPolling'); + jest.spyOn(wrapper.vm, 'saveNote').mockResolvedValue(); + + await wrapper.setData({ note: 'hello world' }); + + await findCommentButton().trigger('click'); + + expect(findTextArea().attributes('disabled')).toBe('disabled'); }); - }); - it('should support quick actions', () => { - expect( - wrapper.find('.js-main-target-form textarea').attributes('data-supports-quick-actions'), - ).toBe('true'); - }); + it('should support quick actions', () => { + mountComponent({ mountFunction: mount }); - it('should link to markdown docs', () => { - const { markdownDocsPath } = notesDataMock; + expect(findTextArea().attributes('data-supports-quick-actions')).toBe('true'); + }); - expect( - wrapper - .find(`a[href="${markdownDocsPath}"]`) - .text() - .trim(), - ).toEqual('Markdown'); - }); + it('should link to markdown docs', () => { + mountComponent({ mountFunction: mount }); - it('should link to quick actions docs', () => { - const { quickActionsDocsPath } = notesDataMock; + const { markdownDocsPath } = notesDataMock; - expect( - wrapper - .find(`a[href="${quickActionsDocsPath}"]`) - .text() - .trim(), - ).toEqual('quick actions'); - }); + expect(wrapper.find(`a[href="${markdownDocsPath}"]`).text()).toBe('Markdown'); + }); + + it('should link to quick actions docs', () => { + mountComponent({ mountFunction: mount }); + + const { quickActionsDocsPath } = notesDataMock; + + expect(wrapper.find(`a[href="${quickActionsDocsPath}"]`).text()).toBe('quick actions'); + }); + + it('should resize textarea after note discarded', async () => { + mountComponent({ mountFunction: mount, initialData: { note: 'foo' } }); - it('should resize textarea after note discarded', done => { - jest.spyOn(wrapper.vm, 'discard'); + jest.spyOn(wrapper.vm, 'discard'); - wrapper.vm.note = 'foo'; - wrapper.vm.discard(); + wrapper.vm.discard(); + + await nextTick(); - wrapper.vm.$nextTick(() => { expect(Autosize.update).toHaveBeenCalled(); - done(); }); }); describe('edit mode', () => { + beforeEach(() => { + mountComponent(); + }); + it('should enter edit mode when arrow up is pressed', () => { jest.spyOn(wrapper.vm, 'editCurrentUserLastNote'); - wrapper.find('.js-main-target-form textarea').value = 'Foo'; - wrapper - .find('.js-main-target-form textarea') - .element.dispatchEvent(keyboardDownEvent(38, true)); + + findTextArea().trigger('keydown.up'); expect(wrapper.vm.editCurrentUserLastNote).toHaveBeenCalled(); }); it('inits autosave', () => { expect(wrapper.vm.autosave).toBeDefined(); - expect(wrapper.vm.autosave.key).toEqual(`autosave/Note/Issue/${noteableDataMock.id}`); + expect(wrapper.vm.autosave.key).toBe(`autosave/Note/Issue/${noteableDataMock.id}`); }); }); describe('event enter', () => { + beforeEach(() => { + mountComponent(); + }); + it('should save note when cmd+enter is pressed', () => { jest.spyOn(wrapper.vm, 'handleSave'); - wrapper.find('.js-main-target-form textarea').value = 'Foo'; - wrapper - .find('.js-main-target-form textarea') - .element.dispatchEvent(keyboardDownEvent(13, true)); + + findTextArea().trigger('keydown.enter', { metaKey: true }); expect(wrapper.vm.handleSave).toHaveBeenCalled(); }); it('should save note when ctrl+enter is pressed', () => { jest.spyOn(wrapper.vm, 'handleSave'); - wrapper.find('.js-main-target-form textarea').value = 'Foo'; - wrapper - .find('.js-main-target-form textarea') - .element.dispatchEvent(keyboardDownEvent(13, false, true)); + + findTextArea().trigger('keydown.enter', { ctrlKey: true }); expect(wrapper.vm.handleSave).toHaveBeenCalled(); }); @@ -216,137 +223,187 @@ describe('issue_comment_form component', () => { describe('actions', () => { it('should be possible to close the issue', () => { - expect( - wrapper - .find('.btn-comment-and-close') - .text() - .trim(), - ).toEqual('Close issue'); + mountComponent(); + + expect(findCloseReopenButton().text()).toBe('Close issue'); }); it('should render comment button as disabled', () => { - expect(wrapper.find('.js-comment-submit-button').attributes('disabled')).toEqual( - 'disabled', - ); + mountComponent(); + + expect(findCommentButton().props('disabled')).toBe(true); }); - it('should enable comment button if it has note', done => { - wrapper.vm.note = 'Foo'; - wrapper.vm.$nextTick(() => { - expect(wrapper.find('.js-comment-submit-button').attributes('disabled')).toBeFalsy(); - done(); - }); + it('should enable comment button if it has note', async () => { + mountComponent(); + + await wrapper.setData({ note: 'Foo' }); + + expect(findCommentButton().props('disabled')).toBe(false); }); - it('should update buttons texts when it has note', done => { - wrapper.vm.note = 'Foo'; - wrapper.vm.$nextTick(() => { - expect( - wrapper - .find('.btn-comment-and-close') - .text() - .trim(), - ).toEqual('Comment & close issue'); - - done(); - }); + it('should update buttons texts when it has note', () => { + mountComponent({ initialData: { note: 'Foo' } }); + + expect(findCloseReopenButton().text()).toBe('Comment & close issue'); }); - it('updates button text with noteable type', done => { - wrapper.setProps({ noteableType: constants.MERGE_REQUEST_NOTEABLE_TYPE }); - - wrapper.vm.$nextTick(() => { - expect( - wrapper - .find('.btn-comment-and-close') - .text() - .trim(), - ).toEqual('Close merge request'); - done(); - }); + it('updates button text with noteable type', () => { + mountComponent({ noteableType: constants.MERGE_REQUEST_NOTEABLE_TYPE }); + + expect(findCloseReopenButton().text()).toBe('Close merge request'); }); describe('when clicking close/reopen button', () => { - it('should disable button and show a loading spinner', () => { - const toggleStateButton = wrapper.find('.js-action-button'); + it('should show a loading spinner', async () => { + mountComponent({ + noteableType: constants.MERGE_REQUEST_NOTEABLE_TYPE, + mountFunction: mount, + }); - toggleStateButton.trigger('click'); + await findCloseReopenButton().trigger('click'); - return wrapper.vm.$nextTick().then(() => { - expect(toggleStateButton.element.disabled).toEqual(true); - expect(toggleStateButton.props('loading')).toBe(true); - }); + expect(findCloseReopenButton().props('loading')).toBe(true); }); }); describe('when toggling state', () => { - it('should update MR count', done => { - jest.spyOn(wrapper.vm, 'closeIssue').mockResolvedValue(); + describe('when issue', () => { + it('emits event to toggle state', () => { + mountComponent({ mountFunction: mount }); + + jest.spyOn(eventHub, '$emit'); + + findCloseReopenButton().trigger('click'); + + expect(eventHub.$emit).toHaveBeenCalledWith('toggle.issuable.state'); + }); + }); + + describe.each` + type | noteableType + ${'merge request'} | ${'MergeRequest'} + ${'epic'} | ${'Epic'} + `('when $type', ({ type, noteableType }) => { + describe('when open', () => { + it(`makes an API call to open it`, () => { + mountComponent({ + noteableType, + noteableData: { ...noteableDataMock, state: constants.OPENED }, + mountFunction: mount, + }); + + jest.spyOn(wrapper.vm, 'closeIssuable').mockResolvedValue(); + + findCloseReopenButton().trigger('click'); + + expect(wrapper.vm.closeIssuable).toHaveBeenCalled(); + }); + + it(`shows an error when the API call fails`, async () => { + mountComponent({ + noteableType, + noteableData: { ...noteableDataMock, state: constants.OPENED }, + mountFunction: mount, + }); + + jest.spyOn(wrapper.vm, 'closeIssuable').mockRejectedValue(); + + await findCloseReopenButton().trigger('click'); + + await wrapper.vm.$nextTick; + + expect(flash).toHaveBeenCalledWith( + `Something went wrong while closing the ${type}. Please try again later.`, + ); + }); + }); + + describe('when closed', () => { + it('makes an API call to close it', () => { + mountComponent({ + noteableType, + noteableData: { ...noteableDataMock, state: constants.CLOSED }, + mountFunction: mount, + }); + + jest.spyOn(wrapper.vm, 'reopenIssuable').mockResolvedValue(); - wrapper.vm.toggleIssueState(); + findCloseReopenButton().trigger('click'); - wrapper.vm.$nextTick(() => { - expect(refreshUserMergeRequestCounts).toHaveBeenCalled(); + expect(wrapper.vm.reopenIssuable).toHaveBeenCalled(); + }); + }); + + it(`shows an error when the API call fails`, async () => { + mountComponent({ + noteableType, + noteableData: { ...noteableDataMock, state: constants.CLOSED }, + mountFunction: mount, + }); + + jest.spyOn(wrapper.vm, 'reopenIssuable').mockRejectedValue(); + + await findCloseReopenButton().trigger('click'); - done(); + await wrapper.vm.$nextTick; + + expect(flash).toHaveBeenCalledWith( + `Something went wrong while reopening the ${type}. Please try again later.`, + ); }); }); + + it('when merge request, should update MR count', async () => { + mountComponent({ + noteableType: constants.MERGE_REQUEST_NOTEABLE_TYPE, + mountFunction: mount, + }); + + jest.spyOn(wrapper.vm, 'closeIssuable').mockResolvedValue(); + + await findCloseReopenButton().trigger('click'); + + expect(refreshUserMergeRequestCounts).toHaveBeenCalled(); + }); }); }); describe('issue is confidential', () => { - it('shows information warning', done => { - store.dispatch('setNoteableData', Object.assign(noteableDataMock, { confidential: true })); - wrapper.vm.$nextTick(() => { - expect(wrapper.find('.confidential-issue-warning')).toBeDefined(); - done(); + it('shows information warning', () => { + mountComponent({ + noteableData: { ...noteableDataMock, confidential: true }, + mountFunction: mount, }); + + expect(wrapper.find('[data-testid="confidential-warning"]').exists()).toBe(true); }); }); }); describe('user is not logged in', () => { beforeEach(() => { - setupStore(null, loggedOutnoteableData); - - mountComponent(); + mountComponent({ userData: null, noteableData: loggedOutnoteableData, mountFunction: mount }); }); it('should render signed out widget', () => { - expect(trimText(wrapper.text())).toEqual('Please register or sign in to reply'); + expect(wrapper.text()).toBe('Please register or sign in to reply'); }); it('should not render submission form', () => { - expect(wrapper.find('textarea').exists()).toBe(false); + expect(findTextArea().exists()).toBe(false); }); }); - describe('when issuable is open', () => { - beforeEach(() => { - setupStore(userDataMock, noteableDataMock); - }); - - it.each([['opened', 'warning'], ['reopened', 'warning']])( - 'when %i, it changes the variant of the btn to %i', - (a, expected) => { - store.state.noteableData.state = a; - - mountComponent(); - - expect(wrapper.find('.js-action-button').props('variant')).toBe(expected); - }, - ); - }); - - describe('when issuable is not open', () => { - beforeEach(() => { - setupStore(userDataMock, noteableDataMock); - - mountComponent(); - }); + describe('close/reopen button variants', () => { + it.each([ + [constants.OPENED, 'warning'], + [constants.REOPENED, 'warning'], + [constants.CLOSED, 'default'], + ])('when %s, the variant of the btn is %s', (state, expected) => { + mountComponent({ noteableData: { ...noteableDataMock, state } }); - it('should render the "default" variant of the button', () => { - expect(wrapper.find('.js-action-button').props('variant')).toBe('warning'); + expect(findCloseReopenButton().props('variant')).toBe(expected); }); }); }); diff --git a/spec/frontend/notes/components/multiline_comment_utils_spec.js b/spec/frontend/notes/components/multiline_comment_utils_spec.js index af4394cc648..99b33e7cd5f 100644 --- a/spec/frontend/notes/components/multiline_comment_utils_spec.js +++ b/spec/frontend/notes/components/multiline_comment_utils_spec.js @@ -34,8 +34,17 @@ describe('Multiline comment utilities', () => { expect(getSymbol(type)).toEqual(result); }); }); - describe('getCommentedLines', () => { - const diffLines = [{ line_code: '1' }, { line_code: '2' }, { line_code: '3' }]; + const inlineDiffLines = [{ line_code: '1' }, { line_code: '2' }, { line_code: '3' }]; + const parallelDiffLines = inlineDiffLines.map(line => ({ + left: { ...line }, + right: { ...line }, + })); + + describe.each` + view | diffLines + ${'inline'} | ${inlineDiffLines} + ${'parallel'} | ${parallelDiffLines} + `('getCommentedLines $view view', ({ diffLines }) => { it('returns a default object when `selectedCommentPosition` is not provided', () => { expect(getCommentedLines(undefined, diffLines)).toEqual({ startLine: 4, endLine: 4 }); }); diff --git a/spec/frontend/notes/components/note_header_spec.js b/spec/frontend/notes/components/note_header_spec.js index 69aab0d051e..1c6d0bafda8 100644 --- a/spec/frontend/notes/components/note_header_spec.js +++ b/spec/frontend/notes/components/note_header_spec.js @@ -22,6 +22,10 @@ describe('NoteHeader component', () => { const findTimestamp = () => wrapper.find({ ref: 'noteTimestamp' }); const findConfidentialIndicator = () => wrapper.find('[data-testid="confidentialIndicator"]'); const findSpinner = () => wrapper.find({ ref: 'spinner' }); + const findAuthorStatus = () => wrapper.find({ ref: 'authorStatus' }); + + const statusHtml = + '"<span class="user-status-emoji has-tooltip" title="foo bar" data-html="true" data-placement="top"><gl-emoji title="basketball and hoop" data-name="basketball" data-unicode-version="6.0">🏀</gl-emoji></span>"'; const author = { avatar_url: null, @@ -30,6 +34,8 @@ describe('NoteHeader component', () => { path: '/root', state: 'active', username: 'root', + show_status: true, + status_tooltip_html: statusHtml, status: { availability: '', }, @@ -109,6 +115,32 @@ describe('NoteHeader component', () => { expect(wrapper.find('.js-user-link').text()).toContain('(Busy)'); }); + it('renders author status', () => { + createComponent({ author }); + + expect(findAuthorStatus().exists()).toBe(true); + }); + + it('does not render author status if show_status=false', () => { + createComponent({ + author: { ...author, status: { availability: AVAILABILITY_STATUS.BUSY }, show_status: false }, + }); + + expect(findAuthorStatus().exists()).toBe(false); + }); + + it('does not render author status if status_tooltip_html=null', () => { + createComponent({ + author: { + ...author, + status: { availability: AVAILABILITY_STATUS.BUSY }, + status_tooltip_html: null, + }, + }); + + expect(findAuthorStatus().exists()).toBe(false); + }); + it('renders deleted user text if author is not passed as a prop', () => { createComponent(); @@ -206,13 +238,12 @@ describe('NoteHeader component', () => { createComponent({ author: { ...author, - status_tooltip_html: - '"<span class="user-status-emoji has-tooltip" title="foo bar" data-html="true" data-placement="top"><gl-emoji title="basketball and hoop" data-name="basketball" data-unicode-version="6.0">🏀</gl-emoji></span>"', + status_tooltip_html: statusHtml, }, }); return nextTick().then(() => { - const authorStatus = wrapper.find({ ref: 'authorStatus' }); + const authorStatus = findAuthorStatus(); authorStatus.trigger('mouseenter'); expect(authorStatus.find('gl-emoji').attributes('title')).toBeUndefined(); diff --git a/spec/frontend/notes/mixins/discussion_navigation_spec.js b/spec/frontend/notes/mixins/discussion_navigation_spec.js index d203435e7bf..4114df618e5 100644 --- a/spec/frontend/notes/mixins/discussion_navigation_spec.js +++ b/spec/frontend/notes/mixins/discussion_navigation_spec.js @@ -42,6 +42,7 @@ describe('Discussion navigation mixin', () => { ); jest.spyOn(utils, 'scrollToElementWithContext'); + jest.spyOn(utils, 'scrollToElement'); expandDiscussion = jest.fn(); const { actions, ...notesRest } = notesModule(); @@ -133,7 +134,7 @@ describe('Discussion navigation mixin', () => { }); it('scrolls to element', () => { - expect(utils.scrollToElementWithContext).toHaveBeenCalledWith( + expect(utils.scrollToElement).toHaveBeenCalledWith( findDiscussion('div.discussion', expected), ); }); @@ -200,7 +201,7 @@ describe('Discussion navigation mixin', () => { }); it('scrolls to discussion', () => { - expect(utils.scrollToElementWithContext).toHaveBeenCalledWith( + expect(utils.scrollToElement).toHaveBeenCalledWith( findDiscussion('div.discussion', expected), ); }); diff --git a/spec/frontend/notes/stores/actions_spec.js b/spec/frontend/notes/stores/actions_spec.js index 920959f41e7..c9912621785 100644 --- a/spec/frontend/notes/stores/actions_spec.js +++ b/spec/frontend/notes/stores/actions_spec.js @@ -174,10 +174,10 @@ describe('Actions Notes Store', () => { axiosMock.onAny().reply(200, {}); }); - describe('closeIssue', () => { + describe('closeMergeRequest', () => { it('sets state as closed', done => { store - .dispatch('closeIssue', { notesData: { closeIssuePath: '' } }) + .dispatch('closeIssuable', { notesData: { closeIssuePath: '' } }) .then(() => { expect(store.state.noteableData.state).toEqual('closed'); expect(store.state.isToggleStateButtonLoading).toEqual(false); @@ -187,10 +187,10 @@ describe('Actions Notes Store', () => { }); }); - describe('reopenIssue', () => { + describe('reopenMergeRequest', () => { it('sets state as reopened', done => { store - .dispatch('reopenIssue', { notesData: { reopenIssuePath: '' } }) + .dispatch('reopenIssuable', { notesData: { reopenIssuePath: '' } }) .then(() => { expect(store.state.noteableData.state).toEqual('reopened'); expect(store.state.isToggleStateButtonLoading).toEqual(false); @@ -253,30 +253,6 @@ describe('Actions Notes Store', () => { }); }); - describe('toggleBlockedIssueWarning', () => { - it('should set issue warning as true', done => { - testAction( - actions.toggleBlockedIssueWarning, - true, - {}, - [{ type: 'TOGGLE_BLOCKED_ISSUE_WARNING', payload: true }], - [], - done, - ); - }); - - it('should set issue warning as false', done => { - testAction( - actions.toggleBlockedIssueWarning, - false, - {}, - [{ type: 'TOGGLE_BLOCKED_ISSUE_WARNING', payload: false }], - [], - done, - ); - }); - }); - describe('fetchData', () => { describe('given there are no notes', () => { const lastFetchedAt = '13579'; @@ -944,10 +920,16 @@ describe('Actions Notes Store', () => { it('when service success, commits and resolves discussion', done => { testSubmitSuggestion(done, () => { expect(commit.mock.calls).toEqual([ + [mutationTypes.SET_RESOLVING_DISCUSSION, true], [mutationTypes.APPLY_SUGGESTION, { discussionId, noteId, suggestionId }], + [mutationTypes.SET_RESOLVING_DISCUSSION, false], ]); - expect(dispatch.mock.calls).toEqual([['resolveDiscussion', { discussionId }]]); + expect(dispatch.mock.calls).toEqual([ + ['stopPolling'], + ['resolveDiscussion', { discussionId }], + ['restartPolling'], + ]); expect(Flash).not.toHaveBeenCalled(); }); }); @@ -958,8 +940,11 @@ describe('Actions Notes Store', () => { Api.applySuggestion.mockReturnValue(Promise.reject(response)); testSubmitSuggestion(done, () => { - expect(commit).not.toHaveBeenCalled(); - expect(dispatch).not.toHaveBeenCalled(); + expect(commit.mock.calls).toEqual([ + [mutationTypes.SET_RESOLVING_DISCUSSION, true], + [mutationTypes.SET_RESOLVING_DISCUSSION, false], + ]); + expect(dispatch.mock.calls).toEqual([['stopPolling'], ['restartPolling']]); expect(Flash).toHaveBeenCalledWith(TEST_ERROR_MESSAGE, 'alert', flashContainer); }); }); @@ -970,8 +955,11 @@ describe('Actions Notes Store', () => { Api.applySuggestion.mockReturnValue(Promise.reject(response)); testSubmitSuggestion(done, () => { - expect(commit).not.toHaveBeenCalled(); - expect(dispatch).not.toHaveBeenCalled(); + expect(commit.mock.calls).toEqual([ + [mutationTypes.SET_RESOLVING_DISCUSSION, true], + [mutationTypes.SET_RESOLVING_DISCUSSION, false], + ]); + expect(dispatch.mock.calls).toEqual([['stopPolling'], ['restartPolling']]); expect(Flash).toHaveBeenCalledWith( 'Something went wrong while applying the suggestion. Please try again.', 'alert', @@ -1015,15 +1003,19 @@ describe('Actions Notes Store', () => { testSubmitSuggestionBatch(done, () => { expect(commit.mock.calls).toEqual([ [mutationTypes.SET_APPLYING_BATCH_STATE, true], + [mutationTypes.SET_RESOLVING_DISCUSSION, true], [mutationTypes.APPLY_SUGGESTION, batchSuggestionsInfo[0]], [mutationTypes.APPLY_SUGGESTION, batchSuggestionsInfo[1]], [mutationTypes.CLEAR_SUGGESTION_BATCH], [mutationTypes.SET_APPLYING_BATCH_STATE, false], + [mutationTypes.SET_RESOLVING_DISCUSSION, false], ]); expect(dispatch.mock.calls).toEqual([ + ['stopPolling'], ['resolveDiscussion', { discussionId: discussionIds[0] }], ['resolveDiscussion', { discussionId: discussionIds[1] }], + ['restartPolling'], ]); expect(Flash).not.toHaveBeenCalled(); @@ -1038,10 +1030,12 @@ describe('Actions Notes Store', () => { testSubmitSuggestionBatch(done, () => { expect(commit.mock.calls).toEqual([ [mutationTypes.SET_APPLYING_BATCH_STATE, true], + [mutationTypes.SET_RESOLVING_DISCUSSION, true], [mutationTypes.SET_APPLYING_BATCH_STATE, false], + [mutationTypes.SET_RESOLVING_DISCUSSION, false], ]); - expect(dispatch).not.toHaveBeenCalled(); + expect(dispatch.mock.calls).toEqual([['stopPolling'], ['restartPolling']]); expect(Flash).toHaveBeenCalledWith(TEST_ERROR_MESSAGE, 'alert', flashContainer); }); }); @@ -1054,10 +1048,12 @@ describe('Actions Notes Store', () => { testSubmitSuggestionBatch(done, () => { expect(commit.mock.calls).toEqual([ [mutationTypes.SET_APPLYING_BATCH_STATE, true], + [mutationTypes.SET_RESOLVING_DISCUSSION, true], [mutationTypes.SET_APPLYING_BATCH_STATE, false], + [mutationTypes.SET_RESOLVING_DISCUSSION, false], ]); - expect(dispatch).not.toHaveBeenCalled(); + expect(dispatch.mock.calls).toEqual([['stopPolling'], ['restartPolling']]); expect(Flash).toHaveBeenCalledWith( 'Something went wrong while applying the batch of suggestions. Please try again.', 'alert', @@ -1072,10 +1068,12 @@ describe('Actions Notes Store', () => { testSubmitSuggestionBatch(done, () => { expect(commit.mock.calls).toEqual([ [mutationTypes.SET_APPLYING_BATCH_STATE, true], + [mutationTypes.SET_RESOLVING_DISCUSSION, true], [mutationTypes.APPLY_SUGGESTION, batchSuggestionsInfo[0]], [mutationTypes.APPLY_SUGGESTION, batchSuggestionsInfo[1]], [mutationTypes.CLEAR_SUGGESTION_BATCH], [mutationTypes.SET_APPLYING_BATCH_STATE, false], + [mutationTypes.SET_RESOLVING_DISCUSSION, false], ]); expect(Flash).not.toHaveBeenCalled(); diff --git a/spec/frontend/notes/stores/mutation_spec.js b/spec/frontend/notes/stores/mutation_spec.js index 2618c3a53b8..ec4de925721 100644 --- a/spec/frontend/notes/stores/mutation_spec.js +++ b/spec/frontend/notes/stores/mutation_spec.js @@ -377,6 +377,16 @@ describe('Notes Store mutations', () => { }); }); + describe('SET_RESOLVING_DISCUSSION', () => { + it('should set resolving discussion state', () => { + const state = {}; + + mutations.SET_RESOLVING_DISCUSSION(state, true); + + expect(state.isResolvingDiscussion).toEqual(true); + }); + }); + describe('UPDATE_NOTE', () => { it('should update a note', () => { const state = { @@ -687,42 +697,6 @@ describe('Notes Store mutations', () => { }); }); - describe('TOGGLE_BLOCKED_ISSUE_WARNING', () => { - it('should set isToggleBlockedIssueWarning as true', () => { - const state = { - discussions: [], - targetNoteHash: null, - lastFetchedAt: null, - isToggleStateButtonLoading: false, - isToggleBlockedIssueWarning: false, - notesData: {}, - userData: {}, - noteableData: {}, - }; - - mutations.TOGGLE_BLOCKED_ISSUE_WARNING(state, true); - - expect(state.isToggleBlockedIssueWarning).toEqual(true); - }); - - it('should set isToggleBlockedIssueWarning as false', () => { - const state = { - discussions: [], - targetNoteHash: null, - lastFetchedAt: null, - isToggleStateButtonLoading: false, - isToggleBlockedIssueWarning: true, - notesData: {}, - userData: {}, - noteableData: {}, - }; - - mutations.TOGGLE_BLOCKED_ISSUE_WARNING(state, false); - - expect(state.isToggleBlockedIssueWarning).toEqual(false); - }); - }); - describe('SET_APPLYING_BATCH_STATE', () => { const buildDiscussions = suggestionsInfo => { const suggestions = suggestionsInfo.map(({ suggestionId }) => ({ id: suggestionId })); |