2019-12-21 20:55:43 +05:30
|
|
|
import axios from 'axios';
|
|
|
|
import MockAdapter from 'axios-mock-adapter';
|
|
|
|
import testAction from 'helpers/vuex_action_helper';
|
2020-04-08 14:13:33 +05:30
|
|
|
import { cloneDeep, merge } from 'lodash';
|
2020-03-13 15:44:24 +05:30
|
|
|
import * as actions from '~/releases/stores/modules/detail/actions';
|
|
|
|
import * as types from '~/releases/stores/modules/detail/mutation_types';
|
2020-04-08 14:13:33 +05:30
|
|
|
import { release as originalRelease } from '../../../mock_data';
|
|
|
|
import createState from '~/releases/stores/modules/detail/state';
|
2019-12-21 20:55:43 +05:30
|
|
|
import createFlash from '~/flash';
|
|
|
|
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
2020-04-08 14:13:33 +05:30
|
|
|
import { redirectTo } from '~/lib/utils/url_utility';
|
2020-04-22 19:07:51 +05:30
|
|
|
import api from '~/api';
|
2020-06-23 00:09:42 +05:30
|
|
|
import { ASSET_LINK_TYPE } from '~/releases/constants';
|
2019-12-21 20:55:43 +05:30
|
|
|
|
|
|
|
jest.mock('~/flash', () => jest.fn());
|
|
|
|
|
|
|
|
jest.mock('~/lib/utils/url_utility', () => ({
|
|
|
|
redirectTo: jest.fn(),
|
|
|
|
joinPaths: jest.requireActual('~/lib/utils/url_utility').joinPaths,
|
|
|
|
}));
|
|
|
|
|
|
|
|
describe('Release detail actions', () => {
|
2020-04-08 14:13:33 +05:30
|
|
|
let state;
|
|
|
|
let release;
|
2019-12-21 20:55:43 +05:30
|
|
|
let mock;
|
|
|
|
let error;
|
|
|
|
|
|
|
|
beforeEach(() => {
|
2020-04-22 19:07:51 +05:30
|
|
|
state = createState({
|
|
|
|
projectId: '18',
|
|
|
|
tagName: 'v1.3',
|
|
|
|
releasesPagePath: 'path/to/releases/page',
|
|
|
|
markdownDocsPath: 'path/to/markdown/docs',
|
|
|
|
markdownPreviewPath: 'path/to/markdown/preview',
|
|
|
|
updateReleaseApiDocsPath: 'path/to/api/docs',
|
|
|
|
});
|
2020-04-08 14:13:33 +05:30
|
|
|
release = cloneDeep(originalRelease);
|
2019-12-21 20:55:43 +05:30
|
|
|
mock = new MockAdapter(axios);
|
|
|
|
gon.api_version = 'v4';
|
|
|
|
error = { message: 'An error occurred' };
|
|
|
|
createFlash.mockClear();
|
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
mock.restore();
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('requestRelease', () => {
|
|
|
|
it(`commits ${types.REQUEST_RELEASE}`, () =>
|
2020-04-08 14:13:33 +05:30
|
|
|
testAction(actions.requestRelease, undefined, state, [{ type: types.REQUEST_RELEASE }]));
|
2019-12-21 20:55:43 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
describe('receiveReleaseSuccess', () => {
|
|
|
|
it(`commits ${types.RECEIVE_RELEASE_SUCCESS}`, () =>
|
2020-04-08 14:13:33 +05:30
|
|
|
testAction(actions.receiveReleaseSuccess, release, state, [
|
|
|
|
{ type: types.RECEIVE_RELEASE_SUCCESS, payload: release },
|
2019-12-21 20:55:43 +05:30
|
|
|
]));
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('receiveReleaseError', () => {
|
|
|
|
it(`commits ${types.RECEIVE_RELEASE_ERROR}`, () =>
|
2020-04-08 14:13:33 +05:30
|
|
|
testAction(actions.receiveReleaseError, error, state, [
|
2019-12-21 20:55:43 +05:30
|
|
|
{ type: types.RECEIVE_RELEASE_ERROR, payload: error },
|
|
|
|
]));
|
|
|
|
|
|
|
|
it('shows a flash with an error message', () => {
|
|
|
|
actions.receiveReleaseError({ commit: jest.fn() }, error);
|
|
|
|
|
|
|
|
expect(createFlash).toHaveBeenCalledTimes(1);
|
|
|
|
expect(createFlash).toHaveBeenCalledWith(
|
|
|
|
'Something went wrong while getting the release details',
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('fetchRelease', () => {
|
|
|
|
let getReleaseUrl;
|
|
|
|
|
|
|
|
beforeEach(() => {
|
2020-04-08 14:13:33 +05:30
|
|
|
state.projectId = '18';
|
|
|
|
state.tagName = 'v1.3';
|
|
|
|
getReleaseUrl = `/api/v4/projects/${state.projectId}/releases/${state.tagName}`;
|
2019-12-21 20:55:43 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
it(`dispatches requestRelease and receiveReleaseSuccess with the camel-case'd release object`, () => {
|
2020-04-08 14:13:33 +05:30
|
|
|
mock.onGet(getReleaseUrl).replyOnce(200, release);
|
2019-12-21 20:55:43 +05:30
|
|
|
|
|
|
|
return testAction(
|
|
|
|
actions.fetchRelease,
|
|
|
|
undefined,
|
2020-04-08 14:13:33 +05:30
|
|
|
state,
|
2019-12-21 20:55:43 +05:30
|
|
|
[],
|
|
|
|
[
|
|
|
|
{ type: 'requestRelease' },
|
|
|
|
{
|
|
|
|
type: 'receiveReleaseSuccess',
|
2020-04-08 14:13:33 +05:30
|
|
|
payload: convertObjectPropsToCamelCase(release, { deep: true }),
|
2019-12-21 20:55:43 +05:30
|
|
|
},
|
|
|
|
],
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it(`dispatches requestRelease and receiveReleaseError with an error object`, () => {
|
|
|
|
mock.onGet(getReleaseUrl).replyOnce(500);
|
|
|
|
|
|
|
|
return testAction(
|
|
|
|
actions.fetchRelease,
|
|
|
|
undefined,
|
2020-04-08 14:13:33 +05:30
|
|
|
state,
|
2019-12-21 20:55:43 +05:30
|
|
|
[],
|
|
|
|
[{ type: 'requestRelease' }, { type: 'receiveReleaseError', payload: expect.anything() }],
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('updateReleaseTitle', () => {
|
|
|
|
it(`commits ${types.UPDATE_RELEASE_TITLE} with the updated release title`, () => {
|
|
|
|
const newTitle = 'The new release title';
|
2020-04-08 14:13:33 +05:30
|
|
|
return testAction(actions.updateReleaseTitle, newTitle, state, [
|
2019-12-21 20:55:43 +05:30
|
|
|
{ type: types.UPDATE_RELEASE_TITLE, payload: newTitle },
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('updateReleaseNotes', () => {
|
|
|
|
it(`commits ${types.UPDATE_RELEASE_NOTES} with the updated release notes`, () => {
|
|
|
|
const newReleaseNotes = 'The new release notes';
|
2020-04-08 14:13:33 +05:30
|
|
|
return testAction(actions.updateReleaseNotes, newReleaseNotes, state, [
|
2019-12-21 20:55:43 +05:30
|
|
|
{ type: types.UPDATE_RELEASE_NOTES, payload: newReleaseNotes },
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-06-23 00:09:42 +05:30
|
|
|
describe('updateAssetLinkUrl', () => {
|
|
|
|
it(`commits ${types.UPDATE_ASSET_LINK_URL} with the updated link URL`, () => {
|
|
|
|
const params = {
|
|
|
|
linkIdToUpdate: 2,
|
|
|
|
newUrl: 'https://example.com/updated',
|
|
|
|
};
|
|
|
|
|
|
|
|
return testAction(actions.updateAssetLinkUrl, params, state, [
|
|
|
|
{ type: types.UPDATE_ASSET_LINK_URL, payload: params },
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('updateAssetLinkName', () => {
|
|
|
|
it(`commits ${types.UPDATE_ASSET_LINK_NAME} with the updated link name`, () => {
|
|
|
|
const params = {
|
|
|
|
linkIdToUpdate: 2,
|
|
|
|
newName: 'Updated link name',
|
|
|
|
};
|
|
|
|
|
|
|
|
return testAction(actions.updateAssetLinkName, params, state, [
|
|
|
|
{ type: types.UPDATE_ASSET_LINK_NAME, payload: params },
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('updateAssetLinkType', () => {
|
|
|
|
it(`commits ${types.UPDATE_ASSET_LINK_TYPE} with the updated link type`, () => {
|
|
|
|
const params = {
|
|
|
|
linkIdToUpdate: 2,
|
|
|
|
newType: ASSET_LINK_TYPE.RUNBOOK,
|
|
|
|
};
|
|
|
|
|
|
|
|
return testAction(actions.updateAssetLinkType, params, state, [
|
|
|
|
{ type: types.UPDATE_ASSET_LINK_TYPE, payload: params },
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('removeAssetLink', () => {
|
|
|
|
it(`commits ${types.REMOVE_ASSET_LINK} with the ID of the asset link to remove`, () => {
|
|
|
|
const idToRemove = 2;
|
|
|
|
return testAction(actions.removeAssetLink, idToRemove, state, [
|
|
|
|
{ type: types.REMOVE_ASSET_LINK, payload: idToRemove },
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-05-24 23:13:21 +05:30
|
|
|
describe('updateReleaseMilestones', () => {
|
|
|
|
it(`commits ${types.UPDATE_RELEASE_MILESTONES} with the updated release milestones`, () => {
|
|
|
|
const newReleaseMilestones = ['v0.0', 'v0.1'];
|
|
|
|
return testAction(actions.updateReleaseMilestones, newReleaseMilestones, state, [
|
|
|
|
{ type: types.UPDATE_RELEASE_MILESTONES, payload: newReleaseMilestones },
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
describe('requestUpdateRelease', () => {
|
|
|
|
it(`commits ${types.REQUEST_UPDATE_RELEASE}`, () =>
|
2020-04-08 14:13:33 +05:30
|
|
|
testAction(actions.requestUpdateRelease, undefined, state, [
|
2019-12-21 20:55:43 +05:30
|
|
|
{ type: types.REQUEST_UPDATE_RELEASE },
|
|
|
|
]));
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('receiveUpdateReleaseSuccess', () => {
|
|
|
|
it(`commits ${types.RECEIVE_UPDATE_RELEASE_SUCCESS}`, () =>
|
2020-04-08 14:13:33 +05:30
|
|
|
testAction(actions.receiveUpdateReleaseSuccess, undefined, { ...state, featureFlags: {} }, [
|
|
|
|
{ type: types.RECEIVE_UPDATE_RELEASE_SUCCESS },
|
|
|
|
]));
|
|
|
|
|
2020-05-24 23:13:21 +05:30
|
|
|
it('redirects to the releases page if releaseShowPage feature flag is enabled', () => {
|
2020-04-08 14:13:33 +05:30
|
|
|
const rootState = { featureFlags: { releaseShowPage: true } };
|
|
|
|
const updatedState = merge({}, state, {
|
|
|
|
releasesPagePath: 'path/to/releases/page',
|
|
|
|
release: {
|
|
|
|
_links: {
|
|
|
|
self: 'path/to/self',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
actions.receiveUpdateReleaseSuccess({ commit: jest.fn(), state: updatedState, rootState });
|
|
|
|
|
|
|
|
expect(redirectTo).toHaveBeenCalledTimes(1);
|
|
|
|
expect(redirectTo).toHaveBeenCalledWith(updatedState.release._links.self);
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when the releaseShowPage feature flag is disabled', () => {});
|
2019-12-21 20:55:43 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
describe('receiveUpdateReleaseError', () => {
|
|
|
|
it(`commits ${types.RECEIVE_UPDATE_RELEASE_ERROR}`, () =>
|
2020-04-08 14:13:33 +05:30
|
|
|
testAction(actions.receiveUpdateReleaseError, error, state, [
|
2019-12-21 20:55:43 +05:30
|
|
|
{ type: types.RECEIVE_UPDATE_RELEASE_ERROR, payload: error },
|
|
|
|
]));
|
|
|
|
|
|
|
|
it('shows a flash with an error message', () => {
|
|
|
|
actions.receiveUpdateReleaseError({ commit: jest.fn() }, error);
|
|
|
|
|
|
|
|
expect(createFlash).toHaveBeenCalledTimes(1);
|
|
|
|
expect(createFlash).toHaveBeenCalledWith(
|
|
|
|
'Something went wrong while saving the release details',
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('updateRelease', () => {
|
2020-04-22 19:07:51 +05:30
|
|
|
let getters;
|
|
|
|
let dispatch;
|
|
|
|
let callOrder;
|
2019-12-21 20:55:43 +05:30
|
|
|
|
|
|
|
beforeEach(() => {
|
2020-04-22 19:07:51 +05:30
|
|
|
state.release = convertObjectPropsToCamelCase(release);
|
2020-04-08 14:13:33 +05:30
|
|
|
state.projectId = '18';
|
2020-04-22 19:07:51 +05:30
|
|
|
state.tagName = state.release.tagName;
|
2019-12-21 20:55:43 +05:30
|
|
|
|
2020-04-22 19:07:51 +05:30
|
|
|
getters = {
|
|
|
|
releaseLinksToDelete: [{ id: '1' }, { id: '2' }],
|
|
|
|
releaseLinksToCreate: [{ id: 'new-link-1' }, { id: 'new-link-2' }],
|
|
|
|
};
|
2019-12-21 20:55:43 +05:30
|
|
|
|
2020-04-22 19:07:51 +05:30
|
|
|
dispatch = jest.fn();
|
|
|
|
|
|
|
|
callOrder = [];
|
|
|
|
jest.spyOn(api, 'updateRelease').mockImplementation(() => {
|
|
|
|
callOrder.push('updateRelease');
|
|
|
|
return Promise.resolve();
|
|
|
|
});
|
|
|
|
jest.spyOn(api, 'deleteReleaseLink').mockImplementation(() => {
|
|
|
|
callOrder.push('deleteReleaseLink');
|
|
|
|
return Promise.resolve();
|
|
|
|
});
|
|
|
|
jest.spyOn(api, 'createReleaseLink').mockImplementation(() => {
|
|
|
|
callOrder.push('createReleaseLink');
|
|
|
|
return Promise.resolve();
|
|
|
|
});
|
2019-12-21 20:55:43 +05:30
|
|
|
});
|
|
|
|
|
2020-04-22 19:07:51 +05:30
|
|
|
it('dispatches requestUpdateRelease and receiveUpdateReleaseSuccess', () => {
|
|
|
|
return actions.updateRelease({ dispatch, state, getters }).then(() => {
|
|
|
|
expect(dispatch.mock.calls).toEqual([
|
|
|
|
['requestUpdateRelease'],
|
|
|
|
['receiveUpdateReleaseSuccess'],
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
});
|
2019-12-21 20:55:43 +05:30
|
|
|
|
2020-04-22 19:07:51 +05:30
|
|
|
it('dispatches requestUpdateRelease and receiveUpdateReleaseError with an error object', () => {
|
|
|
|
jest.spyOn(api, 'updateRelease').mockRejectedValue(error);
|
|
|
|
|
|
|
|
return actions.updateRelease({ dispatch, state, getters }).then(() => {
|
|
|
|
expect(dispatch.mock.calls).toEqual([
|
|
|
|
['requestUpdateRelease'],
|
|
|
|
['receiveUpdateReleaseError', error],
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('updates the Release, then deletes all existing links, and then recreates new links', () => {
|
|
|
|
return actions.updateRelease({ dispatch, state, getters }).then(() => {
|
|
|
|
expect(callOrder).toEqual([
|
|
|
|
'updateRelease',
|
|
|
|
'deleteReleaseLink',
|
|
|
|
'deleteReleaseLink',
|
|
|
|
'createReleaseLink',
|
|
|
|
'createReleaseLink',
|
|
|
|
]);
|
|
|
|
|
|
|
|
expect(api.updateRelease.mock.calls).toEqual([
|
|
|
|
[
|
|
|
|
state.projectId,
|
|
|
|
state.tagName,
|
|
|
|
{
|
|
|
|
name: state.release.name,
|
|
|
|
description: state.release.description,
|
2020-05-24 23:13:21 +05:30
|
|
|
milestones: state.release.milestones.map(milestone => milestone.title),
|
2020-04-22 19:07:51 +05:30
|
|
|
},
|
|
|
|
],
|
|
|
|
]);
|
|
|
|
|
|
|
|
expect(api.deleteReleaseLink).toHaveBeenCalledTimes(getters.releaseLinksToDelete.length);
|
|
|
|
getters.releaseLinksToDelete.forEach(link => {
|
|
|
|
expect(api.deleteReleaseLink).toHaveBeenCalledWith(
|
|
|
|
state.projectId,
|
|
|
|
state.tagName,
|
|
|
|
link.id,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(api.createReleaseLink).toHaveBeenCalledTimes(getters.releaseLinksToCreate.length);
|
|
|
|
getters.releaseLinksToCreate.forEach(link => {
|
|
|
|
expect(api.createReleaseLink).toHaveBeenCalledWith(state.projectId, state.tagName, link);
|
|
|
|
});
|
|
|
|
});
|
2019-12-21 20:55:43 +05:30
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|