diff options
Diffstat (limited to 'spec/frontend/batch_comments')
10 files changed, 1123 insertions, 0 deletions
diff --git a/spec/frontend/batch_comments/components/diff_file_drafts_spec.js b/spec/frontend/batch_comments/components/diff_file_drafts_spec.js new file mode 100644 index 00000000000..6e0b61db9fa --- /dev/null +++ b/spec/frontend/batch_comments/components/diff_file_drafts_spec.js @@ -0,0 +1,61 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import Vuex from 'vuex'; +import DiffFileDrafts from '~/batch_comments/components/diff_file_drafts.vue'; +import DraftNote from '~/batch_comments/components/draft_note.vue'; + +const localVue = createLocalVue(); + +localVue.use(Vuex); + +describe('Batch comments diff file drafts component', () => { + let vm; + + function factory() { + const store = new Vuex.Store({ + modules: { + batchComments: { + namespaced: true, + getters: { + draftsForFile: () => () => [{ id: 1 }, { id: 2 }], + }, + }, + }, + }); + + vm = shallowMount(localVue.extend(DiffFileDrafts), { + store, + localVue, + propsData: { fileHash: 'filehash' }, + }); + } + + afterEach(() => { + vm.destroy(); + }); + + it('renders list of draft notes', () => { + factory(); + + expect(vm.findAll(DraftNote).length).toEqual(2); + }); + + it('renders index of draft note', () => { + factory(); + + expect(vm.findAll('.js-diff-notes-index').length).toEqual(2); + + expect( + vm + .findAll('.js-diff-notes-index') + .at(0) + .text(), + ).toEqual('1'); + + expect( + vm + .findAll('.js-diff-notes-index') + .at(1) + .text(), + ).toEqual('2'); + }); +}); diff --git a/spec/frontend/batch_comments/components/draft_note_spec.js b/spec/frontend/batch_comments/components/draft_note_spec.js new file mode 100644 index 00000000000..eea7f25dbc1 --- /dev/null +++ b/spec/frontend/batch_comments/components/draft_note_spec.js @@ -0,0 +1,125 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import DraftNote from '~/batch_comments/components/draft_note.vue'; +import { createStore } from '~/batch_comments/stores'; +import NoteableNote from '~/notes/components/noteable_note.vue'; +import '~/behaviors/markdown/render_gfm'; +import { createDraft } from '../mock_data'; + +const localVue = createLocalVue(); + +describe('Batch comments draft note component', () => { + let wrapper; + let draft; + + beforeEach(() => { + const store = createStore(); + + draft = createDraft(); + + wrapper = shallowMount(localVue.extend(DraftNote), { + store, + propsData: { draft }, + localVue, + }); + + jest.spyOn(wrapper.vm.$store, 'dispatch').mockImplementation(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders template', () => { + expect(wrapper.find('.draft-pending-label').exists()).toBe(true); + + const note = wrapper.find(NoteableNote); + + expect(note.exists()).toBe(true); + expect(note.props().note).toEqual(draft); + }); + + describe('add comment now', () => { + it('dispatches publishSingleDraft when clicking', () => { + const publishNowButton = wrapper.find({ ref: 'publishNowButton' }); + publishNowButton.vm.$emit('click'); + + expect(wrapper.vm.$store.dispatch).toHaveBeenCalledWith( + 'batchComments/publishSingleDraft', + 1, + ); + }); + + it('sets as loading when draft is publishing', done => { + wrapper.vm.$store.state.batchComments.currentlyPublishingDrafts.push(1); + + wrapper.vm.$nextTick(() => { + const publishNowButton = wrapper.find({ ref: 'publishNowButton' }); + + expect(publishNowButton.props().loading).toBe(true); + + done(); + }); + }); + }); + + describe('update', () => { + it('dispatches updateDraft', done => { + const note = wrapper.find(NoteableNote); + + note.vm.$emit('handleEdit'); + + wrapper.vm + .$nextTick() + .then(() => { + const formData = { + note: draft, + noteText: 'a', + resolveDiscussion: false, + }; + + note.vm.$emit('handleUpdateNote', formData); + + expect(wrapper.vm.$store.dispatch).toHaveBeenCalledWith( + 'batchComments/updateDraft', + formData, + ); + }) + .then(done) + .catch(done.fail); + }); + }); + + describe('deleteDraft', () => { + it('dispatches deleteDraft', () => { + jest.spyOn(window, 'confirm').mockImplementation(() => true); + + const note = wrapper.find(NoteableNote); + + note.vm.$emit('handleDeleteNote', draft); + + expect(wrapper.vm.$store.dispatch).toHaveBeenCalledWith('batchComments/deleteDraft', draft); + }); + }); + + describe('quick actions', () => { + it('renders referenced commands', done => { + wrapper.setProps({ + draft: { + ...draft, + references: { + commands: 'test command', + }, + }, + }); + + wrapper.vm.$nextTick(() => { + const referencedCommands = wrapper.find('.referenced-commands'); + + expect(referencedCommands.exists()).toBe(true); + expect(referencedCommands.text()).toContain('test command'); + + done(); + }); + }); + }); +}); diff --git a/spec/frontend/batch_comments/components/drafts_count_spec.js b/spec/frontend/batch_comments/components/drafts_count_spec.js new file mode 100644 index 00000000000..9d9fffce7e7 --- /dev/null +++ b/spec/frontend/batch_comments/components/drafts_count_spec.js @@ -0,0 +1,43 @@ +import Vue from 'vue'; +import DraftsCount from '~/batch_comments/components/drafts_count.vue'; +import { mountComponentWithStore } from 'helpers/vue_mount_component_helper'; +import { createStore } from '~/batch_comments/stores'; + +describe('Batch comments drafts count component', () => { + let vm; + let Component; + + beforeAll(() => { + Component = Vue.extend(DraftsCount); + }); + + beforeEach(() => { + const store = createStore(); + + store.state.batchComments.drafts.push('comment'); + + vm = mountComponentWithStore(Component, { store }); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('renders count', () => { + expect(vm.$el.querySelector('.drafts-count-number').textContent).toBe('1'); + }); + + it('renders screen reader text', done => { + const el = vm.$el.querySelector('.sr-only'); + + expect(el.textContent).toContain('draft'); + + vm.$store.state.batchComments.drafts.push('comment 2'); + + vm.$nextTick(() => { + expect(el.textContent).toContain('drafts'); + + done(); + }); + }); +}); diff --git a/spec/frontend/batch_comments/components/preview_item_spec.js b/spec/frontend/batch_comments/components/preview_item_spec.js new file mode 100644 index 00000000000..7d951fd7799 --- /dev/null +++ b/spec/frontend/batch_comments/components/preview_item_spec.js @@ -0,0 +1,130 @@ +import Vue from 'vue'; +import PreviewItem from '~/batch_comments/components/preview_item.vue'; +import { mountComponentWithStore } from 'helpers/vue_mount_component_helper'; +import { createStore } from '~/batch_comments/stores'; +import diffsModule from '~/diffs/store/modules'; +import notesModule from '~/notes/stores/modules'; +import '~/behaviors/markdown/render_gfm'; +import { createDraft } from '../mock_data'; + +describe('Batch comments draft preview item component', () => { + let vm; + let Component; + let draft; + + function createComponent(isLast = false, extra = {}, extendStore = () => {}) { + const store = createStore(); + store.registerModule('diffs', diffsModule()); + store.registerModule('notes', notesModule()); + + extendStore(store); + + draft = { + ...createDraft(), + ...extra, + }; + + vm = mountComponentWithStore(Component, { store, props: { draft, isLast } }); + } + + beforeAll(() => { + Component = Vue.extend(PreviewItem); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('renders text content', () => { + createComponent(false, { note_html: '<img src="" /><p>Hello world</p>' }); + + expect(vm.$el.querySelector('.review-preview-item-content').innerHTML).toEqual( + '<p>Hello world</p>', + ); + }); + + it('adds is last class', () => { + createComponent(true); + + expect(vm.$el.classList).toContain('is-last'); + }); + + it('scrolls to draft on click', () => { + createComponent(); + + jest.spyOn(vm.$store, 'dispatch').mockImplementation(); + + vm.$el.click(); + + expect(vm.$store.dispatch).toHaveBeenCalledWith('batchComments/scrollToDraft', vm.draft); + }); + + describe('for file', () => { + it('renders file path', () => { + createComponent(false, { file_path: 'index.js', file_hash: 'abc', position: {} }); + + expect(vm.$el.querySelector('.review-preview-item-header-text').textContent).toContain( + 'index.js', + ); + }); + + it('renders new line position', () => { + createComponent(false, { + file_path: 'index.js', + file_hash: 'abc', + position: { new_line: 1 }, + }); + + expect(vm.$el.querySelector('.bold').textContent).toContain(':1'); + }); + + it('renders old line position', () => { + createComponent(false, { + file_path: 'index.js', + file_hash: 'abc', + position: { old_line: 2 }, + }); + + expect(vm.$el.querySelector('.bold').textContent).toContain(':2'); + }); + + it('renders image position', () => { + createComponent(false, { + file_path: 'index.js', + file_hash: 'abc', + position: { position_type: 'image', x: 10, y: 20 }, + }); + + expect(vm.$el.querySelector('.bold').textContent).toContain('10x 20y'); + }); + }); + + describe('for thread', () => { + beforeEach(() => { + createComponent(false, { discussion_id: '1', resolve_discussion: true }, store => { + store.state.notes.discussions.push({ + id: '1', + notes: [ + { + author: { + name: 'Author Name', + }, + }, + ], + }); + }); + }); + + it('renders title', () => { + expect(vm.$el.querySelector('.review-preview-item-header-text').textContent).toContain( + "Author Name's thread", + ); + }); + + it('it renders thread resolved text', () => { + expect(vm.$el.querySelector('.draft-note-resolution').textContent).toContain( + 'Thread will be resolved', + ); + }); + }); +}); diff --git a/spec/frontend/batch_comments/components/publish_button_spec.js b/spec/frontend/batch_comments/components/publish_button_spec.js new file mode 100644 index 00000000000..97f3a1c8939 --- /dev/null +++ b/spec/frontend/batch_comments/components/publish_button_spec.js @@ -0,0 +1,52 @@ +import Vue from 'vue'; +import PublishButton from '~/batch_comments/components/publish_button.vue'; +import { mountComponentWithStore } from 'helpers/vue_mount_component_helper'; +import { createStore } from '~/batch_comments/stores'; + +describe('Batch comments publish button component', () => { + let vm; + let Component; + + beforeAll(() => { + Component = Vue.extend(PublishButton); + }); + + beforeEach(() => { + const store = createStore(); + + vm = mountComponentWithStore(Component, { store, props: { shouldPublish: true } }); + + jest.spyOn(vm.$store, 'dispatch').mockImplementation(); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('dispatches publishReview on click', () => { + vm.$el.click(); + + expect(vm.$store.dispatch).toHaveBeenCalledWith('batchComments/publishReview', undefined); + }); + + it('dispatches toggleReviewDropdown when shouldPublish is false on click', () => { + vm.shouldPublish = false; + + vm.$el.click(); + + expect(vm.$store.dispatch).toHaveBeenCalledWith( + 'batchComments/toggleReviewDropdown', + undefined, + ); + }); + + it('sets loading when isPublishing is true', done => { + vm.$store.state.batchComments.isPublishing = true; + + vm.$nextTick(() => { + expect(vm.$el.getAttribute('disabled')).toBe('disabled'); + + done(); + }); + }); +}); diff --git a/spec/frontend/batch_comments/components/publish_dropdown_spec.js b/spec/frontend/batch_comments/components/publish_dropdown_spec.js new file mode 100644 index 00000000000..b50ae340691 --- /dev/null +++ b/spec/frontend/batch_comments/components/publish_dropdown_spec.js @@ -0,0 +1,96 @@ +import Vue from 'vue'; +import PreviewDropdown from '~/batch_comments/components/preview_dropdown.vue'; +import { mountComponentWithStore } from 'helpers/vue_mount_component_helper'; +import { createStore } from '~/mr_notes/stores'; +import '~/behaviors/markdown/render_gfm'; +import { createDraft } from '../mock_data'; + +describe('Batch comments publish dropdown component', () => { + let vm; + let Component; + + function createComponent(extendStore = () => {}) { + const store = createStore(); + store.state.batchComments.drafts.push(createDraft(), { ...createDraft(), id: 2 }); + + extendStore(store); + + vm = mountComponentWithStore(Component, { store }); + } + + beforeAll(() => { + Component = Vue.extend(PreviewDropdown); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('toggles dropdown when clicking button', done => { + createComponent(); + + jest.spyOn(vm.$store, 'dispatch'); + + vm.$el.querySelector('.review-preview-dropdown-toggle').click(); + + expect(vm.$store.dispatch).toHaveBeenCalledWith( + 'batchComments/toggleReviewDropdown', + expect.anything(), + ); + + setImmediate(() => { + expect(vm.$el.classList).toContain('show'); + + done(); + }); + }); + + it('toggles dropdown when clicking body', () => { + createComponent(); + + vm.$store.state.batchComments.showPreviewDropdown = true; + + jest.spyOn(vm.$store, 'dispatch').mockImplementation(); + + document.body.click(); + + expect(vm.$store.dispatch).toHaveBeenCalledWith( + 'batchComments/toggleReviewDropdown', + undefined, + ); + }); + + it('renders list of drafts', () => { + createComponent(store => { + Object.assign(store.state.notes, { + isNotesFetched: true, + }); + }); + + expect(vm.$el.querySelectorAll('.dropdown-content li').length).toBe(2); + }); + + it('adds is-last class to last item', () => { + createComponent(store => { + Object.assign(store.state.notes, { + isNotesFetched: true, + }); + }); + + expect(vm.$el.querySelectorAll('.dropdown-content li')[1].querySelector('.is-last')).not.toBe( + null, + ); + }); + + it('renders draft count in dropdown title', () => { + createComponent(); + + expect(vm.$el.querySelector('.dropdown-title').textContent).toContain('2 pending comments'); + }); + + it('renders publish button in footer', () => { + createComponent(); + + expect(vm.$el.querySelector('.dropdown-footer .js-publish-draft-button')).not.toBe(null); + }); +}); diff --git a/spec/frontend/batch_comments/mock_data.js b/spec/frontend/batch_comments/mock_data.js new file mode 100644 index 00000000000..c50fea94fe3 --- /dev/null +++ b/spec/frontend/batch_comments/mock_data.js @@ -0,0 +1,27 @@ +import { TEST_HOST } from 'spec/test_constants'; + +export const createDraft = () => ({ + author: { + id: 1, + name: 'Test', + username: 'test', + state: 'active', + avatar_url: TEST_HOST, + }, + current_user: { can_edit: true, can_award_emoji: false, can_resolve: false }, + discussion_id: null, + file_hash: null, + file_path: null, + id: 1, + line_code: null, + merge_request_id: 1, + note: 'a', + note_html: '<p>Test</p>', + noteable_type: 'MergeRequest', + references: { users: [], commands: '' }, + resolve_discussion: false, + isDraft: true, + position: null, +}); + +export default () => {}; diff --git a/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js b/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js new file mode 100644 index 00000000000..2ec114d026a --- /dev/null +++ b/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js @@ -0,0 +1,403 @@ +import MockAdapter from 'axios-mock-adapter'; +import testAction from 'helpers/vuex_action_helper'; +import * as actions from '~/batch_comments/stores/modules/batch_comments/actions'; +import axios from '~/lib/utils/axios_utils'; + +describe('Batch comments store actions', () => { + let res = {}; + let mock; + + beforeEach(() => { + mock = new MockAdapter(axios); + }); + + afterEach(() => { + res = {}; + mock.restore(); + }); + + describe('saveDraft', () => { + it('dispatches saveNote on root', () => { + const dispatch = jest.fn(); + + actions.saveDraft({ dispatch }, { id: 1 }); + + expect(dispatch).toHaveBeenCalledWith('saveNote', { id: 1, isDraft: true }, { root: true }); + }); + }); + + describe('addDraftToDiscussion', () => { + it('commits ADD_NEW_DRAFT if no errors returned', done => { + res = { id: 1 }; + mock.onAny().reply(200, res); + + testAction( + actions.addDraftToDiscussion, + { endpoint: gl.TEST_HOST, data: 'test' }, + null, + [{ type: 'ADD_NEW_DRAFT', payload: res }], + [], + done, + ); + }); + + it('does not commit ADD_NEW_DRAFT if errors returned', done => { + mock.onAny().reply(500); + + testAction( + actions.addDraftToDiscussion, + { endpoint: gl.TEST_HOST, data: 'test' }, + null, + [], + [], + done, + ); + }); + }); + + describe('createNewDraft', () => { + it('commits ADD_NEW_DRAFT if no errors returned', done => { + res = { id: 1 }; + mock.onAny().reply(200, res); + + testAction( + actions.createNewDraft, + { endpoint: gl.TEST_HOST, data: 'test' }, + null, + [{ type: 'ADD_NEW_DRAFT', payload: res }], + [], + done, + ); + }); + + it('does not commit ADD_NEW_DRAFT if errors returned', done => { + mock.onAny().reply(500); + + testAction( + actions.createNewDraft, + { endpoint: gl.TEST_HOST, data: 'test' }, + null, + [], + [], + done, + ); + }); + }); + + describe('deleteDraft', () => { + let getters; + + beforeEach(() => { + getters = { + getNotesData: { + draftsDiscardPath: gl.TEST_HOST, + }, + }; + }); + + it('commits DELETE_DRAFT if no errors returned', done => { + const commit = jest.fn(); + const context = { + getters, + commit, + }; + res = { id: 1 }; + mock.onAny().reply(200); + + actions + .deleteDraft(context, { id: 1 }) + .then(() => { + expect(commit).toHaveBeenCalledWith('DELETE_DRAFT', 1); + }) + .then(done) + .catch(done.fail); + }); + + it('does not commit DELETE_DRAFT if errors returned', done => { + const commit = jest.fn(); + const context = { + getters, + commit, + }; + mock.onAny().reply(500); + + actions + .deleteDraft(context, { id: 1 }) + .then(() => { + expect(commit).not.toHaveBeenCalledWith('DELETE_DRAFT', 1); + }) + .then(done) + .catch(done.fail); + }); + }); + + describe('fetchDrafts', () => { + let getters; + + beforeEach(() => { + getters = { + getNotesData: { + draftsPath: gl.TEST_HOST, + }, + }; + }); + + it('commits SET_BATCH_COMMENTS_DRAFTS with returned data', done => { + const commit = jest.fn(); + const context = { + getters, + commit, + }; + res = { id: 1 }; + mock.onAny().reply(200, res); + + actions + .fetchDrafts(context) + .then(() => { + expect(commit).toHaveBeenCalledWith('SET_BATCH_COMMENTS_DRAFTS', { id: 1 }); + }) + .then(done) + .catch(done.fail); + }); + }); + + describe('publishReview', () => { + let dispatch; + let commit; + let getters; + let rootGetters; + + beforeEach(() => { + dispatch = jest.fn(); + commit = jest.fn(); + getters = { + getNotesData: { draftsPublishPath: gl.TEST_HOST, discussionsPath: gl.TEST_HOST }, + }; + rootGetters = { discussionsStructuredByLineCode: 'discussions' }; + }); + + it('dispatches actions & commits', done => { + mock.onAny().reply(200); + + actions + .publishReview({ dispatch, commit, getters, rootGetters }) + .then(() => { + expect(commit.mock.calls[0]).toEqual(['REQUEST_PUBLISH_REVIEW']); + expect(commit.mock.calls[1]).toEqual(['RECEIVE_PUBLISH_REVIEW_SUCCESS']); + + expect(dispatch.mock.calls[0]).toEqual(['updateDiscussionsAfterPublish']); + }) + .then(done) + .catch(done.fail); + }); + + it('dispatches error commits', done => { + mock.onAny().reply(500); + + actions + .publishReview({ dispatch, commit, getters, rootGetters }) + .then(() => { + expect(commit.mock.calls[0]).toEqual(['REQUEST_PUBLISH_REVIEW']); + expect(commit.mock.calls[1]).toEqual(['RECEIVE_PUBLISH_REVIEW_ERROR']); + }) + .then(done) + .catch(done.fail); + }); + }); + + describe('discardReview', () => { + it('commits mutations', done => { + const getters = { + getNotesData: { draftsDiscardPath: gl.TEST_HOST }, + }; + const commit = jest.fn(); + mock.onAny().reply(200); + + actions + .discardReview({ getters, commit }) + .then(() => { + expect(commit.mock.calls[0]).toEqual(['REQUEST_DISCARD_REVIEW']); + expect(commit.mock.calls[1]).toEqual(['RECEIVE_DISCARD_REVIEW_SUCCESS']); + }) + .then(done) + .catch(done.fail); + }); + + it('commits error mutations', done => { + const getters = { + getNotesData: { draftsDiscardPath: gl.TEST_HOST }, + }; + const commit = jest.fn(); + mock.onAny().reply(500); + + actions + .discardReview({ getters, commit }) + .then(() => { + expect(commit.mock.calls[0]).toEqual(['REQUEST_DISCARD_REVIEW']); + expect(commit.mock.calls[1]).toEqual(['RECEIVE_DISCARD_REVIEW_ERROR']); + }) + .then(done) + .catch(done.fail); + }); + }); + + describe('updateDraft', () => { + let getters; + + beforeEach(() => { + getters = { + getNotesData: { + draftsPath: gl.TEST_HOST, + }, + }; + }); + + it('commits RECEIVE_DRAFT_UPDATE_SUCCESS with returned data', done => { + const commit = jest.fn(); + const context = { + getters, + commit, + }; + res = { id: 1 }; + mock.onAny().reply(200, res); + + actions + .updateDraft(context, { note: { id: 1 }, noteText: 'test', callback() {} }) + .then(() => { + expect(commit).toHaveBeenCalledWith('RECEIVE_DRAFT_UPDATE_SUCCESS', { id: 1 }); + }) + .then(done) + .catch(done.fail); + }); + + it('calls passed callback', done => { + const commit = jest.fn(); + const context = { + getters, + commit, + }; + const callback = jest.fn(); + res = { id: 1 }; + mock.onAny().reply(200, res); + + actions + .updateDraft(context, { note: { id: 1 }, noteText: 'test', callback }) + .then(() => { + expect(callback).toHaveBeenCalled(); + }) + .then(done) + .catch(done.fail); + }); + }); + + describe('toggleReviewDropdown', () => { + it('dispatches openReviewDropdown', done => { + testAction( + actions.toggleReviewDropdown, + null, + { showPreviewDropdown: false }, + [], + [{ type: 'openReviewDropdown' }], + done, + ); + }); + + it('dispatches closeReviewDropdown when showPreviewDropdown is true', done => { + testAction( + actions.toggleReviewDropdown, + null, + { showPreviewDropdown: true }, + [], + [{ type: 'closeReviewDropdown' }], + done, + ); + }); + }); + + describe('openReviewDropdown', () => { + it('commits OPEN_REVIEW_DROPDOWN', done => { + testAction( + actions.openReviewDropdown, + null, + null, + [{ type: 'OPEN_REVIEW_DROPDOWN' }], + [], + done, + ); + }); + }); + + describe('closeReviewDropdown', () => { + it('commits CLOSE_REVIEW_DROPDOWN', done => { + testAction( + actions.closeReviewDropdown, + null, + null, + [{ type: 'CLOSE_REVIEW_DROPDOWN' }], + [], + done, + ); + }); + }); + + describe('expandAllDiscussions', () => { + it('dispatches expandDiscussion for all drafts', done => { + const state = { + drafts: [ + { + discussion_id: '1', + }, + ], + }; + + testAction( + actions.expandAllDiscussions, + null, + state, + [], + [ + { + type: 'expandDiscussion', + payload: { discussionId: '1' }, + }, + ], + done, + ); + }); + }); + + describe('scrollToDraft', () => { + beforeEach(() => { + window.mrTabs = { + currentAction: 'notes', + tabShown: jest.fn(), + }; + }); + + it('scrolls to draft item', () => { + const dispatch = jest.fn(); + const rootGetters = { + getDiscussion: () => ({ + id: '1', + diff_discussion: true, + }), + }; + const draft = { + discussion_id: '1', + id: '2', + }; + + actions.scrollToDraft({ dispatch, rootGetters }, draft); + + expect(dispatch.mock.calls[0]).toEqual(['closeReviewDropdown']); + + expect(dispatch.mock.calls[1]).toEqual([ + 'expandDiscussion', + { discussionId: '1' }, + { root: true }, + ]); + + expect(window.mrTabs.tabShown).toHaveBeenCalledWith('diffs'); + }); + }); +}); diff --git a/spec/frontend/batch_comments/stores/modules/batch_comments/getters_spec.js b/spec/frontend/batch_comments/stores/modules/batch_comments/getters_spec.js new file mode 100644 index 00000000000..2398bb4feb1 --- /dev/null +++ b/spec/frontend/batch_comments/stores/modules/batch_comments/getters_spec.js @@ -0,0 +1,27 @@ +import * as getters from '~/batch_comments/stores/modules/batch_comments/getters'; + +describe('Batch comments store getters', () => { + describe('draftsForFile', () => { + it('returns drafts for a file hash', () => { + const state = { + drafts: [ + { + file_hash: 'filehash', + comment: 'testing 123', + }, + { + file_hash: 'filehash2', + comment: 'testing 1234', + }, + ], + }; + + expect(getters.draftsForFile(state)('filehash')).toEqual([ + { + file_hash: 'filehash', + comment: 'testing 123', + }, + ]); + }); + }); +}); diff --git a/spec/frontend/batch_comments/stores/modules/batch_comments/mutations_spec.js b/spec/frontend/batch_comments/stores/modules/batch_comments/mutations_spec.js new file mode 100644 index 00000000000..a86726269ef --- /dev/null +++ b/spec/frontend/batch_comments/stores/modules/batch_comments/mutations_spec.js @@ -0,0 +1,159 @@ +import createState from '~/batch_comments/stores/modules/batch_comments/state'; +import mutations from '~/batch_comments/stores/modules/batch_comments/mutations'; +import * as types from '~/batch_comments/stores/modules/batch_comments/mutation_types'; + +describe('Batch comments mutations', () => { + let state; + + beforeEach(() => { + state = createState(); + }); + + describe(types.ADD_NEW_DRAFT, () => { + it('adds processed object into drafts array', () => { + const draft = { id: 1, note: 'test' }; + + mutations[types.ADD_NEW_DRAFT](state, draft); + + expect(state.drafts).toEqual([ + { + ...draft, + isDraft: true, + }, + ]); + }); + }); + + describe(types.DELETE_DRAFT, () => { + it('removes draft from array by ID', () => { + state.drafts.push({ id: 1 }, { id: 2 }); + + mutations[types.DELETE_DRAFT](state, 1); + + expect(state.drafts).toEqual([{ id: 2 }]); + }); + }); + + describe(types.SET_BATCH_COMMENTS_DRAFTS, () => { + it('adds to processed drafts in state', () => { + const drafts = [{ id: 1 }, { id: 2 }]; + + mutations[types.SET_BATCH_COMMENTS_DRAFTS](state, drafts); + + expect(state.drafts).toEqual([ + { + id: 1, + isDraft: true, + }, + { + id: 2, + isDraft: true, + }, + ]); + }); + }); + + describe(types.REQUEST_PUBLISH_REVIEW, () => { + it('sets isPublishing to true', () => { + mutations[types.REQUEST_PUBLISH_REVIEW](state); + + expect(state.isPublishing).toBe(true); + }); + }); + + describe(types.RECEIVE_PUBLISH_REVIEW_SUCCESS, () => { + it('resets drafts', () => { + state.drafts.push('test'); + + mutations[types.RECEIVE_PUBLISH_REVIEW_SUCCESS](state); + + expect(state.drafts).toEqual([]); + }); + + it('sets isPublishing to false', () => { + state.isPublishing = true; + + mutations[types.RECEIVE_PUBLISH_REVIEW_SUCCESS](state); + + expect(state.isPublishing).toBe(false); + }); + }); + + describe(types.RECEIVE_PUBLISH_REVIEW_ERROR, () => { + it('updates isPublishing to false', () => { + state.isPublishing = true; + + mutations[types.RECEIVE_PUBLISH_REVIEW_ERROR](state); + + expect(state.isPublishing).toBe(false); + }); + }); + + describe(types.REQUEST_DISCARD_REVIEW, () => { + it('sets isDiscarding to true', () => { + mutations[types.REQUEST_DISCARD_REVIEW](state); + + expect(state.isDiscarding).toBe(true); + }); + }); + + describe(types.RECEIVE_DISCARD_REVIEW_SUCCESS, () => { + it('emptys drafts array', () => { + state.drafts.push('test'); + + mutations[types.RECEIVE_DISCARD_REVIEW_SUCCESS](state); + + expect(state.drafts).toEqual([]); + }); + + it('sets isDiscarding to false', () => { + state.isDiscarding = true; + + mutations[types.RECEIVE_DISCARD_REVIEW_SUCCESS](state); + + expect(state.isDiscarding).toBe(false); + }); + }); + + describe(types.RECEIVE_DISCARD_REVIEW_ERROR, () => { + it('updates isDiscarding to false', () => { + state.isDiscarding = true; + + mutations[types.RECEIVE_DISCARD_REVIEW_ERROR](state); + + expect(state.isDiscarding).toBe(false); + }); + }); + + describe(types.RECEIVE_DRAFT_UPDATE_SUCCESS, () => { + it('updates draft in store', () => { + state.drafts.push({ id: 1 }); + + mutations[types.RECEIVE_DRAFT_UPDATE_SUCCESS](state, { id: 1, note: 'test' }); + + expect(state.drafts).toEqual([ + { + id: 1, + note: 'test', + isDraft: true, + }, + ]); + }); + }); + + describe(types.OPEN_REVIEW_DROPDOWN, () => { + it('sets showPreviewDropdown to true', () => { + mutations[types.OPEN_REVIEW_DROPDOWN](state); + + expect(state.showPreviewDropdown).toBe(true); + }); + }); + + describe(types.CLOSE_REVIEW_DROPDOWN, () => { + it('sets showPreviewDropdown to false', () => { + mutations[types.CLOSE_REVIEW_DROPDOWN](state); + + expect(state.showPreviewDropdown).toBe(false); + }); + }); +}); |