From 872319738757edc0483346c75a2407f7019b963f Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 5 Dec 2019 12:07:43 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- .../deployment/deployment_mock_data.js | 32 ++++ .../vue_mr_widget/deployment/deployment_spec.js | 194 +++++++++++++++++++++ .../deployment/deployment_view_button_spec.js | 118 +++++++++++++ 3 files changed, 344 insertions(+) create mode 100644 spec/frontend/vue_mr_widget/deployment/deployment_mock_data.js create mode 100644 spec/frontend/vue_mr_widget/deployment/deployment_spec.js create mode 100644 spec/frontend/vue_mr_widget/deployment/deployment_view_button_spec.js (limited to 'spec/frontend/vue_mr_widget') diff --git a/spec/frontend/vue_mr_widget/deployment/deployment_mock_data.js b/spec/frontend/vue_mr_widget/deployment/deployment_mock_data.js new file mode 100644 index 00000000000..f8f4cb627dd --- /dev/null +++ b/spec/frontend/vue_mr_widget/deployment/deployment_mock_data.js @@ -0,0 +1,32 @@ +import { SUCCESS } from '~/vue_merge_request_widget/components/deployment/constants'; + +const 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', + details: {}, + 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/', + }, + ], +}; + +export default deploymentMockData; diff --git a/spec/frontend/vue_mr_widget/deployment/deployment_spec.js b/spec/frontend/vue_mr_widget/deployment/deployment_spec.js new file mode 100644 index 00000000000..78e086e473d --- /dev/null +++ b/spec/frontend/vue_mr_widget/deployment/deployment_spec.js @@ -0,0 +1,194 @@ +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, + SUCCESS, + 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, +}; + +describe('Deployment component', () => { + let wrapper; + + const factory = (options = {}) => { + // This destroys any wrappers created before a nested call to factory reassigns it + if (wrapper && wrapper.destroy) { + wrapper.destroy(); + } + wrapper = mount(DeploymentComponent, { + ...options, + }); + }; + + beforeEach(() => { + factory({ + propsData: { + deployment: deploymentMockData, + showMetrics: false, + }, + }); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('always renders DeploymentInfo', () => { + expect(wrapper.find(DeploymentInfo).exists()).toBe(true); + }); + + describe('status message and buttons', () => { + const noActions = []; + const noDetails = { isManual: false }; + const deployGroup = [DeploymentViewButton, DeploymentStopButton]; + + describe.each` + status | previous | deploymentDetails | text | actionButtons + ${CREATED} | ${true} | ${deployDetail} | ${'Can deploy manually to'} | ${deployGroup} + ${CREATED} | ${true} | ${noDetails} | ${'Will deploy to'} | ${deployGroup} + ${CREATED} | ${false} | ${deployDetail} | ${'Can deploy manually to'} | ${noActions} + ${CREATED} | ${false} | ${noDetails} | ${'Will deploy to'} | ${noActions} + ${RUNNING} | ${true} | ${deployDetail} | ${'Deploying to'} | ${deployGroup} + ${RUNNING} | ${true} | ${noDetails} | ${'Deploying to'} | ${deployGroup} + ${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} + ${FAILED} | ${false} | ${noDetails} | ${'Failed to deploy to'} | ${noActions} + ${CANCELED} | ${true} | ${deployDetail} | ${'Canceled deploy to'} | ${deployGroup} + ${CANCELED} | ${true} | ${noDetails} | ${'Canceled deploy to'} | ${deployGroup} + ${CANCELED} | ${false} | ${deployDetail} | ${'Canceled deploy to'} | ${noActions} + ${CANCELED} | ${false} | ${noDetails} | ${'Canceled deploy to'} | ${noActions} + `( + '$status + previous: $previous + manual: $deploymentDetails.isManual', + ({ status, previous, deploymentDetails, text, actionButtons }) => { + beforeEach(() => { + const previousOrSuccess = Boolean(previous || status === SUCCESS); + const updatedDeploymentData = { + status, + deployed_at: previous ? deploymentMockData.deployed_at : null, + deployed_at_formatted: previous ? deploymentMockData.deployed_at_formatted : null, + external_url: previousOrSuccess ? deploymentMockData.external_url : null, + external_url_formatted: previousOrSuccess + ? deploymentMockData.external_url_formatted + : null, + stop_url: previousOrSuccess ? deploymentMockData.stop_url : null, + details: deploymentDetails, + }; + + factory({ + propsData: { + showMetrics: false, + deployment: { + ...deploymentMockData, + ...updatedDeploymentData, + }, + }, + }); + }); + + it(`renders the text: ${text}`, () => { + expect(wrapper.find(DeploymentInfo).text()).toContain(text); + }); + + if (actionButtons.length > 0) { + describe('renders the expected button group', () => { + actionButtons.forEach(button => { + it(`renders ${button.name}`, () => { + expect(wrapper.find(button).exists()).toBe(true); + }); + }); + }); + } + + if (actionButtons.length === 0) { + describe('does not render the button group', () => { + [DeploymentViewButton, DeploymentStopButton].forEach(button => { + it(`does not render ${button.name}`, () => { + expect(wrapper.find(button).exists()).toBe(false); + }); + }); + }); + } + + if (actionButtons.includes(DeploymentViewButton)) { + it('renders the View button with expected text', () => { + if (status === SUCCESS) { + expect(wrapper.find(DeploymentViewButton).text()).toContain('View app'); + } else { + expect(wrapper.find(DeploymentViewButton).text()).toContain('View previous app'); + } + }); + } + }, + ); + }); + + 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); + }); + }); + + describe('when deployment has no external_url_formatted', () => { + beforeEach(() => { + factory({ + propsData: { + deployment: { ...deploymentMockData, external_url_formatted: null }, + showMetrics: false, + }, + }); + }); + + it('should return false', () => { + expect(wrapper.vm.hasExternalUrls).toEqual(false); + }); + + it('should not render the View Button', () => { + expect(wrapper.find(DeploymentViewButton).exists()).toBe(false); + }); + }); + + describe('when deployment has no external_url', () => { + beforeEach(() => { + factory({ + propsData: { + deployment: { ...deploymentMockData, external_url: null }, + showMetrics: false, + }, + }); + }); + + it('should return false', () => { + expect(wrapper.vm.hasExternalUrls).toEqual(false); + }); + + it('should not render the View Button', () => { + expect(wrapper.find(DeploymentViewButton).exists()).toBe(false); + }); + }); + }); +}); diff --git a/spec/frontend/vue_mr_widget/deployment/deployment_view_button_spec.js b/spec/frontend/vue_mr_widget/deployment/deployment_view_button_spec.js new file mode 100644 index 00000000000..6e3c6f64c68 --- /dev/null +++ b/spec/frontend/vue_mr_widget/deployment/deployment_view_button_spec.js @@ -0,0 +1,118 @@ +import { mount, createLocalVue } 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'; + +describe('Deployment View App button', () => { + let wrapper; + + const factory = (options = {}) => { + const localVue = createLocalVue(); + + wrapper = mount(localVue.extend(DeploymentViewButton), { + localVue, + ...options, + }); + }; + + beforeEach(() => { + factory({ + propsData: { + deployment: deploymentMockData, + isCurrent: true, + }, + }); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('text', () => { + describe('when app is current', () => { + it('shows View app', () => { + expect(wrapper.find(ReviewAppLink).text()).toContain('View app'); + }); + }); + + describe('when app is not current', () => { + beforeEach(() => { + factory({ + propsData: { + deployment: deploymentMockData, + isCurrent: false, + }, + }); + }); + + it('shows View Previous app', () => { + expect(wrapper.find(ReviewAppLink).text()).toContain('View previous app'); + }); + }); + }); + + describe('without changes', () => { + beforeEach(() => { + factory({ + propsData: { + deployment: { ...deploymentMockData, changes: null }, + isCurrent: false, + }, + }); + }); + + it('renders the link to the review app without dropdown', () => { + expect(wrapper.find('.js-mr-wigdet-deployment-dropdown').exists()).toBe(false); + }); + }); + + describe('with a single change', () => { + beforeEach(() => { + factory({ + propsData: { + deployment: { ...deploymentMockData, changes: [deploymentMockData.changes[0]] }, + isCurrent: false, + }, + }); + }); + + it('renders the link to the review app without dropdown', () => { + expect(wrapper.find('.js-mr-wigdet-deployment-dropdown').exists()).toBe(false); + }); + + it('renders the link to the review app linked to to the first change', () => { + const expectedUrl = deploymentMockData.changes[0].external_url; + const deployUrl = wrapper.find('.js-deploy-url'); + + expect(deployUrl.attributes().href).not.toBeNull(); + expect(deployUrl.attributes().href).toEqual(expectedUrl); + }); + }); + + describe('with multiple changes', () => { + beforeEach(() => { + factory({ + propsData: { + deployment: deploymentMockData, + isCurrent: false, + }, + }); + }); + + it('renders the link to the review app with dropdown', () => { + expect(wrapper.find('.js-mr-wigdet-deployment-dropdown').exists()).toBe(true); + }); + + it('renders all the links to the review apps', () => { + const allUrls = wrapper.findAll('.js-deploy-url-menu-item').wrappers; + const expectedUrls = deploymentMockData.changes.map(change => change.external_url); + + expectedUrls.forEach((expectedUrl, idx) => { + const deployUrl = allUrls[idx]; + + expect(deployUrl.attributes().href).not.toBeNull(); + expect(deployUrl.attributes().href).toEqual(expectedUrl); + }); + }); + }); +}); -- cgit v1.2.3