Reset New branch button when issue state changes

This commit is contained in:
💃 Winnie 💃 2017-04-12 19:40:35 +00:00 committed by Alfredo Sumaran
parent defdf45ab5
commit 83c9d8cf86
3 changed files with 172 additions and 138 deletions

View file

@ -20,57 +20,60 @@ class Issue {
});
Issue.initIssueBtnEventListeners();
}
Issue.$btnNewBranch = $('#new-branch');
Issue.initMergeRequests();
Issue.initRelatedBranches();
Issue.initCanCreateBranch();
}
static initIssueBtnEventListeners() {
var issueFailMessage;
issueFailMessage = 'Unable to update this issue at this time.';
return $('a.btn-close, a.btn-reopen').on('click', function(e) {
var $this, isClose, shouldSubmit, url;
const issueFailMessage = 'Unable to update this issue at this time.';
const closeButtons = $('a.btn-close');
const isClosedBadge = $('div.status-box-closed');
const isOpenBadge = $('div.status-box-open');
const projectIssuesCounter = $('.issue_counter');
const reopenButtons = $('a.btn-reopen');
return closeButtons.add(reopenButtons).on('click', function(e) {
var $this, shouldSubmit, url;
e.preventDefault();
e.stopImmediatePropagation();
$this = $(this);
isClose = $this.hasClass('btn-close');
shouldSubmit = $this.hasClass('btn-comment');
if (shouldSubmit) {
Issue.submitNoteForm($this.closest('form'));
}
$this.prop('disabled', true);
Issue.setNewBranchButtonState(true, null);
url = $this.attr('href');
return $.ajax({
type: 'PUT',
url: url,
error: function(jqXHR, textStatus, errorThrown) {
var issueStatus;
issueStatus = isClose ? 'close' : 'open';
return new Flash(issueFailMessage, 'alert');
},
success: function(data, textStatus, jqXHR) {
url: url
}).fail(function(jqXHR, textStatus, errorThrown) {
new Flash(issueFailMessage);
Issue.initCanCreateBranch();
}).done(function(data, textStatus, jqXHR) {
if ('id' in data) {
$(document).trigger('issuable:change');
let total = Number($('.issue_counter').text().replace(/[^\d]/, ''));
if (isClose) {
$('a.btn-close').addClass('hidden');
$('a.btn-reopen').removeClass('hidden');
$('div.status-box-closed').removeClass('hidden');
$('div.status-box-open').addClass('hidden');
total -= 1;
const isClosed = $this.hasClass('btn-close');
closeButtons.toggleClass('hidden', isClosed);
reopenButtons.toggleClass('hidden', !isClosed);
isClosedBadge.toggleClass('hidden', !isClosed);
isOpenBadge.toggleClass('hidden', isClosed);
let numProjectIssues = Number(projectIssuesCounter.text().replace(/[^\d]/, ''));
numProjectIssues = isClosed ? numProjectIssues - 1 : numProjectIssues + 1;
projectIssuesCounter.text(gl.text.addDelimiter(numProjectIssues));
} else {
$('a.btn-reopen').addClass('hidden');
$('a.btn-close').removeClass('hidden');
$('div.status-box-closed').addClass('hidden');
$('div.status-box-open').removeClass('hidden');
total += 1;
}
$('.issue_counter').text(gl.text.addDelimiter(total));
} else {
new Flash(issueFailMessage, 'alert');
}
return $this.prop('disabled', false);
new Flash(issueFailMessage);
}
$this.prop('disabled', false);
Issue.initCanCreateBranch();
});
});
}
@ -86,9 +89,9 @@ class Issue {
static initMergeRequests() {
var $container;
$container = $('#merge-requests');
return $.getJSON($container.data('url')).error(function() {
return new Flash('Failed to load referenced merge requests', 'alert');
}).success(function(data) {
return $.getJSON($container.data('url')).fail(function() {
return new Flash('Failed to load referenced merge requests');
}).done(function(data) {
if ('html' in data) {
return $container.html(data.html);
}
@ -98,9 +101,9 @@ class Issue {
static initRelatedBranches() {
var $container;
$container = $('#related-branches');
return $.getJSON($container.data('url')).error(function() {
return new Flash('Failed to load related branches', 'alert');
}).success(function(data) {
return $.getJSON($container.data('url')).fail(function() {
return new Flash('Failed to load related branches');
}).done(function(data) {
if ('html' in data) {
return $container.html(data.html);
}
@ -108,24 +111,27 @@ class Issue {
}
static initCanCreateBranch() {
var $container;
$container = $('#new-branch');
// If the user doesn't have the required permissions the container isn't
// rendered at all.
if ($container.length === 0) {
if (Issue.$btnNewBranch.length === 0) {
return;
}
return $.getJSON($container.data('path')).error(function() {
$container.find('.unavailable').show();
return new Flash('Failed to check if a new branch can be created.', 'alert');
}).success(function(data) {
if (data.can_create_branch) {
$container.find('.available').show();
} else {
return $container.find('.unavailable').show();
}
return $.getJSON(Issue.$btnNewBranch.data('path')).fail(function() {
Issue.setNewBranchButtonState(false, false);
new Flash('Failed to check if a new branch can be created.');
}).done(function(data) {
Issue.setNewBranchButtonState(false, data.can_create_branch);
});
}
static setNewBranchButtonState(isPending, canCreate) {
if (Issue.$btnNewBranch.length === 0) {
return;
}
Issue.$btnNewBranch.find('.available').toggle(!isPending && canCreate);
Issue.$btnNewBranch.find('.unavailable').toggle(!isPending && !canCreate);
}
}
export default Issue;

View file

@ -0,0 +1,4 @@
---
title: Reset New branch button when issue state changes
merge_request: 5962
author: winniehell

View file

@ -1,18 +1,17 @@
/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, no-use-before-define, comma-dangle, max-len */
/* eslint-disable space-before-function-paren, one-var, one-var-declaration-per-line, no-use-before-define, comma-dangle, max-len */
import Issue from '~/issue';
require('~/lib/utils/text_utility');
describe('Issue', function() {
var INVALID_URL = 'http://goesnowhere.nothing/whereami';
var $boxClosed, $boxOpen, $btnClose, $btnReopen;
let $boxClosed, $boxOpen, $btnClose, $btnReopen;
preloadFixtures('issues/closed-issue.html.raw');
preloadFixtures('issues/issue-with-task-list.html.raw');
preloadFixtures('issues/open-issue.html.raw');
function expectErrorMessage() {
var $flashMessage = $('div.flash-alert');
const $flashMessage = $('div.flash-alert');
expect($flashMessage).toExist();
expect($flashMessage).toBeVisible();
expect($flashMessage).toHaveText('Unable to update this issue at this time.');
@ -26,10 +25,28 @@ describe('Issue', function() {
expectVisibility($btnReopen, !isIssueOpen);
}
function expectPendingRequest(req, $triggeredButton) {
expect(req.type).toBe('PUT');
expect(req.url).toBe($triggeredButton.attr('href'));
expect($triggeredButton).toHaveProp('disabled', true);
function expectNewBranchButtonState(isPending, canCreate) {
if (Issue.$btnNewBranch.length === 0) {
return;
}
const $available = Issue.$btnNewBranch.find('.available');
expect($available).toHaveText('New branch');
if (!isPending && canCreate) {
expect($available).toBeVisible();
} else {
expect($available).toBeHidden();
}
const $unavailable = Issue.$btnNewBranch.find('.unavailable');
expect($unavailable).toHaveText('New branch unavailable');
if (!isPending && !canCreate) {
expect($unavailable).toBeVisible();
} else {
expect($unavailable).toBeHidden();
}
}
function expectVisibility($element, shouldBeVisible) {
@ -81,100 +98,107 @@ describe('Issue', function() {
});
});
describe('close issue', function() {
[true, false].forEach((isIssueInitiallyOpen) => {
describe(`with ${isIssueInitiallyOpen ? 'open' : 'closed'} issue`, function() {
const action = isIssueInitiallyOpen ? 'close' : 'reopen';
function ajaxSpy(req) {
if (req.url === this.$triggeredButton.attr('href')) {
expect(req.type).toBe('PUT');
expect(this.$triggeredButton).toHaveProp('disabled', true);
expectNewBranchButtonState(true, false);
return this.issueStateDeferred;
} else if (req.url === Issue.$btnNewBranch.data('path')) {
expect(req.type).toBe('get');
expectNewBranchButtonState(true, false);
return this.canCreateBranchDeferred;
}
expect(req.url).toBe('unexpected');
return null;
}
beforeEach(function() {
if (isIssueInitiallyOpen) {
loadFixtures('issues/open-issue.html.raw');
} else {
loadFixtures('issues/closed-issue.html.raw');
}
findElements();
this.issue = new Issue();
expectIssueState(isIssueInitiallyOpen);
this.$triggeredButton = isIssueInitiallyOpen ? $btnClose : $btnReopen;
expectIssueState(true);
this.$projectIssuesCounter = $('.issue_counter');
this.$projectIssuesCounter.text('1,001');
this.issueStateDeferred = new jQuery.Deferred();
this.canCreateBranchDeferred = new jQuery.Deferred();
spyOn(jQuery, 'ajax').and.callFake(ajaxSpy.bind(this));
});
it('closes an issue', function() {
spyOn(jQuery, 'ajax').and.callFake(function(req) {
expectPendingRequest(req, $btnClose);
req.success({
it(`${action}s the issue`, function() {
this.$triggeredButton.trigger('click');
this.issueStateDeferred.resolve({
id: 34
});
this.canCreateBranchDeferred.resolve({
can_create_branch: !isIssueInitiallyOpen
});
$btnClose.trigger('click');
expectIssueState(false);
expect($btnClose).toHaveProp('disabled', false);
expect($('.issue_counter')).toHaveText(0);
expectIssueState(!isIssueInitiallyOpen);
expect(this.$triggeredButton).toHaveProp('disabled', false);
expect(this.$projectIssuesCounter.text()).toBe(isIssueInitiallyOpen ? '1,000' : '1,002');
expectNewBranchButtonState(false, !isIssueInitiallyOpen);
});
it('fails to close an issue with success:false', function() {
spyOn(jQuery, 'ajax').and.callFake(function(req) {
expectPendingRequest(req, $btnClose);
req.success({
it(`fails to ${action} the issue if saved:false`, function() {
this.$triggeredButton.trigger('click');
this.issueStateDeferred.resolve({
saved: false
});
this.canCreateBranchDeferred.resolve({
can_create_branch: isIssueInitiallyOpen
});
$btnClose.attr('href', INVALID_URL);
$btnClose.trigger('click');
expectIssueState(true);
expect($btnClose).toHaveProp('disabled', false);
expectIssueState(isIssueInitiallyOpen);
expect(this.$triggeredButton).toHaveProp('disabled', false);
expectErrorMessage();
expect($('.issue_counter')).toHaveText(1);
expect(this.$projectIssuesCounter.text()).toBe('1,001');
expectNewBranchButtonState(false, isIssueInitiallyOpen);
});
it('fails to closes an issue with HTTP error', function() {
spyOn(jQuery, 'ajax').and.callFake(function(req) {
expectPendingRequest(req, $btnClose);
req.error();
it(`fails to ${action} the issue if HTTP error occurs`, function() {
this.$triggeredButton.trigger('click');
this.issueStateDeferred.reject();
this.canCreateBranchDeferred.resolve({
can_create_branch: isIssueInitiallyOpen
});
$btnClose.attr('href', INVALID_URL);
$btnClose.trigger('click');
expectIssueState(true);
expect($btnClose).toHaveProp('disabled', true);
expectIssueState(isIssueInitiallyOpen);
expect(this.$triggeredButton).toHaveProp('disabled', true);
expectErrorMessage();
expect($('.issue_counter')).toHaveText(1);
expect(this.$projectIssuesCounter.text()).toBe('1,001');
expectNewBranchButtonState(false, isIssueInitiallyOpen);
});
it('updates counter', () => {
spyOn(jQuery, 'ajax').and.callFake(function(req) {
expectPendingRequest(req, $btnClose);
req.success({
id: 34
});
it('disables the new branch button if Ajax call fails', function() {
this.$triggeredButton.trigger('click');
this.issueStateDeferred.reject();
this.canCreateBranchDeferred.reject();
expectNewBranchButtonState(false, false);
});
expect($('.issue_counter')).toHaveText(1);
$('.issue_counter').text('1,001');
expect($('.issue_counter').text()).toEqual('1,001');
$btnClose.trigger('click');
expect($('.issue_counter').text()).toEqual('1,000');
});
});
it('does not trigger Ajax call if new branch button is missing', function() {
Issue.$btnNewBranch = $();
this.canCreateBranchDeferred = null;
describe('reopen issue', function() {
beforeEach(function() {
loadFixtures('issues/closed-issue.html.raw');
findElements();
this.issue = new Issue();
expectIssueState(false);
this.$triggeredButton.trigger('click');
this.issueStateDeferred.reject();
});
it('reopens an issue', function() {
spyOn(jQuery, 'ajax').and.callFake(function(req) {
expectPendingRequest(req, $btnReopen);
req.success({
id: 34
});
});
$btnReopen.trigger('click');
expectIssueState(true);
expect($btnReopen).toHaveProp('disabled', false);
expect($('.issue_counter')).toHaveText(1);
});
});
});