diff options
Diffstat (limited to 'spec/frontend/vue_mr_widget/components')
30 files changed, 767 insertions, 485 deletions
diff --git a/spec/frontend/vue_mr_widget/components/approvals/approvals_spec.js b/spec/frontend/vue_mr_widget/components/approvals/approvals_spec.js index 65ca3639dcc..fd8b0dddc61 100644 --- a/spec/frontend/vue_mr_widget/components/approvals/approvals_spec.js +++ b/spec/frontend/vue_mr_widget/components/approvals/approvals_spec.js @@ -15,10 +15,10 @@ import eventHub from '~/vue_merge_request_widget/event_hub'; jest.mock('~/flash'); const TEST_HELP_PATH = 'help/path'; -const testApprovedBy = () => [1, 7, 10].map(id => ({ id })); +const testApprovedBy = () => [1, 7, 10].map((id) => ({ id })); const testApprovals = () => ({ approved: false, - approved_by: testApprovedBy().map(user => ({ user })), + approved_by: testApprovedBy().map((user) => ({ user })), approval_rules_left: [], approvals_left: 4, suggested_approvers: [], @@ -31,10 +31,7 @@ const testApprovalRulesResponse = () => ({ rules: [{ id: 2 }] }); // For some reason, the `Promise.resolve()` needs to be deferred // or the timing doesn't work. const tick = () => Promise.resolve(); -const waitForTick = done => - tick() - .then(done) - .catch(done.fail); +const waitForTick = (done) => tick().then(done).catch(done.fail); describe('MRWidget approvals', () => { let wrapper; @@ -117,7 +114,7 @@ describe('MRWidget approvals', () => { }); describe('when fetch approvals error', () => { - beforeEach(done => { + beforeEach((done) => { jest.spyOn(service, 'fetchApprovals').mockReturnValue(Promise.reject()); createComponent(); waitForTick(done); @@ -134,7 +131,7 @@ describe('MRWidget approvals', () => { describe('action button', () => { describe('when mr is closed', () => { - beforeEach(done => { + beforeEach((done) => { mr.isOpen = false; mr.approvals.user_has_approved = false; mr.approvals.user_can_approve = true; @@ -149,7 +146,7 @@ describe('MRWidget approvals', () => { }); describe('when user cannot approve', () => { - beforeEach(done => { + beforeEach((done) => { mr.approvals.user_has_approved = false; mr.approvals.user_can_approve = false; @@ -169,7 +166,7 @@ describe('MRWidget approvals', () => { }); describe('and MR is unapproved', () => { - beforeEach(done => { + beforeEach((done) => { createComponent(); waitForTick(done); }); @@ -189,7 +186,7 @@ describe('MRWidget approvals', () => { }); describe('with no approvers', () => { - beforeEach(done => { + beforeEach((done) => { mr.approvals.approved_by = []; createComponent(); waitForTick(done); @@ -205,7 +202,7 @@ describe('MRWidget approvals', () => { }); describe('with approvers', () => { - beforeEach(done => { + beforeEach((done) => { mr.approvals.approved_by = [{ user: { id: 7 } }]; createComponent(); waitForTick(done); @@ -222,7 +219,7 @@ describe('MRWidget approvals', () => { }); describe('when approve action is clicked', () => { - beforeEach(done => { + beforeEach((done) => { createComponent(); waitForTick(done); }); @@ -241,7 +238,7 @@ describe('MRWidget approvals', () => { }); describe('and after loading', () => { - beforeEach(done => { + beforeEach((done) => { findAction().vm.$emit('click'); waitForTick(done); }); @@ -260,7 +257,7 @@ describe('MRWidget approvals', () => { }); describe('and error', () => { - beforeEach(done => { + beforeEach((done) => { jest.spyOn(service, 'approveMergeRequest').mockReturnValue(Promise.reject()); findAction().vm.$emit('click'); waitForTick(done); @@ -274,7 +271,7 @@ describe('MRWidget approvals', () => { }); describe('when user has approved', () => { - beforeEach(done => { + beforeEach((done) => { mr.approvals.user_has_approved = true; mr.approvals.user_can_approve = false; @@ -292,7 +289,7 @@ describe('MRWidget approvals', () => { describe('when revoke action is clicked', () => { describe('and successful', () => { - beforeEach(done => { + beforeEach((done) => { findAction().vm.$emit('click'); waitForTick(done); }); @@ -311,7 +308,7 @@ describe('MRWidget approvals', () => { }); describe('and error', () => { - beforeEach(done => { + beforeEach((done) => { jest.spyOn(service, 'unapproveMergeRequest').mockReturnValue(Promise.reject()); findAction().vm.$emit('click'); waitForTick(done); @@ -334,7 +331,7 @@ describe('MRWidget approvals', () => { }); describe('and can approve', () => { - beforeEach(done => { + beforeEach((done) => { mr.approvals.user_can_approve = true; createComponent(); @@ -351,7 +348,7 @@ describe('MRWidget approvals', () => { }); describe('and cannot approve', () => { - beforeEach(done => { + beforeEach((done) => { mr.approvals.user_can_approve = false; createComponent(); @@ -370,7 +367,7 @@ describe('MRWidget approvals', () => { }); describe('approvals summary', () => { - beforeEach(done => { + beforeEach((done) => { createComponent(); waitForTick(done); }); diff --git a/spec/frontend/vue_mr_widget/components/approvals/approvals_summary_spec.js b/spec/frontend/vue_mr_widget/components/approvals/approvals_summary_spec.js index 822d075f28f..b8ba619fbb1 100644 --- a/spec/frontend/vue_mr_widget/components/approvals/approvals_summary_spec.js +++ b/spec/frontend/vue_mr_widget/components/approvals/approvals_summary_spec.js @@ -4,7 +4,7 @@ import ApprovalsSummary from '~/vue_merge_request_widget/components/approvals/ap import { toNounSeriesText } from '~/lib/utils/grammar'; import UserAvatarList from '~/vue_shared/components/user_avatar/user_avatar_list.vue'; -const testApprovers = () => Array.from({ length: 5 }, (_, i) => i).map(id => ({ id })); +const testApprovers = () => Array.from({ length: 5 }, (_, i) => i).map((id) => ({ id })); const testRulesLeft = () => ['Lorem', 'Ipsum', 'dolar & sit']; const TEST_APPROVALS_LEFT = 3; diff --git a/spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js b/spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js index 1401308f7f0..2e1e21299b3 100644 --- a/spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js +++ b/spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js @@ -1,19 +1,20 @@ -import { mount, createLocalVue } from '@vue/test-utils'; +import { mount } from '@vue/test-utils'; import Vuex from 'vuex'; +import Vue, { nextTick } from 'vue'; import MockAdapter from 'axios-mock-adapter'; import { GlLoadingIcon } from '@gitlab/ui'; -import { TEST_HOST } from 'helpers/test_constants'; +import { TEST_HOST as FAKE_ENDPOINT } from 'helpers/test_constants'; import axios from '~/lib/utils/axios_utils'; import ArtifactsListApp from '~/vue_merge_request_widget/components/artifacts_list_app.vue'; -import createStore from '~/vue_merge_request_widget/stores/artifacts_list'; -import { artifactsList } from './mock_data'; +import { getStoreConfig } from '~/vue_merge_request_widget/stores/artifacts_list'; +import { artifacts } from '../mock_data'; + +Vue.use(Vuex); describe('Merge Requests Artifacts list app', () => { let wrapper; + let store; let mock; - const store = createStore(); - const localVue = createLocalVue(); - localVue.use(Vuex); const actionSpies = { fetchArtifacts: jest.fn(), @@ -29,15 +30,20 @@ describe('Merge Requests Artifacts list app', () => { }); const createComponent = () => { + const storeConfig = getStoreConfig(); + store = new Vuex.Store({ + ...storeConfig, + actions: { + ...storeConfig.actions, + ...actionSpies, + }, + }); + wrapper = mount(ArtifactsListApp, { propsData: { - endpoint: TEST_HOST, + endpoint: FAKE_ENDPOINT, }, store, - methods: { - ...actionSpies, - }, - localVue, }); }; @@ -50,7 +56,7 @@ describe('Merge Requests Artifacts list app', () => { beforeEach(() => { createComponent(); store.dispatch('requestArtifacts'); - return wrapper.vm.$nextTick(); + return nextTick(); }); it('renders a loading icon', () => { @@ -72,12 +78,12 @@ describe('Merge Requests Artifacts list app', () => { describe('with results', () => { beforeEach(() => { createComponent(); - mock.onGet(wrapper.vm.$store.state.endpoint).reply(200, artifactsList, {}); + mock.onGet(FAKE_ENDPOINT).reply(200, artifacts, {}); store.dispatch('receiveArtifactsSuccess', { - data: artifactsList, + data: artifacts, status: 200, }); - return wrapper.vm.$nextTick(); + return nextTick(); }); it('renders a title with the number of artifacts', () => { @@ -91,11 +97,11 @@ describe('Merge Requests Artifacts list app', () => { }); describe('on click', () => { - it('renders the list of artifacts', () => { + it('renders the list of artifacts', async () => { findTitle().trigger('click'); - wrapper.vm.$nextTick(() => { - expect(findTableRows().length).toEqual(2); - }); + await nextTick(); + + expect(findTableRows().length).toEqual(2); }); }); }); @@ -103,9 +109,9 @@ describe('Merge Requests Artifacts list app', () => { describe('with error', () => { beforeEach(() => { createComponent(); - mock.onGet(wrapper.vm.$store.state.endpoint).reply(500, {}, {}); + mock.onGet(FAKE_ENDPOINT).reply(500, {}, {}); store.dispatch('receiveArtifactsError'); - return wrapper.vm.$nextTick(); + return nextTick(); }); it('renders the error state', () => { diff --git a/spec/frontend/vue_mr_widget/components/artifacts_list_spec.js b/spec/frontend/vue_mr_widget/components/artifacts_list_spec.js index 1b1624e3e8f..fd432381512 100644 --- a/spec/frontend/vue_mr_widget/components/artifacts_list_spec.js +++ b/spec/frontend/vue_mr_widget/components/artifacts_list_spec.js @@ -1,16 +1,16 @@ import { shallowMount } from '@vue/test-utils'; import { GlLink } from '@gitlab/ui'; import ArtifactsList from '~/vue_merge_request_widget/components/artifacts_list.vue'; -import { artifactsList } from './mock_data'; +import { artifacts } from '../mock_data'; describe('Artifacts List', () => { let wrapper; const data = { - artifacts: artifactsList, + artifacts, }; - const mountComponent = props => { + const mountComponent = (props) => { wrapper = shallowMount(ArtifactsList, { propsData: { ...props, @@ -39,20 +39,10 @@ describe('Artifacts List', () => { }); it('renders job url', () => { - expect( - wrapper - .findAll(GlLink) - .at(1) - .attributes('href'), - ).toEqual(data.artifacts[0].job_path); + expect(wrapper.findAll(GlLink).at(1).attributes('href')).toEqual(data.artifacts[0].job_path); }); it('renders job name', () => { - expect( - wrapper - .findAll(GlLink) - .at(1) - .text(), - ).toEqual(data.artifacts[0].job_name); + expect(wrapper.findAll(GlLink).at(1).text()).toEqual(data.artifacts[0].job_name); }); }); diff --git a/spec/frontend/vue_mr_widget/components/mock_data.js b/spec/frontend/vue_mr_widget/components/mock_data.js deleted file mode 100644 index 73e254f2b1a..00000000000 --- a/spec/frontend/vue_mr_widget/components/mock_data.js +++ /dev/null @@ -1,14 +0,0 @@ -export const artifactsList = [ - { - text: 'result.txt', - url: 'bar', - job_name: 'generate-artifact', - job_path: 'bar', - }, - { - text: 'foo.txt', - url: 'foo', - job_name: 'foo-artifact', - job_path: 'foo', - }, -]; 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 8a604355625..ba2a8ee0a41 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 @@ -8,7 +8,7 @@ describe('Merge Request Collapsible Extension', () => { title: 'View artifacts', }; - const mountComponent = props => { + const mountComponent = (props) => { wrapper = mount(MrCollapsibleSection, { propsData: { ...props, @@ -65,18 +65,8 @@ describe('Merge Request Collapsible Extension', () => { }); it('renders the buttons disabled', () => { - expect( - wrapper - .findAll('button') - .at(0) - .attributes('disabled'), - ).toEqual('disabled'); - expect( - wrapper - .findAll('button') - .at(1) - .attributes('disabled'), - ).toEqual('disabled'); + expect(wrapper.findAll('button').at(0).attributes('disabled')).toEqual('disabled'); + expect(wrapper.findAll('button').at(1).attributes('disabled')).toEqual('disabled'); }); it('renders loading spinner', () => { diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_alert_message_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_alert_message_spec.js index f78fcfb52b4..720ce613b85 100644 --- a/spec/frontend/vue_mr_widget/components/mr_widget_alert_message_spec.js +++ b/spec/frontend/vue_mr_widget/components/mr_widget_alert_message_spec.js @@ -19,7 +19,7 @@ describe('MrWidgetAlertMessage', () => { }); describe('when type is not provided', () => { - it('should render a red message', done => { + it('should render a red message', (done) => { wrapper.vm.$nextTick(() => { expect(wrapper.classes()).toContain('danger_message'); expect(wrapper.classes()).not.toContain('warning_message'); @@ -29,7 +29,7 @@ describe('MrWidgetAlertMessage', () => { }); describe('when type === "danger"', () => { - it('should render a red message', done => { + it('should render a red message', (done) => { wrapper.setProps({ type: 'danger' }); wrapper.vm.$nextTick(() => { expect(wrapper.classes()).toContain('danger_message'); @@ -40,7 +40,7 @@ describe('MrWidgetAlertMessage', () => { }); describe('when type === "warning"', () => { - it('should render a red message', done => { + it('should render a red message', (done) => { wrapper.setProps({ type: 'warning' }); wrapper.vm.$nextTick(() => { expect(wrapper.classes()).toContain('warning_message'); @@ -51,7 +51,7 @@ describe('MrWidgetAlertMessage', () => { }); describe('when helpPath is not provided', () => { - it('should not render a help icon/link', done => { + it('should not render a help icon/link', (done) => { wrapper.vm.$nextTick(() => { const link = wrapper.find(GlLink); @@ -62,7 +62,7 @@ describe('MrWidgetAlertMessage', () => { }); describe('when helpPath is provided', () => { - it('should render a help icon/link', done => { + it('should render a help icon/link', (done) => { wrapper.setProps({ helpPath: '/path/to/help/docs' }); wrapper.vm.$nextTick(() => { const link = wrapper.find(GlLink); diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_header_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_header_spec.js index f9b6ac721d2..db884dfe015 100644 --- a/spec/frontend/vue_mr_widget/components/mr_widget_header_spec.js +++ b/spec/frontend/vue_mr_widget/components/mr_widget_header_spec.js @@ -187,7 +187,7 @@ describe('MRWidgetHeader', () => { expect(link.getAttribute('href')).toBeNull(); }); - it('renders web ide button with blank query string if target & source project branch', done => { + it('renders web ide button with blank query string if target & source project branch', (done) => { vm.mr.targetProjectFullPath = 'root/gitlab-ce'; vm.$nextTick(() => { @@ -202,7 +202,7 @@ describe('MRWidgetHeader', () => { }); }); - it('renders web ide button with relative URL', done => { + it('renders web ide button with relative URL', (done) => { gon.relative_url_root = '/gitlab'; vm.mr.iid = 2; diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_icon_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_icon_spec.js index ea8b33495ab..4bcae904ddc 100644 --- a/spec/frontend/vue_mr_widget/components/mr_widget_icon_spec.js +++ b/spec/frontend/vue_mr_widget/components/mr_widget_icon_spec.js @@ -20,7 +20,7 @@ describe('MrWidgetIcon', () => { }); it('renders icon and container', () => { - expect(wrapper.is('.circle-icon-container')).toBe(true); + expect(wrapper.element.className).toContain('circle-icon-container'); expect(wrapper.find(GlIcon).props('name')).toEqual(TEST_ICON); }); }); diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_memory_usage_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_memory_usage_spec.js index 7a932feb3a7..79a0dd1e760 100644 --- a/spec/frontend/vue_mr_widget/components/mr_widget_memory_usage_spec.js +++ b/spec/frontend/vue_mr_widget/components/mr_widget_memory_usage_spec.js @@ -153,13 +153,13 @@ describe('MemoryUsage', () => { describe('loadMetrics', () => { const returnServicePromise = () => - new Promise(resolve => { + new Promise((resolve) => { resolve({ data: metricsMockData, }); }); - it('should load metrics data using MRWidgetService', done => { + it('should load metrics data using MRWidgetService', (done) => { jest.spyOn(MRWidgetService, 'fetchMetrics').mockReturnValue(returnServicePromise(true)); jest.spyOn(vm, 'computeGraphData').mockImplementation(() => {}); @@ -179,7 +179,7 @@ describe('MemoryUsage', () => { expect(el.querySelector('.js-usage-info')).toBeDefined(); }); - it('should show loading metrics message while metrics are being loaded', done => { + it('should show loading metrics message while metrics are being loaded', (done) => { vm.loadingMetrics = true; vm.hasMetrics = false; vm.loadFailed = false; @@ -194,7 +194,7 @@ describe('MemoryUsage', () => { }); }); - it('should show deployment memory usage when metrics are loaded', done => { + it('should show deployment memory usage when metrics are loaded', (done) => { // ignore BoostrapVue warnings jest.spyOn(console, 'warn').mockImplementation(); @@ -210,7 +210,7 @@ describe('MemoryUsage', () => { }); }); - it('should show failure message when metrics loading failed', done => { + it('should show failure message when metrics loading failed', (done) => { vm.loadingMetrics = false; vm.hasMetrics = false; vm.loadFailed = true; @@ -223,7 +223,7 @@ describe('MemoryUsage', () => { }); }); - it('should show metrics unavailable message when metrics loading failed', done => { + it('should show metrics unavailable message when metrics loading failed', (done) => { vm.loadingMetrics = false; vm.hasMetrics = false; vm.loadFailed = false; 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 index d67f1adadf2..85468c5b0db 100644 --- 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 @@ -46,7 +46,7 @@ describe('MrWidgetPipelineContainer', () => { }); it('renders deployments', () => { - const expectedProps = mockStore.deployments.map(dep => + const expectedProps = mockStore.deployments.map((dep) => expect.objectContaining({ deployment: dep, showMetrics: false, @@ -55,7 +55,7 @@ describe('MrWidgetPipelineContainer', () => { const deployments = wrapper.findAll('.mr-widget-extension .js-pre-deployment'); - expect(deployments.wrappers.map(x => x.props())).toEqual(expectedProps); + expect(deployments.wrappers.map((x) => x.props())).toEqual(expectedProps); }); }); @@ -79,7 +79,7 @@ describe('MrWidgetPipelineContainer', () => { }); it('renders deployments', () => { - const expectedProps = mockStore.postMergeDeployments.map(dep => + const expectedProps = mockStore.postMergeDeployments.map((dep) => expect.objectContaining({ deployment: dep, showMetrics: true, @@ -88,12 +88,14 @@ describe('MrWidgetPipelineContainer', () => { const deployments = wrapper.findAll('.mr-widget-extension .js-post-deployment'); - expect(deployments.wrappers.map(x => x.props())).toEqual(expectedProps); + expect(deployments.wrappers.map((x) => x.props())).toEqual(expectedProps); }); }); describe('with artifacts path', () => { it('renders the artifacts app', () => { + factory(); + expect(wrapper.find(ArtifactsApp).isVisible()).toBe(true); }); }); diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_spec.js index 7ecd8629607..3e5ab5cd32d 100644 --- a/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_spec.js +++ b/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_spec.js @@ -72,21 +72,13 @@ describe('MRWidgetPipeline', () => { }); it('should render pipeline ID', () => { - expect( - findPipelineID() - .text() - .trim(), - ).toBe(`#${mockData.pipeline.id}`); + expect(findPipelineID().text().trim()).toBe(`#${mockData.pipeline.id}`); }); it('should render pipeline status and commit id', () => { expect(findPipelineInfoContainer().text()).toMatch(mockData.pipeline.details.status.label); - expect( - findCommitLink() - .text() - .trim(), - ).toBe(mockData.pipeline.commit.short_id); + expect(findCommitLink().text().trim()).toBe(mockData.pipeline.commit.short_id); expect(findCommitLink().attributes('href')).toBe(mockData.pipeline.commit.commit_path); }); @@ -130,7 +122,7 @@ describe('MRWidgetPipeline', () => { it.each(mockData.buildsWithCoverage)( 'should have name and coverage for build %s listed in tooltip', - build => { + (build) => { const tooltipText = findPipelineCoverageTooltipText(); expect(tooltipText).toContain(`${build.name} (${build.coverage}%)`); @@ -148,11 +140,7 @@ describe('MRWidgetPipeline', () => { }); it('should render pipeline ID', () => { - expect( - findPipelineID() - .text() - .trim(), - ).toBe(`#${mockData.pipeline.id}`); + expect(findPipelineID().text().trim()).toBe(`#${mockData.pipeline.id}`); }); it('should render pipeline status', () => { diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_rebase_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_rebase_spec.js index 9923434a7dd..bdd038edd71 100644 --- a/spec/frontend/vue_mr_widget/components/mr_widget_rebase_spec.js +++ b/spec/frontend/vue_mr_widget/components/mr_widget_rebase_spec.js @@ -1,135 +1,181 @@ -import Vue from 'vue'; -import mountComponent from 'helpers/vue_mount_component_helper'; +import { nextTick } from 'vue'; +import { shallowMount } from '@vue/test-utils'; import eventHub from '~/vue_merge_request_widget/event_hub'; -import component from '~/vue_merge_request_widget/components/states/mr_widget_rebase.vue'; +import WidgetRebase from '~/vue_merge_request_widget/components/states/mr_widget_rebase.vue'; + +let wrapper; + +function factory(propsData, mergeRequestWidgetGraphql) { + wrapper = shallowMount(WidgetRebase, { + propsData, + data() { + return { + state: { + rebaseInProgress: propsData.mr.rebaseInProgress, + targetBranch: propsData.mr.targetBranch, + userPermissions: { + pushToSourceBranch: propsData.mr.canPushToSourceBranch, + }, + }, + }; + }, + provide: { glFeatures: { mergeRequestWidgetGraphql } }, + mocks: { + $apollo: { + queries: { + state: { loading: false }, + }, + }, + }, + }); +} describe('Merge request widget rebase component', () => { - let Component; - let vm; - - const findRebaseMessageEl = () => vm.$el.querySelector('[data-testid="rebase-message"]'); - const findRebaseMessageElText = () => findRebaseMessageEl().textContent.trim(); - - beforeEach(() => { - Component = Vue.extend(component); - }); + const findRebaseMessageEl = () => wrapper.find('[data-testid="rebase-message"]'); + const findRebaseMessageElText = () => findRebaseMessageEl().text(); afterEach(() => { - vm.$destroy(); + wrapper.destroy(); + wrapper = null; }); - describe('While rebasing', () => { - it('should show progress message', () => { - vm = mountComponent(Component, { - mr: { rebaseInProgress: true }, - service: {}, + [true, false].forEach((mergeRequestWidgetGraphql) => { + describe(`widget graphql is ${mergeRequestWidgetGraphql ? 'enabled' : 'dislabed'}`, () => { + describe('While rebasing', () => { + it('should show progress message', () => { + factory( + { + mr: { rebaseInProgress: true }, + service: {}, + }, + mergeRequestWidgetGraphql, + ); + + expect(findRebaseMessageElText()).toContain('Rebase in progress'); + }); }); - expect(findRebaseMessageElText()).toContain('Rebase in progress'); - }); - }); - - describe('With permissions', () => { - beforeEach(() => { - vm = mountComponent(Component, { - mr: { - rebaseInProgress: false, - canPushToSourceBranch: true, - }, - service: {}, - }); - }); - - it('it should render rebase button and warning message', () => { - const text = findRebaseMessageElText(); - - expect(text).toContain('Fast-forward merge is not possible.'); - expect(text.replace(/\s\s+/g, ' ')).toContain( - 'Rebase the source branch onto the target branch.', - ); - }); + describe('With permissions', () => { + it('it should render rebase button and warning message', () => { + factory( + { + mr: { + rebaseInProgress: false, + canPushToSourceBranch: true, + }, + service: {}, + }, + mergeRequestWidgetGraphql, + ); + + const text = findRebaseMessageElText(); + + expect(text).toContain('Fast-forward merge is not possible.'); + expect(text.replace(/\s\s+/g, ' ')).toContain( + 'Rebase the source branch onto the target branch.', + ); + }); + + it('it should render error message when it fails', async () => { + factory( + { + mr: { + rebaseInProgress: false, + canPushToSourceBranch: true, + }, + service: {}, + }, + mergeRequestWidgetGraphql, + ); - it('it should render error message when it fails', done => { - vm.rebasingError = 'Something went wrong!'; + wrapper.setData({ rebasingError: 'Something went wrong!' }); - Vue.nextTick(() => { - expect(findRebaseMessageElText()).toContain('Something went wrong!'); - done(); + await nextTick(); + expect(findRebaseMessageElText()).toContain('Something went wrong!'); + }); }); - }); - }); - describe('Without permissions', () => { - it('should render a message explaining user does not have permissions', () => { - vm = mountComponent(Component, { - mr: { - rebaseInProgress: false, - canPushToSourceBranch: false, - targetBranch: 'foo', - }, - service: {}, - }); - - const text = findRebaseMessageElText(); + describe('Without permissions', () => { + it('should render a message explaining user does not have permissions', () => { + factory( + { + mr: { + rebaseInProgress: false, + canPushToSourceBranch: false, + targetBranch: 'foo', + }, + service: {}, + }, + mergeRequestWidgetGraphql, + ); + + const text = findRebaseMessageElText(); + + expect(text).toContain('Fast-forward merge is not possible.'); + expect(text).toContain('Rebase the source branch onto'); + expect(text).toContain('foo'); + expect(text.replace(/\s\s+/g, ' ')).toContain( + 'to allow this merge request to be merged.', + ); + }); + + it('should render the correct target branch name', () => { + const targetBranch = 'fake-branch-to-test-with'; + factory( + { + mr: { + rebaseInProgress: false, + canPushToSourceBranch: false, + targetBranch, + }, + service: {}, + }, + mergeRequestWidgetGraphql, + ); - expect(text).toContain('Fast-forward merge is not possible.'); - expect(text).toContain('Rebase the source branch onto'); - expect(text).toContain('foo'); - expect(text.replace(/\s\s+/g, ' ')).toContain('to allow this merge request to be merged.'); - }); + const elem = findRebaseMessageEl(); - it('should render the correct target branch name', () => { - const targetBranch = 'fake-branch-to-test-with'; - vm = mountComponent(Component, { - mr: { - rebaseInProgress: false, - canPushToSourceBranch: false, - targetBranch, - }, - service: {}, + expect(elem.text()).toContain( + `Fast-forward merge is not possible. Rebase the source branch onto ${targetBranch} to allow this merge request to be merged.`, + ); + }); }); - const elem = findRebaseMessageEl(); - - expect(elem.innerHTML).toContain( - `Fast-forward merge is not possible. Rebase the source branch onto <span class="label-branch">${targetBranch}</span> to allow this merge request to be merged.`, - ); - }); - }); - - describe('methods', () => { - it('checkRebaseStatus', done => { - jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); - vm = mountComponent(Component, { - mr: {}, - service: { - rebase() { - return Promise.resolve(); - }, - poll() { - return Promise.resolve({ - data: { - rebase_in_progress: false, - merge_error: null, + describe('methods', () => { + it('checkRebaseStatus', async () => { + jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); + factory( + { + mr: {}, + service: { + rebase() { + return Promise.resolve(); + }, + poll() { + return Promise.resolve({ + data: { + rebase_in_progress: false, + merge_error: null, + }, + }); + }, }, - }); - }, - }, - }); + }, + mergeRequestWidgetGraphql, + ); - vm.rebase(); + wrapper.vm.rebase(); + + // Wait for the rebase request + await nextTick(); + // Wait for the polling request + await nextTick(); + // Wait for the eventHub to be called + await nextTick(); - // Wait for the rebase request - vm.$nextTick() - // Wait for the polling request - .then(vm.$nextTick()) - // Wait for the eventHub to be called - .then(vm.$nextTick()) - .then(() => { expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetRebaseSuccess'); - }) - .then(done) - .catch(done.fail); + }); + }); }); }); }); diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_related_links_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_related_links_spec.js index 0c4ec7ed99b..a33401c5ba9 100644 --- a/spec/frontend/vue_mr_widget/components/mr_widget_related_links_spec.js +++ b/spec/frontend/vue_mr_widget/components/mr_widget_related_links_spec.js @@ -5,7 +5,7 @@ import relatedLinksComponent from '~/vue_merge_request_widget/components/mr_widg describe('MRWidgetRelatedLinks', () => { let vm; - const createComponent = data => { + const createComponent = (data) => { const Component = Vue.extend(relatedLinksComponent); return mountComponent(Component, data); diff --git a/spec/frontend/vue_mr_widget/components/review_app_link_spec.js b/spec/frontend/vue_mr_widget/components/review_app_link_spec.js index 7d47621c64a..81a52890db7 100644 --- a/spec/frontend/vue_mr_widget/components/review_app_link_spec.js +++ b/spec/frontend/vue_mr_widget/components/review_app_link_spec.js @@ -1,7 +1,7 @@ import Vue from 'vue'; import { mockTracking, triggerEvent } from 'helpers/tracking_helper'; +import mountComponent from 'helpers/vue_mount_component_helper'; import component from '~/vue_merge_request_widget/components/review_app_link.vue'; -import mountComponent from '../../helpers/vue_mount_component_helper'; describe('review app link', () => { const Component = Vue.extend(component); diff --git a/spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap b/spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap new file mode 100644 index 00000000000..c425a3a86a9 --- /dev/null +++ b/spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap @@ -0,0 +1,183 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MRWidgetAutoMergeEnabled when graphql is disabled template should have correct elements 1`] = ` +<div + class="mr-widget-body media" +> + <status-icon-stub + status="success" + /> + + <div + class="media-body" + > + <h4 + class="gl-display-flex" + > + <span + class="gl-mr-3" + > + <span + class="js-status-text-before-author" + data-testid="beforeStatusText" + > + Set by + </span> + + <mr-widget-author-stub + author="[object Object]" + showauthorname="true" + /> + + <span + class="js-status-text-after-author" + data-testid="afterStatusText" + > + to be merged automatically when the pipeline succeeds + </span> + </span> + + <a + class="btn btn-sm btn-default js-cancel-auto-merge" + data-testid="cancelAutomaticMergeButton" + href="#" + role="button" + > + <!----> + + Cancel automatic merge + + </a> + </h4> + + <section + class="mr-info-list" + > + <p> + + The changes will be merged into + + <a + class="label-branch" + href="/foo/bar" + > + foo + </a> + </p> + + <p + class="gl-display-flex" + > + <span + class="gl-mr-3" + > + The source branch will not be deleted + </span> + + <a + class="btn btn-sm btn-default js-remove-source-branch" + data-testid="removeSourceBranchButton" + href="#" + role="button" + > + <!----> + + Delete source branch + + </a> + </p> + </section> + </div> +</div> +`; + +exports[`MRWidgetAutoMergeEnabled when graphql is enabled template should have correct elements 1`] = ` +<div + class="mr-widget-body media" +> + <status-icon-stub + status="success" + /> + + <div + class="media-body" + > + <h4 + class="gl-display-flex" + > + <span + class="gl-mr-3" + > + <span + class="js-status-text-before-author" + data-testid="beforeStatusText" + > + Set by + </span> + + <mr-widget-author-stub + author="[object Object]" + showauthorname="true" + /> + + <span + class="js-status-text-after-author" + data-testid="afterStatusText" + > + to be merged automatically when the pipeline succeeds + </span> + </span> + + <a + class="btn btn-sm btn-default js-cancel-auto-merge" + data-testid="cancelAutomaticMergeButton" + href="#" + role="button" + > + <!----> + + Cancel automatic merge + + </a> + </h4> + + <section + class="mr-info-list" + > + <p> + + The changes will be merged into + + <a + class="label-branch" + href="/foo/bar" + > + foo + </a> + </p> + + <p + class="gl-display-flex" + > + <span + class="gl-mr-3" + > + The source branch will not be deleted + </span> + + <a + class="btn btn-sm btn-default js-remove-source-branch" + data-testid="removeSourceBranchButton" + href="#" + role="button" + > + <!----> + + Delete source branch + + </a> + </p> + </section> + </div> +</div> +`; diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_enabled_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_enabled_spec.js index ae0f605c419..850bbd93df5 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_enabled_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_enabled_spec.js @@ -1,20 +1,81 @@ -import Vue from 'vue'; -import mountComponent from 'helpers/vue_mount_component_helper'; +import { nextTick } from 'vue'; +import { shallowMount } from '@vue/test-utils'; import { trimText } from 'helpers/text_helper'; +import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import autoMergeEnabledComponent from '~/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue'; import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service'; import eventHub from '~/vue_merge_request_widget/event_hub'; import { MWPS_MERGE_STRATEGY } from '~/vue_merge_request_widget/constants'; +let wrapper; +let mergeRequestWidgetGraphqlEnabled = false; + +function convertPropsToGraphqlState(props) { + return { + autoMergeStrategy: props.autoMergeStrategy, + cancelAutoMergePath: 'http://text.com', + mergeUser: { + id: props.mergeUserId, + ...props.setToAutoMergeBy, + }, + targetBranch: props.targetBranch, + targetBranchCommitsPath: props.targetBranchPath, + shouldRemoveSourceBranch: props.shouldRemoveSourceBranch, + forceRemoveSourceBranch: props.shouldRemoveSourceBranch, + userPermissions: { + removeSourceBranch: props.canRemoveSourceBranch, + }, + }; +} + +function factory(propsData) { + let state = {}; + + if (mergeRequestWidgetGraphqlEnabled) { + state = convertPropsToGraphqlState(propsData); + } + + wrapper = extendedWrapper( + shallowMount(autoMergeEnabledComponent, { + propsData: { + mr: propsData, + service: new MRWidgetService({}), + }, + data() { + return { state }; + }, + provide: { glFeatures: { mergeRequestWidgetGraphql: mergeRequestWidgetGraphqlEnabled } }, + mocks: { + $apollo: { + queries: { + state: { loading: false }, + }, + }, + }, + }), + ); +} + +const targetBranchPath = '/foo/bar'; +const targetBranch = 'foo'; +const sha = '1EA2EZ34'; +const defaultMrProps = () => ({ + shouldRemoveSourceBranch: false, + canRemoveSourceBranch: true, + canCancelAutomaticMerge: true, + mergeUserId: 1, + currentUserId: 1, + setToAutoMergeBy: {}, + sha, + targetBranchPath, + targetBranch, + autoMergeStrategy: MWPS_MERGE_STRATEGY, +}); + describe('MRWidgetAutoMergeEnabled', () => { - let vm; let oldWindowGl; - const targetBranchPath = '/foo/bar'; - const targetBranch = 'foo'; - const sha = '1EA2EZ34'; beforeEach(() => { - const Component = Vue.extend(autoMergeEnabledComponent); jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); oldWindowGl = window.gl; @@ -23,216 +84,234 @@ describe('MRWidgetAutoMergeEnabled', () => { defaultAvatarUrl: 'no_avatar.png', }, }; - - vm = mountComponent(Component, { - mr: { - shouldRemoveSourceBranch: false, - canRemoveSourceBranch: true, - canCancelAutomaticMerge: true, - mergeUserId: 1, - currentUserId: 1, - setToAutoMergeBy: {}, - sha, - targetBranchPath, - targetBranch, - autoMergeStrategy: MWPS_MERGE_STRATEGY, - }, - service: new MRWidgetService({}), - }); }); afterEach(() => { - vm.$destroy(); window.gl = oldWindowGl; + wrapper.destroy(); + wrapper = null; }); - describe('computed', () => { - describe('canRemoveSourceBranch', () => { - it('should return true when user is able to remove source branch', () => { - expect(vm.canRemoveSourceBranch).toBeTruthy(); + [true, false].forEach((mergeRequestWidgetGraphql) => { + describe(`when graphql is ${mergeRequestWidgetGraphql ? 'enabled' : 'disabled'}`, () => { + beforeEach(() => { + mergeRequestWidgetGraphqlEnabled = mergeRequestWidgetGraphql; }); - it('should return false when user id is not the same with who set the MWPS', () => { - vm.mr.mergeUserId = 2; - - expect(vm.canRemoveSourceBranch).toBeFalsy(); - - vm.mr.currentUserId = 2; - - expect(vm.canRemoveSourceBranch).toBeTruthy(); + describe('computed', () => { + describe('canRemoveSourceBranch', () => { + it('should return true when user is able to remove source branch', () => { + factory({ + ...defaultMrProps(), + }); - vm.mr.currentUserId = 3; + expect(wrapper.findByTestId('removeSourceBranchButton').exists()).toBe(true); + }); - expect(vm.canRemoveSourceBranch).toBeFalsy(); - }); + it.each` + mergeUserId | currentUserId + ${2} | ${1} + ${1} | ${2} + `( + 'should return false when user id is not the same with who set the MWPS', + ({ mergeUserId, currentUserId }) => { + factory({ + ...defaultMrProps(), + mergeUserId, + currentUserId, + }); + + expect(wrapper.findByTestId('removeSourceBranchButton').exists()).toBe(false); + }, + ); - it('should return false when shouldRemoveSourceBranch set to false', () => { - vm.mr.shouldRemoveSourceBranch = true; + it('should return false when shouldRemoveSourceBranch set to false', () => { + factory({ + ...defaultMrProps(), + shouldRemoveSourceBranch: true, + }); - expect(vm.canRemoveSourceBranch).toBeFalsy(); - }); + expect(wrapper.findByTestId('removeSourceBranchButton').exists()).toBe(false); + }); - it('should return false if user is not able to remove the source branch', () => { - vm.mr.canRemoveSourceBranch = false; + it('should return false if user is not able to remove the source branch', () => { + factory({ + ...defaultMrProps(), + canRemoveSourceBranch: false, + }); - expect(vm.canRemoveSourceBranch).toBeFalsy(); - }); - }); + expect(wrapper.findByTestId('removeSourceBranchButton').exists()).toBe(false); + }); + }); - describe('statusTextBeforeAuthor', () => { - it('should return "Set by" if the MWPS is selected', () => { - Vue.set(vm.mr, 'autoMergeStrategy', MWPS_MERGE_STRATEGY); + describe('statusTextBeforeAuthor', () => { + it('should return "Set by" if the MWPS is selected', () => { + factory({ + ...defaultMrProps(), + autoMergeStrategy: MWPS_MERGE_STRATEGY, + }); - expect(vm.statusTextBeforeAuthor).toBe('Set by'); - }); - }); + expect(wrapper.findByTestId('beforeStatusText').text()).toBe('Set by'); + }); + }); - describe('statusTextAfterAuthor', () => { - it('should return "to be merged automatically..." if MWPS is selected', () => { - Vue.set(vm.mr, 'autoMergeStrategy', MWPS_MERGE_STRATEGY); + describe('statusTextAfterAuthor', () => { + it('should return "to be merged automatically..." if MWPS is selected', () => { + factory({ + ...defaultMrProps(), + autoMergeStrategy: MWPS_MERGE_STRATEGY, + }); - expect(vm.statusTextAfterAuthor).toBe( - 'to be merged automatically when the pipeline succeeds', - ); - }); - }); + expect(wrapper.findByTestId('afterStatusText').text()).toBe( + 'to be merged automatically when the pipeline succeeds', + ); + }); + }); - describe('cancelButtonText', () => { - it('should return "Cancel automatic merge" if MWPS is selected', () => { - Vue.set(vm.mr, 'autoMergeStrategy', MWPS_MERGE_STRATEGY); + describe('cancelButtonText', () => { + it('should return "Cancel automatic merge" if MWPS is selected', () => { + factory({ + ...defaultMrProps(), + autoMergeStrategy: MWPS_MERGE_STRATEGY, + }); - expect(vm.cancelButtonText).toBe('Cancel automatic merge'); + expect(wrapper.findByTestId('cancelAutomaticMergeButton').text()).toBe( + 'Cancel automatic merge', + ); + }); + }); }); - }); - }); - describe('methods', () => { - describe('cancelAutomaticMerge', () => { - it('should set flag and call service then tell main component to update the widget with data', done => { - const mrObj = { - is_new_mr_data: true, - }; - jest.spyOn(vm.service, 'cancelAutomaticMerge').mockReturnValue( - new Promise(resolve => { - resolve({ - data: mrObj, + describe('methods', () => { + describe('cancelAutomaticMerge', () => { + it('should set flag and call service then tell main component to update the widget with data', (done) => { + factory({ + ...defaultMrProps(), }); - }), - ); - - vm.cancelAutomaticMerge(); - setImmediate(() => { - expect(vm.isCancellingAutoMerge).toBeTruthy(); - expect(eventHub.$emit).toHaveBeenCalledWith('UpdateWidgetData', mrObj); - done(); + const mrObj = { + is_new_mr_data: true, + }; + jest.spyOn(wrapper.vm.service, 'cancelAutomaticMerge').mockReturnValue( + new Promise((resolve) => { + resolve({ + data: mrObj, + }); + }), + ); + + wrapper.vm.cancelAutomaticMerge(); + setImmediate(() => { + expect(wrapper.vm.isCancellingAutoMerge).toBeTruthy(); + expect(eventHub.$emit).toHaveBeenCalledWith('UpdateWidgetData', mrObj); + done(); + }); + }); }); - }); - }); - describe('removeSourceBranch', () => { - it('should set flag and call service then request main component to update the widget', done => { - jest.spyOn(vm.service, 'merge').mockReturnValue( - Promise.resolve({ - data: { - status: MWPS_MERGE_STRATEGY, - }, - }), - ); - - vm.removeSourceBranch(); - setImmediate(() => { - expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested'); - expect(vm.service.merge).toHaveBeenCalledWith({ - sha, - auto_merge_strategy: MWPS_MERGE_STRATEGY, - should_remove_source_branch: true, - }); - done(); + describe('removeSourceBranch', () => { + it('should set flag and call service then request main component to update the widget', (done) => { + factory({ + ...defaultMrProps(), + }); + jest.spyOn(wrapper.vm.service, 'merge').mockReturnValue( + Promise.resolve({ + data: { + status: MWPS_MERGE_STRATEGY, + }, + }), + ); + + wrapper.vm.removeSourceBranch(); + setImmediate(() => { + expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested'); + expect(wrapper.vm.service.merge).toHaveBeenCalledWith({ + sha, + auto_merge_strategy: MWPS_MERGE_STRATEGY, + should_remove_source_branch: true, + }); + done(); + }); + }); }); }); - }); - }); - describe('template', () => { - it('should have correct elements', () => { - expect(vm.$el.classList.contains('mr-widget-body')).toBeTruthy(); - expect(vm.$el.innerText).toContain('to be merged automatically when the pipeline succeeds'); + describe('template', () => { + it('should have correct elements', () => { + factory({ + ...defaultMrProps(), + }); - expect(vm.$el.innerText).toContain('The changes will be merged into'); - expect(vm.$el.innerText).toContain(targetBranch); - expect(vm.$el.innerText).toContain('The source branch will not be deleted'); - expect(vm.$el.querySelector('.js-cancel-auto-merge').innerText).toContain( - 'Cancel automatic merge', - ); + expect(wrapper.element).toMatchSnapshot(); + }); - expect(vm.$el.querySelector('.js-cancel-auto-merge').getAttribute('disabled')).toBeFalsy(); - expect(vm.$el.querySelector('.js-remove-source-branch').innerText).toContain( - 'Delete source branch', - ); + it('should disable cancel auto merge button when the action is in progress', async () => { + factory({ + ...defaultMrProps(), + }); + wrapper.setData({ + isCancellingAutoMerge: true, + }); - expect(vm.$el.querySelector('.js-remove-source-branch').getAttribute('disabled')).toBeFalsy(); - }); + await nextTick(); - it('should disable cancel auto merge button when the action is in progress', done => { - vm.isCancellingAutoMerge = true; + expect(wrapper.find('.js-cancel-auto-merge').attributes('disabled')).toBe('disabled'); + }); - Vue.nextTick(() => { - expect(vm.$el.querySelector('.js-cancel-auto-merge').getAttribute('disabled')).toBeTruthy(); - done(); - }); - }); + it('should show source branch will be deleted text when it source branch set to remove', () => { + factory({ + ...defaultMrProps(), + shouldRemoveSourceBranch: true, + }); - it('should show source branch will be deleted text when it source branch set to remove', done => { - vm.mr.shouldRemoveSourceBranch = true; + const normalizedText = wrapper.text().replace(/\s+/g, ' '); - Vue.nextTick(() => { - const normalizedText = vm.$el.innerText.replace(/\s+/g, ' '); + expect(normalizedText).toContain('The source branch will be deleted'); + expect(normalizedText).not.toContain('The source branch will not be deleted'); + }); - expect(normalizedText).toContain('The source branch will be deleted'); - expect(normalizedText).not.toContain('The source branch will not be deleted'); - done(); - }); - }); + it('should not show delete source branch button when user not able to delete source branch', () => { + factory({ + ...defaultMrProps(), + currentUserId: 4, + }); - it('should not show delete source branch button when user not able to delete source branch', done => { - vm.mr.currentUserId = 4; + expect(wrapper.find('.js-remove-source-branch').exists()).toBe(false); + }); - Vue.nextTick(() => { - expect(vm.$el.querySelector('.js-remove-source-branch')).toEqual(null); - done(); - }); - }); + it('should disable delete source branch button when the action is in progress', async () => { + factory({ + ...defaultMrProps(), + }); + wrapper.setData({ + isRemovingSourceBranch: true, + }); - it('should disable delete source branch button when the action is in progress', done => { - vm.isRemovingSourceBranch = true; + await nextTick(); - Vue.nextTick(() => { - expect( - vm.$el.querySelector('.js-remove-source-branch').getAttribute('disabled'), - ).toBeTruthy(); - done(); - }); - }); + expect(wrapper.find('.js-remove-source-branch').attributes('disabled')).toBe('disabled'); + }); - it('should render the status text as "...to merged automatically" if MWPS is selected', done => { - Vue.set(vm.mr, 'autoMergeStrategy', MWPS_MERGE_STRATEGY); + it('should render the status text as "...to merged automatically" if MWPS is selected', () => { + factory({ + ...defaultMrProps(), + autoMergeStrategy: MWPS_MERGE_STRATEGY, + }); - Vue.nextTick(() => { - const statusText = trimText(vm.$el.querySelector('.js-status-text-after-author').innerText); + const statusText = trimText(wrapper.find('.js-status-text-after-author').text()); - expect(statusText).toBe('to be merged automatically when the pipeline succeeds'); - done(); - }); - }); + expect(statusText).toBe('to be merged automatically when the pipeline succeeds'); + }); - it('should render the cancel button as "Cancel automatic merge" if MWPS is selected', done => { - Vue.set(vm.mr, 'autoMergeStrategy', MWPS_MERGE_STRATEGY); + it('should render the cancel button as "Cancel automatic merge" if MWPS is selected', () => { + factory({ + ...defaultMrProps(), + autoMergeStrategy: MWPS_MERGE_STRATEGY, + }); - Vue.nextTick(() => { - const cancelButtonText = trimText(vm.$el.querySelector('.js-cancel-auto-merge').innerText); + const cancelButtonText = trimText(wrapper.find('.js-cancel-auto-merge').text()); - expect(cancelButtonText).toBe('Cancel automatic merge'); - done(); + expect(cancelButtonText).toBe('Cancel automatic merge'); + }); }); }); }); diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js index aae9b8660e2..dca3798f7ea 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js @@ -1,3 +1,4 @@ +import { nextTick } from 'vue'; import { shallowMount } from '@vue/test-utils'; import { GlLoadingIcon, GlButton } from '@gitlab/ui'; import AutoMergeFailedComponent from '~/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue'; @@ -8,43 +9,60 @@ describe('MRWidgetAutoMergeFailed', () => { const mergeError = 'This is the merge error'; const findButton = () => wrapper.find(GlButton); - const createComponent = (props = {}) => { + const createComponent = (props = {}, mergeRequestWidgetGraphql = false) => { wrapper = shallowMount(AutoMergeFailedComponent, { propsData: { ...props }, - }); - }; + data() { + if (mergeRequestWidgetGraphql) { + return { mergeError: props.mr?.mergeError }; + } - beforeEach(() => { - createComponent({ - mr: { mergeError }, + return {}; + }, + provide: { + glFeatures: { mergeRequestWidgetGraphql }, + }, }); - }); + }; afterEach(() => { wrapper.destroy(); }); - it('renders failed message', () => { - expect(wrapper.text()).toContain('This merge request failed to be merged automatically'); - }); + [true, false].forEach((mergeRequestWidgetGraphql) => { + describe(`when graphql is ${mergeRequestWidgetGraphql ? 'enabled' : 'dislabed'}`, () => { + beforeEach(() => { + createComponent( + { + mr: { mergeError }, + }, + mergeRequestWidgetGraphql, + ); + }); - it('renders merge error provided', () => { - expect(wrapper.text()).toContain(mergeError); - }); + it('renders failed message', () => { + expect(wrapper.text()).toContain('This merge request failed to be merged automatically'); + }); - it('render refresh button', () => { - expect(findButton().text()).toEqual('Refresh'); - }); + it('renders merge error provided', () => { + expect(wrapper.text()).toContain(mergeError); + }); + + it('render refresh button', () => { + expect(findButton().text()).toBe('Refresh'); + }); + + it('emits event and shows loading icon when button is clicked', async () => { + jest.spyOn(eventHub, '$emit'); + findButton().vm.$emit('click'); - it('emits event and shows loading icon when button is clicked', () => { - jest.spyOn(eventHub, '$emit'); - findButton().vm.$emit('click'); + expect(eventHub.$emit.mock.calls[0][0]).toBe('MRWidgetUpdateRequested'); - expect(eventHub.$emit.mock.calls[0][0]).toBe('MRWidgetUpdateRequested'); + await nextTick(); - return wrapper.vm.$nextTick(() => { - expect(findButton().attributes('disabled')).toBe('true'); - expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); + expect(findButton().attributes('disabled')).toBe('true'); + expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); + }); }); }); }); diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_closed_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_closed_spec.js index 322f440763c..55d7e2391b2 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_closed_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_closed_spec.js @@ -39,10 +39,7 @@ describe('MRWidgetClosed', () => { it('renders closed by information with author and time', () => { expect( - vm.$el - .querySelector('.js-mr-widget-author') - .textContent.trim() - .replace(/\s\s+/g, ' '), + vm.$el.querySelector('.js-mr-widget-author').textContent.trim().replace(/\s\s+/g, ' '), ).toContain('Closed by Administrator less than a minute ago'); }); @@ -54,10 +51,7 @@ describe('MRWidgetClosed', () => { it('renders information about the changes not being merged', () => { expect( - vm.$el - .querySelector('.mr-info-list') - .textContent.trim() - .replace(/\s\s+/g, ' '), + vm.$el.querySelector('.mr-info-list').textContent.trim().replace(/\s\s+/g, ' '), ).toContain('The changes were not merged into so_long_jquery'); }); diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_commit_message_dropdown_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_commit_message_dropdown_spec.js index 56832f82b05..706d60368b5 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_commit_message_dropdown_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_commit_message_dropdown_spec.js @@ -47,7 +47,8 @@ describe('Commits message dropdown component', () => { }); it('should have correct message for the first dropdown list element', () => { - expect(findFirstDropdownElement().text()).toBe('78d5b7 Commit 1'); + expect(findFirstDropdownElement().text()).toContain('78d5b7'); + expect(findFirstDropdownElement().text()).toContain('Commit 1'); }); it('should emit a commit title on selecting commit', () => { diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_commits_header_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_commits_header_spec.js index 62fc3330444..e4123b2ca83 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_commits_header_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_commits_header_spec.js @@ -4,7 +4,7 @@ import CommitsHeader from '~/vue_merge_request_widget/components/states/commits_ describe('Commits header component', () => { let wrapper; - const createComponent = props => { + const createComponent = (props) => { wrapper = shallowMount(CommitsHeader, { propsData: { isSquashEnabled: false, @@ -98,6 +98,8 @@ describe('Commits header component', () => { }); it('does has merge commit part of the message', () => { + createComponent(); + expect(findHeaderWrapper().text()).toContain('1 merge commit'); }); }); @@ -108,21 +110,21 @@ describe('Commits header component', () => { wrapper.setData({ expanded: true }); }); - it('toggle has aria-label equal to collapse', done => { + it('toggle has aria-label equal to collapse', (done) => { wrapper.vm.$nextTick(() => { expect(findCommitToggle().attributes('aria-label')).toBe('Collapse'); done(); }); }); - it('has a chevron-down icon', done => { + it('has a chevron-down icon', (done) => { wrapper.vm.$nextTick(() => { expect(findCommitToggle().props('icon')).toBe('chevron-down'); done(); }); }); - it('has a collapse text', done => { + it('has a collapse text', (done) => { wrapper.vm.$nextTick(() => { expect(findHeaderWrapper().text()).toBe('Collapse'); done(); diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js index ad21e6e6f4f..f5a059698b6 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js @@ -54,7 +54,7 @@ describe('MRWidgetConflicts', () => { vm.destroy(); }); - [false, true].forEach(featureEnabled => { + [false, true].forEach((featureEnabled) => { describe(`with GraphQL feature flag ${featureEnabled ? 'enabled' : 'disabled'}`, () => { beforeEach(() => { mergeRequestWidgetGraphql = featureEnabled; @@ -167,12 +167,9 @@ describe('MRWidgetConflicts', () => { }, }); - expect( - vm - .text() - .trim() - .replace(/\s\s+/g, ' '), - ).toContain('ask someone with write access'); + expect(vm.text().trim().replace(/\s\s+/g, ' ')).toContain( + 'ask someone with write access', + ); }); it('should not have action buttons', async () => { diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js index 6778a8f4a1f..48c1a9eedf9 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js @@ -49,7 +49,7 @@ describe('MRWidgetFailedToMerge', () => { }); describe('mergeError', () => { - it('removes forced line breaks', done => { + it('removes forced line breaks', (done) => { mr.mergeError = 'contains<br />line breaks<br />'; Vue.nextTick() @@ -98,7 +98,7 @@ describe('MRWidgetFailedToMerge', () => { }); describe('while it is refreshing', () => { - it('renders Refresing now', done => { + it('renders Refresing now', (done) => { vm.isRefreshing = true; Vue.nextTick(() => { @@ -139,7 +139,7 @@ describe('MRWidgetFailedToMerge', () => { }); }); - it('should just generic merge failed message if merge_error is not available', done => { + it('should just generic merge failed message if merge_error is not available', (done) => { vm.mr.mergeError = null; Vue.nextTick(() => { @@ -149,7 +149,7 @@ describe('MRWidgetFailedToMerge', () => { }); }); - it('should show refresh label when refresh requested', done => { + it('should show refresh label when refresh requested', (done) => { vm.refresh(); Vue.nextTick(() => { expect(vm.$el.innerText).not.toContain('Merge failed. Refreshing'); diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_merged_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_merged_spec.js index 9b51e8583ba..36c4174c03d 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_merged_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_merged_spec.js @@ -123,9 +123,9 @@ describe('MRWidgetMerged', () => { describe('methods', () => { describe('removeSourceBranch', () => { - it('should set flag and call service then request main component to update the widget', done => { + it('should set flag and call service then request main component to update the widget', (done) => { jest.spyOn(vm.service, 'removeSourceBranch').mockReturnValue( - new Promise(resolve => { + new Promise((resolve) => { resolve({ data: { message: 'Branch was deleted', @@ -173,7 +173,7 @@ describe('MRWidgetMerged', () => { ); }); - it('hides button to copy commit SHA if SHA does not exist', done => { + it('hides button to copy commit SHA if SHA does not exist', (done) => { vm.mr.mergeCommitSha = null; Vue.nextTick(() => { @@ -189,7 +189,7 @@ describe('MRWidgetMerged', () => { expect(selectors.mergeCommitShaLink.href).toBe(vm.mr.mergeCommitPath); }); - it('should not show source branch deleted text', done => { + it('should not show source branch deleted text', (done) => { vm.mr.sourceBranchRemoved = false; Vue.nextTick(() => { @@ -199,7 +199,7 @@ describe('MRWidgetMerged', () => { }); }); - it('should show source branch deleting text', done => { + it('should show source branch deleting text', (done) => { vm.mr.isRemovingSourceBranch = true; vm.mr.sourceBranchRemoved = false; diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js index f45368bf443..848677bf4d2 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js @@ -25,7 +25,7 @@ describe('MRWidgetMissingBranch', () => { wrapper.destroy(); }); - [true, false].forEach(mergeRequestWidgetGraphql => { + [true, false].forEach((mergeRequestWidgetGraphql) => { describe(`widget GraphQL feature flag is ${ mergeRequestWidgetGraphql ? 'enabled' : 'disabled' }`, () => { diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js index 9057ffaea45..8eddf59820c 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js @@ -20,7 +20,7 @@ jest.mock('~/commons/nav/user_merge_requests', () => ({ const commitMessage = 'This is the commit message'; const squashCommitMessage = 'This is the squash commit message'; const commitMessageWithDescription = 'This is the commit message description'; -const createTestMr = customConfig => { +const createTestMr = (customConfig) => { const mr = { isPipelineActive: false, pipeline: null, @@ -346,8 +346,8 @@ describe('ReadyToMerge', () => { }); describe('handleMergeButtonClick', () => { - const returnPromise = status => - new Promise(resolve => { + const returnPromise = (status) => + new Promise((resolve) => { resolve({ data: { status, @@ -355,7 +355,7 @@ describe('ReadyToMerge', () => { }); }); - it('should handle merge when pipeline succeeds', done => { + it('should handle merge when pipeline succeeds', (done) => { jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); jest .spyOn(vm.service, 'merge') @@ -381,7 +381,7 @@ describe('ReadyToMerge', () => { }); }); - it('should handle merge failed', done => { + it('should handle merge failed', (done) => { jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); jest.spyOn(vm.service, 'merge').mockReturnValue(returnPromise('failed')); vm.handleMergeButtonClick(false, true); @@ -398,7 +398,7 @@ describe('ReadyToMerge', () => { }); }); - it('should handle merge action accepted case', done => { + it('should handle merge action accepted case', (done) => { jest.spyOn(vm.service, 'merge').mockReturnValue(returnPromise('success')); jest.spyOn(vm, 'initiateMergePolling').mockImplementation(() => {}); vm.handleMergeButtonClick(); @@ -433,8 +433,8 @@ describe('ReadyToMerge', () => { }); describe('handleMergePolling', () => { - const returnPromise = state => - new Promise(resolve => { + const returnPromise = (state) => + new Promise((resolve) => { resolve({ data: { state, @@ -447,7 +447,7 @@ describe('ReadyToMerge', () => { loadFixtures('merge_requests/merge_request_of_current_user.html'); }); - it('should call start and stop polling when MR merged', done => { + it('should call start and stop polling when MR merged', (done) => { jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); jest.spyOn(vm.service, 'poll').mockReturnValue(returnPromise('merged')); jest.spyOn(vm, 'initiateRemoveSourceBranchPolling').mockImplementation(() => {}); @@ -476,11 +476,14 @@ describe('ReadyToMerge', () => { }); }); - it('updates status box', done => { + it('updates status box', (done) => { jest.spyOn(vm.service, 'poll').mockReturnValue(returnPromise('merged')); jest.spyOn(vm, 'initiateRemoveSourceBranchPolling').mockImplementation(() => {}); - vm.handleMergePolling(() => {}, () => {}); + vm.handleMergePolling( + () => {}, + () => {}, + ); setImmediate(() => { const statusBox = document.querySelector('.status-box'); @@ -492,11 +495,14 @@ describe('ReadyToMerge', () => { }); }); - it('updates merge request count badge', done => { + it('updates merge request count badge', (done) => { jest.spyOn(vm.service, 'poll').mockReturnValue(returnPromise('merged')); jest.spyOn(vm, 'initiateRemoveSourceBranchPolling').mockImplementation(() => {}); - vm.handleMergePolling(() => {}, () => {}); + vm.handleMergePolling( + () => {}, + () => {}, + ); setImmediate(() => { expect(document.querySelector('.js-merge-counter').textContent).toBe('0'); @@ -505,7 +511,7 @@ describe('ReadyToMerge', () => { }); }); - it('should continue polling until MR is merged', done => { + it('should continue polling until MR is merged', (done) => { jest.spyOn(vm.service, 'poll').mockReturnValue(returnPromise('some_other_state')); jest.spyOn(vm, 'initiateRemoveSourceBranchPolling').mockImplementation(() => {}); @@ -541,8 +547,8 @@ describe('ReadyToMerge', () => { }); describe('handleRemoveBranchPolling', () => { - const returnPromise = state => - new Promise(resolve => { + const returnPromise = (state) => + new Promise((resolve) => { resolve({ data: { source_branch_exists: state, @@ -550,7 +556,7 @@ describe('ReadyToMerge', () => { }); }); - it('should call start and stop polling when MR merged', done => { + it('should call start and stop polling when MR merged', (done) => { jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); jest.spyOn(vm.service, 'poll').mockReturnValue(returnPromise(false)); @@ -583,7 +589,7 @@ describe('ReadyToMerge', () => { }); }); - it('should continue polling until MR is merged', done => { + it('should continue polling until MR is merged', (done) => { jest.spyOn(vm.service, 'poll').mockReturnValue(returnPromise(true)); let cpc = false; // continuePollingCalled @@ -657,10 +663,7 @@ describe('ReadyToMerge', () => { const findCommitsHeaderElement = () => wrapper.find(CommitsHeader); const findCommitEditElements = () => wrapper.findAll(CommitEdit); const findCommitDropdownElement = () => wrapper.find(CommitMessageDropdown); - const findFirstCommitEditLabel = () => - findCommitEditElements() - .at(0) - .props('label'); + const findFirstCommitEditLabel = () => findCommitEditElements().at(0).props('label'); describe('squash checkbox', () => { it('should be rendered when squash before merge is enabled and there is more than 1 commit', () => { diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js index f9490ac77ff..cc160f6182d 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js @@ -8,7 +8,7 @@ const localVue = createLocalVue(); describe('Squash before merge component', () => { let wrapper; - const createComponent = props => { + const createComponent = (props) => { wrapper = shallowMount(localVue.extend(SquashBeforeMerge), { localVue, propsData: { diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_wip_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_wip_spec.js index 907906ebe98..6d63d4b1be3 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_wip_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_wip_spec.js @@ -47,12 +47,12 @@ describe('Wip', () => { }; describe('handleRemoveWIP', () => { - it('should make a request to service and handle response', done => { + it('should make a request to service and handle response', (done) => { const vm = createComponent(); jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); jest.spyOn(vm.service, 'removeWIP').mockReturnValue( - new Promise(resolve => { + new Promise((resolve) => { resolve({ data: mrObj, }); @@ -92,7 +92,7 @@ describe('Wip', () => { ); }); - it('should not show removeWIP button is user cannot update MR', done => { + it('should not show removeWIP button is user cannot update MR', (done) => { vm.mr.removeWIPPath = ''; Vue.nextTick(() => { diff --git a/spec/frontend/vue_mr_widget/components/terraform/mr_widget_terraform_container_spec.js b/spec/frontend/vue_mr_widget/components/terraform/mr_widget_terraform_container_spec.js index 7fe6b44ecc7..8da0d0f16d6 100644 --- a/spec/frontend/vue_mr_widget/components/terraform/mr_widget_terraform_container_spec.js +++ b/spec/frontend/vue_mr_widget/components/terraform/mr_widget_terraform_container_spec.js @@ -15,7 +15,7 @@ describe('MrWidgetTerraformConainer', () => { const propsData = { endpoint: '/path/to/terraform/report.json' }; const findHeader = () => wrapper.find('[data-testid="terraform-header-text"]'); - const findPlans = () => wrapper.findAll(TerraformPlan).wrappers.map(x => x.props('plan')); + const findPlans = () => wrapper.findAll(TerraformPlan).wrappers.map((x) => x.props('plan')); const mockPollingApi = (response, body, header) => { mock.onGet(propsData.endpoint).reply(response, body, header); diff --git a/spec/frontend/vue_mr_widget/components/terraform/terraform_plan_spec.js b/spec/frontend/vue_mr_widget/components/terraform/terraform_plan_spec.js index cc68ba0d9df..ea4eb44ebfe 100644 --- a/spec/frontend/vue_mr_widget/components/terraform/terraform_plan_spec.js +++ b/spec/frontend/vue_mr_widget/components/terraform/terraform_plan_spec.js @@ -14,7 +14,7 @@ describe('TerraformPlan', () => { const findIcon = () => wrapper.find('[data-testid="change-type-icon"]'); const findLogButton = () => wrapper.find('[data-testid="terraform-report-link"]'); - const mountWrapper = propsData => { + const mountWrapper = (propsData) => { wrapper = shallowMount(TerraformPlan, { stubs: { GlLink, GlSprintf }, propsData }); }; |