Commit 3d20a428 authored by Fatih Acet's avatar Fatih Acet

Merge branch 'fix-epic-tasklist' into 'master'

Fix tasklist for epics

Closes #4021

See merge request gitlab-org/gitlab-ee!3435
parents 3a7db45c 0bf8b565
...@@ -16,6 +16,10 @@ export default { ...@@ -16,6 +16,10 @@ export default {
required: true, required: true,
type: String, type: String,
}, },
updateEndpoint: {
required: true,
type: String,
},
canUpdate: { canUpdate: {
required: true, required: true,
type: Boolean, type: Boolean,
...@@ -262,6 +266,8 @@ export default { ...@@ -262,6 +266,8 @@ export default {
:description-text="state.descriptionText" :description-text="state.descriptionText"
:updated-at="state.updatedAt" :updated-at="state.updatedAt"
:task-status="state.taskStatus" :task-status="state.taskStatus"
:issuable-type="issuableType"
:update-url="updateEndpoint"
/> />
<edited-component <edited-component
v-if="hasUpdated" v-if="hasUpdated"
......
...@@ -22,6 +22,16 @@ ...@@ -22,6 +22,16 @@
required: false, required: false,
default: '', default: '',
}, },
issuableType: {
type: String,
required: false,
default: 'issue',
},
updateUrl: {
type: String,
required: false,
default: null,
},
}, },
data() { data() {
return { return {
...@@ -48,7 +58,7 @@ ...@@ -48,7 +58,7 @@
if (this.canUpdate) { if (this.canUpdate) {
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new TaskList({ new TaskList({
dataType: 'issue', dataType: this.issuableType,
fieldName: 'description', fieldName: 'description',
selector: '.detail-page-description', selector: '.detail-page-description',
}); });
...@@ -95,7 +105,9 @@ ...@@ -95,7 +105,9 @@
<textarea <textarea
class="hidden js-task-list-field" class="hidden js-task-list-field"
v-if="descriptionText" v-if="descriptionText"
v-model="descriptionText"> v-model="descriptionText"
:data-update-url="updateUrl"
>
</textarea> </textarea>
</div> </div>
</template> </template>
...@@ -213,6 +213,7 @@ module IssuablesHelper ...@@ -213,6 +213,7 @@ module IssuablesHelper
def issuable_initial_data(issuable) def issuable_initial_data(issuable)
data = { data = {
endpoint: issuable_path(issuable), endpoint: issuable_path(issuable),
updateEndpoint: "#{issuable_path(issuable)}.json",
canUpdate: can?(current_user, :"update_#{issuable.to_ability_name}", issuable), canUpdate: can?(current_user, :"update_#{issuable.to_ability_name}", issuable),
canDestroy: can?(current_user, :"destroy_#{issuable.to_ability_name}", issuable), canDestroy: can?(current_user, :"destroy_#{issuable.to_ability_name}", issuable),
canAdmin: can?(current_user, :"admin_#{issuable.to_ability_name}", issuable), canAdmin: can?(current_user, :"admin_#{issuable.to_ability_name}", issuable),
......
---
title: Fix tasklist for epics
merge_request:
author:
type: fixed
...@@ -12,6 +12,10 @@ ...@@ -12,6 +12,10 @@
type: String, type: String,
required: true, required: true,
}, },
updateEndpoint: {
type: String,
required: true,
},
canUpdate: { canUpdate: {
required: true, required: true,
type: Boolean, type: Boolean,
...@@ -111,7 +115,9 @@ ...@@ -111,7 +115,9 @@
:can-update="canUpdate" :can-update="canUpdate"
:can-destroy="canDestroy" :can-destroy="canDestroy"
:endpoint="endpoint" :endpoint="endpoint"
:update-endpoint="updateEndpoint"
:issuable-ref="issuableRef" :issuable-ref="issuableRef"
issuable-type="epic"
:initial-title-html="initialTitleHtml" :initial-title-html="initialTitleHtml"
:initial-title-text="initialTitleText" :initial-title-text="initialTitleText"
:initial-description-html="initialDescriptionHtml" :initial-description-html="initialDescriptionHtml"
......
...@@ -3,7 +3,16 @@ require 'spec_helper' ...@@ -3,7 +3,16 @@ require 'spec_helper'
feature 'Update Epic', :js do feature 'Update Epic', :js do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:group) { create(:group, :public) } let(:group) { create(:group, :public) }
let(:epic) { create(:epic, group: group) }
let(:markdown) do
<<-MARKDOWN.strip_heredoc
This is a task list:
- [ ] Incomplete entry 1
MARKDOWN
end
let(:epic) { create(:epic, group: group, description: markdown) }
before do before do
stub_licensed_features(epics: true) stub_licensed_features(epics: true)
...@@ -51,6 +60,16 @@ feature 'Update Epic', :js do ...@@ -51,6 +60,16 @@ feature 'Update Epic', :js do
expect(page).not_to have_selector('.uploading-container .button-attach-file') expect(page).not_to have_selector('.uploading-container .button-attach-file')
end end
it 'updates the tasklist' do
expect(page).to have_selector('ul.task-list', count: 1)
expect(page).to have_selector('li.task-list-item', count: 1)
expect(page).to have_selector('ul input[checked]', count: 0)
find('.task-list .task-list-item', text: 'Incomplete entry 1').find('input').click
expect(page).to have_selector('ul input[checked]', count: 1)
end
# Autocomplete is disabled for epics until #4084 is resolved # Autocomplete is disabled for epics until #4084 is resolved
describe 'autocomplete disabled' do describe 'autocomplete disabled' do
it 'does not open atwho container' do it 'does not open atwho container' do
......
...@@ -174,6 +174,7 @@ describe IssuablesHelper do ...@@ -174,6 +174,7 @@ describe IssuablesHelper do
expected_data = { expected_data = {
'endpoint' => "/#{@project.full_path}/issues/#{issue.iid}", 'endpoint' => "/#{@project.full_path}/issues/#{issue.iid}",
'updateEndpoint' => "/#{@project.full_path}/issues/#{issue.iid}.json",
'canUpdate' => true, 'canUpdate' => true,
'canDestroy' => true, 'canDestroy' => true,
'canAdmin' => true, 'canAdmin' => true,
...@@ -198,6 +199,7 @@ describe IssuablesHelper do ...@@ -198,6 +199,7 @@ describe IssuablesHelper do
expected_data = { expected_data = {
'endpoint' => "/groups/#{@group.full_path}/-/epics/#{epic.iid}", 'endpoint' => "/groups/#{@group.full_path}/-/epics/#{epic.iid}",
'updateEndpoint' => "/groups/#{@group.full_path}/-/epics/#{epic.iid}.json",
'issueLinksEndpoint' => "/groups/#{@group.full_path}/-/epics/#{epic.iid}/issues", 'issueLinksEndpoint' => "/groups/#{@group.full_path}/-/epics/#{epic.iid}/issues",
'canUpdate' => true, 'canUpdate' => true,
'canDestroy' => true, 'canDestroy' => true,
......
...@@ -34,6 +34,7 @@ describe('EpicShowApp', () => { ...@@ -34,6 +34,7 @@ describe('EpicShowApp', () => {
canUpdate, canUpdate,
canDestroy, canDestroy,
endpoint, endpoint,
updateEndpoint,
initialTitleHtml, initialTitleHtml,
initialTitleText, initialTitleText,
startDate, startDate,
...@@ -59,6 +60,7 @@ describe('EpicShowApp', () => { ...@@ -59,6 +60,7 @@ describe('EpicShowApp', () => {
canUpdate, canUpdate,
canDestroy, canDestroy,
endpoint, endpoint,
updateEndpoint,
issuableRef: '', issuableRef: '',
initialTitleHtml, initialTitleHtml,
initialTitleText, initialTitleText,
......
export const contentProps = { export const contentProps = {
endpoint: '', endpoint: '',
updateEndpoint: gl.TEST_HOST,
canAdmin: true, canAdmin: true,
canUpdate: true, canUpdate: true,
canDestroy: true, canDestroy: true,
......
...@@ -35,11 +35,12 @@ describe('Issuable output', () => { ...@@ -35,11 +35,12 @@ describe('Issuable output', () => {
canUpdate: true, canUpdate: true,
canDestroy: true, canDestroy: true,
endpoint: '/gitlab-org/gitlab-shell/issues/9/realtime_changes', endpoint: '/gitlab-org/gitlab-shell/issues/9/realtime_changes',
updateEndpoint: gl.TEST_HOST,
issuableRef: '#1', issuableRef: '#1',
initialTitleHtml: '', initialTitleHtml: '',
initialTitleText: '', initialTitleText: '',
initialDescriptionHtml: '', initialDescriptionHtml: 'test',
initialDescriptionText: '', initialDescriptionText: 'test',
markdownPreviewPath: '/', markdownPreviewPath: '/',
markdownDocsPath: '/', markdownDocsPath: '/',
projectNamespace: '/', projectNamespace: '/',
......
import Vue from 'vue'; import Vue from 'vue';
import descriptionComponent from '~/issue_show/components/description.vue'; import descriptionComponent from '~/issue_show/components/description.vue';
import * as taskList from '~/task_list';
import mountComponent from '../../helpers/vue_mount_component_helper';
describe('Description component', () => { describe('Description component', () => {
let vm; let vm;
let DescriptionComponent;
const props = {
canUpdate: true,
descriptionHtml: 'test',
descriptionText: 'test',
updatedAt: new Date().toString(),
taskStatus: '',
updateUrl: gl.TEST_HOST,
};
beforeEach(() => { beforeEach(() => {
const Component = Vue.extend(descriptionComponent); DescriptionComponent = Vue.extend(descriptionComponent);
if (!document.querySelector('.issuable-meta')) { if (!document.querySelector('.issuable-meta')) {
const metaData = document.createElement('div'); const metaData = document.createElement('div');
...@@ -15,15 +26,11 @@ describe('Description component', () => { ...@@ -15,15 +26,11 @@ describe('Description component', () => {
document.body.appendChild(metaData); document.body.appendChild(metaData);
} }
vm = new Component({ vm = mountComponent(DescriptionComponent, props);
propsData: { });
canUpdate: true,
descriptionHtml: 'test', afterEach(() => {
descriptionText: 'test', vm.$destroy();
updatedAt: new Date().toString(),
taskStatus: '',
},
}).$mount();
}); });
it('animates description changes', (done) => { it('animates description changes', (done) => {
...@@ -44,34 +51,46 @@ describe('Description component', () => { ...@@ -44,34 +51,46 @@ describe('Description component', () => {
}); });
}); });
// TODO: gl.TaskList no longer exists. rewrite these tests once we have a way to rewire ES modules describe('TaskList', () => {
beforeEach(() => {
// it('re-inits the TaskList when description changed', (done) => { vm = mountComponent(DescriptionComponent, Object.assign({}, props, {
// spyOn(gl, 'TaskList'); issuableType: 'issuableType',
// vm.descriptionHtml = 'changed'; }));
// spyOn(taskList, 'default');
// setTimeout(() => { });
// expect(
// gl.TaskList, it('re-inits the TaskList when description changed', (done) => {
// ).toHaveBeenCalled(); vm.descriptionHtml = 'changed';
//
// done(); setTimeout(() => {
// }); expect(taskList.default).toHaveBeenCalled();
// }); done();
});
// it('does not re-init the TaskList when canUpdate is false', (done) => { });
// spyOn(gl, 'TaskList');
// vm.canUpdate = false; it('does not re-init the TaskList when canUpdate is false', (done) => {
// vm.descriptionHtml = 'changed'; vm.canUpdate = false;
// vm.descriptionHtml = 'changed';
// setTimeout(() => {
// expect( setTimeout(() => {
// gl.TaskList, expect(taskList.default).not.toHaveBeenCalled();
// ).not.toHaveBeenCalled(); done();
// });
// done(); });
// });
// }); it('calls with issuableType dataType', (done) => {
vm.descriptionHtml = 'changed';
setTimeout(() => {
expect(taskList.default).toHaveBeenCalledWith({
dataType: 'issuableType',
fieldName: 'description',
selector: '.detail-page-description',
});
done();
});
});
});
describe('taskStatus', () => { describe('taskStatus', () => {
it('adds full taskStatus', (done) => { it('adds full taskStatus', (done) => {
...@@ -126,4 +145,8 @@ describe('Description component', () => { ...@@ -126,4 +145,8 @@ describe('Description component', () => {
}); });
}); });
}); });
it('sets data-update-url', () => {
expect(vm.$el.querySelector('textarea').dataset.updateUrl).toEqual(gl.TEST_HOST);
});
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment