From 79bd1b8717599205fd1bf4f79048d3065b500536 Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Tue, 29 Jan 2019 18:31:31 -0600 Subject: [PATCH] Enable fast task lists for merge requests Allow single tasks to be updated quickly --- .../merge_requests/application_controller.rb | 3 +- app/services/merge_requests/update_service.rb | 7 +- app/services/task_list_toggle_service.rb | 3 +- spec/services/issues/update_service_spec.rb | 72 +----------------- .../merge_requests/update_service_spec.rb | 2 + .../services/task_list_toggle_service_spec.rb | 11 +++ .../issuable_shared_examples.rb | 73 +++++++++++++++++++ 7 files changed, 98 insertions(+), 73 deletions(-) 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/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