Skip to content
Snippets Groups Projects
Commit 7c38405b authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 1fa79760
No related branches found
No related tags found
No related merge requests found
Showing
with 502 additions and 192 deletions
// eslint-disable-next-line import/prefer-default-export
export const resetStore = store => {
store.replaceState({
notes: [],
targetNoteHash: null,
lastFetchedAt: null,
notesData: {},
userData: {},
noteableData: {},
});
};
import $ from 'jquery';
import { TEST_HOST } from 'spec/test_constants';
import AxiosMockAdapter from 'axios-mock-adapter';
import Api from '~/api';
import actionsModule, * as actions from '~/notes/stores/actions';
import Flash from '~/flash';
import * as actions from '~/notes/stores/actions';
import * as mutationTypes from '~/notes/stores/mutation_types';
import * as notesConstants from '~/notes/constants';
import createStore from '~/notes/stores';
Loading
Loading
@@ -19,21 +19,20 @@ import {
import axios from '~/lib/utils/axios_utils';
 
const TEST_ERROR_MESSAGE = 'Test error message';
jest.mock('~/flash');
 
describe('Actions Notes Store', () => {
let commit;
let dispatch;
let state;
let store;
let flashSpy;
let axiosMock;
 
beforeEach(() => {
store = createStore();
commit = jasmine.createSpy('commit');
dispatch = jasmine.createSpy('dispatch');
commit = jest.fn();
dispatch = jest.fn();
state = {};
flashSpy = spyOnDependency(actionsModule, 'Flash');
axiosMock = new AxiosMockAdapter(axios);
});
 
Loading
Loading
@@ -244,10 +243,10 @@ describe('Actions Notes Store', () => {
});
 
describe('poll', () => {
beforeEach(done => {
jasmine.clock().install();
jest.useFakeTimers();
 
spyOn(axios, 'get').and.callThrough();
beforeEach(done => {
jest.spyOn(axios, 'get');
 
store
.dispatch('setNotesData', notesDataMock)
Loading
Loading
@@ -255,10 +254,6 @@ describe('Actions Notes Store', () => {
.catch(done.fail);
});
 
afterEach(() => {
jasmine.clock().uninstall();
});
it('calls service with last fetched state', done => {
axiosMock
.onAny()
Loading
Loading
@@ -271,7 +266,7 @@ describe('Actions Notes Store', () => {
expect(axios.get).toHaveBeenCalled();
expect(store.state.lastFetchedAt).toBe('123456');
 
jasmine.clock().tick(1500);
jest.advanceTimersByTime(1500);
})
.then(
() =>
Loading
Loading
@@ -280,8 +275,8 @@ describe('Actions Notes Store', () => {
}),
)
.then(() => {
expect(axios.get.calls.count()).toBe(2);
expect(axios.get.calls.mostRecent().args[1].headers).toEqual({
expect(axios.get.mock.calls.length).toBe(2);
expect(axios.get.mock.calls[axios.get.mock.calls.length - 1][1].headers).toEqual({
'X-Last-Fetched-At': '123456',
});
})
Loading
Loading
@@ -310,13 +305,13 @@ describe('Actions Notes Store', () => {
beforeEach(() => {
axiosMock.onDelete(endpoint).replyOnce(200, {});
 
$('body').attr('data-page', '');
document.body.setAttribute('data-page', '');
});
 
afterEach(() => {
axiosMock.restore();
 
$('body').attr('data-page', '');
document.body.setAttribute('data-page', '');
});
 
it('commits DELETE_NOTE and dispatches updateMergeRequestWidget', done => {
Loading
Loading
@@ -347,7 +342,7 @@ describe('Actions Notes Store', () => {
it('dispatches removeDiscussionsFromDiff on merge request page', done => {
const note = { path: endpoint, id: 1 };
 
$('body').attr('data-page', 'projects:merge_requests:show');
document.body.setAttribute('data-page', 'projects:merge_requests:show');
 
testAction(
actions.removeNote,
Loading
Loading
@@ -381,13 +376,13 @@ describe('Actions Notes Store', () => {
beforeEach(() => {
axiosMock.onDelete(endpoint).replyOnce(200, {});
 
$('body').attr('data-page', '');
document.body.setAttribute('data-page', '');
});
 
afterEach(() => {
axiosMock.restore();
 
$('body').attr('data-page', '');
document.body.setAttribute('data-page', '');
});
 
it('dispatches removeNote', done => {
Loading
Loading
@@ -534,7 +529,7 @@ describe('Actions Notes Store', () => {
 
describe('updateMergeRequestWidget', () => {
it('calls mrWidget checkStatus', () => {
spyOn(mrWidgetEventHub, '$emit');
jest.spyOn(mrWidgetEventHub, '$emit').mockImplementation(() => {});
 
actions.updateMergeRequestWidget();
 
Loading
Loading
@@ -589,7 +584,7 @@ describe('Actions Notes Store', () => {
 
actions.updateOrCreateNotes({ commit, state, getters, dispatch }, [note]);
 
expect(commit.calls.allArgs()).toEqual([[mutationTypes.UPDATE_NOTE, note]]);
expect(commit.mock.calls).toEqual([[mutationTypes.UPDATE_NOTE, note]]);
});
 
it('Creates a new note if none exisits', () => {
Loading
Loading
@@ -597,7 +592,7 @@ describe('Actions Notes Store', () => {
const getters = { notesById: {} };
actions.updateOrCreateNotes({ commit, state, getters, dispatch }, [note]);
 
expect(commit.calls.allArgs()).toEqual([[mutationTypes.ADD_NEW_NOTE, note]]);
expect(commit.mock.calls).toEqual([[mutationTypes.ADD_NEW_NOTE, note]]);
});
 
describe('Discussion notes', () => {
Loading
Loading
@@ -619,7 +614,7 @@ describe('Actions Notes Store', () => {
 
actions.updateOrCreateNotes({ commit, state, getters, dispatch }, [discussionNote]);
 
expect(commit.calls.allArgs()).toEqual([
expect(commit.mock.calls).toEqual([
[mutationTypes.ADD_NEW_REPLY_TO_DISCUSSION, discussionNote],
]);
});
Loading
Loading
@@ -630,7 +625,7 @@ describe('Actions Notes Store', () => {
 
actions.updateOrCreateNotes({ commit, state, getters, dispatch }, [diffNote]);
 
expect(dispatch.calls.allArgs()).toEqual([
expect(dispatch.mock.calls).toEqual([
['fetchDiscussions', { path: state.notesData.discussionsPath }],
]);
});
Loading
Loading
@@ -645,7 +640,7 @@ describe('Actions Notes Store', () => {
 
actions.updateOrCreateNotes({ commit, state, getters, dispatch }, [discussionNote]);
 
expect(commit.calls.allArgs()).toEqual([[mutationTypes.ADD_NEW_NOTE, discussionNote]]);
expect(commit.mock.calls).toEqual([[mutationTypes.ADD_NEW_NOTE, discussionNote]]);
});
});
});
Loading
Loading
@@ -770,7 +765,7 @@ describe('Actions Notes Store', () => {
.then(() => done.fail('Expected error to be thrown!'))
.catch(err => {
expect(err).toBe(error);
expect(flashSpy).not.toHaveBeenCalled();
expect(Flash).not.toHaveBeenCalled();
})
.then(done)
.catch(done.fail);
Loading
Loading
@@ -792,7 +787,7 @@ describe('Actions Notes Store', () => {
)
.then(resp => {
expect(resp.hasFlash).toBe(true);
expect(flashSpy).toHaveBeenCalledWith(
expect(Flash).toHaveBeenCalledWith(
'Your comment could not be submitted because something went wrong',
'alert',
flashContainer,
Loading
Loading
@@ -818,7 +813,7 @@ describe('Actions Notes Store', () => {
)
.then(data => {
expect(data).toBe(res);
expect(flashSpy).not.toHaveBeenCalled();
expect(Flash).not.toHaveBeenCalled();
})
.then(done)
.catch(done.fail);
Loading
Loading
@@ -833,9 +828,8 @@ describe('Actions Notes Store', () => {
let flashContainer;
 
beforeEach(() => {
spyOn(Api, 'applySuggestion');
dispatch.and.returnValue(Promise.resolve());
Api.applySuggestion.and.returnValue(Promise.resolve());
jest.spyOn(Api, 'applySuggestion').mockReturnValue(Promise.resolve());
dispatch.mockReturnValue(Promise.resolve());
flashContainer = {};
});
 
Loading
Loading
@@ -852,32 +846,32 @@ describe('Actions Notes Store', () => {
 
it('when service success, commits and resolves discussion', done => {
testSubmitSuggestion(done, () => {
expect(commit.calls.allArgs()).toEqual([
expect(commit.mock.calls).toEqual([
[mutationTypes.APPLY_SUGGESTION, { discussionId, noteId, suggestionId }],
]);
 
expect(dispatch.calls.allArgs()).toEqual([['resolveDiscussion', { discussionId }]]);
expect(flashSpy).not.toHaveBeenCalled();
expect(dispatch.mock.calls).toEqual([['resolveDiscussion', { discussionId }]]);
expect(Flash).not.toHaveBeenCalled();
});
});
 
it('when service fails, flashes error message', done => {
const response = { response: { data: { message: TEST_ERROR_MESSAGE } } };
 
Api.applySuggestion.and.returnValue(Promise.reject(response));
Api.applySuggestion.mockReturnValue(Promise.reject(response));
 
testSubmitSuggestion(done, () => {
expect(commit).not.toHaveBeenCalled();
expect(dispatch).not.toHaveBeenCalled();
expect(flashSpy).toHaveBeenCalledWith(`${TEST_ERROR_MESSAGE}.`, 'alert', flashContainer);
expect(Flash).toHaveBeenCalledWith(`${TEST_ERROR_MESSAGE}.`, 'alert', flashContainer);
});
});
 
it('when resolve discussion fails, fail gracefully', done => {
dispatch.and.returnValue(Promise.reject());
dispatch.mockReturnValue(Promise.reject());
 
testSubmitSuggestion(done, () => {
expect(flashSpy).not.toHaveBeenCalled();
expect(Flash).not.toHaveBeenCalled();
});
});
});
Loading
Loading
@@ -887,13 +881,13 @@ describe('Actions Notes Store', () => {
const filter = 0;
 
beforeEach(() => {
dispatch.and.returnValue(new Promise(() => {}));
dispatch.mockReturnValue(new Promise(() => {}));
});
 
it('fetches discussions with filter and persistFilter false', () => {
actions.filterDiscussion({ dispatch }, { path, filter, persistFilter: false });
 
expect(dispatch.calls.allArgs()).toEqual([
expect(dispatch.mock.calls).toEqual([
['setLoadingState', true],
['fetchDiscussions', { path, filter, persistFilter: false }],
]);
Loading
Loading
@@ -902,7 +896,7 @@ describe('Actions Notes Store', () => {
it('fetches discussions with filter and persistFilter true', () => {
actions.filterDiscussion({ dispatch }, { path, filter, persistFilter: true });
 
expect(dispatch.calls.allArgs()).toEqual([
expect(dispatch.mock.calls).toEqual([
['setLoadingState', true],
['fetchDiscussions', { path, filter, persistFilter: true }],
]);
Loading
Loading
import { mount } from '@vue/test-utils';
import { GlIcon, GlLoadingIcon, GlButton } from '@gitlab/ui';
import DeploymentActionButton from '~/vue_merge_request_widget/components/deployment/deployment_action_button.vue';
import {
CREATED,
RUNNING,
DEPLOYING,
REDEPLOYING,
} from '~/vue_merge_request_widget/components/deployment/constants';
import { actionButtonMocks } from './deployment_mock_data';
const baseProps = {
actionsConfiguration: actionButtonMocks[DEPLOYING],
actionInProgress: null,
computedDeploymentStatus: CREATED,
};
describe('Deployment action button', () => {
let wrapper;
const factory = (options = {}) => {
wrapper = mount(DeploymentActionButton, {
...options,
});
};
afterEach(() => {
wrapper.destroy();
});
describe('when passed only icon', () => {
beforeEach(() => {
factory({
propsData: baseProps,
slots: { default: ['<gl-icon name="stop" />'] },
stubs: {
'gl-icon': GlIcon,
},
});
});
it('renders slot correctly', () => {
expect(wrapper.find(GlIcon).exists()).toBe(true);
});
});
describe('when passed multiple items', () => {
beforeEach(() => {
factory({
propsData: baseProps,
slots: {
default: ['<gl-icon name="play" />', `<span>${actionButtonMocks[DEPLOYING]}</span>`],
},
stubs: {
'gl-icon': GlIcon,
},
});
});
it('renders slot correctly', () => {
expect(wrapper.find(GlIcon).exists()).toBe(true);
expect(wrapper.text()).toContain(actionButtonMocks[DEPLOYING]);
});
});
describe('when its action is in progress', () => {
beforeEach(() => {
factory({
propsData: {
...baseProps,
actionInProgress: actionButtonMocks[DEPLOYING].actionName,
},
});
});
it('is disabled and shows the loading icon', () => {
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
expect(wrapper.find(GlButton).props('disabled')).toBe(true);
});
});
describe('when another action is in progress', () => {
beforeEach(() => {
factory({
propsData: {
...baseProps,
actionInProgress: actionButtonMocks[REDEPLOYING].actionName,
},
});
});
it('is disabled and does not show the loading icon', () => {
expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.find(GlButton).props('disabled')).toBe(true);
});
});
describe('when action status is running', () => {
beforeEach(() => {
factory({
propsData: {
...baseProps,
actionInProgress: actionButtonMocks[REDEPLOYING].actionName,
computedDeploymentStatus: RUNNING,
},
});
});
it('is disabled and does not show the loading icon', () => {
expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.find(GlButton).props('disabled')).toBe(true);
});
});
describe('when no action is in progress', () => {
beforeEach(() => {
factory({
propsData: baseProps,
});
});
it('is not disabled nor does it show the loading icon', () => {
expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.find(GlButton).props('disabled')).toBe(false);
});
});
});
import { mount } from '@vue/test-utils';
import createFlash from '~/flash';
import { visitUrl } from '~/lib/utils/url_utility';
import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
import DeploymentActions from '~/vue_merge_request_widget/components/deployment/deployment_actions.vue';
import {
CREATED,
MANUAL_DEPLOY,
FAILED,
DEPLOYING,
REDEPLOYING,
STOPPING,
} from '~/vue_merge_request_widget/components/deployment/constants';
import {
actionButtonMocks,
deploymentMockData,
playDetails,
retryDetails,
} from './deployment_mock_data';
jest.mock('~/flash');
jest.mock('~/lib/utils/url_utility');
describe('DeploymentAction component', () => {
let wrapper;
let executeActionSpy;
const factory = (options = {}) => {
// This destroys any wrappers created before a nested call to factory reassigns it
if (wrapper && wrapper.destroy) {
wrapper.destroy();
}
wrapper = mount(DeploymentActions, {
...options,
provide: { glFeatures: { deployFromFooter: true } },
});
};
const findStopButton = () => wrapper.find('.js-stop-env');
const findDeployButton = () => wrapper.find('.js-manual-deploy-action');
const findRedeployButton = () => wrapper.find('.js-manual-redeploy-action');
beforeEach(() => {
executeActionSpy = jest.spyOn(MRWidgetService, 'executeInlineAction');
factory({
propsData: {
computedDeploymentStatus: CREATED,
deployment: deploymentMockData,
showVisualReviewApp: false,
},
});
});
afterEach(() => {
wrapper.destroy();
});
describe('actions do not appear when conditions are unmet', () => {
describe('when there is no stop_url', () => {
beforeEach(() => {
factory({
propsData: {
computedDeploymentStatus: CREATED,
deployment: {
...deploymentMockData,
stop_url: null,
},
showVisualReviewApp: false,
},
});
});
it('the stop button does not appear', () => {
expect(findStopButton().exists()).toBe(false);
});
});
describe('when there is no play_path in details', () => {
it('the manual deploy button does not appear', () => {
expect(findDeployButton().exists()).toBe(false);
});
});
describe('when there is no retry_path in details', () => {
it('the manual redeploy button does not appear', () => {
expect(findRedeployButton().exists()).toBe(false);
});
});
});
describe('when conditions are met', () => {
describe.each`
configConst | computedDeploymentStatus | displayConditionChanges | finderFn | endpoint
${STOPPING} | ${CREATED} | ${{}} | ${findStopButton} | ${deploymentMockData.stop_url}
${DEPLOYING} | ${MANUAL_DEPLOY} | ${playDetails} | ${findDeployButton} | ${playDetails.playable_build.play_path}
${REDEPLOYING} | ${FAILED} | ${retryDetails} | ${findRedeployButton} | ${retryDetails.playable_build.retry_path}
`(
'$configConst action',
({ configConst, computedDeploymentStatus, displayConditionChanges, finderFn, endpoint }) => {
describe(`${configConst} action`, () => {
const confirmAction = () => {
jest.spyOn(window, 'confirm').mockReturnValueOnce(true);
finderFn().trigger('click');
};
const rejectAction = () => {
jest.spyOn(window, 'confirm').mockReturnValueOnce(false);
finderFn().trigger('click');
};
beforeEach(() => {
factory({
propsData: {
computedDeploymentStatus,
deployment: {
...deploymentMockData,
details: displayConditionChanges,
},
showVisualReviewApp: false,
},
});
});
it('the button is rendered', () => {
expect(finderFn().exists()).toBe(true);
});
describe('when clicked', () => {
describe('should show a confirm dialog but not call executeInlineAction when declined', () => {
beforeEach(() => {
executeActionSpy.mockResolvedValueOnce();
rejectAction();
});
it('should show the confirm dialog', () => {
expect(window.confirm).toHaveBeenCalled();
expect(window.confirm).toHaveBeenCalledWith(
actionButtonMocks[configConst].confirmMessage,
);
});
it('should not execute the action', () => {
expect(MRWidgetService.executeInlineAction).not.toHaveBeenCalled();
});
});
describe('should show a confirm dialog and call executeInlineAction when accepted', () => {
beforeEach(() => {
executeActionSpy.mockResolvedValueOnce();
confirmAction();
});
it('should show the confirm dialog', () => {
expect(window.confirm).toHaveBeenCalled();
expect(window.confirm).toHaveBeenCalledWith(
actionButtonMocks[configConst].confirmMessage,
);
});
it('should execute the action with expected URL', () => {
expect(MRWidgetService.executeInlineAction).toHaveBeenCalled();
expect(MRWidgetService.executeInlineAction).toHaveBeenCalledWith(endpoint);
});
it('should not throw an error', () => {
expect(createFlash).not.toHaveBeenCalled();
});
describe('response includes redirect_url', () => {
const url = '/root/example';
beforeEach(() => {
executeActionSpy.mockResolvedValueOnce({
data: { redirect_url: url },
});
confirmAction();
});
it('calls visit url with the redirect_url', () => {
expect(visitUrl).toHaveBeenCalled();
expect(visitUrl).toHaveBeenCalledWith(url);
});
});
describe('it should call the executeAction method ', () => {
beforeEach(() => {
jest.spyOn(wrapper.vm, 'executeAction').mockImplementation();
confirmAction();
});
it('calls with the expected arguments', () => {
expect(wrapper.vm.executeAction).toHaveBeenCalled();
expect(wrapper.vm.executeAction).toHaveBeenCalledWith(
endpoint,
actionButtonMocks[configConst],
);
});
});
describe('when executeInlineAction errors', () => {
beforeEach(() => {
executeActionSpy.mockRejectedValueOnce();
confirmAction();
});
it('should call createFlash with error message', () => {
expect(createFlash).toHaveBeenCalled();
expect(createFlash).toHaveBeenCalledWith(
actionButtonMocks[configConst].errorMessage,
);
});
});
});
});
});
},
);
});
});
import { SUCCESS } from '~/vue_merge_request_widget/components/deployment/constants';
import {
DEPLOYING,
REDEPLOYING,
SUCCESS,
STOPPING,
} from '~/vue_merge_request_widget/components/deployment/constants';
const actionButtonMocks = {
[STOPPING]: {
actionName: STOPPING,
buttonText: 'Stop environment',
busyText: 'This environment is being deployed',
confirmMessage: 'Are you sure you want to stop this environment?',
errorMessage: 'Something went wrong while stopping this environment. Please try again.',
},
[DEPLOYING]: {
actionName: DEPLOYING,
buttonText: 'Deploy',
busyText: 'This environment is being deployed',
confirmMessage: 'Are you sure you want to deploy this environment?',
errorMessage: 'Something went wrong while deploying this environment. Please try again.',
},
[REDEPLOYING]: {
actionName: REDEPLOYING,
buttonText: 'Re-deploy',
busyText: 'This environment is being re-deployed',
confirmMessage: 'Are you sure you want to re-deploy this environment?',
errorMessage: 'Something went wrong while deploying this environment. Please try again.',
},
};
 
const deploymentMockData = {
id: 15,
Loading
Loading
@@ -29,4 +58,16 @@ const deploymentMockData = {
],
};
 
export default deploymentMockData;
const playDetails = {
playable_build: {
play_path: '/root/test-deployments/-/jobs/1131/play',
},
};
const retryDetails = {
playable_build: {
retry_path: '/root/test-deployments/-/jobs/1131/retry',
},
};
export { actionButtonMocks, deploymentMockData, playDetails, retryDetails };
Loading
Loading
@@ -2,7 +2,6 @@ import { mount } from '@vue/test-utils';
import DeploymentComponent from '~/vue_merge_request_widget/components/deployment/deployment.vue';
import DeploymentInfo from '~/vue_merge_request_widget/components/deployment/deployment_info.vue';
import DeploymentViewButton from '~/vue_merge_request_widget/components/deployment/deployment_view_button.vue';
import DeploymentStopButton from '~/vue_merge_request_widget/components/deployment/deployment_stop_button.vue';
import {
CREATED,
RUNNING,
Loading
Loading
@@ -10,15 +9,7 @@ import {
FAILED,
CANCELED,
} from '~/vue_merge_request_widget/components/deployment/constants';
import deploymentMockData from './deployment_mock_data';
const deployDetail = {
playable_build: {
retry_path: '/root/test-deployments/-/jobs/1131/retry',
play_path: '/root/test-deployments/-/jobs/1131/play',
},
isManual: true,
};
import { deploymentMockData, playDetails, retryDetails } from './deployment_mock_data';
 
describe('Deployment component', () => {
let wrapper;
Loading
Loading
@@ -30,6 +21,7 @@ describe('Deployment component', () => {
}
wrapper = mount(DeploymentComponent, {
...options,
provide: { glFeatures: { deployFromFooter: true } },
});
};
 
Loading
Loading
@@ -53,28 +45,39 @@ describe('Deployment component', () => {
describe('status message and buttons', () => {
const noActions = [];
const noDetails = { isManual: false };
const deployGroup = [DeploymentViewButton, DeploymentStopButton];
const deployDetail = {
...playDetails,
isManual: true,
};
const retryDetail = {
...retryDetails,
isManual: true,
};
const defaultGroup = ['.js-deploy-url', '.js-stop-env'];
const manualDeployGroup = ['.js-manual-deploy-action', ...defaultGroup];
const manualRedeployGroup = ['.js-manual-redeploy-action', ...defaultGroup];
 
describe.each`
status | previous | deploymentDetails | text | actionButtons
${CREATED} | ${true} | ${deployDetail} | ${'Can be manually deployed to'} | ${deployGroup}
${CREATED} | ${true} | ${noDetails} | ${'Will deploy to'} | ${deployGroup}
${CREATED} | ${true} | ${deployDetail} | ${'Can be manually deployed to'} | ${manualDeployGroup}
${CREATED} | ${true} | ${noDetails} | ${'Will deploy to'} | ${defaultGroup}
${CREATED} | ${false} | ${deployDetail} | ${'Can be manually deployed to'} | ${noActions}
${CREATED} | ${false} | ${noDetails} | ${'Will deploy to'} | ${noActions}
${RUNNING} | ${true} | ${deployDetail} | ${'Deploying to'} | ${deployGroup}
${RUNNING} | ${true} | ${noDetails} | ${'Deploying to'} | ${deployGroup}
${RUNNING} | ${true} | ${deployDetail} | ${'Deploying to'} | ${defaultGroup}
${RUNNING} | ${true} | ${noDetails} | ${'Deploying to'} | ${defaultGroup}
${RUNNING} | ${false} | ${deployDetail} | ${'Deploying to'} | ${noActions}
${RUNNING} | ${false} | ${noDetails} | ${'Deploying to'} | ${noActions}
${SUCCESS} | ${true} | ${deployDetail} | ${'Deployed to'} | ${deployGroup}
${SUCCESS} | ${true} | ${noDetails} | ${'Deployed to'} | ${deployGroup}
${SUCCESS} | ${false} | ${deployDetail} | ${'Deployed to'} | ${deployGroup}
${SUCCESS} | ${false} | ${noDetails} | ${'Deployed to'} | ${deployGroup}
${FAILED} | ${true} | ${deployDetail} | ${'Failed to deploy to'} | ${deployGroup}
${FAILED} | ${true} | ${noDetails} | ${'Failed to deploy to'} | ${deployGroup}
${FAILED} | ${false} | ${deployDetail} | ${'Failed to deploy to'} | ${noActions}
${SUCCESS} | ${true} | ${deployDetail} | ${'Deployed to'} | ${defaultGroup}
${SUCCESS} | ${true} | ${noDetails} | ${'Deployed to'} | ${defaultGroup}
${SUCCESS} | ${false} | ${deployDetail} | ${'Deployed to'} | ${defaultGroup}
${SUCCESS} | ${false} | ${noDetails} | ${'Deployed to'} | ${defaultGroup}
${FAILED} | ${true} | ${retryDetail} | ${'Failed to deploy to'} | ${manualRedeployGroup}
${FAILED} | ${true} | ${noDetails} | ${'Failed to deploy to'} | ${defaultGroup}
${FAILED} | ${false} | ${retryDetail} | ${'Failed to deploy to'} | ${noActions}
${FAILED} | ${false} | ${noDetails} | ${'Failed to deploy to'} | ${noActions}
${CANCELED} | ${true} | ${deployDetail} | ${'Canceled deployment to'} | ${deployGroup}
${CANCELED} | ${true} | ${noDetails} | ${'Canceled deployment to'} | ${deployGroup}
${CANCELED} | ${true} | ${deployDetail} | ${'Canceled deployment to'} | ${defaultGroup}
${CANCELED} | ${true} | ${noDetails} | ${'Canceled deployment to'} | ${defaultGroup}
${CANCELED} | ${false} | ${deployDetail} | ${'Canceled deployment to'} | ${noActions}
${CANCELED} | ${false} | ${noDetails} | ${'Canceled deployment to'} | ${noActions}
`(
Loading
Loading
@@ -112,7 +115,7 @@ describe('Deployment component', () => {
if (actionButtons.length > 0) {
describe('renders the expected button group', () => {
actionButtons.forEach(button => {
it(`renders ${button.name}`, () => {
it(`renders ${button}`, () => {
expect(wrapper.find(button).exists()).toBe(true);
});
});
Loading
Loading
@@ -121,8 +124,8 @@ describe('Deployment component', () => {
 
if (actionButtons.length === 0) {
describe('does not render the button group', () => {
[DeploymentViewButton, DeploymentStopButton].forEach(button => {
it(`does not render ${button.name}`, () => {
defaultGroup.forEach(button => {
it(`does not render ${button}`, () => {
expect(wrapper.find(button).exists()).toBe(false);
});
});
Loading
Loading
@@ -144,10 +147,6 @@ describe('Deployment component', () => {
 
describe('hasExternalUrls', () => {
describe('when deployment has both external_url_formatted and external_url', () => {
it('should return true', () => {
expect(wrapper.vm.hasExternalUrls).toEqual(true);
});
it('should render the View Button', () => {
expect(wrapper.find(DeploymentViewButton).exists()).toBe(true);
});
Loading
Loading
@@ -163,10 +162,6 @@ describe('Deployment component', () => {
});
});
 
it('should return false', () => {
expect(wrapper.vm.hasExternalUrls).toEqual(false);
});
it('should not render the View Button', () => {
expect(wrapper.find(DeploymentViewButton).exists()).toBe(false);
});
Loading
Loading
@@ -182,10 +177,6 @@ describe('Deployment component', () => {
});
});
 
it('should return false', () => {
expect(wrapper.vm.hasExternalUrls).toEqual(false);
});
it('should not render the View Button', () => {
expect(wrapper.find(DeploymentViewButton).exists()).toBe(false);
});
Loading
Loading
import { mount } from '@vue/test-utils';
import DeploymentViewButton from '~/vue_merge_request_widget/components/deployment/deployment_view_button.vue';
import ReviewAppLink from '~/vue_merge_request_widget/components/review_app_link.vue';
import deploymentMockData from './deployment_mock_data';
import { deploymentMockData } from './deployment_mock_data';
 
const appButtonText = {
text: 'View app',
Loading
Loading
// eslint-disable-next-line import/prefer-default-export
export const resetStore = store => {
store.replaceState({
notes: [],
targetNoteHash: null,
lastFetchedAt: null,
notesData: {},
userData: {},
noteableData: {},
});
};
export * from '../../frontend/notes/helpers.js';
import Vue from 'vue';
import deploymentStopComponent from '~/vue_merge_request_widget/components/deployment/deployment_stop_button.vue';
import { SUCCESS } from '~/vue_merge_request_widget/components/deployment/constants';
import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
import mountComponent from '../../helpers/vue_mount_component_helper';
describe('Deployment component', () => {
const Component = Vue.extend(deploymentStopComponent);
let deploymentMockData;
beforeEach(() => {
deploymentMockData = {
id: 15,
name: 'review/diplo',
url: '/root/review-apps/environments/15',
stop_url: '/root/review-apps/environments/15/stop',
metrics_url: '/root/review-apps/environments/15/deployments/1/metrics',
metrics_monitoring_url: '/root/review-apps/environments/15/metrics',
external_url: 'http://gitlab.com.',
external_url_formatted: 'gitlab',
deployed_at: '2017-03-22T22:44:42.258Z',
deployed_at_formatted: 'Mar 22, 2017 10:44pm',
deployment_manual_actions: [],
status: SUCCESS,
changes: [
{
path: 'index.html',
external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/index.html',
},
{
path: 'imgs/gallery.html',
external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/imgs/gallery.html',
},
{
path: 'about/',
external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/about/',
},
],
};
});
let vm;
afterEach(() => {
vm.$destroy();
});
describe('', () => {
beforeEach(() => {
vm = mountComponent(Component, {
stopUrl: deploymentMockData.stop_url,
isDeployInProgress: false,
});
});
describe('stopEnvironment', () => {
const url = '/foo/bar';
const returnPromise = () =>
new Promise(resolve => {
resolve({
data: {
redirect_url: url,
},
});
});
const mockStopEnvironment = () => {
vm.stopEnvironment(deploymentMockData);
return vm;
};
it('should show a confirm dialog and call service.stopEnvironment when confirmed', done => {
spyOn(window, 'confirm').and.returnValue(true);
spyOn(MRWidgetService, 'stopEnvironment').and.returnValue(returnPromise(true));
const visitUrl = spyOnDependency(deploymentStopComponent, 'visitUrl').and.returnValue(true);
vm = mockStopEnvironment();
expect(window.confirm).toHaveBeenCalled();
expect(MRWidgetService.stopEnvironment).toHaveBeenCalledWith(deploymentMockData.stop_url);
setTimeout(() => {
expect(visitUrl).toHaveBeenCalledWith(url);
done();
}, 333);
});
it('should show a confirm dialog but should not work if the dialog is rejected', () => {
spyOn(window, 'confirm').and.returnValue(false);
spyOn(MRWidgetService, 'stopEnvironment').and.returnValue(returnPromise(false));
vm = mockStopEnvironment();
expect(window.confirm).toHaveBeenCalled();
expect(MRWidgetService.stopEnvironment).not.toHaveBeenCalled();
});
});
});
});
Loading
Loading
@@ -195,6 +195,39 @@ describe Gitlab::Danger::CommitLinter do
end
end
 
[
'[ci skip] A commit message',
'[Ci skip] A commit message',
'[API] A commit message'
].each do |message|
context "when subject is '#{message}'" do
let(:commit_message) { message }
it 'does not add a problem' do
expect(commit_linter).not_to receive(:add_problem)
commit_linter.lint
end
end
end
[
'[ci skip]A commit message',
'[Ci skip] A commit message',
'[ci skip] a commit message',
'! A commit message'
].each do |message|
context "when subject is '#{message}'" do
let(:commit_message) { message }
it 'adds a problem' do
expect(commit_linter).to receive(:add_problem).with(:subject_starts_with_lowercase, described_class::DEFAULT_SUBJECT_DESCRIPTION)
commit_linter.lint
end
end
end
context 'when subject ends with a period' do
let(:commit_message) { 'A B C.' }
 
Loading
Loading
Loading
Loading
@@ -273,6 +273,7 @@ MergeRequest::Metrics:
- modified_paths_size
- commits_count
- first_approved_at
- first_reassigned_at
Ci::Pipeline:
- id
- project_id
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment