diff options
author | Sean McGivern <sean@gitlab.com> | 2019-02-07 16:47:33 +0000 |
---|---|---|
committer | Sean McGivern <sean@gitlab.com> | 2019-02-07 16:47:33 +0000 |
commit | aa2fe07db2e6a0e3dc92a06e61d1a9518adadebc (patch) | |
tree | eb926f01f7bb23d09e50b2b8e7ecd1983819c14a | |
parent | 68f9453c66b50b1478af79a3db906c41fc8cc658 (diff) | |
parent | 8bf5e4795db1915552c1b999ee0c4a9c00466526 (diff) | |
download | gitlab-ce-aa2fe07db2e6a0e3dc92a06e61d1a9518adadebc.tar.gz |
Merge branch '19745-new-tasklists-for-merge-requests' into 'master'
Enable fast task lists for merge requests
Closes #19745
See merge request gitlab-org/gitlab-ce!24779
-rw-r--r-- | app/assets/javascripts/issue_show/components/description.vue | 14 | ||||
-rw-r--r-- | app/assets/javascripts/merge_request.js | 8 | ||||
-rw-r--r-- | app/controllers/projects/merge_requests/application_controller.rb | 3 | ||||
-rw-r--r-- | app/services/merge_requests/update_service.rb | 7 | ||||
-rw-r--r-- | app/services/task_list_toggle_service.rb | 3 | ||||
-rw-r--r-- | locale/gitlab.pot | 5 | ||||
-rw-r--r-- | spec/javascripts/issue_show/components/description_spec.js | 10 | ||||
-rw-r--r-- | spec/javascripts/merge_request_spec.js | 53 | ||||
-rw-r--r-- | spec/services/issues/update_service_spec.rb | 72 | ||||
-rw-r--r-- | spec/services/merge_requests/update_service_spec.rb | 2 | ||||
-rw-r--r-- | spec/services/task_list_toggle_service_spec.rb | 11 | ||||
-rw-r--r-- | spec/support/shared_examples/issuable_shared_examples.rb | 73 |
12 files changed, 164 insertions, 97 deletions
diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue index e664269b199..58f14bac8c8 100644 --- a/app/assets/javascripts/issue_show/components/description.vue +++ b/app/assets/javascripts/issue_show/components/description.vue @@ -1,6 +1,7 @@ <script> import $ from 'jquery'; -import { __ } from '~/locale'; +import { s__, sprintf } from '~/locale'; +import createFlash from '~/flash'; import animateMixin from '../mixins/animate'; import TaskList from '../../task_list'; import recaptchaModalImplementor from '../../vue_shared/mixins/recaptcha_modal_implementor'; @@ -91,9 +92,14 @@ export default { }, taskListUpdateError() { - window.Flash( - __( - 'Someone edited this issue at the same time you did. The description has been updated and you will need to make your changes again.', + createFlash( + sprintf( + s__( + 'Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again.', + ), + { + issueType: this.issuableType, + }, ), ); diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js index ac3b47cd218..3b42a154af8 100644 --- a/app/assets/javascripts/merge_request.js +++ b/app/assets/javascripts/merge_request.js @@ -2,6 +2,7 @@ import $ from 'jquery'; import { __ } from '~/locale'; +import createFlash from '~/flash'; import TaskList from './task_list'; import MergeRequestTabs from './merge_request_tabs'; import IssuablesHelper from './helpers/issuables_helper'; @@ -40,6 +41,13 @@ function MergeRequest(opts) { document.querySelector('#task_status').innerText = result.task_status; document.querySelector('#task_status_short').innerText = result.task_status_short; }, + onError: () => { + createFlash( + __( + 'Someone edited this merge request at the same time you did. Please refresh the page to see changes.', + ), + ); + }, }); } } diff --git a/app/controllers/projects/merge_requests/application_controller.rb b/app/controllers/projects/merge_requests/application_controller.rb index 54ff7ded8e5..6045ee4e171 100644 --- a/app/controllers/projects/merge_requests/application_controller.rb +++ b/app/controllers/projects/merge_requests/application_controller.rb @@ -34,7 +34,8 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont :task_num, :title, :discussion_locked, - label_ids: [] + label_ids: [], + update_task: [:index, :checked, :line_number, :line_source] ] end diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb index 86a04587f79..8112c2a4299 100644 --- a/app/services/merge_requests/update_service.rb +++ b/app/services/merge_requests/update_service.rb @@ -21,7 +21,7 @@ module MergeRequests end handle_wip_event(merge_request) - update(merge_request) + update_task_event(merge_request) || update(merge_request) end # rubocop:disable Metrics/AbcSize @@ -83,6 +83,11 @@ module MergeRequests end # rubocop:enable Metrics/AbcSize + def handle_task_changes(merge_request) + todo_service.mark_pending_todos_as_done(merge_request, current_user) + todo_service.update_merge_request(merge_request, current_user) + end + def merge_from_quick_action(merge_request) last_diff_sha = params.delete(:merge) return unless merge_request.mergeable_with_quick_action?(current_user, last_diff_sha: last_diff_sha) diff --git a/app/services/task_list_toggle_service.rb b/app/services/task_list_toggle_service.rb index b5c4cd3235d..cfe187d9b12 100644 --- a/app/services/task_list_toggle_service.rb +++ b/app/services/task_list_toggle_service.rb @@ -32,7 +32,8 @@ class TaskListToggleService source_line_index = line_number - 1 markdown_task = source_lines[source_line_index] - return unless markdown_task == line_source + # The source in the DB could be using either \n or \r\n line endings + return unless markdown_task == line_source || markdown_task == line_source + "\r" return unless source_checkbox = Taskable::ITEM_PATTERN.match(markdown_task) currently_checked = TaskList::Item.new(source_checkbox[1]).complete? diff --git a/locale/gitlab.pot b/locale/gitlab.pot index efaaddfbaa1..937db5b7305 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -6643,7 +6643,10 @@ msgstr "" msgid "Snippets" msgstr "" -msgid "Someone edited this issue at the same time you did. The description has been updated and you will need to make your changes again." +msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again." +msgstr "" + +msgid "Someone edited this merge request at the same time you did. Please refresh the page to see changes." msgstr "" msgid "Something went wrong on our end" diff --git a/spec/javascripts/issue_show/components/description_spec.js b/spec/javascripts/issue_show/components/description_spec.js index 72716b97f5f..2eeed6770be 100644 --- a/spec/javascripts/issue_show/components/description_spec.js +++ b/spec/javascripts/issue_show/components/description_spec.js @@ -21,7 +21,8 @@ describe('Description component', () => { if (!document.querySelector('.issuable-meta')) { const metaData = document.createElement('div'); metaData.classList.add('issuable-meta'); - metaData.innerHTML = '<span id="task_status"></span><span id="task_status_short"></span>'; + metaData.innerHTML = + '<div class="flash-container"></div><span id="task_status"></span><span id="task_status_short"></span>'; document.body.appendChild(metaData); } @@ -33,6 +34,10 @@ describe('Description component', () => { vm.$destroy(); }); + afterAll(() => { + $('.issuable-meta .flash-container').remove(); + }); + it('animates description changes', done => { vm.descriptionHtml = 'changed'; @@ -192,12 +197,11 @@ describe('Description component', () => { it('should create flash notification and emit an event to parent', () => { const msg = 'Someone edited this issue at the same time you did. The description has been updated and you will need to make your changes again.'; - spyOn(window, 'Flash'); spyOn(vm, '$emit'); vm.taskListUpdateError(); - expect(window.Flash).toHaveBeenCalledWith(msg); + expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(msg); expect(vm.$emit).toHaveBeenCalledWith('taskListUpdateFailed'); }); }); diff --git a/spec/javascripts/merge_request_spec.js b/spec/javascripts/merge_request_spec.js index 32623d1781a..ab809930804 100644 --- a/spec/javascripts/merge_request_spec.js +++ b/spec/javascripts/merge_request_spec.js @@ -40,30 +40,51 @@ describe('MergeRequest', function() { expect($('.js-task-list-field').val()).toBe('- [x] Task List Item'); }); - it('submits an ajax request on tasklist:changed', done => { + describe('tasklist', () => { const lineNumber = 8; const lineSource = '- [ ] item 8'; const index = 3; const checked = true; - $('.js-task-list-field').trigger({ - type: 'tasklist:changed', - detail: { lineNumber, lineSource, index, checked }, + it('submits an ajax request on tasklist:changed', done => { + $('.js-task-list-field').trigger({ + type: 'tasklist:changed', + detail: { lineNumber, lineSource, index, checked }, + }); + + setTimeout(() => { + expect(axios.patch).toHaveBeenCalledWith( + `${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`, + { + merge_request: { + description: '- [ ] Task List Item', + lock_version: undefined, + update_task: { line_number: lineNumber, line_source: lineSource, index, checked }, + }, + }, + ); + + done(); + }); }); - setTimeout(() => { - expect(axios.patch).toHaveBeenCalledWith( - `${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`, - { - merge_request: { - description: '- [ ] Task List Item', - lock_version: undefined, - update_task: { line_number: lineNumber, line_source: lineSource, index, checked }, - }, - }, - ); + it('shows an error notification when tasklist update failed', done => { + mock + .onPatch(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`) + .reply(409, {}); + + $('.js-task-list-field').trigger({ + type: 'tasklist:changed', + detail: { lineNumber, lineSource, index, checked }, + }); + + setTimeout(() => { + expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe( + 'Someone edited this merge request at the same time you did. Please refresh the page to see changes.', + ); - done(); + done(); + }); }); }); }); diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index ef76e2311b1..931e47d3a77 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -471,6 +471,8 @@ describe Issues::UpdateService, :mailer do it { expect(issue.tasks?).to eq(true) } + it_behaves_like 'updating a single task' + context 'when tasks are marked as completed' do before do update_issue(description: "- [x] Task 1\n- [X] Task 2") @@ -543,76 +545,6 @@ describe Issues::UpdateService, :mailer do end end - context 'when updating a single task' do - before do - update_issue(description: "- [ ] Task 1\n- [ ] Task 2") - end - - it { expect(issue.tasks?).to eq(true) } - - context 'when a task is marked as completed' do - before do - update_issue(update_task: { index: 1, checked: true, line_source: '- [ ] Task 1', line_number: 1 }) - end - - it 'creates system note about task status change' do - note1 = find_note('marked the task **Task 1** as completed') - - expect(note1).not_to be_nil - - description_notes = find_notes('description') - expect(description_notes.length).to eq(1) - end - end - - context 'when a task is marked as incomplete' do - before do - update_issue(description: "- [x] Task 1\n- [X] Task 2") - update_issue(update_task: { index: 2, checked: false, line_source: '- [X] Task 2', line_number: 2 }) - end - - it 'creates system note about task status change' do - note1 = find_note('marked the task **Task 2** as incomplete') - - expect(note1).not_to be_nil - - description_notes = find_notes('description') - expect(description_notes.length).to eq(1) - end - end - - context 'when the task position has been modified' do - before do - update_issue(description: "- [ ] Task 1\n- [ ] Task 3\n- [ ] Task 2") - end - - it 'raises an exception' do - expect(Note.count).to eq(2) - expect do - update_issue(update_task: { index: 2, checked: true, line_source: '- [ ] Task 2', line_number: 2 }) - end.to raise_error(ActiveRecord::StaleObjectError) - expect(Note.count).to eq(2) - end - end - - context 'when the content changes but not task line number' do - before do - update_issue(description: "Paragraph\n\n- [ ] Task 1\n- [x] Task 2") - update_issue(description: "Paragraph with more words\n\n- [ ] Task 1\n- [x] Task 2") - update_issue(update_task: { index: 2, checked: false, line_source: '- [x] Task 2', line_number: 4 }) - end - - it 'creates system note about task status change' do - note1 = find_note('marked the task **Task 2** as incomplete') - - expect(note1).not_to be_nil - - description_notes = find_notes('description') - expect(description_notes.length).to eq(2) - end - end - end - context 'updating labels' do let(:label3) { create(:label, project: project) } let(:result) { described_class.new(project, user, params).execute(issue).reload } diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index be5ad849ba7..20580bf14b9 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -466,6 +466,8 @@ describe MergeRequests::UpdateService, :mailer do it { expect(@merge_request.tasks?).to eq(true) } + it_behaves_like 'updating a single task' + context 'when tasks are marked as completed' do before do update_merge_request({ description: "- [x] Task 1\n- [X] Task 2" }) diff --git a/spec/services/task_list_toggle_service_spec.rb b/spec/services/task_list_toggle_service_spec.rb index cc64dd25085..7c5480d382f 100644 --- a/spec/services/task_list_toggle_service_spec.rb +++ b/spec/services/task_list_toggle_service_spec.rb @@ -67,6 +67,17 @@ describe TaskListToggleService do expect(toggler.execute).to be_falsey end + it 'tolerates \r\n line endings' do + rn_markdown = markdown.gsub("\n", "\r\n") + toggler = described_class.new(rn_markdown, markdown_html, + toggle_as_checked: true, + line_source: '* [ ] Task 1', line_number: 1) + + expect(toggler.execute).to be_truthy + expect(toggler.updated_markdown.lines[0]).to eq "* [x] Task 1\r\n" + expect(toggler.updated_markdown_html).to include('disabled checked> Task 1') + end + it 'returns false if markdown is nil' do toggler = described_class.new(nil, markdown_html, toggle_as_checked: false, diff --git a/spec/support/shared_examples/issuable_shared_examples.rb b/spec/support/shared_examples/issuable_shared_examples.rb index 42f3b4db23c..c3d40c5b231 100644 --- a/spec/support/shared_examples/issuable_shared_examples.rb +++ b/spec/support/shared_examples/issuable_shared_examples.rb @@ -36,3 +36,76 @@ shared_examples 'system notes for milestones' do end end end + +shared_examples 'updating a single task' do + def update_issuable(opts) + issuable = try(:issue) || try(:merge_request) + described_class.new(project, user, opts).execute(issuable) + end + + before do + update_issuable(description: "- [ ] Task 1\n- [ ] Task 2") + end + + context 'when a task is marked as completed' do + before do + update_issuable(update_task: { index: 1, checked: true, line_source: '- [ ] Task 1', line_number: 1 }) + end + + it 'creates system note about task status change' do + note1 = find_note('marked the task **Task 1** as completed') + + expect(note1).not_to be_nil + + description_notes = find_notes('description') + expect(description_notes.length).to eq(1) + end + end + + context 'when a task is marked as incomplete' do + before do + update_issuable(description: "- [x] Task 1\n- [X] Task 2") + update_issuable(update_task: { index: 2, checked: false, line_source: '- [X] Task 2', line_number: 2 }) + end + + it 'creates system note about task status change' do + note1 = find_note('marked the task **Task 2** as incomplete') + + expect(note1).not_to be_nil + + description_notes = find_notes('description') + expect(description_notes.length).to eq(1) + end + end + + context 'when the task position has been modified' do + before do + update_issuable(description: "- [ ] Task 1\n- [ ] Task 3\n- [ ] Task 2") + end + + it 'raises an exception' do + expect(Note.count).to eq(2) + expect do + update_issuable(update_task: { index: 2, checked: true, line_source: '- [ ] Task 2', line_number: 2 }) + end.to raise_error(ActiveRecord::StaleObjectError) + expect(Note.count).to eq(2) + end + end + + context 'when the content changes but not task line number' do + before do + update_issuable(description: "Paragraph\n\n- [ ] Task 1\n- [x] Task 2") + update_issuable(description: "Paragraph with more words\n\n- [ ] Task 1\n- [x] Task 2") + update_issuable(update_task: { index: 2, checked: false, line_source: '- [x] Task 2', line_number: 4 }) + end + + it 'creates system note about task status change' do + note1 = find_note('marked the task **Task 2** as incomplete') + + expect(note1).not_to be_nil + + description_notes = find_notes('description') + expect(description_notes.length).to eq(2) + end + end +end |