summaryrefslogtreecommitdiff
path: root/spec/frontend/batch_comments
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/batch_comments')
-rw-r--r--spec/frontend/batch_comments/components/diff_file_drafts_spec.js61
-rw-r--r--spec/frontend/batch_comments/components/draft_note_spec.js125
-rw-r--r--spec/frontend/batch_comments/components/drafts_count_spec.js43
-rw-r--r--spec/frontend/batch_comments/components/preview_item_spec.js130
-rw-r--r--spec/frontend/batch_comments/components/publish_button_spec.js52
-rw-r--r--spec/frontend/batch_comments/components/publish_dropdown_spec.js96
-rw-r--r--spec/frontend/batch_comments/mock_data.js27
-rw-r--r--spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js403
-rw-r--r--spec/frontend/batch_comments/stores/modules/batch_comments/getters_spec.js27
-rw-r--r--spec/frontend/batch_comments/stores/modules/batch_comments/mutations_spec.js159
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);
+ });
+ });
+});