diff options
author | Robert Speicher <rspeicher@gmail.com> | 2021-01-20 22:34:23 +0300 |
---|---|---|
committer | Robert Speicher <rspeicher@gmail.com> | 2021-01-20 22:34:23 +0300 |
commit | 6438df3a1e0fb944485cebf07976160184697d72 (patch) | |
tree | 00b09bfd170e77ae9391b1a2f5a93ef6839f2597 /spec/frontend/vue_mr_widget | |
parent | 42bcd54d971da7ef2854b896a7b34f4ef8601067 (diff) |
Add latest changes from gitlab-org/gitlab@13-8-stable-eev13.8.0-rc42
Diffstat (limited to 'spec/frontend/vue_mr_widget')
38 files changed, 1019 insertions, 739 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 }); }; diff --git a/spec/frontend/vue_mr_widget/deployment/deployment_spec.js b/spec/frontend/vue_mr_widget/deployment/deployment_spec.js index 19a5566c3b1..87bf32351bd 100644 --- a/spec/frontend/vue_mr_widget/deployment/deployment_spec.js +++ b/spec/frontend/vue_mr_widget/deployment/deployment_spec.js @@ -116,7 +116,7 @@ describe('Deployment component', () => { if (actionButtons.length > 0) { describe('renders the expected button group', () => { - actionButtons.forEach(button => { + actionButtons.forEach((button) => { it(`renders ${button}`, () => { expect(wrapper.find(button).exists()).toBe(true); }); @@ -126,7 +126,7 @@ describe('Deployment component', () => { if (actionButtons.length === 0) { describe('does not render the button group', () => { - defaultGroup.forEach(button => { + defaultGroup.forEach((button) => { it(`does not render ${button}`, () => { expect(wrapper.find(button).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 index a12757d4cce..a5d91468ef2 100644 --- a/spec/frontend/vue_mr_widget/deployment/deployment_view_button_spec.js +++ b/spec/frontend/vue_mr_widget/deployment/deployment_view_button_spec.js @@ -90,7 +90,7 @@ describe('Deployment View App button', () => { 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); + const expectedUrls = deploymentMockData.changes.map((change) => change.external_url); expectedUrls.forEach((expectedUrl, idx) => { const deployUrl = allUrls[idx]; diff --git a/spec/frontend/vue_mr_widget/mock_data.js b/spec/frontend/vue_mr_widget/mock_data.js index 8ee920f06a1..aa2345abccf 100644 --- a/spec/frontend/vue_mr_widget/mock_data.js +++ b/spec/frontend/vue_mr_widget/mock_data.js @@ -1,5 +1,20 @@ import { SUCCESS } from '~/vue_merge_request_widget/components/deployment/constants'; +export const artifacts = [ + { + text: 'result.txt', + url: 'bar', + job_name: 'generate-artifact', + job_path: 'bar', + }, + { + text: 'foo.txt', + url: 'foo', + job_name: 'foo-artifact', + job_path: 'foo', + }, +]; + export default { id: 132, iid: 22, @@ -84,6 +99,7 @@ export default { coverage: '92.16', path: '/root/acets-app/pipelines/172', details: { + artifacts, status: { icon: 'status_success', favicon: 'favicon_status_success', @@ -127,7 +143,6 @@ export default { dropdown_path: '/root/acets-app/pipelines/172/stage.json?stage=review', }, ], - artifacts: [], manual_actions: [ { name: 'stop_review', @@ -194,7 +209,10 @@ export default { updated_at: '2017-04-07T15:28:44.800Z', }, pipelineCoverageDelta: '15.25', - buildsWithCoverage: [{ name: 'karma', coverage: '40.2' }, { name: 'rspec', coverage: '80.4' }], + buildsWithCoverage: [ + { name: 'karma', coverage: '40.2' }, + { name: 'rspec', coverage: '80.4' }, + ], work_in_progress: false, source_branch_exists: false, mergeable_discussions_state: true, @@ -272,6 +290,7 @@ export const mockStore = { pipeline: { id: 0, details: { + artifacts, status: { details_path: '/root/review-app-tester/pipelines/66', favicon: @@ -291,6 +310,7 @@ export const mockStore = { mergePipeline: { id: 1, details: { + artifacts, status: { details_path: '/root/review-app-tester/pipelines/66', favicon: diff --git a/spec/frontend/vue_mr_widget/mr_widget_how_to_merge_modal_spec.js b/spec/frontend/vue_mr_widget/mr_widget_how_to_merge_modal_spec.js index aaaee3327a8..bd22183cbea 100644 --- a/spec/frontend/vue_mr_widget/mr_widget_how_to_merge_modal_spec.js +++ b/spec/frontend/vue_mr_widget/mr_widget_how_to_merge_modal_spec.js @@ -50,19 +50,11 @@ describe('MRWidgetHowToMerge', () => { it('should render different instructions based on if the user can merge', () => { mountComponent({ props: { canMerge: true } }); - expect( - findInstructionsFields() - .at(2) - .text(), - ).toContain('git push origin'); + expect(findInstructionsFields().at(2).text()).toContain('git push origin'); }); it('should render different instructions based on if the merge is based off a fork', () => { mountComponent({ props: { isFork: true } }); - expect( - findInstructionsFields() - .at(0) - .text(), - ).toContain('FETCH_HEAD'); + expect(findInstructionsFields().at(0).text()).toContain('FETCH_HEAD'); }); }); diff --git a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js index a20cd5b4400..1ea7fe1fbfe 100644 --- a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js +++ b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js @@ -1,12 +1,13 @@ -import Vue from 'vue'; +import { mount } from '@vue/test-utils'; +import Vue, { nextTick } from 'vue'; import MockAdapter from 'axios-mock-adapter'; -import mountComponent from 'helpers/vue_mount_component_helper'; import Api from '~/api'; import axios from '~/lib/utils/axios_utils'; -import mrWidgetOptions from '~/vue_merge_request_widget/mr_widget_options.vue'; +import MrWidgetOptions from '~/vue_merge_request_widget/mr_widget_options.vue'; import eventHub from '~/vue_merge_request_widget/event_hub'; import notify from '~/lib/utils/notify'; import SmartInterval from '~/smart_interval'; +import { setFaviconOverlay } from '~/lib/utils/favicon'; import { stateKey } from '~/vue_merge_request_widget/stores/state_maps'; import mockData from './mock_data'; import { faviconDataUrl, overlayDataUrl } from '../lib/utils/mock_data'; @@ -14,58 +15,56 @@ import { SUCCESS } from '~/vue_merge_request_widget/components/deployment/consta jest.mock('~/smart_interval'); -const returnPromise = data => - new Promise(resolve => { +jest.mock('~/lib/utils/favicon'); + +const returnPromise = (data) => + new Promise((resolve) => { resolve({ data, }); }); -describe('mrWidgetOptions', () => { - let vm; +describe('MrWidgetOptions', () => { + let wrapper; let mock; - let MrWidgetOptions; const COLLABORATION_MESSAGE = 'Allows commits from members who can merge to the target branch'; beforeEach(() => { - // Prevent component mounting - delete mrWidgetOptions.el; - gl.mrWidgetData = { ...mockData }; gon.features = { asyncMrWidget: true }; mock = new MockAdapter(axios); mock.onGet(mockData.merge_request_widget_path).reply(() => [200, { ...mockData }]); mock.onGet(mockData.merge_request_cached_widget_path).reply(() => [200, { ...mockData }]); - - MrWidgetOptions = Vue.extend(mrWidgetOptions); }); afterEach(() => { mock.restore(); - vm.$destroy(); - vm = null; + wrapper.destroy(); + wrapper = null; gl.mrWidgetData = {}; gon.features = {}; }); const createComponent = (mrData = mockData) => { - if (vm) { - vm.$destroy(); + if (wrapper) { + wrapper.destroy(); } - vm = mountComponent(MrWidgetOptions, { - mrData: { ...mrData }, + wrapper = mount(MrWidgetOptions, { + propsData: { + mrData: { ...mrData }, + }, }); return axios.waitForAll(); }; - const findSuggestPipeline = () => vm.$el.querySelector('[data-testid="mr-suggest-pipeline"]'); - const findSuggestPipelineButton = () => findSuggestPipeline().querySelector('button'); - const findSecurityMrWidget = () => vm.$el.querySelector('[data-testid="security-mr-widget"]'); + const findSuggestPipeline = () => wrapper.find('[data-testid="mr-suggest-pipeline"]'); + const findSuggestPipelineButton = () => findSuggestPipeline().find('button'); + const findSecurityMrWidget = () => wrapper.find('[data-testid="security-mr-widget"]'); describe('default', () => { beforeEach(() => { @@ -74,147 +73,147 @@ describe('mrWidgetOptions', () => { describe('data', () => { it('should instantiate Store and Service', () => { - expect(vm.mr).toBeDefined(); - expect(vm.service).toBeDefined(); + expect(wrapper.vm.mr).toBeDefined(); + expect(wrapper.vm.service).toBeDefined(); }); }); describe('computed', () => { describe('componentName', () => { it('should return merged component', () => { - expect(vm.componentName).toEqual('mr-widget-merged'); + expect(wrapper.vm.componentName).toEqual('mr-widget-merged'); }); it('should return conflicts component', () => { - vm.mr.state = 'conflicts'; + wrapper.vm.mr.state = 'conflicts'; - expect(vm.componentName).toEqual('mr-widget-conflicts'); + expect(wrapper.vm.componentName).toEqual('mr-widget-conflicts'); }); }); describe('shouldRenderMergeHelp', () => { it('should return false for the initial merged state', () => { - expect(vm.shouldRenderMergeHelp).toBeFalsy(); + expect(wrapper.vm.shouldRenderMergeHelp).toBeFalsy(); }); it('should return true for a state which requires help widget', () => { - vm.mr.state = 'conflicts'; + wrapper.vm.mr.state = 'conflicts'; - expect(vm.shouldRenderMergeHelp).toBeTruthy(); + expect(wrapper.vm.shouldRenderMergeHelp).toBeTruthy(); }); }); describe('shouldRenderPipelines', () => { it('should return true when hasCI is true', () => { - vm.mr.hasCI = true; + wrapper.vm.mr.hasCI = true; - expect(vm.shouldRenderPipelines).toBeTruthy(); + expect(wrapper.vm.shouldRenderPipelines).toBeTruthy(); }); it('should return false when hasCI is false', () => { - vm.mr.hasCI = false; + wrapper.vm.mr.hasCI = false; - expect(vm.shouldRenderPipelines).toBeFalsy(); + expect(wrapper.vm.shouldRenderPipelines).toBeFalsy(); }); }); describe('shouldRenderRelatedLinks', () => { it('should return false for the initial data', () => { - expect(vm.shouldRenderRelatedLinks).toBeFalsy(); + expect(wrapper.vm.shouldRenderRelatedLinks).toBeFalsy(); }); it('should return true if there is relatedLinks in MR', () => { - Vue.set(vm.mr, 'relatedLinks', {}); + Vue.set(wrapper.vm.mr, 'relatedLinks', {}); - expect(vm.shouldRenderRelatedLinks).toBeTruthy(); + expect(wrapper.vm.shouldRenderRelatedLinks).toBeTruthy(); }); }); describe('shouldRenderSourceBranchRemovalStatus', () => { beforeEach(() => { - vm.mr.state = 'readyToMerge'; + wrapper.vm.mr.state = 'readyToMerge'; }); it('should return true when cannot remove source branch and branch will be removed', () => { - vm.mr.canRemoveSourceBranch = false; - vm.mr.shouldRemoveSourceBranch = true; + wrapper.vm.mr.canRemoveSourceBranch = false; + wrapper.vm.mr.shouldRemoveSourceBranch = true; - expect(vm.shouldRenderSourceBranchRemovalStatus).toEqual(true); + expect(wrapper.vm.shouldRenderSourceBranchRemovalStatus).toEqual(true); }); it('should return false when can remove source branch and branch will be removed', () => { - vm.mr.canRemoveSourceBranch = true; - vm.mr.shouldRemoveSourceBranch = true; + wrapper.vm.mr.canRemoveSourceBranch = true; + wrapper.vm.mr.shouldRemoveSourceBranch = true; - expect(vm.shouldRenderSourceBranchRemovalStatus).toEqual(false); + expect(wrapper.vm.shouldRenderSourceBranchRemovalStatus).toEqual(false); }); it('should return false when cannot remove source branch and branch will not be removed', () => { - vm.mr.canRemoveSourceBranch = false; - vm.mr.shouldRemoveSourceBranch = false; + wrapper.vm.mr.canRemoveSourceBranch = false; + wrapper.vm.mr.shouldRemoveSourceBranch = false; - expect(vm.shouldRenderSourceBranchRemovalStatus).toEqual(false); + expect(wrapper.vm.shouldRenderSourceBranchRemovalStatus).toEqual(false); }); it('should return false when in merged state', () => { - vm.mr.canRemoveSourceBranch = false; - vm.mr.shouldRemoveSourceBranch = true; - vm.mr.state = 'merged'; + wrapper.vm.mr.canRemoveSourceBranch = false; + wrapper.vm.mr.shouldRemoveSourceBranch = true; + wrapper.vm.mr.state = 'merged'; - expect(vm.shouldRenderSourceBranchRemovalStatus).toEqual(false); + expect(wrapper.vm.shouldRenderSourceBranchRemovalStatus).toEqual(false); }); it('should return false when in nothing to merge state', () => { - vm.mr.canRemoveSourceBranch = false; - vm.mr.shouldRemoveSourceBranch = true; - vm.mr.state = 'nothingToMerge'; + wrapper.vm.mr.canRemoveSourceBranch = false; + wrapper.vm.mr.shouldRemoveSourceBranch = true; + wrapper.vm.mr.state = 'nothingToMerge'; - expect(vm.shouldRenderSourceBranchRemovalStatus).toEqual(false); + expect(wrapper.vm.shouldRenderSourceBranchRemovalStatus).toEqual(false); }); }); describe('shouldRenderCollaborationStatus', () => { describe('when collaboration is allowed', () => { beforeEach(() => { - vm.mr.allowCollaboration = true; + wrapper.vm.mr.allowCollaboration = true; }); describe('when merge request is opened', () => { - beforeEach(done => { - vm.mr.isOpen = true; - vm.$nextTick(done); + beforeEach((done) => { + wrapper.vm.mr.isOpen = true; + nextTick(done); }); it('should render collaboration status', () => { - expect(vm.$el.textContent).toContain(COLLABORATION_MESSAGE); + expect(wrapper.text()).toContain(COLLABORATION_MESSAGE); }); }); describe('when merge request is not opened', () => { - beforeEach(done => { - vm.mr.isOpen = false; - vm.$nextTick(done); + beforeEach((done) => { + wrapper.vm.mr.isOpen = false; + nextTick(done); }); it('should not render collaboration status', () => { - expect(vm.$el.textContent).not.toContain(COLLABORATION_MESSAGE); + expect(wrapper.text()).not.toContain(COLLABORATION_MESSAGE); }); }); }); describe('when collaboration is not allowed', () => { beforeEach(() => { - vm.mr.allowCollaboration = false; + wrapper.vm.mr.allowCollaboration = false; }); describe('when merge request is opened', () => { - beforeEach(done => { - vm.mr.isOpen = true; - vm.$nextTick(done); + beforeEach((done) => { + wrapper.vm.mr.isOpen = true; + nextTick(done); }); it('should not render collaboration status', () => { - expect(vm.$el.textContent).not.toContain(COLLABORATION_MESSAGE); + expect(wrapper.text()).not.toContain(COLLABORATION_MESSAGE); }); }); }); @@ -222,56 +221,56 @@ describe('mrWidgetOptions', () => { describe('showMergePipelineForkWarning', () => { describe('when the source project and target project are the same', () => { - beforeEach(done => { - Vue.set(vm.mr, 'mergePipelinesEnabled', true); - Vue.set(vm.mr, 'sourceProjectId', 1); - Vue.set(vm.mr, 'targetProjectId', 1); - vm.$nextTick(done); + beforeEach((done) => { + Vue.set(wrapper.vm.mr, 'mergePipelinesEnabled', true); + Vue.set(wrapper.vm.mr, 'sourceProjectId', 1); + Vue.set(wrapper.vm.mr, 'targetProjectId', 1); + nextTick(done); }); it('should be false', () => { - expect(vm.showMergePipelineForkWarning).toEqual(false); + expect(wrapper.vm.showMergePipelineForkWarning).toEqual(false); }); }); describe('when merge pipelines are not enabled', () => { - beforeEach(done => { - Vue.set(vm.mr, 'mergePipelinesEnabled', false); - Vue.set(vm.mr, 'sourceProjectId', 1); - Vue.set(vm.mr, 'targetProjectId', 2); - vm.$nextTick(done); + beforeEach((done) => { + Vue.set(wrapper.vm.mr, 'mergePipelinesEnabled', false); + Vue.set(wrapper.vm.mr, 'sourceProjectId', 1); + Vue.set(wrapper.vm.mr, 'targetProjectId', 2); + nextTick(done); }); it('should be false', () => { - expect(vm.showMergePipelineForkWarning).toEqual(false); + expect(wrapper.vm.showMergePipelineForkWarning).toEqual(false); }); }); describe('when merge pipelines are enabled _and_ the source project and target project are different', () => { - beforeEach(done => { - Vue.set(vm.mr, 'mergePipelinesEnabled', true); - Vue.set(vm.mr, 'sourceProjectId', 1); - Vue.set(vm.mr, 'targetProjectId', 2); - vm.$nextTick(done); + beforeEach((done) => { + Vue.set(wrapper.vm.mr, 'mergePipelinesEnabled', true); + Vue.set(wrapper.vm.mr, 'sourceProjectId', 1); + Vue.set(wrapper.vm.mr, 'targetProjectId', 2); + nextTick(done); }); it('should be true', () => { - expect(vm.showMergePipelineForkWarning).toEqual(true); + expect(wrapper.vm.showMergePipelineForkWarning).toEqual(true); }); }); }); describe('formattedHumanAccess', () => { it('when user is a tool admin but not a member of project', () => { - vm.mr.humanAccess = null; + wrapper.vm.mr.humanAccess = null; - expect(vm.formattedHumanAccess).toEqual(''); + expect(wrapper.vm.formattedHumanAccess).toEqual(''); }); it('when user a member of the project', () => { - vm.mr.humanAccess = 'Owner'; + wrapper.vm.mr.humanAccess = 'Owner'; - expect(vm.formattedHumanAccess).toEqual('owner'); + expect(wrapper.vm.formattedHumanAccess).toEqual('owner'); }); }); }); @@ -282,9 +281,9 @@ describe('mrWidgetOptions', () => { let isCbExecuted; beforeEach(() => { - jest.spyOn(vm.service, 'checkStatus').mockReturnValue(returnPromise(mockData)); - jest.spyOn(vm.mr, 'setData').mockImplementation(() => {}); - jest.spyOn(vm, 'handleNotification').mockImplementation(() => {}); + jest.spyOn(wrapper.vm.service, 'checkStatus').mockReturnValue(returnPromise(mockData)); + jest.spyOn(wrapper.vm.mr, 'setData').mockImplementation(() => {}); + jest.spyOn(wrapper.vm, 'handleNotification').mockImplementation(() => {}); isCbExecuted = false; cb = () => { @@ -293,12 +292,12 @@ describe('mrWidgetOptions', () => { }); it('should tell service to check status if document is visible', () => { - vm.checkStatus(cb); + wrapper.vm.checkStatus(cb); - return vm.$nextTick().then(() => { - expect(vm.service.checkStatus).toHaveBeenCalled(); - expect(vm.mr.setData).toHaveBeenCalled(); - expect(vm.handleNotification).toHaveBeenCalledWith(mockData); + return nextTick().then(() => { + expect(wrapper.vm.service.checkStatus).toHaveBeenCalled(); + expect(wrapper.vm.mr.setData).toHaveBeenCalled(); + expect(wrapper.vm.handleNotification).toHaveBeenCalledWith(mockData); expect(isCbExecuted).toBeTruthy(); }); }); @@ -306,11 +305,11 @@ describe('mrWidgetOptions', () => { describe('initPolling', () => { it('should call SmartInterval', () => { - vm.initPolling(); + wrapper.vm.initPolling(); expect(SmartInterval).toHaveBeenCalledWith( expect.objectContaining({ - callback: vm.checkStatus, + callback: wrapper.vm.checkStatus, }), ); }); @@ -318,11 +317,11 @@ describe('mrWidgetOptions', () => { describe('initDeploymentsPolling', () => { it('should call SmartInterval', () => { - vm.initDeploymentsPolling(); + wrapper.vm.initDeploymentsPolling(); expect(SmartInterval).toHaveBeenCalledWith( expect.objectContaining({ - callback: vm.fetchPreMergeDeployments, + callback: wrapper.vm.fetchPreMergeDeployments, }), ); }); @@ -331,15 +330,15 @@ describe('mrWidgetOptions', () => { describe('fetchDeployments', () => { it('should fetch deployments', () => { jest - .spyOn(vm.service, 'fetchDeployments') + .spyOn(wrapper.vm.service, 'fetchDeployments') .mockReturnValue(returnPromise([{ id: 1, status: SUCCESS }])); - vm.fetchPreMergeDeployments(); + wrapper.vm.fetchPreMergeDeployments(); - return vm.$nextTick().then(() => { - expect(vm.service.fetchDeployments).toHaveBeenCalled(); - expect(vm.mr.deployments.length).toEqual(1); - expect(vm.mr.deployments[0].id).toBe(1); + return nextTick().then(() => { + expect(wrapper.vm.service.fetchDeployments).toHaveBeenCalled(); + expect(wrapper.vm.mr.deployments.length).toEqual(1); + expect(wrapper.vm.mr.deployments[0].id).toBe(1); }); }); }); @@ -347,13 +346,13 @@ describe('mrWidgetOptions', () => { describe('fetchActionsContent', () => { it('should fetch content of Cherry Pick and Revert modals', () => { jest - .spyOn(vm.service, 'fetchMergeActionsContent') + .spyOn(wrapper.vm.service, 'fetchMergeActionsContent') .mockReturnValue(returnPromise('hello world')); - vm.fetchActionsContent(); + wrapper.vm.fetchActionsContent(); - return vm.$nextTick().then(() => { - expect(vm.service.fetchMergeActionsContent).toHaveBeenCalled(); + return nextTick().then(() => { + expect(wrapper.vm.service.fetchMergeActionsContent).toHaveBeenCalled(); expect(document.body.textContent).toContain('hello world'); }); }); @@ -362,46 +361,46 @@ describe('mrWidgetOptions', () => { describe('bindEventHubListeners', () => { it.each` event | method | methodArgs - ${'MRWidgetUpdateRequested'} | ${'checkStatus'} | ${x => [x]} - ${'MRWidgetRebaseSuccess'} | ${'checkStatus'} | ${x => [x, true]} + ${'MRWidgetUpdateRequested'} | ${'checkStatus'} | ${(x) => [x]} + ${'MRWidgetRebaseSuccess'} | ${'checkStatus'} | ${(x) => [x, true]} ${'FetchActionsContent'} | ${'fetchActionsContent'} | ${() => []} ${'EnablePolling'} | ${'resumePolling'} | ${() => []} ${'DisablePolling'} | ${'stopPolling'} | ${() => []} `('should bind to $event', ({ event, method, methodArgs }) => { - jest.spyOn(vm, method).mockImplementation(); + jest.spyOn(wrapper.vm, method).mockImplementation(); const eventArg = {}; eventHub.$emit(event, eventArg); - expect(vm[method]).toHaveBeenCalledWith(...methodArgs(eventArg)); + expect(wrapper.vm[method]).toHaveBeenCalledWith(...methodArgs(eventArg)); }); it('should bind to SetBranchRemoveFlag', () => { - expect(vm.mr.isRemovingSourceBranch).toBe(false); + expect(wrapper.vm.mr.isRemovingSourceBranch).toBe(false); eventHub.$emit('SetBranchRemoveFlag', [true]); - expect(vm.mr.isRemovingSourceBranch).toBe(true); + expect(wrapper.vm.mr.isRemovingSourceBranch).toBe(true); }); it('should bind to FailedToMerge', () => { - vm.mr.state = ''; - vm.mr.mergeError = ''; + wrapper.vm.mr.state = ''; + wrapper.vm.mr.mergeError = ''; const mergeError = 'Something bad happened!'; eventHub.$emit('FailedToMerge', mergeError); - expect(vm.mr.state).toBe('failedToMerge'); - expect(vm.mr.mergeError).toBe(mergeError); + expect(wrapper.vm.mr.state).toBe('failedToMerge'); + expect(wrapper.vm.mr.mergeError).toBe(mergeError); }); it('should bind to UpdateWidgetData', () => { - jest.spyOn(vm.mr, 'setData').mockImplementation(); + jest.spyOn(wrapper.vm.mr, 'setData').mockImplementation(); const data = { ...mockData }; eventHub.$emit('UpdateWidgetData', data); - expect(vm.mr.setData).toHaveBeenCalledWith(data); + expect(wrapper.vm.mr.setData).toHaveBeenCalledWith(data); }); }); @@ -421,26 +420,18 @@ describe('mrWidgetOptions', () => { document.body.removeChild(document.getElementById('favicon')); }); - it('should call setFavicon method', done => { - vm.mr.ciStatusFaviconPath = overlayDataUrl; - vm.setFaviconHelper() - .then(() => { - /* - It would be better if we'd could mock commonUtils.setFaviconURL - with a spy and test that it was called. We are doing the following - tests as a proxy to show that the function has been called - */ - expect(faviconElement.getAttribute('href')).not.toEqual(null); - expect(faviconElement.getAttribute('href')).not.toEqual(overlayDataUrl); - expect(faviconElement.getAttribute('href')).not.toEqual(faviconDataUrl); - }) - .then(done) - .catch(done.fail); + it('should call setFavicon method', async () => { + wrapper.vm.mr.ciStatusFaviconPath = overlayDataUrl; + + await wrapper.vm.setFaviconHelper(); + + expect(setFaviconOverlay).toHaveBeenCalledWith(overlayDataUrl); }); - it('should not call setFavicon when there is no ciStatusFaviconPath', done => { - vm.mr.ciStatusFaviconPath = null; - vm.setFaviconHelper() + it('should not call setFavicon when there is no ciStatusFaviconPath', (done) => { + wrapper.vm.mr.ciStatusFaviconPath = null; + wrapper.vm + .setFaviconHelper() .then(() => { expect(faviconElement.getAttribute('href')).toEqual(null); done(); @@ -459,12 +450,12 @@ describe('mrWidgetOptions', () => { beforeEach(() => { jest.spyOn(notify, 'notifyMe').mockImplementation(() => {}); - vm.mr.ciStatus = 'failed'; - vm.mr.gitlabLogo = 'logo.png'; + wrapper.vm.mr.ciStatus = 'failed'; + wrapper.vm.mr.gitlabLogo = 'logo.png'; }); it('should call notifyMe', () => { - vm.handleNotification(data); + wrapper.vm.handleNotification(data); expect(notify.notifyMe).toHaveBeenCalledWith( 'Pipeline running-label', @@ -474,15 +465,15 @@ describe('mrWidgetOptions', () => { }); it('should not call notifyMe if the status has not changed', () => { - vm.mr.ciStatus = data.ci_status; + wrapper.vm.mr.ciStatus = data.ci_status; - vm.handleNotification(data); + wrapper.vm.handleNotification(data); expect(notify.notifyMe).not.toHaveBeenCalled(); }); it('should not notify if no pipeline provided', () => { - vm.handleNotification({ + wrapper.vm.handleNotification({ ...data, pipeline: undefined, }); @@ -493,63 +484,65 @@ describe('mrWidgetOptions', () => { describe('resumePolling', () => { it('should call stopTimer on pollingInterval', () => { - jest.spyOn(vm.pollingInterval, 'resume').mockImplementation(() => {}); + jest.spyOn(wrapper.vm.pollingInterval, 'resume').mockImplementation(() => {}); - vm.resumePolling(); + wrapper.vm.resumePolling(); - expect(vm.pollingInterval.resume).toHaveBeenCalled(); + expect(wrapper.vm.pollingInterval.resume).toHaveBeenCalled(); }); }); describe('stopPolling', () => { it('should call stopTimer on pollingInterval', () => { - jest.spyOn(vm.pollingInterval, 'stopTimer').mockImplementation(() => {}); + jest.spyOn(wrapper.vm.pollingInterval, 'stopTimer').mockImplementation(() => {}); - vm.stopPolling(); + wrapper.vm.stopPolling(); - expect(vm.pollingInterval.stopTimer).toHaveBeenCalled(); + expect(wrapper.vm.pollingInterval.stopTimer).toHaveBeenCalled(); }); }); }); describe('rendering relatedLinks', () => { - beforeEach(done => { - vm.mr.relatedLinks = { - assignToMe: null, - closing: ` - <a class="close-related-link" href="#"> - Close - </a> - `, - mentioned: '', - }; - Vue.nextTick(done); + beforeEach(() => { + createComponent({ + ...mockData, + issues_links: { + closing: ` + <a class="close-related-link" href="#"> + Close + </a> + `, + }, + }); + + return nextTick(); }); it('renders if there are relatedLinks', () => { - expect(vm.$el.querySelector('.close-related-link')).toBeDefined(); + expect(wrapper.find('.close-related-link').exists()).toBe(true); }); - it('does not render if state is nothingToMerge', done => { - vm.mr.state = stateKey.nothingToMerge; - Vue.nextTick(() => { - expect(vm.$el.querySelector('.close-related-link')).toBeNull(); + it('does not render if state is nothingToMerge', (done) => { + wrapper.vm.mr.state = stateKey.nothingToMerge; + nextTick(() => { + expect(wrapper.find('.close-related-link').exists()).toBe(false); done(); }); }); }); describe('rendering source branch removal status', () => { - it('renders when user cannot remove branch and branch should be removed', done => { - vm.mr.canRemoveSourceBranch = false; - vm.mr.shouldRemoveSourceBranch = true; - vm.mr.state = 'readyToMerge'; + it('renders when user cannot remove branch and branch should be removed', (done) => { + wrapper.vm.mr.canRemoveSourceBranch = false; + wrapper.vm.mr.shouldRemoveSourceBranch = true; + wrapper.vm.mr.state = 'readyToMerge'; - vm.$nextTick(() => { - const tooltip = vm.$el.querySelector('[data-testid="question-o-icon"]'); + nextTick(() => { + const tooltip = wrapper.find('[data-testid="question-o-icon"]'); - expect(vm.$el.textContent).toContain('Deletes source branch'); - expect(tooltip.getAttribute('title')).toBe( + expect(wrapper.text()).toContain('Deletes source branch'); + expect(tooltip.attributes('title')).toBe( 'A user with write access to the source branch selected this option', ); @@ -557,14 +550,14 @@ describe('mrWidgetOptions', () => { }); }); - it('does not render in merged state', done => { - vm.mr.canRemoveSourceBranch = false; - vm.mr.shouldRemoveSourceBranch = true; - vm.mr.state = 'merged'; + it('does not render in merged state', (done) => { + wrapper.vm.mr.canRemoveSourceBranch = false; + wrapper.vm.mr.shouldRemoveSourceBranch = true; + wrapper.vm.mr.state = 'merged'; - vm.$nextTick(() => { - expect(vm.$el.textContent).toContain('The source branch has been deleted'); - expect(vm.$el.textContent).not.toContain('Deletes source branch'); + nextTick(() => { + expect(wrapper.text()).toContain('The source branch has been deleted'); + expect(wrapper.text()).not.toContain('Deletes source branch'); done(); }); @@ -601,8 +594,8 @@ describe('mrWidgetOptions', () => { status: SUCCESS, }; - beforeEach(done => { - vm.mr.deployments.push( + beforeEach((done) => { + wrapper.vm.mr.deployments.push( { ...deploymentMockData, }, @@ -612,33 +605,32 @@ describe('mrWidgetOptions', () => { }, ); - vm.$nextTick(done); + nextTick(done); }); it('renders multiple deployments', () => { - expect(vm.$el.querySelectorAll('.deploy-heading').length).toBe(2); + expect(wrapper.findAll('.deploy-heading').length).toBe(2); }); it('renders dropdpown with multiple file changes', () => { expect( - vm.$el - .querySelector('.js-mr-wigdet-deployment-dropdown') - .querySelectorAll('.js-filtered-dropdown-result').length, + wrapper.find('.js-mr-wigdet-deployment-dropdown').findAll('.js-filtered-dropdown-result') + .length, ).toEqual(changes.length); }); }); describe('code quality widget', () => { it('renders the component', () => { - expect(vm.$el.querySelector('.js-codequality-widget')).toExist(); + expect(wrapper.find('.js-codequality-widget').exists()).toBe(true); }); }); describe('pipeline for target branch after merge', () => { describe('with information for target branch pipeline', () => { - beforeEach(done => { - vm.mr.state = 'merged'; - vm.mr.mergePipeline = { + beforeEach((done) => { + wrapper.vm.mr.state = 'merged'; + wrapper.vm.mr.mergePipeline = { id: 127, user: { id: 1, @@ -744,16 +736,16 @@ describe('mrWidgetOptions', () => { }, cancel_path: '/root/ci-web-terminal/pipelines/127/cancel', }; - vm.$nextTick(done); + nextTick(done); }); it('renders pipeline block', () => { - expect(vm.$el.querySelector('.js-post-merge-pipeline')).not.toBeNull(); + expect(wrapper.find('.js-post-merge-pipeline').exists()).toBe(true); }); describe('with post merge deployments', () => { - beforeEach(done => { - vm.mr.postMergeDeployments = [ + beforeEach((done) => { + wrapper.vm.mr.postMergeDeployments = [ { id: 15, name: 'review/diplo', @@ -785,46 +777,46 @@ describe('mrWidgetOptions', () => { }, ]; - vm.$nextTick(done); + nextTick(done); }); it('renders post deployment information', () => { - expect(vm.$el.querySelector('.js-post-deployment')).not.toBeNull(); + expect(wrapper.find('.js-post-deployment').exists()).toBe(true); }); }); }); describe('without information for target branch pipeline', () => { - beforeEach(done => { - vm.mr.state = 'merged'; + beforeEach((done) => { + wrapper.vm.mr.state = 'merged'; - vm.$nextTick(done); + nextTick(done); }); it('does not render pipeline block', () => { - expect(vm.$el.querySelector('.js-post-merge-pipeline')).toBeNull(); + expect(wrapper.find('.js-post-merge-pipeline').exists()).toBe(false); }); }); describe('when state is not merged', () => { - beforeEach(done => { - vm.mr.state = 'archived'; + beforeEach((done) => { + wrapper.vm.mr.state = 'archived'; - vm.$nextTick(done); + nextTick(done); }); it('does not render pipeline block', () => { - expect(vm.$el.querySelector('.js-post-merge-pipeline')).toBeNull(); + expect(wrapper.find('.js-post-merge-pipeline').exists()).toBe(false); }); it('does not render post deployment information', () => { - expect(vm.$el.querySelector('.js-post-deployment')).toBeNull(); + expect(wrapper.find('.js-post-deployment').exists()).toBe(false); }); }); }); it('should not suggest pipelines when feature flag is not present', () => { - expect(findSuggestPipeline()).toBeNull(); + expect(findSuggestPipeline().exists()).toBe(false); }); }); @@ -853,11 +845,11 @@ describe('mrWidgetOptions', () => { if (shouldRender) { it('renders', () => { - expect(findSecurityMrWidget()).toEqual(expect.any(HTMLElement)); + expect(findSecurityMrWidget().exists()).toBe(true); }); } else { it('does not render', () => { - expect(findSecurityMrWidget()).toBeNull(); + expect(findSecurityMrWidget().exists()).toBe(false); }); } }); @@ -866,41 +858,35 @@ describe('mrWidgetOptions', () => { describe('suggestPipeline', () => { beforeEach(() => { mock.onAny().reply(200); - - // This is needed because some grandchildren Bootstrap components throw warnings - // https://gitlab.com/gitlab-org/gitlab/issues/208458 - jest.spyOn(console, 'warn').mockImplementation(); }); describe('given feature flag is enabled', () => { beforeEach(() => { createComponent(); - vm.mr.hasCI = false; + wrapper.vm.mr.hasCI = false; }); it('should suggest pipelines when none exist', () => { - expect(findSuggestPipeline()).toEqual(expect.any(Element)); + expect(findSuggestPipeline().exists()).toBe(true); }); it.each([ { isDismissedSuggestPipeline: true }, { mergeRequestAddCiConfigPath: null }, { hasCI: true }, - ])('with %s, should not suggest pipeline', async obj => { - Object.assign(vm.mr, obj); + ])('with %s, should not suggest pipeline', async (obj) => { + Object.assign(wrapper.vm.mr, obj); - await vm.$nextTick(); + await nextTick(); - expect(findSuggestPipeline()).toBeNull(); + expect(findSuggestPipeline().exists()).toBe(false); }); it('should allow dismiss of the suggest pipeline message', async () => { - findSuggestPipelineButton().click(); - - await vm.$nextTick(); + await findSuggestPipelineButton().trigger('click'); - expect(findSuggestPipeline()).toBeNull(); + expect(findSuggestPipeline().exists()).toBe(false); }); }); }); 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 index 026ea0e4d0a..1dfa7564535 100644 --- a/spec/frontend/vue_mr_widget/stores/artifacts_list/actions_spec.js +++ b/spec/frontend/vue_mr_widget/stores/artifacts_list/actions_spec.js @@ -22,7 +22,7 @@ describe('Artifacts App Store Actions', () => { }); describe('setEndpoint', () => { - it('should commit SET_ENDPOINT mutation', done => { + it('should commit SET_ENDPOINT mutation', (done) => { testAction( setEndpoint, 'endpoint.json', @@ -35,7 +35,7 @@ describe('Artifacts App Store Actions', () => { }); describe('requestArtifacts', () => { - it('should commit REQUEST_ARTIFACTS mutation', done => { + it('should commit REQUEST_ARTIFACTS mutation', (done) => { testAction( requestArtifacts, null, @@ -62,7 +62,7 @@ describe('Artifacts App Store Actions', () => { }); describe('success', () => { - it('dispatches requestArtifacts and receiveArtifactsSuccess ', done => { + it('dispatches requestArtifacts and receiveArtifactsSuccess ', (done) => { mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, [ { text: 'result.txt', @@ -106,7 +106,7 @@ describe('Artifacts App Store Actions', () => { mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500); }); - it('dispatches requestArtifacts and receiveArtifactsError ', done => { + it('dispatches requestArtifacts and receiveArtifactsError ', (done) => { testAction( fetchArtifacts, null, @@ -127,7 +127,7 @@ describe('Artifacts App Store Actions', () => { }); describe('receiveArtifactsSuccess', () => { - it('should commit RECEIVE_ARTIFACTS_SUCCESS mutation with 200', done => { + it('should commit RECEIVE_ARTIFACTS_SUCCESS mutation with 200', (done) => { testAction( receiveArtifactsSuccess, { data: { summary: {} }, status: 200 }, @@ -138,7 +138,7 @@ describe('Artifacts App Store Actions', () => { ); }); - it('should not commit RECEIVE_ARTIFACTS_SUCCESS mutation with 204', done => { + it('should not commit RECEIVE_ARTIFACTS_SUCCESS mutation with 204', (done) => { testAction( receiveArtifactsSuccess, { data: { summary: {} }, status: 204 }, @@ -151,7 +151,7 @@ describe('Artifacts App Store Actions', () => { }); describe('receiveArtifactsError', () => { - it('should commit RECEIVE_ARTIFACTS_ERROR mutation', done => { + it('should commit RECEIVE_ARTIFACTS_ERROR mutation', (done) => { testAction( receiveArtifactsError, null, diff --git a/spec/frontend/vue_mr_widget/stores/artifacts_list/getters_spec.js b/spec/frontend/vue_mr_widget/stores/artifacts_list/getters_spec.js index 62ee6f5f189..dc90fef63c6 100644 --- a/spec/frontend/vue_mr_widget/stores/artifacts_list/getters_spec.js +++ b/spec/frontend/vue_mr_widget/stores/artifacts_list/getters_spec.js @@ -1,6 +1,6 @@ import { title } from '~/vue_merge_request_widget/stores/artifacts_list/getters'; import state from '~/vue_merge_request_widget/stores/artifacts_list/state'; -import { artifactsList } from '../../components/mock_data'; +import { artifacts } from '../../mock_data'; describe('Artifacts Store Getters', () => { let localState; @@ -24,7 +24,7 @@ describe('Artifacts Store Getters', () => { }); describe('when it has artifacts', () => { it('returns artifacts message', () => { - localState.artifacts = artifactsList; + localState.artifacts = artifacts; expect(title(localState)).toBe('View 2 exposed artifacts'); }); }); 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 index 8b2c10ec50a..cfc846075ea 100644 --- a/spec/frontend/vue_mr_widget/stores/mr_widget_store_spec.js +++ b/spec/frontend/vue_mr_widget/stores/mr_widget_store_spec.js @@ -156,7 +156,7 @@ describe('MergeRequestStore', () => { it.each(['sast_comparison_path', 'secret_scanning_comparison_path'])( 'should set %s path', - property => { + (property) => { // Ensure something is set in the mock data expect(property in mockData).toBe(true); const expectedValue = mockData[property]; |