diff options
-rw-r--r-- | app/assets/javascripts/issue_show/components/description.vue | 8 | ||||
-rw-r--r-- | app/assets/javascripts/task_list.js | 9 | ||||
-rw-r--r-- | locale/gitlab.pot | 3 | ||||
-rw-r--r-- | spec/javascripts/issue_show/components/app_spec.js | 13 | ||||
-rw-r--r-- | spec/javascripts/issue_show/components/description_spec.js | 14 | ||||
-rw-r--r-- | spec/javascripts/task_list_spec.js | 156 |
6 files changed, 196 insertions, 7 deletions
diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue index 56e873c6ba0..f42787a7147 100644 --- a/app/assets/javascripts/issue_show/components/description.vue +++ b/app/assets/javascripts/issue_show/components/description.vue @@ -1,6 +1,6 @@ <script> import $ from 'jquery'; -import createFlash from '~/flash'; +import { __ } from '~/locale'; import animateMixin from '../mixins/animate'; import TaskList from '../../task_list'; import recaptchaModalImplementor from '../../vue_shared/mixins/recaptcha_modal_implementor'; @@ -91,8 +91,10 @@ export default { }, taskListUpdateError() { - createFlash( - 'Someone edited this issue at the same time you did and we updated the issue description.', + window.Flash( + __( + 'Someone edited this issue at the same time you did and we updated the issue description.', + ), ); this.$emit('taskListUpdateFailed'); diff --git a/app/assets/javascripts/task_list.js b/app/assets/javascripts/task_list.js index c3932db7be2..161c44fa156 100644 --- a/app/assets/javascripts/task_list.js +++ b/app/assets/javascripts/task_list.js @@ -9,8 +9,9 @@ export default class TaskList { this.dataType = options.dataType; this.fieldName = options.fieldName; this.lockVersion = options.lockVersion; - this.onSuccess = options.onSuccess || (() => {}); this.taskListContainerSelector = `${this.selector} .js-task-list-container`; + this.updateHandler = this.update.bind(this); + this.onSuccess = options.onSuccess || (() => {}); this.onError = options.onError || function showFlash(e) { @@ -27,10 +28,10 @@ export default class TaskList { } init() { - // Prevent duplicate event bindings - this.disable(); + this.disable(); // Prevent duplicate event bindings + $(this.taskListContainerSelector).taskList('enable'); - $(document).on('tasklist:changed', this.taskListContainerSelector, this.update.bind(this)); + $(document).on('tasklist:changed', this.taskListContainerSelector, this.updateHandler); } getTaskListTarget(e = {}) { diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 5b794ae587b..0f50d797c69 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -6527,6 +6527,9 @@ msgstr "" msgid "Snippets" msgstr "" +msgid "Someone edited this issue at the same time you did and we updated the issue description." +msgstr "" + msgid "Something went wrong on our end" msgstr "" diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js index c3f624aebd3..028a36d3496 100644 --- a/spec/javascripts/issue_show/components/app_spec.js +++ b/spec/javascripts/issue_show/components/app_spec.js @@ -469,5 +469,18 @@ describe('Issuable output', () => { .then(done) .catch(done.fail); }); + + it('should show error message if store update fails', done => { + spyOn(vm.service, 'getData').and.returnValue(Promise.reject()); + spyOn(window, 'Flash'); + vm.issuableType = 'merge request'; + + vm.updateStoreState() + .then(() => { + expect(window.Flash).toHaveBeenCalledWith(`Error updating ${vm.issuableType}`); + }) + .then(done) + .catch(done.fail); + }); }); }); diff --git a/spec/javascripts/issue_show/components/description_spec.js b/spec/javascripts/issue_show/components/description_spec.js index 52148f4c66b..b6833079e6e 100644 --- a/spec/javascripts/issue_show/components/description_spec.js +++ b/spec/javascripts/issue_show/components/description_spec.js @@ -187,4 +187,18 @@ describe('Description component', () => { it('sets data-update-url', () => { expect(vm.$el.querySelector('textarea').dataset.updateUrl).toEqual(gl.TEST_HOST); }); + + describe('taskListUpdateError', () => { + it('should create flash notification and emit an event to parent', () => { + const msg = + 'Someone edited this issue at the same time you did and we updated the issue description.'; + spyOn(window, 'Flash'); + spyOn(vm, '$emit'); + + vm.taskListUpdateError(); + + expect(window.Flash).toHaveBeenCalledWith(msg); + expect(vm.$emit).toHaveBeenCalledWith('taskListUpdateFailed'); + }); + }); }); diff --git a/spec/javascripts/task_list_spec.js b/spec/javascripts/task_list_spec.js new file mode 100644 index 00000000000..00f8b2d6d77 --- /dev/null +++ b/spec/javascripts/task_list_spec.js @@ -0,0 +1,156 @@ +import $ from 'jquery'; +import TaskList from '~/task_list'; +import axios from '~/lib/utils/axios_utils'; + +describe('TaskList', () => { + let taskList; + let currentTarget; + const taskListOptions = { + selector: '.task-list', + dataType: 'issue', + fieldName: 'description', + lockVersion: 2, + }; + const createTaskList = () => new TaskList(taskListOptions); + + beforeEach(() => { + setFixtures(` + <div class="task-list"> + <div class="js-task-list-container"></div> + </div> + `); + + currentTarget = $('<div></div>'); + taskList = createTaskList(); + }); + + it('should call init when the class constructed', () => { + spyOn(TaskList.prototype, 'init').and.callThrough(); + spyOn(TaskList.prototype, 'disable'); + spyOn($.prototype, 'taskList'); + spyOn($.prototype, 'on'); + + taskList = createTaskList(); + const $taskListEl = $(taskList.taskListContainerSelector); + + expect(taskList.init).toHaveBeenCalled(); + expect(taskList.disable).toHaveBeenCalled(); + expect($taskListEl.taskList).toHaveBeenCalledWith('enable'); + expect($(document).on).toHaveBeenCalledWith( + 'tasklist:changed', + taskList.taskListContainerSelector, + taskList.updateHandler, + ); + }); + + describe('getTaskListTarget', () => { + it('should return currentTarget from event object if exists', () => { + const $target = taskList.getTaskListTarget({ currentTarget }); + + expect($target).toEqual(currentTarget); + }); + + // it('should return element of the taskListContainerSelector', () => { + // const $target = taskList.getTaskListTarget(); + + // expect($target).toEqual($(taskList.taskListContainerSelector)); + // }); + }); + + describe('disableTaskListItems', () => { + it('should call taskList method with disable param', () => { + spyOn($.prototype, 'taskList'); + + taskList.disableTaskListItems({ currentTarget }); + + expect(currentTarget.taskList).toHaveBeenCalledWith('disable'); + }); + }); + + describe('enableTaskListItems', () => { + it('should call taskList method with enable param', () => { + spyOn($.prototype, 'taskList'); + + taskList.enableTaskListItems({ currentTarget }); + + expect(currentTarget.taskList).toHaveBeenCalledWith('enable'); + }); + }); + + describe('disable', () => { + it('should disable task list items and off document event', () => { + spyOn(taskList, 'disableTaskListItems'); + spyOn($.prototype, 'off'); + + taskList.disable(); + + expect(taskList.disableTaskListItems).toHaveBeenCalled(); + expect($(document).off).toHaveBeenCalledWith( + 'tasklist:changed', + taskList.taskListContainerSelector, + ); + }); + }); + + describe('update', () => { + it('should disable task list items and make a patch request then enable them again', done => { + const response = { data: { lock_version: 3 } }; + spyOn(taskList, 'enableTaskListItems'); + spyOn(taskList, 'disableTaskListItems'); + spyOn(taskList, 'onSuccess'); + spyOn(axios, 'patch').and.returnValue(Promise.resolve(response)); + + const value = 'hello world'; + const endpoint = '/foo'; + const target = $(`<input data-update-url="${endpoint}" value="${value}" />`); + const detail = { + index: 2, + checked: true, + lineNumber: 8, + lineSource: '- [ ] check item', + }; + const event = { target, detail }; + const patchData = { + [taskListOptions.dataType]: { + [taskListOptions.fieldName]: value, + lock_version: taskListOptions.lockVersion, + update_task: { + index: detail.index, + checked: detail.checked, + line_number: detail.lineNumber, + line_source: detail.lineSource, + }, + }, + }; + + taskList + .update(event) + .then(() => { + expect(taskList.disableTaskListItems).toHaveBeenCalledWith(event); + expect(axios.patch).toHaveBeenCalledWith(endpoint, patchData); + expect(taskList.enableTaskListItems).toHaveBeenCalledWith(event); + expect(taskList.onSuccess).toHaveBeenCalledWith(response.data); + expect(taskList.lockVersion).toEqual(response.data.lock_version); + }) + .then(done) + .catch(done.fail); + }); + }); + + it('should handle request error and enable task list items', done => { + const response = { data: { error: 1 } }; + spyOn(taskList, 'enableTaskListItems'); + spyOn(taskList, 'onError'); + spyOn(axios, 'patch').and.returnValue(Promise.reject({ response })); // eslint-disable-line prefer-promise-reject-errors + + const event = { detail: {} }; + taskList + .update(event) + .then(() => { + expect(taskList.enableTaskListItems).toHaveBeenCalledWith(event); + expect(taskList.onError).toHaveBeenCalledWith(response.data); + }) + .then(done) + .catch(done.fail); + }); +}); |