diff options
Diffstat (limited to 'spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js')
-rw-r--r-- | spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js b/spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js new file mode 100644 index 00000000000..a285d26f404 --- /dev/null +++ b/spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js @@ -0,0 +1,234 @@ +import { mount } from '@vue/test-utils'; +import waitForPromises from 'helpers/wait_for_promises'; +import createFlash from '~/flash'; +import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal'; +import { visitUrl } from '~/lib/utils/url_utility'; +import { + CREATED, + MANUAL_DEPLOY, + FAILED, + DEPLOYING, + REDEPLOYING, + STOPPING, +} from '~/vue_merge_request_widget/components/deployment/constants'; +import DeploymentActions from '~/vue_merge_request_widget/components/deployment/deployment_actions.vue'; +import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service'; +import { + actionButtonMocks, + deploymentMockData, + playDetails, + retryDetails, +} from './deployment_mock_data'; + +jest.mock('~/flash'); +jest.mock('~/lib/utils/url_utility'); +jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal', () => { + return { + confirmAction: jest.fn(), + }; +}); + +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); + }; + + 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, + }, + }); + }); + + afterEach(() => { + wrapper.destroy(); + confirmAction.mockReset(); + }); + + 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, + }, + }, + }); + }); + + 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`, () => { + beforeEach(() => { + factory({ + propsData: { + computedDeploymentStatus, + deployment: { + ...deploymentMockData, + details: displayConditionChanges, + }, + }, + }); + }); + + 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(); + confirmAction.mockResolvedValueOnce(false); + finderFn().trigger('click'); + }); + + it('should show the confirm dialog', () => { + expect(confirmAction).toHaveBeenCalled(); + expect(confirmAction).toHaveBeenCalledWith( + actionButtonMocks[configConst].confirmMessage, + { + primaryBtnVariant: actionButtonMocks[configConst].buttonVariant, + primaryBtnText: actionButtonMocks[configConst].buttonText, + }, + ); + }); + + 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.mockResolvedValueOnce(true); + finderFn().trigger('click'); + }); + + it('should show the confirm dialog', () => { + expect(confirmAction).toHaveBeenCalled(); + expect(confirmAction).toHaveBeenCalledWith( + actionButtonMocks[configConst].confirmMessage, + { + primaryBtnVariant: actionButtonMocks[configConst].buttonVariant, + primaryBtnText: actionButtonMocks[configConst].buttonText, + }, + ); + }); + + 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(async () => { + executeActionSpy.mockResolvedValueOnce({ + data: { redirect_url: url }, + }); + + await waitForPromises(); + + confirmAction.mockResolvedValueOnce(true); + finderFn().trigger('click'); + }); + + it('calls visit url with the redirect_url', () => { + expect(visitUrl).toHaveBeenCalled(); + expect(visitUrl).toHaveBeenCalledWith(url); + }); + }); + + describe('it should call the executeAction method ', () => { + beforeEach(async () => { + jest.spyOn(wrapper.vm, 'executeAction').mockImplementation(); + + await waitForPromises(); + + confirmAction.mockResolvedValueOnce(true); + finderFn().trigger('click'); + }); + + it('calls with the expected arguments', () => { + expect(wrapper.vm.executeAction).toHaveBeenCalled(); + expect(wrapper.vm.executeAction).toHaveBeenCalledWith( + endpoint, + actionButtonMocks[configConst], + ); + }); + }); + + describe('when executeInlineAction errors', () => { + beforeEach(async () => { + executeActionSpy.mockRejectedValueOnce(); + + await waitForPromises(); + + confirmAction.mockResolvedValueOnce(true); + finderFn().trigger('click'); + }); + + it('should call createFlash with error message', () => { + expect(createFlash).toHaveBeenCalled(); + expect(createFlash).toHaveBeenCalledWith({ + message: actionButtonMocks[configConst].errorMessage, + }); + }); + }); + }); + }); + }); + }, + ); + }); +}); |