diff options
Diffstat (limited to 'spec/frontend')
9 files changed, 460 insertions, 0 deletions
diff --git a/spec/frontend/issuables_list/components/issuable_spec.js b/spec/frontend/issuables_list/components/issuable_spec.js index 6148f3c68f2..b6851a0e24c 100644 --- a/spec/frontend/issuables_list/components/issuable_spec.js +++ b/spec/frontend/issuables_list/components/issuable_spec.js @@ -45,6 +45,7 @@ describe('Issuable component', () => { ...props, }, sync: false, + attachToDocument: true, }); }; diff --git a/spec/frontend/issuables_list/components/issuables_list_app_spec.js b/spec/frontend/issuables_list/components/issuables_list_app_spec.js index e598a9c5a5d..dad4e74cb40 100644 --- a/spec/frontend/issuables_list/components/issuables_list_app_spec.js +++ b/spec/frontend/issuables_list/components/issuables_list_app_spec.js @@ -49,6 +49,7 @@ describe('Issuables list component', () => { }, localVue, sync: false, + attachToDocument: true, }); }; diff --git a/spec/frontend/registry/settings/components/__snapshots__/registry_settings_app_spec.js.snap b/spec/frontend/registry/settings/components/__snapshots__/registry_settings_app_spec.js.snap new file mode 100644 index 00000000000..c6dbb1da8e9 --- /dev/null +++ b/spec/frontend/registry/settings/components/__snapshots__/registry_settings_app_spec.js.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Registry List renders 1`] = ` +<div> + <p> + + Tag retention policies are designed to: + + </p> + + <ul> + <li> + Keep and protect the images that matter most. + </li> + + <li> + + Automatically remove extra images that aren't designed to be kept. + + </li> + </ul> + + <p> + Read more about the + <a + href="foo" + target="_blank" + > + Container Registry tag retention policies + </a> + </p> +</div> +`; diff --git a/spec/frontend/registry/settings/components/registry_settings_app_spec.js b/spec/frontend/registry/settings/components/registry_settings_app_spec.js new file mode 100644 index 00000000000..666d970aa6b --- /dev/null +++ b/spec/frontend/registry/settings/components/registry_settings_app_spec.js @@ -0,0 +1,40 @@ +import Vuex from 'vuex'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import component from '~/registry/settings/components/registry_settings_app.vue'; +import { createStore } from '~/registry/settings/stores/'; + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe('Registry List', () => { + let wrapper; + let store; + + const helpPagePath = 'foo'; + const findHelpLink = () => wrapper.find({ ref: 'help-link' }).find('a'); + + const mountComponent = (options = {}) => + shallowMount(component, { + sync: false, + store, + ...options, + }); + + beforeEach(() => { + store = createStore(); + store.dispatch('setInitialState', { helpPagePath }); + wrapper = mountComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders', () => { + expect(wrapper.element).toMatchSnapshot(); + }); + + it('renders an help link dependant on the helphPagePath', () => { + expect(findHelpLink().attributes('href')).toBe(helpPagePath); + }); +}); diff --git a/spec/frontend/registry/settings/stores/actions_spec.js b/spec/frontend/registry/settings/stores/actions_spec.js new file mode 100644 index 00000000000..205c3a6ee21 --- /dev/null +++ b/spec/frontend/registry/settings/stores/actions_spec.js @@ -0,0 +1,20 @@ +import * as actions from '~/registry/settings/stores/actions'; +import * as types from '~/registry/settings/stores/mutation_types'; +import testAction from 'helpers/vuex_action_helper'; + +jest.mock('~/flash.js'); + +describe('Actions Registry Store', () => { + describe('setInitialState', () => { + it('should set the initial state', done => { + testAction( + actions.setInitialState, + 'foo', + {}, + [{ type: types.SET_INITIAL_STATE, payload: 'foo' }], + [], + done, + ); + }); + }); +}); diff --git a/spec/frontend/registry/settings/stores/mutations_spec.js b/spec/frontend/registry/settings/stores/mutations_spec.js new file mode 100644 index 00000000000..421cd3f13cb --- /dev/null +++ b/spec/frontend/registry/settings/stores/mutations_spec.js @@ -0,0 +1,21 @@ +import mutations from '~/registry/settings/stores/mutations'; +import * as types from '~/registry/settings/stores/mutation_types'; +import createState from '~/registry/settings/stores/state'; + +describe('Mutations Registry Store', () => { + let mockState; + + beforeEach(() => { + mockState = createState(); + }); + + describe('SET_INITIAL_STATE', () => { + it('should set the initial state', () => { + const payload = { helpPagePath: 'foo', registrySettingsEndpoint: 'bar' }; + const expectedState = { ...mockState, ...payload }; + mutations[types.SET_INITIAL_STATE](mockState, payload); + + expect(mockState.endpoint).toEqual(expectedState.endpoint); + }); + }); +}); 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); + }); + }); + }); +}); |