Commit 8fa2932d authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'ph-axios-2' into 'master'

More conversions to axios

See merge request gitlab-org/gitlab-ce!16800
parents f88fbd2d 965ff965
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, one-var, no-underscore-dangle, one-var-declaration-per-line, object-shorthand, no-unused-vars, no-new, comma-dangle, consistent-return, quotes, dot-notation, quote-props, prefer-arrow-callback, max-len */
import 'vendor/jquery.waitforimages';
import axios from './lib/utils/axios_utils';
import { addDelimiter } from './lib/utils/text_utility';
import Flash from './flash';
import flash from './flash';
import TaskList from './task_list';
import CreateMergeRequestDropdown from './create_merge_request_dropdown';
import IssuablesHelper from './helpers/issuables_helper';
......@@ -42,12 +43,8 @@ export default class Issue {
this.disableCloseReopenButton($button);
url = $button.attr('href');
return $.ajax({
type: 'PUT',
url: url
})
.fail(() => new Flash(issueFailMessage))
.done((data) => {
return axios.put(url)
.then(({ data }) => {
const isClosedBadge = $('div.status-box-issue-closed');
const isOpenBadge = $('div.status-box-open');
const projectIssuesCounter = $('.issue_counter');
......@@ -74,9 +71,10 @@ export default class Issue {
}
}
} else {
new Flash(issueFailMessage);
flash(issueFailMessage);
}
})
.catch(() => flash(issueFailMessage))
.then(() => {
this.disableCloseReopenButton($button, false);
});
......@@ -115,24 +113,22 @@ export default class Issue {
static initMergeRequests() {
var $container;
$container = $('#merge-requests');
return $.getJSON($container.data('url')).fail(function() {
return new Flash('Failed to load referenced merge requests');
}).done(function(data) {
return axios.get($container.data('url'))
.then(({ data }) => {
if ('html' in data) {
return $container.html(data.html);
$container.html(data.html);
}
});
}).catch(() => flash('Failed to load referenced merge requests'));
}
static initRelatedBranches() {
var $container;
$container = $('#related-branches');
return $.getJSON($container.data('url')).fail(function() {
return new Flash('Failed to load related branches');
}).done(function(data) {
return axios.get($container.data('url'))
.then(({ data }) => {
if ('html' in data) {
return $container.html(data.html);
$container.html(data.html);
}
});
}).catch(() => flash('Failed to load related branches'));
}
}
import _ from 'underscore';
import axios from './lib/utils/axios_utils';
import { visitUrl } from './lib/utils/url_utility';
import bp from './breakpoints';
import { numberToHumanSize } from './lib/utils/number_utils';
......@@ -8,6 +9,7 @@ export default class Job {
constructor(options) {
this.timeout = null;
this.state = null;
this.fetchingStatusFavicon = false;
this.options = options || $('.js-build-options').data();
this.pagePath = this.options.pagePath;
......@@ -171,12 +173,23 @@ export default class Job {
}
getBuildTrace() {
return $.ajax({
url: `${this.pagePath}/trace.json`,
data: { state: this.state },
return axios.get(`${this.pagePath}/trace.json`, {
params: { state: this.state },
})
.done((log) => {
setCiStatusFavicon(`${this.pagePath}/status.json`);
.then((res) => {
const log = res.data;
if (!this.fetchingStatusFavicon) {
this.fetchingStatusFavicon = true;
setCiStatusFavicon(`${this.pagePath}/status.json`)
.then(() => {
this.fetchingStatusFavicon = false;
})
.catch(() => {
this.fetchingStatusFavicon = false;
});
}
if (log.state) {
this.state = log.state;
......@@ -217,7 +230,7 @@ export default class Job {
visitUrl(this.pagePath);
}
})
.fail(() => {
.catch(() => {
this.$buildRefreshAnimation.remove();
})
.then(() => {
......
......@@ -2,9 +2,12 @@
/* global Issuable */
/* global ListLabel */
import _ from 'underscore';
import { __ } from './locale';
import axios from './lib/utils/axios_utils';
import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
import DropdownUtils from './filtered_search/dropdown_utils';
import CreateLabelDropdown from './create_label';
import flash from './flash';
export default class LabelsSelect {
constructor(els, options = {}) {
......@@ -82,12 +85,8 @@ export default class LabelsSelect {
}
$loading.removeClass('hidden').fadeIn();
$dropdown.trigger('loading.gl.dropdown');
return $.ajax({
type: 'PUT',
url: issueUpdateURL,
dataType: 'JSON',
data: data
}).done(function(data) {
axios.put(issueUpdateURL, data)
.then(({ data }) => {
var labelCount, template, labelTooltipTitle, labelTitles;
$loading.fadeOut();
$dropdown.trigger('loaded.gl.dropdown');
......@@ -128,15 +127,15 @@ export default class LabelsSelect {
$('.has-tooltip', $value).tooltip({
container: 'body'
});
});
})
.catch(() => flash(__('Error saving label update.')));
};
$dropdown.glDropdown({
showMenuAbove: showMenuAbove,
data: function(term, callback) {
return $.ajax({
url: labelUrl
}).done(function(data) {
data = _.chain(data).groupBy(function(label) {
axios.get(labelUrl)
.then((res) => {
let data = _.chain(res.data).groupBy(function(label) {
return label.title;
}).map(function(label) {
var color;
......@@ -174,7 +173,8 @@ export default class LabelsSelect {
if (showMenuAbove) {
$dropdown.data('glDropdown').positionMenuAbove();
}
});
})
.catch(() => flash(__('Error fetching labels.')));
},
renderRow: function(label, instance) {
var $a, $li, color, colorEl, indeterminate, removesAll, selectedClass, spacing, i, marked, dropdownName, dropdownValue;
......
import axios from './axios_utils';
import Cache from './cache';
class AjaxCache extends Cache {
......@@ -18,22 +19,15 @@ class AjaxCache extends Cache {
let pendingRequest = this.pendingRequests[endpoint];
if (!pendingRequest) {
pendingRequest = new Promise((resolve, reject) => {
// jQuery 2 is not Promises/A+ compatible (missing catch)
$.ajax(endpoint) // eslint-disable-line promise/catch-or-return
.then(data => resolve(data),
(jqXHR, textStatus, errorThrown) => {
const error = new Error(`${endpoint}: ${errorThrown}`);
error.textStatus = textStatus;
reject(error);
},
);
})
.then((data) => {
pendingRequest = axios.get(endpoint)
.then(({ data }) => {
this.internalStorage[endpoint] = data;
delete this.pendingRequests[endpoint];
})
.catch((error) => {
.catch((e) => {
const error = new Error(`${endpoint}: ${e.message}`);
error.textStatus = e.message;
delete this.pendingRequests[endpoint];
throw error;
});
......
......@@ -19,6 +19,10 @@ axios.interceptors.response.use((config) => {
window.activeVueResources -= 1;
return config;
}, (e) => {
window.activeVueResources -= 1;
return Promise.reject(e);
});
export default axios;
......
import { getLocationHash } from './url_utility';
import axios from './axios_utils';
import { getLocationHash } from './url_utility';
export const getPagePath = (index = 0) => $('body').attr('data-page').split(':')[index];
......@@ -28,16 +28,11 @@ export const isInIssuePage = () => {
return page === 'issues' && action === 'show';
};
export const ajaxGet = url => $.ajax({
type: 'GET',
url,
dataType: 'script',
});
export const ajaxPost = (url, data) => $.ajax({
type: 'POST',
url,
data,
export const ajaxGet = url => axios.get(url, {
params: { format: 'js' },
responseType: 'text',
}).then(({ data }) => {
$.globalEval(data);
});
export const rstrip = (val) => {
......@@ -412,7 +407,6 @@ window.gl.utils = {
getGroupSlug,
isInIssuePage,
ajaxGet,
ajaxPost,
rstrip,
updateTooltipTitle,
disableButtonIfEmptyField,
......
/* eslint-disable no-param-reassign, comma-dangle */
import axios from '../lib/utils/axios_utils';
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
......@@ -10,20 +11,11 @@
}
fetchConflictsData() {
return $.ajax({
dataType: 'json',
url: this.conflictsPath
});
return axios.get(this.conflictsPath);
}
submitResolveConflicts(data) {
return $.ajax({
url: this.resolveConflictsPath,
data: JSON.stringify(data),
contentType: 'application/json',
dataType: 'json',
method: 'POST'
});
return axios.post(this.resolveConflictsPath, data);
}
}
......
......@@ -38,24 +38,23 @@ $(() => {
showDiffViewTypeSwitcher() { return mergeConflictsStore.fileTextTypePresent(); }
},
created() {
mergeConflictsService
.fetchConflictsData()
.done((data) => {
mergeConflictsService.fetchConflictsData()
.then(({ data }) => {
if (data.type === 'error') {
mergeConflictsStore.setFailedRequest(data.message);
} else {
mergeConflictsStore.setConflictsData(data);
}
})
.error(() => {
mergeConflictsStore.setFailedRequest();
})
.always(() => {
mergeConflictsStore.setLoadingState(false);
this.$nextTick(() => {
syntaxHighlight($('.js-syntax-highlight'));
});
})
.catch(() => {
mergeConflictsStore.setLoadingState(false);
mergeConflictsStore.setFailedRequest();
});
},
methods: {
......@@ -82,10 +81,10 @@ $(() => {
mergeConflictsService
.submitResolveConflicts(mergeConflictsStore.getCommitData())
.done((data) => {
.then(({ data }) => {
window.location.href = data.redirect_to;
})
.error(() => {
.catch(() => {
mergeConflictsStore.setSubmitState(false);
new Flash('Failed to save merge conflicts resolutions. Please try again!');
});
......
/* eslint-disable no-new, class-methods-use-this */
import Cookies from 'js-cookie';
import Flash from './flash';
import axios from './lib/utils/axios_utils';
import flash from './flash';
import BlobForkSuggestion from './blob/blob_fork_suggestion';
import initChangesDropdown from './init_changes_dropdown';
import bp from './breakpoints';
......@@ -244,14 +245,21 @@ export default class MergeRequestTabs {
if (this.commitsLoaded) {
return;
}
this.ajaxGet({
url: `${source}.json`,
success: (data) => {
this.toggleLoading(true);
axios.get(`${source}.json`)
.then(({ data }) => {
document.querySelector('div#commits').innerHTML = data.html;
localTimeAgo($('.js-timeago', 'div#commits'));
this.commitsLoaded = true;
this.scrollToElement('#commits');
},
this.toggleLoading(false);
})
.catch(() => {
this.toggleLoading(false);
flash('An error occurred while fetching this tab.');
});
}
......@@ -283,9 +291,10 @@ export default class MergeRequestTabs {
// some pages like MergeRequestsController#new has query parameters on that anchor
const urlPathname = parseUrlPathname(source);
this.ajaxGet({
url: `${urlPathname}.json${location.search}`,
success: (data) => {
this.toggleLoading(true);
axios.get(`${urlPathname}.json${location.search}`)
.then(({ data }) => {
const $container = $('#diffs');
$container.html(data.html);
......@@ -335,7 +344,12 @@ export default class MergeRequestTabs {
// (discussion and diff tabs) and `:target` only applies to the first
anchor.addClass('target');
}
},
this.toggleLoading(false);
})
.catch(() => {
this.toggleLoading(false);
flash('An error occurred while fetching this tab.');
});
}
......@@ -346,17 +360,6 @@ export default class MergeRequestTabs {
$('.mr-loading-status .loading').toggle(status);
}
ajaxGet(options) {
const defaults = {
beforeSend: () => this.toggleLoading(true),
error: () => new Flash('An error occurred while fetching this tab.', 'alert'),
complete: () => this.toggleLoading(false),
dataType: 'json',
type: 'GET',
};
$.ajax($.extend({}, defaults, options));
}
diffViewType() {
return $('.inline-parallel-buttons a.active').data('view-type');
}
......
import Flash from './flash';
import axios from './lib/utils/axios_utils';
import flash from './flash';
export default class Milestone {
constructor() {
......@@ -33,15 +34,12 @@ export default class Milestone {
const tabElId = $target.attr('href');
if (endpoint && !$target.hasClass('is-loaded')) {
$.ajax({
url: endpoint,
dataType: 'JSON',
})
.fail(() => new Flash('Error loading milestone tab'))
.done((data) => {
axios.get(endpoint)
.then(({ data }) => {
$(tabElId).html(data.html);
$target.addClass('is-loaded');
});
})
.catch(() => flash('Error loading milestone tab'));
}
}
}
......@@ -2,6 +2,7 @@
/* global Issuable */
/* global ListMilestone */
import _ from 'underscore';
import axios from './lib/utils/axios_utils';
import { timeFor } from './lib/utils/datetime_utility';
export default class MilestoneSelect {
......@@ -52,9 +53,8 @@ export default class MilestoneSelect {
}
return $dropdown.glDropdown({
showMenuAbove: showMenuAbove,
data: (term, callback) => $.ajax({
url: milestonesUrl
}).done((data) => {
data: (term, callback) => axios.get(milestonesUrl)
.then(({ data }) => {
const extraOptions = [];
if (showAny) {
extraOptions.push({
......@@ -200,11 +200,8 @@ export default class MilestoneSelect {
data[abilityName].milestone_id = selected != null ? selected : null;
$loading.removeClass('hidden').fadeIn();
$dropdown.trigger('loading.gl.dropdown');
return $.ajax({
type: 'PUT',
url: issueUpdateURL,
data: data
}).done((data) => {
return axios.put(issueUpdateURL, data)
.then(({ data }) => {
$dropdown.trigger('loaded.gl.dropdown');
$loading.fadeOut();
$selectBox.hide();
......
......@@ -18,13 +18,14 @@ import 'vendor/jquery.atwho';
import AjaxCache from '~/lib/utils/ajax_cache';
import axios from './lib/utils/axios_utils';
import { getLocationHash } from './lib/utils/url_utility';
import axios from './lib/utils/axios_utils';
import Flash from './flash';
import CommentTypeToggle from './comment_type_toggle';
import GLForm from './gl_form';
import loadAwardsHandler from './awards_handler';
import Autosave from './autosave';
import TaskList from './task_list';
import { ajaxPost, isInViewport, getPagePath, scrollToElement, isMetaKey } from './lib/utils/common_utils';
import { isInViewport, getPagePath, scrollToElement, isMetaKey } from './lib/utils/common_utils';
import imageDiffHelper from './image_diff/helpers/index';
import { localTimeAgo } from './lib/utils/datetime_utility';
......@@ -1399,7 +1400,7 @@ export default class Notes {
* 2) Identify comment type; a) Main thread b) Discussion thread c) Discussion resolve
* 3) Build temporary placeholder element (using `createPlaceholderNote`)
* 4) Show placeholder note on UI
* 5) Perform network request to submit the note using `ajaxPost`
* 5) Perform network request to submit the note using `axios.post`
* a) If request is successfully completed
* 1. Remove placeholder element
* 2. Show submitted Note element
......@@ -1481,8 +1482,10 @@ export default class Notes {
/* eslint-disable promise/catch-or-return */
// Make request to submit comment on server
ajaxPost(formAction, formData)
.then((note) => {
axios.post(formAction, formData)
.then((res) => {
const note = res.data;
// Submission successful! remove placeholder
$notesContainer.find(`#${noteUniqueId}`).remove();
......@@ -1555,7 +1558,7 @@ export default class Notes {
}
$form.trigger('ajax:success', [note]);
}).fail(() => {
}).catch(() => {
// Submission failed, remove placeholder note and show Flash error message
$notesContainer.find(`#${noteUniqueId}`).remove();
......@@ -1594,7 +1597,7 @@ export default class Notes {
*
* 1) Get Form metadata
* 2) Update note element with new content
* 3) Perform network request to submit the updated note using `ajaxPost`
* 3) Perform network request to submit the updated note using `axios.post`
* a) If request is successfully completed
* 1. Show submitted Note element
* b) If request failed
......@@ -1625,12 +1628,12 @@ export default class Notes {
/* eslint-disable promise/catch-or-return */
// Make request to update comment on server
ajaxPost(formAction, formData)
.then((note) => {
axios.post(formAction, formData)
.then(({ data }) => {
// Submission successful! render final note element
this.updateNote(note, $editingNote);
this.updateNote(data, $editingNote);
})
.fail(() => {
.catch(() => {
// Submission failed, revert back to original note
$noteBodyText.html(_.escape(cachedNoteBodyText));
$editingNote.removeClass('being-posted fade-in');
......
/* eslint-disable space-before-function-paren, one-var, one-var-declaration-per-line, no-use-before-define, comma-dangle, max-len */
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import Issue from '~/issue';
import '~/lib/utils/text_utility';
......@@ -88,20 +90,24 @@ describe('Issue', function() {
[true, false].forEach((isIssueInitiallyOpen) => {
describe(`with ${isIssueInitiallyOpen ? 'open' : 'closed'} issue`, function() {
const action = isIssueInitiallyOpen ? 'close' : 'reopen';
let mock;
function ajaxSpy(req) {
if (req.url === this.$triggeredButton.attr('href')) {
expect(req.type).toBe('PUT');
function mockCloseButtonResponseSuccess(url, response) {
mock.onPut(url).reply(() => {
expectNewBranchButtonState(true, false);
return this.issueStateDeferred;
} else if (req.url === Issue.createMrDropdownWrap.dataset.canCreatePath) {
expect(req.type).toBe('GET');
expectNewBranchButtonState(true, false);
return this.canCreateBranchDeferred;
return [200, response];
});
}
function mockCloseButtonResponseError(url) {
mock.onPut(url).networkError();
}
expect(req.url).toBe('unexpected');
return null;
function mockCanCreateBranch(canCreateBranch) {
mock.onGet(/(.*)\/can_create_branch$/).reply(200, {
can_create_branch: canCreateBranch,
});
}
beforeEach(function() {
......@@ -111,6 +117,11 @@ describe('Issue', function() {
loadFixtures('issues/closed-issue.html.raw');
}
mock = new MockAdapter(axios);
mock.onGet(/(.*)\/related_branches$/).reply(200, {});
mock.onGet(/(.*)\/referenced_merge_requests$/).reply(200, {});
findElements(isIssueInitiallyOpen);
this.issue = new Issue();
expectIssueState(isIssueInitiallyOpen);
......@@ -120,71 +131,89 @@ describe('Issue', function() {
this.$projectIssuesCounter = $('.issue_counter').first();
this.$projectIssuesCounter.text('1,001');
this.issueStateDeferred = new jQuery.Deferred();
this.canCreateBranchDeferred = new jQuery.Deferred();
spyOn(axios, 'get').and.callThrough();
});
spyOn(jQuery, 'ajax').and.callFake(ajaxSpy.bind(this));
afterEach(() => {
mock.restore();
$('div.flash-alert').remove();
});
it(`${action}s the issue`, function() {
this.$triggeredButton.trigger('click');
this.issueStateDeferred.resolve({
it(`${action}s the issue`, function(done) {
mockCloseButtonResponseSuccess(this.$triggeredButton.attr('href'), {
id: 34
});
this.canCreateBranchDeferred.resolve({
can_create_branch: !isIssueInitiallyOpen
});
mockCanCreateBranch(!isIssueInitiallyOpen);
this.$triggeredButton.trigger('click');
setTimeout(() => {
expectIssueState(!isIssueInitiallyOpen);
expect(this.$triggeredButton.get(0).getAttribute('disabled')).toBeNull();
expect(this.$projectIssuesCounter.text()).toBe(isIssueInitiallyOpen ? '1,000' : '1,002');
expectNewBranchButtonState(false, !isIssueInitiallyOpen);
done();
});
});
it(`fails to ${action} the issue if saved:false`, function() {
this.$triggeredButton.trigger('click');
this.issueStateDeferred.resolve({
it(`fails to ${action} the issue if saved:false`, function(done) {
mockCloseButtonResponseSuccess(this.$triggeredButton.attr('href'), {
saved: false
});
this.canCreateBranchDeferred.resolve({
can_create_branch: isIssueInitiallyOpen
});
mockCanCreateBranch(isIssueInitiallyOpen);
this.$triggeredButton.trigger('click');
setTimeout(() => {
expectIssueState(isIssueInitiallyOpen);
expect(this.$triggeredButton.get(0).getAttribute('disabled')).toBeNull();
expectErrorMessage();
expect(this.$projectIssuesCounter.text()).toBe('1,001');
expectNewBranchButtonState(false, isIssueInitiallyOpen);
done();
});
});
it(`fails to ${action} the issue if HTTP error occurs`, function() {
it(`fails to ${action} the issue if HTTP error occurs`, function(done) {
mockCloseButtonResponseError(this.$triggeredButton.attr('href'));
mockCanCreateBranch(isIssueInitiallyOpen);
this.$triggeredButton.trigger('click');
this.issueStateDeferred.reject();
this.canCreateBranchDeferred.resolve({
can_create_branch: isIssueInitiallyOpen
});
setTimeout(() => {
expectIssueState(isIssueInitiallyOpen);
expect(this.$triggeredButton.get(0).getAttribute('disabled')).toBeNull();
expectErrorMessage();
expect(this.$projectIssuesCounter.text()).toBe('1,001');
expectNewBranchButtonState(false, isIssueInitiallyOpen);
done();
});
});
it('disables the new branch button if Ajax call fails', function() {
mockCloseButtonResponseError(this.$triggeredButton.attr('href'));
mock.onGet(/(.*)\/can_create_branch$/).networkError();
this.$triggeredButton.trigger('click');
this.issueStateDeferred.reject();
this.canCreateBranchDeferred.reject();
expectNewBranchButtonState(false, false);
});
it('does not trigger Ajax call if new branch button is missing', function() {
it('does not trigger Ajax call if new branch button is missing', function(done) {
mockCloseButtonResponseError(this.$triggeredButton.attr('href'));
Issue.$btnNewBranch = $();
this.canCreateBranchDeferred = null;
this.$triggeredButton.trigger('click');
this.issueStateDeferred.reject();
setTimeout(() => {
expect(axios.get).not.toHaveBeenCalled();
done();
});
});
});
});
......
This diff is collapsed.
/* eslint-disable no-new */
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import IssuableContext from '~/issuable_context';
import LabelsSelect from '~/labels_select';
......@@ -10,35 +12,44 @@ import '~/users_select';
(() => {
let saveLabelCount = 0;
let mock;
describe('Issue dropdown sidebar', () => {
preloadFixtures('static/issue_sidebar_label.html.raw');
beforeEach(() => {
loadFixtures('static/issue_sidebar_label.html.raw');
mock = new MockAdapter(axios);
new IssuableContext('{"id":1,"name":"Administrator","username":"root"}');
new LabelsSelect();
spyOn(jQuery, 'ajax').and.callFake((req) => {
const d = $.Deferred();
let LABELS_DATA = [];
mock.onGet('/root/test/labels.json').reply(() => {
const labels = Array(10).fill().map((_, i) => ({
id: i,
title: `test ${i}`,
color: '#5CB85C',
}));
if (req.url === '/root/test/labels.json') {
for (let i = 0; i < 10; i += 1) {
LABELS_DATA.push({ id: i, title: `test ${i}`, color: '#5CB85C' });
}
} else if (req.url === '/root/test/issues/2.json') {
const tmp = [];
for (let i = 0; i < saveLabelCount; i += 1) {
tmp.push({ id: i, title: `test ${i}`, color: '#5CB85C' });
}
LABELS_DATA = { labels: tmp };
}
return [200, labels];
});
mock.onPut('/root/test/issues/2.json').reply(() => {
const labels = Array(saveLabelCount).fill().map((_, i) => ({
id: i,
title: `test ${i}`,
color: '#5CB85C',
}));
d.resolve(LABELS_DATA);
return d.promise();
return [200, { labels }];
});
});
afterEach(() => {
mock.restore();
});
it('changes collapsed tooltip when changing labels when less than 5', (done) => {
saveLabelCount = 5;
$('.edit-link').get(0).click();
......
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import AjaxCache from '~/lib/utils/ajax_cache';
describe('AjaxCache', () => {
......@@ -87,66 +89,53 @@ describe('AjaxCache', () => {
});
describe('retrieve', () => {
let ajaxSpy;
let mock;
beforeEach(() => {
spyOn(jQuery, 'ajax').and.callFake(url => ajaxSpy(url));
mock = new MockAdapter(axios);
spyOn(axios, 'get').and.callThrough();
});
afterEach(() => {
mock.restore();
});
it('stores and returns data from Ajax call if cache is empty', (done) => {
ajaxSpy = (url) => {
expect(url).toBe(dummyEndpoint);
const deferred = $.Deferred();
deferred.resolve(dummyResponse);
return deferred.promise();
};
mock.onGet(dummyEndpoint).reply(200, dummyResponse);
AjaxCache.retrieve(dummyEndpoint)
.then((data) => {
expect(data).toBe(dummyResponse);
expect(AjaxCache.internalStorage[dummyEndpoint]).toBe(dummyResponse);
expect(data).toEqual(dummyResponse);
expect(AjaxCache.internalStorage[dummyEndpoint]).toEqual(dummyResponse);
})
.then(done)
.catch(fail);
});
it('makes no Ajax call if request is pending', () => {
const responseDeferred = $.Deferred();
ajaxSpy = (url) => {
expect(url).toBe(dummyEndpoint);
// neither reject nor resolve to keep request pending
return responseDeferred.promise();
};
const unexpectedResponse = data => fail(`Did not expect response: ${data}`);
it('makes no Ajax call if request is pending', (done) => {
mock.onGet(dummyEndpoint).reply(200, dummyResponse);
AjaxCache.retrieve(dummyEndpoint)
.then(unexpectedResponse)
.then(done)
.catch(fail);
AjaxCache.retrieve(dummyEndpoint)
.then(unexpectedResponse)
.then(done)
.catch(fail);
expect($.ajax.calls.count()).toBe(1);
expect(axios.get.calls.count()).toBe(1);
});
it('returns undefined if Ajax call fails and cache is empty', (done) => {
const dummyStatusText = 'exploded';
const dummyErrorMessage = 'server exploded';
ajaxSpy = (url) => {
expect(url).toBe(dummyEndpoint);
const deferred = $.Deferred();
deferred.reject(null, dummyStatusText, dummyErrorMessage);
return deferred.promise();
};
const errorMessage = 'Network Error';
mock.onGet(dummyEndpoint).networkError();
AjaxCache.retrieve(dummyEndpoint)
.then(data => fail(`Received unexpected data: ${JSON.stringify(data)}`))
.catch((error) => {
expect(error.message).toBe(`${dummyEndpoint}: ${dummyErrorMessage}`);
expect(error.textStatus).toBe(dummyStatusText);
expect(error.message).toBe(`${dummyEndpoint}: ${errorMessage}`);
expect(error.textStatus).toBe(errorMessage);
done();
})
.catch(fail);
......@@ -154,7 +143,9 @@ describe('AjaxCache', () => {
it('makes no Ajax call if matching data exists', (done) => {
AjaxCache.internalStorage[dummyEndpoint] = dummyResponse;
ajaxSpy = () => fail(new Error('expected no Ajax call!'));
mock.onGet(dummyEndpoint).reply(() => {
fail(new Error('expected no Ajax call!'));
});
AjaxCache.retrieve(dummyEndpoint)
.then((data) => {
......@@ -171,12 +162,7 @@ describe('AjaxCache', () => {
AjaxCache.internalStorage[dummyEndpoint] = oldDummyResponse;
ajaxSpy = (url) => {
expect(url).toBe(dummyEndpoint);
const deferred = $.Deferred();
deferred.resolve(dummyResponse);
return deferred.promise();
};
mock.onGet(dummyEndpoint).reply(200, dummyResponse);
// Call without forceRetrieve param
AjaxCache.retrieve(dummyEndpoint)
......@@ -189,7 +175,7 @@ describe('AjaxCache', () => {
// Call with forceRetrieve param
AjaxCache.retrieve(dummyEndpoint, true)
.then((data) => {
expect(data).toBe(dummyResponse);
expect(data).toEqual(dummyResponse);
})
.then(done)
.catch(fail);
......
/* eslint-disable promise/catch-or-return */
import * as commonUtils from '~/lib/utils/common_utils';
import axios from '~/lib/utils/axios_utils';
import * as commonUtils from '~/lib/utils/common_utils';
import MockAdapter from 'axios-mock-adapter';
describe('common_utils', () => {
......@@ -460,17 +459,6 @@ describe('common_utils', () => {
});
});
describe('ajaxPost', () => {
it('should perform `$.ajax` call and do `POST` request', () => {
const requestURL = '/some/random/api';
const data = { keyname: 'value' };
const ajaxSpy = spyOn($, 'ajax').and.callFake(() => {});
commonUtils.ajaxPost(requestURL, data);
expect(ajaxSpy.calls.allArgs()[0][0].type).toEqual('POST');
});
});
describe('spriteIcon', () => {
let beforeGon;
......
/* eslint-disable no-var, comma-dangle, object-shorthand */
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import * as urlUtils from '~/lib/utils/url_utility';
import MergeRequestTabs from '~/merge_request_tabs';
import '~/commit/pipelines/pipelines_bundle';
......@@ -46,7 +47,7 @@ import 'vendor/jquery.scrollTo';
describe('activateTab', function () {
beforeEach(function () {
spyOn($, 'ajax').and.callFake(function () {});
spyOn(axios, 'get').and.returnValue(Promise.resolve({ data: {} }));
loadFixtures('merge_requests/merge_request_with_task_list.html.raw');
this.subject = this.class.activateTab;
});
......@@ -148,7 +149,7 @@ import 'vendor/jquery.scrollTo';
describe('setCurrentAction', function () {
beforeEach(function () {
spyOn($, 'ajax').and.callFake(function () {});
spyOn(axios, 'get').and.returnValue(Promise.resolve({ data: {} }));
this.subject = this.class.setCurrentAction;
});
......@@ -214,13 +215,21 @@ import 'vendor/jquery.scrollTo';
});
describe('tabShown', () => {
let mock;
beforeEach(function () {
spyOn($, 'ajax').and.callFake(function (options) {
options.success({ html: '' });
mock = new MockAdapter(axios);
mock.onGet(/(.*)\/diffs\.json/).reply(200, {
data: { html: '' },
});
loadFixtures('merge_requests/merge_request_with_task_list.html.raw');
});
afterEach(() => {
mock.restore();
});
describe('with "Side-by-side"/parallel diff view', () => {
beforeEach(function () {
this.class.diffViewType = () => 'parallel';
......@@ -292,16 +301,20 @@ import 'vendor/jquery.scrollTo';
it('triggers Ajax request to JSON endpoint', function (done) {
const url = '/foo/bar/merge_requests/1/diffs';
spyOn(this.class, 'ajaxGet').and.callFake((options) => {
expect(options.url).toEqual(`${url}.json`);
spyOn(axios, 'get').and.callFake((reqUrl) => {
expect(reqUrl).toBe(`${url}.json`);
done();
return Promise.resolve({ data: {} });
});
this.class.loadDiff(url);
});
it('triggers scroll event when diff already loaded', function (done) {
spyOn(this.class, 'ajaxGet').and.callFake(() => done.fail());
spyOn(axios, 'get').and.callFake(done.fail);
spyOn(document, 'dispatchEvent');
this.class.diffsLoaded = true;
......@@ -316,6 +329,7 @@ import 'vendor/jquery.scrollTo';
describe('with inline diff', () => {
let noteId;
let noteLineNumId;
let mock;
beforeEach(() => {
const diffsResponse = getJSONFixture(inlineChangesTabJsonFixture);
......@@ -330,29 +344,40 @@ import 'vendor/jquery.scrollTo';
.attr('href')
.replace('#', '');
spyOn($, 'ajax').and.callFake(function (options) {
options.success(diffsResponse);
mock = new MockAdapter(axios);
mock.onGet(/(.*)\/diffs\.json/).reply(200, diffsResponse);
});
afterEach(() => {
mock.restore();
});
describe('with note fragment hash', () => {
it('should expand and scroll to linked fragment hash #note_xxx', function () {
it('should expand and scroll to linked fragment hash #note_xxx', function (done) {
spyOn(urlUtils, 'getLocationHash').and.returnValue(noteId);
this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
setTimeout(() => {
expect(noteId.length).toBeGreaterThan(0);
expect(Notes.instance.toggleDiffNote).toHaveBeenCalledWith({
target: jasmine.any(Object),
lineType: 'old',
forceShow: true,
});
done();
});
});
it('should gracefully ignore non-existant fragment hash', function () {
it('should gracefully ignore non-existant fragment hash', function (done) {
spyOn(urlUtils, 'getLocationHash').and.returnValue('note_something-that-does-not-exist');
this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
setTimeout(() => {
expect(Notes.instance.toggleDiffNote).not.toHaveBeenCalled();
done();
});
});
});
......@@ -370,6 +395,7 @@ import 'vendor/jquery.scrollTo';
describe('with parallel diff', () => {
let noteId;
let noteLineNumId;
let mock;
beforeEach(() => {
const diffsResponse = getJSONFixture(parallelChangesTabJsonFixture);
......@@ -384,30 +410,40 @@ import 'vendor/jquery.scrollTo';
.attr('href')
.replace('#', '');
spyOn($, 'ajax').and.callFake(function (options) {
options.success(diffsResponse);
mock = new MockAdapter(axios);
mock.onGet(/(.*)\/diffs\.json/).reply(200, diffsResponse);
});
afterEach(() => {
mock.restore();
});
describe('with note fragment hash', () => {
it('should expand and scroll to linked fragment hash #note_xxx', function () {
it('should expand and scroll to linked fragment hash #note_xxx', function (done) {
spyOn(urlUtils, 'getLocationHash').and.returnValue(noteId);
this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
setTimeout(() => {
expect(noteId.length).toBeGreaterThan(0);
expect(Notes.instance.toggleDiffNote).toHaveBeenCalledWith({
target: jasmine.any(Object),
lineType: 'new',
forceShow: true,
});
done();
});
});
it('should gracefully ignore non-existant fragment hash', function () {
it('should gracefully ignore non-existant fragment hash', function (done) {
spyOn(urlUtils, 'getLocationHash').and.returnValue('note_something-that-does-not-exist');
this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
setTimeout(() => {
expect(Notes.instance.toggleDiffNote).not.toHaveBeenCalled();
done();
});
});
});
......
/* eslint-disable space-before-function-paren, no-unused-expressions, no-var, object-shorthand, comma-dangle, max-len */
import _ from 'underscore';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import * as urlUtils from '~/lib/utils/url_utility';
import 'autosize';
import '~/gl_form';
import '~/lib/utils/text_utility';
import '~/render_gfm';
import Notes from '~/notes';
import timeoutPromise from './helpers/set_timeout_promise_helper';
(function() {
window.gon || (window.gon = {});
......@@ -119,6 +122,7 @@ import Notes from '~/notes';
let noteEntity;
let $form;
let $notesContainer;
let mock;
beforeEach(() => {
this.notes = new Notes('', []);
......@@ -136,17 +140,22 @@ import Notes from '~/notes';
$form = $('form.js-main-target-form');
$notesContainer = $('ul.main-notes-list');
$form.find('textarea.js-note-text').val(sampleComment);
mock = new MockAdapter(axios);
mock.onPost(/(.*)\/notes$/).reply(200, noteEntity);
});
it('updates note and resets edit form', () => {
const deferred = $.Deferred();
spyOn($, 'ajax').and.returnValue(deferred.promise());
afterEach(() => {
mock.restore();
});
it('updates note and resets edit form', (done) => {
spyOn(this.notes, 'revertNoteEditForm');
spyOn(this.notes, 'setupNewNote');
$('.js-comment-button').click();
deferred.resolve(noteEntity);
setTimeout(() => {
const $targetNote = $notesContainer.find(`#note_${noteEntity.id}`);
const updatedNote = Object.assign({}, noteEntity);
updatedNote.note = 'bar';
......@@ -154,6 +163,9 @@ import Notes from '~/notes';
expect(this.notes.revertNoteEditForm).toHaveBeenCalledWith($targetNote);
expect(this.notes.setupNewNote).toHaveBeenCalled();
done();
});
});
});
......@@ -479,8 +491,19 @@ import Notes from '~/notes';
};
let $form;
let $notesContainer;
let mock;
function mockNotesPost() {
mock.onPost(/(.*)\/notes$/).reply(200, note);
}
function mockNotesPostError() {
mock.onPost(/(.*)\/notes$/).networkError();
}
beforeEach(() => {
mock = new MockAdapter(axios);
this.notes = new Notes('', []);
window.gon.current_username = 'root';
window.gon.current_user_fullname = 'Administrator';
......@@ -489,63 +512,92 @@ import Notes from '~/notes';
$form.find('textarea.js-note-text').val(sampleComment);
});
afterEach(() => {
mock.restore();
});
it('should show placeholder note while new comment is being posted', () => {
mockNotesPost();
$('.js-comment-button').click();
expect($notesContainer.find('.note.being-posted').length > 0).toEqual(true);
});
it('should remove placeholder note when new comment is done posting', () => {
const deferred = $.Deferred();
spyOn($, 'ajax').and.returnValue(deferred.promise());
it('should remove placeholder note when new comment is done posting', (done) => {
mockNotesPost();
$('.js-comment-button').click();
deferred.resolve(note);
setTimeout(() => {
expect($notesContainer.find('.note.being-posted').length).toEqual(0);
done();
});
});
it('should show actual note element when new comment is done posting', () => {
const deferred = $.Deferred();
spyOn($, 'ajax').and.returnValue(deferred.promise());
it('should show actual note element when new comment is done posting', (done) => {
mockNotesPost();
$('.js-comment-button').click();
deferred.resolve(note);
setTimeout(() => {
expect($notesContainer.find(`#note_${note.id}`).length > 0).toEqual(true);
done();
});
});
it('should reset Form when new comment is done posting', () => {
const deferred = $.Deferred();
spyOn($, 'ajax').and.returnValue(deferred.promise());
it('should reset Form when new comment is done posting', (done) => {
mockNotesPost();
$('.js-comment-button').click();
deferred.resolve(note);
setTimeout(() => {
expect($form.find('textarea.js-note-text').val()).toEqual('');
done();
});
});
it('should show flash error message when new comment failed to be posted', () => {
const deferred = $.Deferred();
spyOn($, 'ajax').and.returnValue(deferred.promise());
it('should show flash error message when new comment failed to be posted', (done) => {
mockNotesPostError();
$('.js-comment-button').click();
deferred.reject();
setTimeout(() => {
expect($notesContainer.parent().find('.flash-container .flash-text').is(':visible')).toEqual(true);
done();
});
});
it('should show flash error message when comment failed to be updated', () => {
const deferred = $.Deferred();
spyOn($, 'ajax').and.returnValue(deferred.promise());
it('should show flash error message when comment failed to be updated', (done) => {
mockNotesPost();
$('.js-comment-button').click();
deferred.resolve(note);
timeoutPromise()
.then(() => {
const $noteEl = $notesContainer.find(`#note_${note.id}`);
$noteEl.find('.js-note-edit').click();
$noteEl.find('textarea.js-note-text').val(updatedComment);
$noteEl.find('.js-comment-save-button').click();
deferred.reject();
mock.restore();
mockNotesPostError();
$noteEl.find('.js-comment-save-button').click();
})
.then(timeoutPromise)
.then(() => {
const $updatedNoteEl = $notesContainer.find(`#note_${note.id}`);
expect($updatedNoteEl.hasClass('.being-posted')).toEqual(false); // Remove being-posted visuals
expect($updatedNoteEl.find('.note-text').text().trim()).toEqual(sampleComment); // See if comment reverted back to original
expect($('.flash-container').is(':visible')).toEqual(true); // Flash error message shown
done();
})
.catch(done.fail);
});
});
......@@ -563,8 +615,12 @@ import Notes from '~/notes';
};
let $form;
let $notesContainer;
let mock;
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onPost(/(.*)\/notes$/).reply(200, note);
this.notes = new Notes('', []);
window.gon.current_username = 'root';
window.gon.current_user_fullname = 'Administrator';
......@@ -582,15 +638,20 @@ import Notes from '~/notes';
$form.find('textarea.js-note-text').val(sampleComment);
});
it('should remove slash command placeholder when comment with slash commands is done posting', () => {
const deferred = $.Deferred();
spyOn($, 'ajax').and.returnValue(deferred.promise());
afterEach(() => {
mock.restore();
});
it('should remove slash command placeholder when comment with slash commands is done posting', (done) => {
spyOn(gl.awardsHandler, 'addAwardToEmojiBar').and.callThrough();
$('.js-comment-button').click();
expect($notesContainer.find('.system-note.being-posted').length).toEqual(1); // Placeholder shown
deferred.resolve(note);
setTimeout(() => {
expect($notesContainer.find('.system-note.being-posted').length).toEqual(0); // Placeholder removed
done();
});
});
});
......@@ -607,8 +668,12 @@ import Notes from '~/notes';
};
let $form;
let $notesContainer;
let mock;
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onPost(/(.*)\/notes$/).reply(200, note);
this.notes = new Notes('', []);
window.gon.current_username = 'root';
window.gon.current_user_fullname = 'Administrator';
......@@ -617,12 +682,14 @@ import Notes from '~/notes';
$form.find('textarea.js-note-text').html(sampleComment);
});
it('should not render a script tag', () => {
const deferred = $.Deferred();
spyOn($, 'ajax').and.returnValue(deferred.promise());
afterEach(() => {
mock.restore();
});
it('should not render a script tag', (done) => {
$('.js-comment-button').click();
deferred.resolve(note);
setTimeout(() => {
const $noteEl = $notesContainer.find(`#note_${note.id}`);
$noteEl.find('.js-note-edit').click();
$noteEl.find('textarea.js-note-text').html(updatedComment);
......@@ -630,6 +697,9 @@ import Notes from '~/notes';
const $updatedNoteEl = $notesContainer.find(`#note_${note.id}`).find('.js-task-list-container');
expect($updatedNoteEl.find('.note-text').text().trim()).toEqual('');
done();
});
});
});
......
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