diff options
Diffstat (limited to 'spec/frontend/vue_mr_widget')
6 files changed, 421 insertions, 2 deletions
diff --git a/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js b/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js index a7ecb863cfb..8a604355625 100644 --- a/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js +++ b/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js @@ -61,7 +61,7 @@ describe('Merge Request Collapsible Extension', () => { describe('while loading', () => { beforeEach(() => { - mountComponent(Object.assign({}, data, { isLoading: true })); + mountComponent({ ...data, isLoading: true }); }); it('renders the buttons disabled', () => { @@ -86,7 +86,7 @@ describe('Merge Request Collapsible Extension', () => { describe('with error', () => { beforeEach(() => { - mountComponent(Object.assign({}, data, { hasError: true })); + mountComponent({ ...data, hasError: true }); }); it('does not render the buttons', () => { diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_container_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_container_spec.js new file mode 100644 index 00000000000..5f3a8654990 --- /dev/null +++ b/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_container_spec.js @@ -0,0 +1,100 @@ +import { mount } from '@vue/test-utils'; +import MrWidgetPipelineContainer from '~/vue_merge_request_widget/components/mr_widget_pipeline_container.vue'; +import MrWidgetPipeline from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue'; +import ArtifactsApp from '~/vue_merge_request_widget/components/artifacts_list_app.vue'; +import { mockStore } from '../mock_data'; +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; + +describe('MrWidgetPipelineContainer', () => { + let wrapper; + let mock; + + const factory = (props = {}) => { + wrapper = mount(MrWidgetPipelineContainer, { + propsData: { + mr: { ...mockStore }, + ...props, + }, + }); + }; + + beforeEach(() => { + mock = new MockAdapter(axios); + mock.onGet().reply(200, {}); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('when pre merge', () => { + beforeEach(() => { + factory(); + }); + + it('renders pipeline', () => { + expect(wrapper.find(MrWidgetPipeline).exists()).toBe(true); + expect(wrapper.find(MrWidgetPipeline).props()).toMatchObject({ + pipeline: mockStore.pipeline, + pipelineCoverageDelta: mockStore.pipelineCoverageDelta, + ciStatus: mockStore.ciStatus, + hasCi: mockStore.hasCI, + sourceBranch: mockStore.sourceBranch, + sourceBranchLink: mockStore.sourceBranchLink, + }); + }); + + it('renders deployments', () => { + const expectedProps = mockStore.deployments.map(dep => + expect.objectContaining({ + deployment: dep, + showMetrics: false, + }), + ); + + const deployments = wrapper.findAll('.mr-widget-extension .js-pre-deployment'); + + expect(deployments.wrappers.map(x => x.props())).toEqual(expectedProps); + }); + }); + + describe('when post merge', () => { + beforeEach(() => { + factory({ + isPostMerge: true, + }); + }); + + it('renders pipeline', () => { + expect(wrapper.find(MrWidgetPipeline).exists()).toBe(true); + expect(wrapper.find(MrWidgetPipeline).props()).toMatchObject({ + pipeline: mockStore.mergePipeline, + pipelineCoverageDelta: mockStore.pipelineCoverageDelta, + ciStatus: mockStore.ciStatus, + hasCi: mockStore.hasCI, + sourceBranch: mockStore.targetBranch, + sourceBranchLink: mockStore.targetBranch, + }); + }); + + it('renders deployments', () => { + const expectedProps = mockStore.postMergeDeployments.map(dep => + expect.objectContaining({ + deployment: dep, + showMetrics: true, + }), + ); + + const deployments = wrapper.findAll('.mr-widget-extension .js-post-deployment'); + + expect(deployments.wrappers.map(x => x.props())).toEqual(expectedProps); + }); + }); + + describe('with artifacts path', () => { + it('renders the artifacts app', () => { + expect(wrapper.find(ArtifactsApp).isVisible()).toBe(true); + }); + }); +}); diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_terraform_plan_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_terraform_plan_spec.js index 1951b56587a..91e95b2bdb1 100644 --- a/spec/frontend/vue_mr_widget/components/mr_widget_terraform_plan_spec.js +++ b/spec/frontend/vue_mr_widget/components/mr_widget_terraform_plan_spec.js @@ -3,6 +3,7 @@ import { shallowMount } from '@vue/test-utils'; import axios from '~/lib/utils/axios_utils'; import MockAdapter from 'axios-mock-adapter'; import MrWidgetTerraformPlan from '~/vue_merge_request_widget/components/mr_widget_terraform_plan.vue'; +import Poll from '~/lib/utils/poll'; const plan = { create: 10, @@ -57,11 +58,23 @@ describe('MrWidgetTerraformPlan', () => { }); describe('successful poll', () => { + let pollRequest; + let pollStop; + beforeEach(() => { + pollRequest = jest.spyOn(Poll.prototype, 'makeRequest'); + pollStop = jest.spyOn(Poll.prototype, 'stop'); + mockPollingApi(200, { 'tfplan.json': plan }, {}); + return mountWrapper(); }); + afterEach(() => { + pollRequest.mockRestore(); + pollStop.mockRestore(); + }); + it('content change text', () => { expect(wrapper.find(GlSprintf).exists()).toBe(true); }); @@ -69,6 +82,11 @@ describe('MrWidgetTerraformPlan', () => { it('renders button when url is found', () => { expect(wrapper.find('a').text()).toContain('View full log'); }); + + it('does not make additional requests after poll is successful', () => { + expect(pollRequest).toHaveBeenCalledTimes(1); + expect(pollStop).toHaveBeenCalledTimes(1); + }); }); describe('polling fails', () => { diff --git a/spec/frontend/vue_mr_widget/stores/artifacts_list/actions_spec.js b/spec/frontend/vue_mr_widget/stores/artifacts_list/actions_spec.js new file mode 100644 index 00000000000..026ea0e4d0a --- /dev/null +++ b/spec/frontend/vue_mr_widget/stores/artifacts_list/actions_spec.js @@ -0,0 +1,165 @@ +import MockAdapter from 'axios-mock-adapter'; +import testAction from 'helpers/vuex_action_helper'; +import { TEST_HOST } from 'helpers/test_constants'; +import axios from '~/lib/utils/axios_utils'; +import { + setEndpoint, + requestArtifacts, + clearEtagPoll, + stopPolling, + fetchArtifacts, + receiveArtifactsSuccess, + receiveArtifactsError, +} from '~/vue_merge_request_widget/stores/artifacts_list/actions'; +import state from '~/vue_merge_request_widget/stores/artifacts_list/state'; +import * as types from '~/vue_merge_request_widget/stores/artifacts_list/mutation_types'; + +describe('Artifacts App Store Actions', () => { + let mockedState; + + beforeEach(() => { + mockedState = state(); + }); + + describe('setEndpoint', () => { + it('should commit SET_ENDPOINT mutation', done => { + testAction( + setEndpoint, + 'endpoint.json', + mockedState, + [{ type: types.SET_ENDPOINT, payload: 'endpoint.json' }], + [], + done, + ); + }); + }); + + describe('requestArtifacts', () => { + it('should commit REQUEST_ARTIFACTS mutation', done => { + testAction( + requestArtifacts, + null, + mockedState, + [{ type: types.REQUEST_ARTIFACTS }], + [], + done, + ); + }); + }); + + describe('fetchArtifacts', () => { + let mock; + + beforeEach(() => { + mockedState.endpoint = `${TEST_HOST}/endpoint.json`; + mock = new MockAdapter(axios); + }); + + afterEach(() => { + mock.restore(); + stopPolling(); + clearEtagPoll(); + }); + + describe('success', () => { + it('dispatches requestArtifacts and receiveArtifactsSuccess ', done => { + mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, [ + { + text: 'result.txt', + url: 'asda', + job_name: 'generate-artifact', + job_path: 'asda', + }, + ]); + + testAction( + fetchArtifacts, + null, + mockedState, + [], + [ + { + type: 'requestArtifacts', + }, + { + payload: { + data: [ + { + text: 'result.txt', + url: 'asda', + job_name: 'generate-artifact', + job_path: 'asda', + }, + ], + status: 200, + }, + type: 'receiveArtifactsSuccess', + }, + ], + done, + ); + }); + }); + + describe('error', () => { + beforeEach(() => { + mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500); + }); + + it('dispatches requestArtifacts and receiveArtifactsError ', done => { + testAction( + fetchArtifacts, + null, + mockedState, + [], + [ + { + type: 'requestArtifacts', + }, + { + type: 'receiveArtifactsError', + }, + ], + done, + ); + }); + }); + }); + + describe('receiveArtifactsSuccess', () => { + it('should commit RECEIVE_ARTIFACTS_SUCCESS mutation with 200', done => { + testAction( + receiveArtifactsSuccess, + { data: { summary: {} }, status: 200 }, + mockedState, + [{ type: types.RECEIVE_ARTIFACTS_SUCCESS, payload: { summary: {} } }], + [], + done, + ); + }); + + it('should not commit RECEIVE_ARTIFACTS_SUCCESS mutation with 204', done => { + testAction( + receiveArtifactsSuccess, + { data: { summary: {} }, status: 204 }, + mockedState, + [], + [], + done, + ); + }); + }); + + describe('receiveArtifactsError', () => { + it('should commit RECEIVE_ARTIFACTS_ERROR mutation', done => { + testAction( + receiveArtifactsError, + null, + mockedState, + [{ type: types.RECEIVE_ARTIFACTS_ERROR }], + [], + done, + ); + }); + }); +}); diff --git a/spec/frontend/vue_mr_widget/stores/get_state_key_spec.js b/spec/frontend/vue_mr_widget/stores/get_state_key_spec.js index 0f5d47b3bfe..e54cd345a37 100644 --- a/spec/frontend/vue_mr_widget/stores/get_state_key_spec.js +++ b/spec/frontend/vue_mr_widget/stores/get_state_key_spec.js @@ -35,10 +35,12 @@ describe('getStateKey', () => { expect(bound()).toEqual('autoMergeEnabled'); + context.canMerge = true; context.isSHAMismatch = true; expect(bound()).toEqual('shaMismatch'); + context.canMerge = false; context.isPipelineBlocked = true; expect(bound()).toEqual('pipelineBlocked'); @@ -100,4 +102,26 @@ describe('getStateKey', () => { expect(bound()).toEqual('rebase'); }); + + it.each` + canMerge | isSHAMismatch | stateKey + ${true} | ${true} | ${'shaMismatch'} + ${false} | ${true} | ${'notAllowedToMerge'} + ${false} | ${false} | ${'notAllowedToMerge'} + `( + 'returns $stateKey when canMerge is $canMerge and isSHAMismatch is $isSHAMismatch', + ({ canMerge, isSHAMismatch, stateKey }) => { + const bound = getStateKey.bind( + { + canMerge, + isSHAMismatch, + }, + { + commits_count: 2, + }, + ); + + expect(bound()).toEqual(stateKey); + }, + ); }); diff --git a/spec/frontend/vue_mr_widget/stores/mr_widget_store_spec.js b/spec/frontend/vue_mr_widget/stores/mr_widget_store_spec.js new file mode 100644 index 00000000000..48326eda404 --- /dev/null +++ b/spec/frontend/vue_mr_widget/stores/mr_widget_store_spec.js @@ -0,0 +1,112 @@ +import MergeRequestStore from '~/vue_merge_request_widget/stores/mr_widget_store'; +import { stateKey } from '~/vue_merge_request_widget/stores/state_maps'; +import mockData from '../mock_data'; + +describe('MergeRequestStore', () => { + let store; + + beforeEach(() => { + store = new MergeRequestStore(mockData); + }); + + describe('setData', () => { + it('should set isSHAMismatch when the diff SHA changes', () => { + store.setData({ ...mockData, diff_head_sha: 'a-different-string' }); + + expect(store.isSHAMismatch).toBe(true); + }); + + it('should not set isSHAMismatch when other data changes', () => { + store.setData({ ...mockData, work_in_progress: !mockData.work_in_progress }); + + expect(store.isSHAMismatch).toBe(false); + }); + + it('should update cached sha after rebasing', () => { + store.setData({ ...mockData, diff_head_sha: 'abc123' }, true); + + expect(store.isSHAMismatch).toBe(false); + expect(store.sha).toBe('abc123'); + }); + + describe('isPipelinePassing', () => { + it('is true when the CI status is `success`', () => { + store.setData({ ...mockData, ci_status: 'success' }); + + expect(store.isPipelinePassing).toBe(true); + }); + + it('is true when the CI status is `success-with-warnings`', () => { + store.setData({ ...mockData, ci_status: 'success-with-warnings' }); + + expect(store.isPipelinePassing).toBe(true); + }); + + it('is false when the CI status is `failed`', () => { + store.setData({ ...mockData, ci_status: 'failed' }); + + expect(store.isPipelinePassing).toBe(false); + }); + + it('is false when the CI status is anything except `success`', () => { + store.setData({ ...mockData, ci_status: 'foobarbaz' }); + + expect(store.isPipelinePassing).toBe(false); + }); + }); + + describe('isPipelineSkipped', () => { + it('should set isPipelineSkipped=true when the CI status is `skipped`', () => { + store.setData({ ...mockData, ci_status: 'skipped' }); + + expect(store.isPipelineSkipped).toBe(true); + }); + + it('should set isPipelineSkipped=false when the CI status is anything except `skipped`', () => { + store.setData({ ...mockData, ci_status: 'foobarbaz' }); + + expect(store.isPipelineSkipped).toBe(false); + }); + }); + + describe('isNothingToMergeState', () => { + it('returns true when nothingToMerge', () => { + store.state = stateKey.nothingToMerge; + + expect(store.isNothingToMergeState).toBe(true); + }); + + it('returns false when not nothingToMerge', () => { + store.state = 'state'; + + expect(store.isNothingToMergeState).toBe(false); + }); + }); + }); + + describe('setPaths', () => { + it('should set the add ci config path', () => { + store.setData({ ...mockData }); + + expect(store.mergeRequestAddCiConfigPath).toBe('/group2/project2/new/pipeline'); + }); + + it('should set humanAccess=Maintainer when user has that role', () => { + store.setData({ ...mockData }); + + expect(store.humanAccess).toBe('Maintainer'); + }); + + it('should set pipelinesEmptySvgPath', () => { + store.setData({ ...mockData }); + + expect(store.pipelinesEmptySvgPath).toBe('/path/to/svg'); + }); + + it('should set newPipelinePath', () => { + store.setData({ ...mockData }); + + expect(store.newPipelinePath).toBe('/group2/project2/pipelines/new'); + }); + }); +}); |