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
This commit is contained in:
commit
aa2fe07db2
|
@ -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,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
@ -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.',
|
||||
),
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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, {});
|
||||
|
||||
done();
|
||||
$('.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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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" })
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue