Async notification subscriptions in issue boards
This commit is contained in:
parent
d2699aea57
commit
f494dbc515
|
@ -1,12 +1,13 @@
|
||||||
/* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren */
|
/* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren */
|
||||||
/* global BoardService */
|
|
||||||
|
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import VueResource from 'vue-resource';
|
import VueResource from 'vue-resource';
|
||||||
import Flash from '../flash';
|
import Flash from '../flash';
|
||||||
|
import { __ } from '../locale';
|
||||||
import FilteredSearchBoards from './filtered_search_boards';
|
import FilteredSearchBoards from './filtered_search_boards';
|
||||||
import eventHub from './eventhub';
|
import eventHub from './eventhub';
|
||||||
|
import sidebarEventHub from '../sidebar/event_hub';
|
||||||
import './models/issue';
|
import './models/issue';
|
||||||
import './models/label';
|
import './models/label';
|
||||||
import './models/list';
|
import './models/list';
|
||||||
|
@ -14,7 +15,7 @@ import './models/milestone';
|
||||||
import './models/assignee';
|
import './models/assignee';
|
||||||
import './stores/boards_store';
|
import './stores/boards_store';
|
||||||
import './stores/modal_store';
|
import './stores/modal_store';
|
||||||
import './services/board_service';
|
import BoardService from './services/board_service';
|
||||||
import './mixins/modal_mixins';
|
import './mixins/modal_mixins';
|
||||||
import './mixins/sortable_default_options';
|
import './mixins/sortable_default_options';
|
||||||
import './filters/due_date_filters';
|
import './filters/due_date_filters';
|
||||||
|
@ -77,11 +78,16 @@ $(() => {
|
||||||
});
|
});
|
||||||
Store.rootPath = this.boardsEndpoint;
|
Store.rootPath = this.boardsEndpoint;
|
||||||
|
|
||||||
// Listen for updateTokens event
|
|
||||||
eventHub.$on('updateTokens', this.updateTokens);
|
eventHub.$on('updateTokens', this.updateTokens);
|
||||||
|
eventHub.$on('newDetailIssue', this.updateDetailIssue);
|
||||||
|
eventHub.$on('clearDetailIssue', this.clearDetailIssue);
|
||||||
|
sidebarEventHub.$on('toggleSubscription', this.toggleSubscription);
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
eventHub.$off('updateTokens', this.updateTokens);
|
eventHub.$off('updateTokens', this.updateTokens);
|
||||||
|
eventHub.$off('newDetailIssue', this.updateDetailIssue);
|
||||||
|
eventHub.$off('clearDetailIssue', this.clearDetailIssue);
|
||||||
|
sidebarEventHub.$off('toggleSubscription', this.toggleSubscription);
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.filterManager = new FilteredSearchBoards(Store.filter, true);
|
this.filterManager = new FilteredSearchBoards(Store.filter, true);
|
||||||
|
@ -112,6 +118,46 @@ $(() => {
|
||||||
methods: {
|
methods: {
|
||||||
updateTokens() {
|
updateTokens() {
|
||||||
this.filterManager.updateTokens();
|
this.filterManager.updateTokens();
|
||||||
|
},
|
||||||
|
updateDetailIssue(newIssue) {
|
||||||
|
const sidebarInfoEndpoint = newIssue.sidebarInfoEndpoint;
|
||||||
|
if (sidebarInfoEndpoint && newIssue.subscribed === undefined) {
|
||||||
|
newIssue.setFetchingState('subscriptions', true);
|
||||||
|
BoardService.getIssueInfo(sidebarInfoEndpoint)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
newIssue.setFetchingState('subscriptions', false);
|
||||||
|
newIssue.updateData({
|
||||||
|
subscribed: data.subscribed,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
newIssue.setFetchingState('subscriptions', false);
|
||||||
|
Flash(__('An error occurred while fetching sidebar data'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Store.detail.issue = newIssue;
|
||||||
|
},
|
||||||
|
clearDetailIssue() {
|
||||||
|
Store.detail.issue = {};
|
||||||
|
},
|
||||||
|
toggleSubscription(id) {
|
||||||
|
const issue = Store.detail.issue;
|
||||||
|
if (issue.id === id && issue.toggleSubscriptionEndpoint) {
|
||||||
|
issue.setFetchingState('subscriptions', true);
|
||||||
|
BoardService.toggleIssueSubscription(issue.toggleSubscriptionEndpoint)
|
||||||
|
.then(() => {
|
||||||
|
issue.setFetchingState('subscriptions', false);
|
||||||
|
issue.updateData({
|
||||||
|
subscribed: !issue.subscribed,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
issue.setFetchingState('subscriptions', false);
|
||||||
|
Flash(__('An error occurred when toggling the notification subscription'));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,25 +1,11 @@
|
||||||
|
<script>
|
||||||
import './issue_card_inner';
|
import './issue_card_inner';
|
||||||
|
import eventHub from '../eventhub';
|
||||||
|
|
||||||
const Store = gl.issueBoards.BoardsStore;
|
const Store = gl.issueBoards.BoardsStore;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BoardsIssueCard',
|
name: 'BoardsIssueCard',
|
||||||
template: `
|
|
||||||
<li class="card"
|
|
||||||
:class="{ 'user-can-drag': !disabled && issue.id, 'is-disabled': disabled || !issue.id, 'is-active': issueDetailVisible }"
|
|
||||||
:index="index"
|
|
||||||
:data-issue-id="issue.id"
|
|
||||||
@mousedown="mouseDown"
|
|
||||||
@mousemove="mouseMove"
|
|
||||||
@mouseup="showIssue($event)">
|
|
||||||
<issue-card-inner
|
|
||||||
:list="list"
|
|
||||||
:issue="issue"
|
|
||||||
:issue-link-base="issueLinkBase"
|
|
||||||
:root-path="rootPath"
|
|
||||||
:update-filters="true" />
|
|
||||||
</li>
|
|
||||||
`,
|
|
||||||
components: {
|
components: {
|
||||||
'issue-card-inner': gl.issueBoards.IssueCardInner,
|
'issue-card-inner': gl.issueBoards.IssueCardInner,
|
||||||
},
|
},
|
||||||
|
@ -56,12 +42,30 @@ export default {
|
||||||
this.showDetail = false;
|
this.showDetail = false;
|
||||||
|
|
||||||
if (Store.detail.issue && Store.detail.issue.id === this.issue.id) {
|
if (Store.detail.issue && Store.detail.issue.id === this.issue.id) {
|
||||||
Store.detail.issue = {};
|
eventHub.$emit('clearDetailIssue');
|
||||||
} else {
|
} else {
|
||||||
Store.detail.issue = this.issue;
|
eventHub.$emit('newDetailIssue', this.issue);
|
||||||
Store.detail.list = this.list;
|
Store.detail.list = this.list;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<li class="card"
|
||||||
|
:class="{ 'user-can-drag': !disabled && issue.id, 'is-disabled': disabled || !issue.id, 'is-active': issueDetailVisible }"
|
||||||
|
:index="index"
|
||||||
|
:data-issue-id="issue.id"
|
||||||
|
@mousedown="mouseDown"
|
||||||
|
@mousemove="mouseMove"
|
||||||
|
@mouseup="showIssue($event)">
|
||||||
|
<issue-card-inner
|
||||||
|
:list="list"
|
||||||
|
:issue="issue"
|
||||||
|
:issue-link-base="issueLinkBase"
|
||||||
|
:root-path="rootPath"
|
||||||
|
:update-filters="true" />
|
||||||
|
</li>
|
||||||
|
</template>
|
|
@ -1,6 +1,6 @@
|
||||||
/* global Sortable */
|
/* global Sortable */
|
||||||
import boardNewIssue from './board_new_issue';
|
import boardNewIssue from './board_new_issue';
|
||||||
import boardCard from './board_card';
|
import boardCard from './board_card.vue';
|
||||||
import eventHub from '../eventhub';
|
import eventHub from '../eventhub';
|
||||||
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
|
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,13 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Flash from '../../flash';
|
import Flash from '../../flash';
|
||||||
import eventHub from '../../sidebar/event_hub';
|
import eventHub from '../../sidebar/event_hub';
|
||||||
import AssigneeTitle from '../../sidebar/components/assignees/assignee_title';
|
import assigneeTitle from '../../sidebar/components/assignees/assignee_title';
|
||||||
import Assignees from '../../sidebar/components/assignees/assignees';
|
import assignees from '../../sidebar/components/assignees/assignees';
|
||||||
import DueDateSelectors from '../../due_date_select';
|
import DueDateSelectors from '../../due_date_select';
|
||||||
import './sidebar/remove_issue';
|
import './sidebar/remove_issue';
|
||||||
import IssuableContext from '../../issuable_context';
|
import IssuableContext from '../../issuable_context';
|
||||||
import LabelsSelect from '../../labels_select';
|
import LabelsSelect from '../../labels_select';
|
||||||
|
import subscriptions from '../../sidebar/components/subscriptions/subscriptions.vue';
|
||||||
|
|
||||||
const Store = gl.issueBoards.BoardsStore;
|
const Store = gl.issueBoards.BoardsStore;
|
||||||
|
|
||||||
|
@ -117,11 +118,11 @@ gl.issueBoards.BoardSidebar = Vue.extend({
|
||||||
new DueDateSelectors();
|
new DueDateSelectors();
|
||||||
new LabelsSelect();
|
new LabelsSelect();
|
||||||
new Sidebar();
|
new Sidebar();
|
||||||
gl.Subscription.bindAll('.subscription');
|
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
assigneeTitle,
|
||||||
|
assignees,
|
||||||
removeBtn: gl.issueBoards.RemoveIssueBtn,
|
removeBtn: gl.issueBoards.RemoveIssueBtn,
|
||||||
'assignee-title': AssigneeTitle,
|
subscriptions,
|
||||||
assignees: Assignees,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,6 +17,11 @@ class ListIssue {
|
||||||
this.assignees = [];
|
this.assignees = [];
|
||||||
this.selected = false;
|
this.selected = false;
|
||||||
this.position = obj.relative_position || Infinity;
|
this.position = obj.relative_position || Infinity;
|
||||||
|
this.isFetching = {
|
||||||
|
subscriptions: true,
|
||||||
|
};
|
||||||
|
this.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
|
||||||
|
this.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
|
||||||
|
|
||||||
if (obj.milestone) {
|
if (obj.milestone) {
|
||||||
this.milestone = new ListMilestone(obj.milestone);
|
this.milestone = new ListMilestone(obj.milestone);
|
||||||
|
@ -73,6 +78,14 @@ class ListIssue {
|
||||||
return gl.issueBoards.BoardsStore.state.lists.filter(list => list.findIssue(this.id));
|
return gl.issueBoards.BoardsStore.state.lists.filter(list => list.findIssue(this.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateData(newData) {
|
||||||
|
Object.assign(this, newData);
|
||||||
|
}
|
||||||
|
|
||||||
|
setFetchingState(key, value) {
|
||||||
|
this.isFetching[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
update (url) {
|
update (url) {
|
||||||
const data = {
|
const data = {
|
||||||
issue: {
|
issue: {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
class BoardService {
|
export default class BoardService {
|
||||||
constructor ({ boardsEndpoint, listsEndpoint, bulkUpdatePath, boardId }) {
|
constructor ({ boardsEndpoint, listsEndpoint, bulkUpdatePath, boardId }) {
|
||||||
this.boards = Vue.resource(`${boardsEndpoint}{/id}.json`, {}, {
|
this.boards = Vue.resource(`${boardsEndpoint}{/id}.json`, {}, {
|
||||||
issues: {
|
issues: {
|
||||||
|
@ -88,6 +88,14 @@ class BoardService {
|
||||||
|
|
||||||
return this.issues.bulkUpdate(data);
|
return this.issues.bulkUpdate(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getIssueInfo(endpoint) {
|
||||||
|
return Vue.http.get(endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
static toggleIssueSubscription(endpoint) {
|
||||||
|
return Vue.http.post(endpoint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.BoardService = BoardService;
|
window.BoardService = BoardService;
|
||||||
|
|
|
@ -14,7 +14,6 @@ export default () => {
|
||||||
});
|
});
|
||||||
new LabelsSelect();
|
new LabelsSelect();
|
||||||
new IssuableContext(sidebarOptions.currentUser);
|
new IssuableContext(sidebarOptions.currentUser);
|
||||||
gl.Subscription.bindAll('.subscription');
|
|
||||||
new DueDateSelectors();
|
new DueDateSelectors();
|
||||||
window.sidebar = new Sidebar();
|
window.sidebar = new Sidebar();
|
||||||
};
|
};
|
||||||
|
|
|
@ -80,7 +80,6 @@ import './right_sidebar';
|
||||||
import './search';
|
import './search';
|
||||||
import './search_autocomplete';
|
import './search_autocomplete';
|
||||||
import './smart_interval';
|
import './smart_interval';
|
||||||
import './subscription';
|
|
||||||
import './subscription_select';
|
import './subscription_select';
|
||||||
import initBreadcrumbs from './breadcrumb';
|
import initBreadcrumbs from './breadcrumb';
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import Store from '../../stores/sidebar_store';
|
||||||
import Mediator from '../../sidebar_mediator';
|
import Mediator from '../../sidebar_mediator';
|
||||||
import eventHub from '../../event_hub';
|
import eventHub from '../../event_hub';
|
||||||
import Flash from '../../../flash';
|
import Flash from '../../../flash';
|
||||||
|
import { __ } from '../../../locale';
|
||||||
import subscriptions from './subscriptions.vue';
|
import subscriptions from './subscriptions.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -21,7 +22,7 @@ export default {
|
||||||
onToggleSubscription() {
|
onToggleSubscription() {
|
||||||
this.mediator.toggleSubscription()
|
this.mediator.toggleSubscription()
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
Flash('Error occurred when toggling the notification subscription');
|
Flash(__('Error occurred when toggling the notification subscription'));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -14,6 +14,10 @@ export default {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
loadingButton,
|
loadingButton,
|
||||||
|
@ -32,7 +36,7 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleSubscription() {
|
toggleSubscription() {
|
||||||
eventHub.$emit('toggleSubscription');
|
eventHub.$emit('toggleSubscription', this.id);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
class Subscription {
|
|
||||||
constructor(containerElm) {
|
|
||||||
this.containerElm = containerElm;
|
|
||||||
|
|
||||||
const subscribeButton = containerElm.querySelector('.js-subscribe-button');
|
|
||||||
if (subscribeButton) {
|
|
||||||
// remove class so we don't bind twice
|
|
||||||
subscribeButton.classList.remove('js-subscribe-button');
|
|
||||||
subscribeButton.addEventListener('click', this.toggleSubscription.bind(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleSubscription(event) {
|
|
||||||
const button = event.currentTarget;
|
|
||||||
const buttonSpan = button.querySelector('span');
|
|
||||||
if (!buttonSpan || button.classList.contains('disabled')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
button.classList.add('disabled');
|
|
||||||
|
|
||||||
const isSubscribed = buttonSpan.innerHTML.trim().toLowerCase() !== 'subscribe';
|
|
||||||
const toggleActionUrl = this.containerElm.dataset.url;
|
|
||||||
|
|
||||||
$.post(toggleActionUrl, () => {
|
|
||||||
button.classList.remove('disabled');
|
|
||||||
|
|
||||||
// hack to allow this to work with the issue boards Vue object
|
|
||||||
if (document.querySelector('html').classList.contains('issue-boards-page')) {
|
|
||||||
gl.issueBoards.boardStoreIssueSet(
|
|
||||||
'subscribed',
|
|
||||||
!gl.issueBoards.BoardsStore.detail.issue.subscribed,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
buttonSpan.innerHTML = isSubscribed ? 'Subscribe' : 'Unsubscribe';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static bindAll(selector) {
|
|
||||||
[].forEach.call(document.querySelectorAll(selector), elm => new Subscription(elm));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.gl = window.gl || {};
|
|
||||||
window.gl.Subscription = Subscription;
|
|
|
@ -1,7 +1,5 @@
|
||||||
- if current_user
|
- if current_user
|
||||||
.block.light.subscription{ ":data-url" => "'#{build_issue_link_base}/' + issue.iid + '/toggle_subscription'" }
|
.block.subscriptions
|
||||||
%span.issuable-header-text.hide-collapsed.pull-left
|
%subscriptions{ ":loading" => "issue.isFetching && issue.isFetching.subscriptions",
|
||||||
Notifications
|
":subscribed" => "issue.subscribed",
|
||||||
%button.btn.btn-default.pull-right.js-subscribe-button.issuable-subscribe-button.hide-collapsed{ type: "button" }
|
":id" => "issue.id" }
|
||||||
%span
|
|
||||||
{{issue.subscribed ? 'Unsubscribe' : 'Subscribe'}}
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Update Issue Boards to fetch the notification subscription status asynchronously
|
||||||
|
merge_request:
|
||||||
|
author:
|
||||||
|
type: performance
|
|
@ -331,11 +331,29 @@ describe 'Issue Boards', :js do
|
||||||
context 'subscription' do
|
context 'subscription' do
|
||||||
it 'changes issue subscription' do
|
it 'changes issue subscription' do
|
||||||
click_card(card)
|
click_card(card)
|
||||||
|
wait_for_requests
|
||||||
|
|
||||||
page.within('.subscription') do
|
page.within('.subscriptions') do
|
||||||
click_button 'Subscribe'
|
click_button 'Subscribe'
|
||||||
wait_for_requests
|
wait_for_requests
|
||||||
expect(page).to have_content("Unsubscribe")
|
|
||||||
|
expect(page).to have_content('Unsubscribe')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has "Unsubscribe" button when already subscribed' do
|
||||||
|
create(:subscription, user: user, project: project, subscribable: issue2, subscribed: true)
|
||||||
|
visit project_board_path(project, board)
|
||||||
|
wait_for_requests
|
||||||
|
|
||||||
|
click_card(card)
|
||||||
|
wait_for_requests
|
||||||
|
|
||||||
|
page.within('.subscriptions') do
|
||||||
|
click_button 'Unsubscribe'
|
||||||
|
wait_for_requests
|
||||||
|
|
||||||
|
expect(page).to have_content('Subscribe')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,10 +9,11 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import '~/boards/models/assignee';
|
import '~/boards/models/assignee';
|
||||||
|
|
||||||
|
import eventHub from '~/boards/eventhub';
|
||||||
import '~/boards/models/list';
|
import '~/boards/models/list';
|
||||||
import '~/boards/models/label';
|
import '~/boards/models/label';
|
||||||
import '~/boards/stores/boards_store';
|
import '~/boards/stores/boards_store';
|
||||||
import boardCard from '~/boards/components/board_card';
|
import boardCard from '~/boards/components/board_card.vue';
|
||||||
import './mock_data';
|
import './mock_data';
|
||||||
|
|
||||||
describe('Board card', () => {
|
describe('Board card', () => {
|
||||||
|
@ -157,33 +158,35 @@ describe('Board card', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets detail issue to card issue on mouse up', () => {
|
it('sets detail issue to card issue on mouse up', () => {
|
||||||
|
spyOn(eventHub, '$emit');
|
||||||
|
|
||||||
triggerEvent('mousedown');
|
triggerEvent('mousedown');
|
||||||
triggerEvent('mouseup');
|
triggerEvent('mouseup');
|
||||||
|
|
||||||
expect(gl.issueBoards.BoardsStore.detail.issue).toEqual(vm.issue);
|
expect(eventHub.$emit).toHaveBeenCalledWith('newDetailIssue', vm.issue);
|
||||||
expect(gl.issueBoards.BoardsStore.detail.list).toEqual(vm.list);
|
expect(gl.issueBoards.BoardsStore.detail.list).toEqual(vm.list);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds active class if detail issue is set', (done) => {
|
it('adds active class if detail issue is set', (done) => {
|
||||||
triggerEvent('mousedown');
|
vm.detailIssue.issue = vm.issue;
|
||||||
triggerEvent('mouseup');
|
|
||||||
|
|
||||||
setTimeout(() => {
|
Vue.nextTick()
|
||||||
expect(vm.$el.classList.contains('is-active')).toBe(true);
|
.then(() => {
|
||||||
done();
|
expect(vm.$el.classList.contains('is-active')).toBe(true);
|
||||||
}, 0);
|
})
|
||||||
|
.then(done)
|
||||||
|
.catch(done.fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resets detail issue to empty if already set', () => {
|
it('resets detail issue to empty if already set', () => {
|
||||||
triggerEvent('mousedown');
|
spyOn(eventHub, '$emit');
|
||||||
triggerEvent('mouseup');
|
|
||||||
|
|
||||||
expect(gl.issueBoards.BoardsStore.detail.issue).toEqual(vm.issue);
|
gl.issueBoards.BoardsStore.detail.issue = vm.issue;
|
||||||
|
|
||||||
triggerEvent('mousedown');
|
triggerEvent('mousedown');
|
||||||
triggerEvent('mouseup');
|
triggerEvent('mouseup');
|
||||||
|
|
||||||
expect(gl.issueBoards.BoardsStore.detail.issue).toEqual({});
|
expect(eventHub.$emit).toHaveBeenCalledWith('clearDetailIssue');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -133,6 +133,19 @@ describe('Issue model', () => {
|
||||||
expect(relativePositionIssue.position).toBe(1);
|
expect(relativePositionIssue.position).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('updates data', () => {
|
||||||
|
issue.updateData({ subscribed: true });
|
||||||
|
expect(issue.subscribed).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets fetching state', () => {
|
||||||
|
expect(issue.isFetching.subscriptions).toBe(true);
|
||||||
|
|
||||||
|
issue.setFetchingState('subscriptions', false);
|
||||||
|
|
||||||
|
expect(issue.isFetching.subscriptions).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
describe('update', () => {
|
describe('update', () => {
|
||||||
it('passes assignee ids when there are assignees', (done) => {
|
it('passes assignee ids when there are assignees', (done) => {
|
||||||
spyOn(Vue.http, 'patch').and.callFake((url, data) => {
|
spyOn(Vue.http, 'patch').and.callFake((url, data) => {
|
||||||
|
|
|
@ -98,7 +98,6 @@ describe('LoadingButton', function () {
|
||||||
it('does not call given callback when disabled because of loading', () => {
|
it('does not call given callback when disabled because of loading', () => {
|
||||||
vm = mountComponent(LoadingButton, {
|
vm = mountComponent(LoadingButton, {
|
||||||
loading: true,
|
loading: true,
|
||||||
indeterminate: true,
|
|
||||||
});
|
});
|
||||||
spyOn(vm, '$emit');
|
spyOn(vm, '$emit');
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue