Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/vue_mr_widget/components')
-rw-r--r--spec/frontend/vue_mr_widget/components/added_commit_message_spec.js31
-rw-r--r--spec/frontend/vue_mr_widget/components/approvals/approvals_spec.js424
-rw-r--r--spec/frontend/vue_mr_widget/components/approvals/approvals_summary_optional_spec.js45
-rw-r--r--spec/frontend/vue_mr_widget/components/approvals/approvals_summary_spec.js142
-rw-r--r--spec/frontend/vue_mr_widget/components/approvals/humanized_text_spec.js18
-rw-r--r--spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js126
-rw-r--r--spec/frontend/vue_mr_widget/components/artifacts_list_spec.js48
-rw-r--r--spec/frontend/vue_mr_widget/components/extensions/actions_spec.js47
-rw-r--r--spec/frontend/vue_mr_widget/components/extensions/child_content_spec.js40
-rw-r--r--spec/frontend/vue_mr_widget/components/extensions/index_spec.js34
-rw-r--r--spec/frontend/vue_mr_widget/components/extensions/status_icon_spec.js36
-rw-r--r--spec/frontend/vue_mr_widget/components/extensions/utils_spec.js20
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js97
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_alert_message_spec.js45
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_author_spec.js62
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_author_time_spec.js43
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_container_spec.js48
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_expandable_section_spec.js66
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_icon_spec.js26
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_memory_usage_spec.js227
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_pipeline_container_spec.js131
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_pipeline_spec.js282
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_rebase_spec.js292
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_related_links_spec.js114
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_status_icon_spec.js60
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_suggest_pipeline_spec.js135
-rw-r--r--spec/frontend/vue_mr_widget/components/pipeline_tour_mock_data.js9
-rw-r--r--spec/frontend/vue_mr_widget/components/review_app_link_spec.js48
-rw-r--r--spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap145
-rw-r--r--spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_pipeline_failed_spec.js.snap24
-rw-r--r--spec/frontend/vue_mr_widget/components/states/__snapshots__/new_ready_to_merge_spec.js.snap37
-rw-r--r--spec/frontend/vue_mr_widget/components/states/commit_edit_spec.js76
-rw-r--r--spec/frontend/vue_mr_widget/components/states/merge_checks_failed_spec.js26
-rw-r--r--spec/frontend/vue_mr_widget/components/states/merge_failed_pipeline_confirmation_dialog_spec.js78
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_archived_spec.js31
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_enabled_spec.js328
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js68
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_checking_spec.js31
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_closed_spec.js63
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_commit_message_dropdown_spec.js61
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_commits_header_spec.js136
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js252
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js158
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_merged_spec.js235
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_merging_spec.js74
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js49
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_not_allowed_spec.js26
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_nothing_to_merge_spec.js28
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_pipeline_blocked_spec.js28
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_pipeline_failed_spec.js30
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js905
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_sha_mismatch_spec.js42
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js104
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_unresolved_discussions_spec.js64
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_wip_spec.js103
-rw-r--r--spec/frontend/vue_mr_widget/components/states/new_ready_to_merge_spec.js31
-rw-r--r--spec/frontend/vue_mr_widget/components/terraform/mock_data.js31
-rw-r--r--spec/frontend/vue_mr_widget/components/terraform/mr_widget_terraform_container_spec.js174
-rw-r--r--spec/frontend/vue_mr_widget/components/terraform/terraform_plan_spec.js93
59 files changed, 0 insertions, 6227 deletions
diff --git a/spec/frontend/vue_mr_widget/components/added_commit_message_spec.js b/spec/frontend/vue_mr_widget/components/added_commit_message_spec.js
deleted file mode 100644
index 150680caa7e..00000000000
--- a/spec/frontend/vue_mr_widget/components/added_commit_message_spec.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import AddedCommentMessage from '~/vue_merge_request_widget/components/added_commit_message.vue';
-
-let wrapper;
-
-function factory(propsData) {
- wrapper = shallowMount(AddedCommentMessage, {
- propsData: {
- isFastForwardEnabled: false,
- targetBranch: 'main',
- ...propsData,
- },
- provide: {
- glFeatures: {
- restructuredMrWidget: true.valueOf,
- },
- },
- });
-}
-
-describe('Widget added commit message', () => {
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('displays changes where not merged when state is closed', () => {
- factory({ state: 'closed' });
-
- expect(wrapper.element.outerHTML).toContain('The changes were not merged');
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/approvals/approvals_spec.js b/spec/frontend/vue_mr_widget/components/approvals/approvals_spec.js
deleted file mode 100644
index 05cd1bb5b3d..00000000000
--- a/spec/frontend/vue_mr_widget/components/approvals/approvals_spec.js
+++ /dev/null
@@ -1,424 +0,0 @@
-import { nextTick } from 'vue';
-import { GlButton, GlSprintf } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import createFlash from '~/flash';
-import Approvals from '~/vue_merge_request_widget/components/approvals/approvals.vue';
-import ApprovalsSummary from '~/vue_merge_request_widget/components/approvals/approvals_summary.vue';
-import ApprovalsSummaryOptional from '~/vue_merge_request_widget/components/approvals/approvals_summary_optional.vue';
-import {
- FETCH_LOADING,
- FETCH_ERROR,
- APPROVE_ERROR,
- UNAPPROVE_ERROR,
-} from '~/vue_merge_request_widget/components/approvals/messages';
-import eventHub from '~/vue_merge_request_widget/event_hub';
-
-jest.mock('~/flash');
-
-const RULE_NAME = 'first_rule';
-const TEST_HELP_PATH = 'help/path';
-const testApprovedBy = () => [1, 7, 10].map((id) => ({ id }));
-const testApprovals = () => ({
- approved: false,
- approved_by: testApprovedBy().map((user) => ({ user })),
- approval_rules_left: [],
- approvals_left: 4,
- suggested_approvers: [],
- user_can_approve: true,
- user_has_approved: true,
- require_password_to_approve: false,
- invalid_approvers_rules: [],
-});
-const testApprovalRulesResponse = () => ({ rules: [{ id: 2 }] });
-
-describe('MRWidget approvals', () => {
- let wrapper;
- let service;
- let mr;
-
- const createComponent = (props = {}) => {
- wrapper = shallowMount(Approvals, {
- propsData: {
- mr,
- service,
- ...props,
- },
- stubs: {
- GlSprintf,
- },
- });
- };
-
- const findAction = () => wrapper.find(GlButton);
- const findActionData = () => {
- const action = findAction();
-
- return !action.exists()
- ? null
- : {
- variant: action.props('variant'),
- category: action.props('category'),
- text: action.text(),
- };
- };
- const findSummary = () => wrapper.find(ApprovalsSummary);
- const findOptionalSummary = () => wrapper.find(ApprovalsSummaryOptional);
- const findInvalidRules = () => wrapper.find('[data-testid="invalid-rules"]');
-
- beforeEach(() => {
- service = {
- ...{
- fetchApprovals: jest.fn().mockReturnValue(Promise.resolve(testApprovals())),
- fetchApprovalSettings: jest
- .fn()
- .mockReturnValue(Promise.resolve(testApprovalRulesResponse())),
- approveMergeRequest: jest.fn().mockReturnValue(Promise.resolve(testApprovals())),
- unapproveMergeRequest: jest.fn().mockReturnValue(Promise.resolve(testApprovals())),
- approveMergeRequestWithAuth: jest.fn().mockReturnValue(Promise.resolve(testApprovals())),
- },
- };
- mr = {
- ...{
- setApprovals: jest.fn(),
- setApprovalRules: jest.fn(),
- },
- approvalsHelpPath: TEST_HELP_PATH,
- approvals: testApprovals(),
- approvalRules: [],
- isOpen: true,
- state: 'open',
- };
-
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- describe('when created', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('shows loading message', () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({ fetchingApprovals: true });
-
- return nextTick().then(() => {
- expect(wrapper.text()).toContain(FETCH_LOADING);
- });
- });
-
- it('fetches approvals', () => {
- expect(service.fetchApprovals).toHaveBeenCalled();
- });
- });
-
- describe('when fetch approvals error', () => {
- beforeEach(() => {
- jest.spyOn(service, 'fetchApprovals').mockReturnValue(Promise.reject());
- createComponent();
- return nextTick();
- });
-
- it('still shows loading message', () => {
- expect(wrapper.text()).toContain(FETCH_LOADING);
- });
-
- it('flashes error', () => {
- expect(createFlash).toHaveBeenCalledWith({ message: FETCH_ERROR });
- });
- });
-
- describe('action button', () => {
- describe('when mr is closed', () => {
- beforeEach(() => {
- mr.isOpen = false;
- mr.approvals.user_has_approved = false;
- mr.approvals.user_can_approve = true;
-
- createComponent();
- return nextTick();
- });
-
- it('action is not rendered', () => {
- expect(findActionData()).toBe(null);
- });
- });
-
- describe('when user cannot approve', () => {
- beforeEach(() => {
- mr.approvals.user_has_approved = false;
- mr.approvals.user_can_approve = false;
-
- createComponent();
- return nextTick();
- });
-
- it('action is not rendered', () => {
- expect(findActionData()).toBe(null);
- });
- });
-
- describe('when user can approve', () => {
- beforeEach(() => {
- mr.approvals.user_has_approved = false;
- mr.approvals.user_can_approve = true;
- });
-
- describe('and MR is unapproved', () => {
- beforeEach(() => {
- createComponent();
- return nextTick();
- });
-
- it('approve action is rendered', () => {
- expect(findActionData()).toEqual({
- variant: 'confirm',
- text: 'Approve',
- category: 'primary',
- });
- });
- });
-
- describe('and MR is approved', () => {
- beforeEach(() => {
- mr.approvals.approved = true;
- });
-
- describe('with no approvers', () => {
- beforeEach(() => {
- mr.approvals.approved_by = [];
- createComponent();
- return nextTick();
- });
-
- it('approve action (with inverted style) is rendered', () => {
- expect(findActionData()).toEqual({
- variant: 'confirm',
- text: 'Approve',
- category: 'secondary',
- });
- });
- });
-
- describe('with approvers', () => {
- beforeEach(() => {
- mr.approvals.approved_by = [{ user: { id: 7 } }];
- createComponent();
- return nextTick();
- });
-
- it('approve additionally action is rendered', () => {
- expect(findActionData()).toEqual({
- variant: 'confirm',
- text: 'Approve additionally',
- category: 'secondary',
- });
- });
- });
- });
-
- describe('when approve action is clicked', () => {
- beforeEach(() => {
- createComponent();
- return nextTick();
- });
-
- it('shows loading icon', () => {
- jest.spyOn(service, 'approveMergeRequest').mockReturnValue(new Promise(() => {}));
- const action = findAction();
-
- expect(action.props('loading')).toBe(false);
-
- action.vm.$emit('click');
-
- return nextTick().then(() => {
- expect(action.props('loading')).toBe(true);
- });
- });
-
- describe('and after loading', () => {
- beforeEach(() => {
- findAction().vm.$emit('click');
- return nextTick();
- });
-
- it('calls service approve', () => {
- expect(service.approveMergeRequest).toHaveBeenCalled();
- });
-
- it('emits to eventHub', () => {
- expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
- });
-
- it('calls store setApprovals', () => {
- expect(mr.setApprovals).toHaveBeenCalledWith(testApprovals());
- });
- });
-
- describe('and error', () => {
- beforeEach(() => {
- jest.spyOn(service, 'approveMergeRequest').mockReturnValue(Promise.reject());
- findAction().vm.$emit('click');
- return nextTick();
- });
-
- it('flashes error message', () => {
- expect(createFlash).toHaveBeenCalledWith({ message: APPROVE_ERROR });
- });
- });
- });
- });
-
- describe('when user has approved', () => {
- beforeEach(() => {
- mr.approvals.user_has_approved = true;
- mr.approvals.user_can_approve = false;
-
- createComponent();
- return nextTick();
- });
-
- it('revoke action is rendered', () => {
- expect(findActionData()).toEqual({
- category: 'primary',
- variant: 'default',
- text: 'Revoke approval',
- });
- });
-
- describe('when revoke action is clicked', () => {
- describe('and successful', () => {
- beforeEach(() => {
- findAction().vm.$emit('click');
- return nextTick();
- });
-
- it('calls service unapprove', () => {
- expect(service.unapproveMergeRequest).toHaveBeenCalled();
- });
-
- it('emits to eventHub', () => {
- expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
- });
-
- it('calls store setApprovals', () => {
- expect(mr.setApprovals).toHaveBeenCalledWith(testApprovals());
- });
- });
-
- describe('and error', () => {
- beforeEach(() => {
- jest.spyOn(service, 'unapproveMergeRequest').mockReturnValue(Promise.reject());
- findAction().vm.$emit('click');
- return nextTick();
- });
-
- it('flashes error message', () => {
- expect(createFlash).toHaveBeenCalledWith({ message: UNAPPROVE_ERROR });
- });
- });
- });
- });
- });
-
- describe('approvals optional summary', () => {
- describe('when no approvals required and no approvers', () => {
- beforeEach(() => {
- mr.approvals.approved_by = [];
- mr.approvals.approvals_required = 0;
- mr.approvals.user_has_approved = false;
- });
-
- describe('and can approve', () => {
- beforeEach(() => {
- mr.approvals.user_can_approve = true;
-
- createComponent();
- return nextTick();
- });
-
- it('is shown', () => {
- expect(findSummary().exists()).toBe(false);
- expect(findOptionalSummary().props()).toEqual({
- canApprove: true,
- helpPath: TEST_HELP_PATH,
- });
- });
- });
-
- describe('and cannot approve', () => {
- beforeEach(() => {
- mr.approvals.user_can_approve = false;
-
- createComponent();
- return nextTick();
- });
-
- it('is shown', () => {
- expect(findSummary().exists()).toBe(false);
- expect(findOptionalSummary().props()).toEqual({
- canApprove: false,
- helpPath: TEST_HELP_PATH,
- });
- });
- });
- });
- });
-
- describe('approvals summary', () => {
- beforeEach(() => {
- createComponent();
- return nextTick();
- });
-
- it('is rendered with props', () => {
- const expected = testApprovals();
- const summary = findSummary();
-
- expect(findOptionalSummary().exists()).toBe(false);
- expect(summary.exists()).toBe(true);
- expect(summary.props()).toMatchObject({
- approvalsLeft: expected.approvals_left,
- rulesLeft: expected.approval_rules_left,
- approvers: testApprovedBy(),
- });
- });
- });
-
- describe('invalid rules', () => {
- beforeEach(() => {
- mr.approvals.merge_request_approvers_available = true;
- createComponent();
- });
-
- it('does not render related components', () => {
- expect(findInvalidRules().exists()).toBe(false);
- });
-
- describe('when invalid rules are present', () => {
- beforeEach(() => {
- mr.approvals.invalid_approvers_rules = [{ name: RULE_NAME }];
- createComponent();
- });
-
- it('renders related components', () => {
- const invalidRules = findInvalidRules();
-
- expect(invalidRules.exists()).toBe(true);
-
- const invalidRulesText = invalidRules.text();
-
- expect(invalidRulesText).toContain(RULE_NAME);
- expect(invalidRulesText).toContain(
- 'GitLab has approved this rule automatically to unblock the merge request.',
- );
- expect(invalidRulesText).toContain('Learn more.');
- });
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/approvals/approvals_summary_optional_spec.js b/spec/frontend/vue_mr_widget/components/approvals/approvals_summary_optional_spec.js
deleted file mode 100644
index 65cafc647e0..00000000000
--- a/spec/frontend/vue_mr_widget/components/approvals/approvals_summary_optional_spec.js
+++ /dev/null
@@ -1,45 +0,0 @@
-import { GlLink } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import ApprovalsSummaryOptional from '~/vue_merge_request_widget/components/approvals/approvals_summary_optional.vue';
-
-const TEST_HELP_PATH = 'help/path';
-
-describe('MRWidget approvals summary optional', () => {
- let wrapper;
-
- const createComponent = (props = {}) => {
- wrapper = shallowMount(ApprovalsSummaryOptional, {
- propsData: props,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- const findHelpLink = () => wrapper.find(GlLink);
-
- describe('when can approve', () => {
- beforeEach(() => {
- createComponent({ canApprove: true, helpPath: TEST_HELP_PATH });
- });
-
- it('shows help link', () => {
- const link = findHelpLink();
-
- expect(link.exists()).toBe(true);
- expect(link.attributes('href')).toBe(TEST_HELP_PATH);
- });
- });
-
- describe('when cannot approve', () => {
- beforeEach(() => {
- createComponent({ canApprove: false, helpPath: TEST_HELP_PATH });
- });
-
- it('does not show help link', () => {
- expect(findHelpLink().exists()).toBe(false);
- });
- });
-});
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
deleted file mode 100644
index c2606346292..00000000000
--- a/spec/frontend/vue_mr_widget/components/approvals/approvals_summary_spec.js
+++ /dev/null
@@ -1,142 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { toNounSeriesText } from '~/lib/utils/grammar';
-import ApprovalsSummary from '~/vue_merge_request_widget/components/approvals/approvals_summary.vue';
-import {
- APPROVED_BY_OTHERS,
- APPROVED_BY_YOU,
- APPROVED_BY_YOU_AND_OTHERS,
-} from '~/vue_merge_request_widget/components/approvals/messages';
-import UserAvatarList from '~/vue_shared/components/user_avatar/user_avatar_list.vue';
-
-const exampleUserId = 1;
-const testApprovers = () => Array.from({ length: 5 }, (_, i) => i).map((id) => ({ id }));
-const testRulesLeft = () => ['Lorem', 'Ipsum', 'dolar & sit'];
-const TEST_APPROVALS_LEFT = 3;
-
-describe('MRWidget approvals summary', () => {
- const originalUserId = gon.current_user_id;
- let wrapper;
-
- const createComponent = (props = {}) => {
- wrapper = shallowMount(ApprovalsSummary, {
- propsData: {
- approved: false,
- approvers: testApprovers(),
- approvalsLeft: TEST_APPROVALS_LEFT,
- rulesLeft: testRulesLeft(),
- ...props,
- },
- });
- };
-
- const findAvatars = () => wrapper.find(UserAvatarList);
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- gon.current_user_id = originalUserId;
- });
-
- describe('when approved', () => {
- beforeEach(() => {
- createComponent({
- approved: true,
- });
- });
-
- it('shows approved message', () => {
- expect(wrapper.text()).toContain(APPROVED_BY_OTHERS);
- });
-
- it('renders avatar list for approvers', () => {
- const avatars = findAvatars();
-
- expect(avatars.exists()).toBe(true);
- expect(avatars.props()).toEqual(
- expect.objectContaining({
- items: testApprovers(),
- }),
- );
- });
-
- describe('by the current user', () => {
- beforeEach(() => {
- gon.current_user_id = exampleUserId;
- createComponent({
- approvers: [{ id: exampleUserId }],
- approved: true,
- });
- });
-
- it('shows "Approved by you" message', () => {
- expect(wrapper.text()).toContain(APPROVED_BY_YOU);
- });
- });
-
- describe('by the current user and others', () => {
- beforeEach(() => {
- gon.current_user_id = exampleUserId;
- createComponent({
- approvers: [{ id: exampleUserId }, { id: exampleUserId + 1 }],
- approved: true,
- });
- });
-
- it('shows "Approved by you and others" message', () => {
- expect(wrapper.text()).toContain(APPROVED_BY_YOU_AND_OTHERS);
- });
- });
-
- describe('by other users than the current user', () => {
- beforeEach(() => {
- gon.current_user_id = exampleUserId;
- createComponent({
- approvers: [{ id: exampleUserId + 1 }],
- approved: true,
- });
- });
-
- it('shows "Approved by others" message', () => {
- expect(wrapper.text()).toContain(APPROVED_BY_OTHERS);
- });
- });
- });
-
- describe('when not approved', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('render message', () => {
- const names = toNounSeriesText(testRulesLeft());
-
- expect(wrapper.text()).toContain(`Requires ${TEST_APPROVALS_LEFT} approvals from ${names}.`);
- });
- });
-
- describe('when no rulesLeft', () => {
- beforeEach(() => {
- createComponent({
- rulesLeft: [],
- });
- });
-
- it('renders message', () => {
- expect(wrapper.text()).toContain(
- `Requires ${TEST_APPROVALS_LEFT} approvals from eligible users`,
- );
- });
- });
-
- describe('when no approvers', () => {
- beforeEach(() => {
- createComponent({
- approvers: [],
- });
- });
-
- it('does not render avatar list', () => {
- expect(wrapper.find(UserAvatarList).exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/approvals/humanized_text_spec.js b/spec/frontend/vue_mr_widget/components/approvals/humanized_text_spec.js
deleted file mode 100644
index d6776c00b29..00000000000
--- a/spec/frontend/vue_mr_widget/components/approvals/humanized_text_spec.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { humanizeInvalidApproversRules } from '~/vue_merge_request_widget/components/approvals/humanized_text';
-
-const testRules = [{ name: 'Lorem' }, { name: 'Ipsum' }, { name: 'Dolar' }];
-
-describe('humanizeInvalidApproversRules', () => {
- it('returns text in regards to a single rule', () => {
- const [singleRule] = testRules;
- expect(humanizeInvalidApproversRules([singleRule])).toBe('"Lorem"');
- });
-
- it('returns empty text when there is no rule', () => {
- expect(humanizeInvalidApproversRules([])).toBe('');
- });
-
- it('returns text in regards to multiple rules', () => {
- expect(humanizeInvalidApproversRules(testRules)).toBe('"Lorem", "Ipsum" and "Dolar"');
- });
-});
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
deleted file mode 100644
index e2386bc7f2b..00000000000
--- a/spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js
+++ /dev/null
@@ -1,126 +0,0 @@
-import { GlLoadingIcon } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-import Vue, { nextTick } from 'vue';
-import Vuex from 'vuex';
-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 { 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 actionSpies = {
- fetchArtifacts: jest.fn(),
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- wrapper.destroy();
- mock.restore();
- });
-
- const createComponent = () => {
- const storeConfig = getStoreConfig();
- store = new Vuex.Store({
- ...storeConfig,
- actions: {
- ...storeConfig.actions,
- ...actionSpies,
- },
- });
-
- wrapper = mount(ArtifactsListApp, {
- propsData: {
- endpoint: FAKE_ENDPOINT,
- },
- store,
- });
- };
-
- const findButtons = () => wrapper.findAll('button');
- const findTitle = () => wrapper.find('[data-testid="mr-collapsible-title"]');
- const findErrorMessage = () => wrapper.find('.js-error-state');
- const findTableRows = () => wrapper.findAll('tbody tr');
-
- describe('while loading', () => {
- beforeEach(() => {
- createComponent();
- store.dispatch('requestArtifacts');
- return nextTick();
- });
-
- it('renders a loading icon', () => {
- const loadingIcon = wrapper.find(GlLoadingIcon);
- expect(loadingIcon.exists()).toBe(true);
- });
-
- it('renders loading text', () => {
- expect(findTitle().text()).toBe('Loading artifacts');
- });
-
- it('renders disabled buttons', () => {
- const buttons = findButtons();
- expect(buttons.at(0).attributes('disabled')).toBe('disabled');
- expect(buttons.at(1).attributes('disabled')).toBe('disabled');
- });
- });
-
- describe('with results', () => {
- beforeEach(() => {
- createComponent();
- mock.onGet(FAKE_ENDPOINT).reply(200, artifacts, {});
- store.dispatch('receiveArtifactsSuccess', {
- data: artifacts,
- status: 200,
- });
- return nextTick();
- });
-
- it('renders a title with the number of artifacts', () => {
- expect(findTitle().text()).toBe('View 2 exposed artifacts');
- });
-
- it('renders both buttons enabled', () => {
- const buttons = findButtons();
- expect(buttons.at(0).attributes('disabled')).toBe(undefined);
- expect(buttons.at(1).attributes('disabled')).toBe(undefined);
- });
-
- describe('on click', () => {
- it('renders the list of artifacts', async () => {
- findTitle().trigger('click');
- await nextTick();
-
- expect(findTableRows().length).toEqual(2);
- });
- });
- });
-
- describe('with error', () => {
- beforeEach(() => {
- createComponent();
- mock.onGet(FAKE_ENDPOINT).reply(500, {}, {});
- store.dispatch('receiveArtifactsError');
- return nextTick();
- });
-
- it('renders the error state', () => {
- expect(findErrorMessage().text()).toBe('An error occurred while fetching the artifacts');
- });
-
- it('does not render buttons', () => {
- const buttons = findButtons();
- expect(buttons.exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/artifacts_list_spec.js b/spec/frontend/vue_mr_widget/components/artifacts_list_spec.js
deleted file mode 100644
index 712abfe228a..00000000000
--- a/spec/frontend/vue_mr_widget/components/artifacts_list_spec.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import { GlLink } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import ArtifactsList from '~/vue_merge_request_widget/components/artifacts_list.vue';
-import { artifacts } from '../mock_data';
-
-describe('Artifacts List', () => {
- let wrapper;
-
- const data = {
- artifacts,
- };
-
- const mountComponent = (props) => {
- wrapper = shallowMount(ArtifactsList, {
- propsData: {
- ...props,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- beforeEach(() => {
- mountComponent(data);
- });
-
- it('renders list of artifacts', () => {
- expect(wrapper.findAll('tbody tr').length).toEqual(data.artifacts.length);
- });
-
- it('renders link for the artifact', () => {
- expect(wrapper.find(GlLink).attributes('href')).toEqual(data.artifacts[0].url);
- });
-
- it('renders artifact name', () => {
- expect(wrapper.find(GlLink).text()).toEqual(data.artifacts[0].text);
- });
-
- it('renders job url', () => {
- 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);
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/extensions/actions_spec.js b/spec/frontend/vue_mr_widget/components/extensions/actions_spec.js
deleted file mode 100644
index a13db2f4d72..00000000000
--- a/spec/frontend/vue_mr_widget/components/extensions/actions_spec.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import { GlButton, GlDropdownItem } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Actions from '~/vue_merge_request_widget/components/extensions/actions.vue';
-
-let wrapper;
-
-function factory(propsData = {}) {
- wrapper = shallowMount(Actions, {
- propsData: { ...propsData, widget: 'test' },
- });
-}
-
-describe('MR widget extension actions', () => {
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('tertiaryButtons', () => {
- it('renders buttons', () => {
- factory({
- tertiaryButtons: [{ text: 'hello world', href: 'https://gitlab.com', target: '_blank' }],
- });
-
- expect(wrapper.findAllComponents(GlButton)).toHaveLength(1);
- });
-
- it('calls action click handler', async () => {
- const onClick = jest.fn();
-
- factory({
- tertiaryButtons: [{ text: 'hello world', onClick }],
- });
-
- await wrapper.findComponent(GlButton).vm.$emit('click');
-
- expect(onClick).toHaveBeenCalled();
- });
-
- it('renders tertiary actions in dropdown', () => {
- factory({
- tertiaryButtons: [{ text: 'hello world', href: 'https://gitlab.com', target: '_blank' }],
- });
-
- expect(wrapper.findAllComponents(GlDropdownItem)).toHaveLength(1);
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/extensions/child_content_spec.js b/spec/frontend/vue_mr_widget/components/extensions/child_content_spec.js
deleted file mode 100644
index 198a4c2823a..00000000000
--- a/spec/frontend/vue_mr_widget/components/extensions/child_content_spec.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import ChildContent from '~/vue_merge_request_widget/components/extensions/child_content.vue';
-
-let wrapper;
-const mockData = () => ({
- header: 'Test header',
- text: 'Test content',
- icon: {
- name: 'error',
- },
-});
-
-function factory(propsData) {
- wrapper = shallowMount(ChildContent, {
- propsData: {
- ...propsData,
- widgetLabel: 'Test',
- },
- });
-}
-
-describe('MR widget extension child content', () => {
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- it('renders child components', () => {
- factory({
- data: {
- ...mockData(),
- children: [mockData()],
- },
- level: 2,
- });
-
- expect(wrapper.find('[data-testid="child-content"]').exists()).toBe(true);
- expect(wrapper.find('[data-testid="child-content"]').props('level')).toBe(3);
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/extensions/index_spec.js b/spec/frontend/vue_mr_widget/components/extensions/index_spec.js
deleted file mode 100644
index dc25596655a..00000000000
--- a/spec/frontend/vue_mr_widget/components/extensions/index_spec.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import {
- registerExtension,
- registeredExtensions,
-} from '~/vue_merge_request_widget/components/extensions';
-import ExtensionBase from '~/vue_merge_request_widget/components/extensions/base.vue';
-
-describe('MR widget extension registering', () => {
- it('registers a extension', () => {
- registerExtension({
- name: 'Test',
- props: ['helloWorld'],
- computed: {
- test() {},
- },
- methods: {
- test() {},
- },
- });
-
- expect(registeredExtensions.extensions[0]).toEqual(
- expect.objectContaining({
- extends: ExtensionBase,
- name: 'Test',
- computed: {
- helloWorld: expect.any(Function),
- test: expect.any(Function),
- },
- methods: {
- test: expect.any(Function),
- },
- }),
- );
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/extensions/status_icon_spec.js b/spec/frontend/vue_mr_widget/components/extensions/status_icon_spec.js
deleted file mode 100644
index f3aa5bb774f..00000000000
--- a/spec/frontend/vue_mr_widget/components/extensions/status_icon_spec.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import { GlIcon, GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import StatusIcon from '~/vue_merge_request_widget/components/extensions/status_icon.vue';
-
-let wrapper;
-
-function factory(propsData = {}) {
- wrapper = shallowMount(StatusIcon, {
- propsData,
- });
-}
-
-describe('MR widget extensions status icon', () => {
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders loading icon', () => {
- factory({ name: 'test', isLoading: true, iconName: 'failed' });
-
- expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
- });
-
- it('renders status icon', () => {
- factory({ name: 'test', isLoading: false, iconName: 'failed' });
-
- expect(wrapper.findComponent(GlIcon).exists()).toBe(true);
- expect(wrapper.findComponent(GlIcon).props('name')).toBe('status-failed');
- });
-
- it('sets aria-label for status icon', () => {
- factory({ name: 'test', isLoading: false, iconName: 'failed' });
-
- expect(wrapper.findComponent(GlIcon).props('ariaLabel')).toBe('Failed test');
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/extensions/utils_spec.js b/spec/frontend/vue_mr_widget/components/extensions/utils_spec.js
deleted file mode 100644
index 5799799ad5e..00000000000
--- a/spec/frontend/vue_mr_widget/components/extensions/utils_spec.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import { generateText } from '~/vue_merge_request_widget/components/extensions/utils';
-
-describe('generateText', () => {
- it.each`
- text | expectedText
- ${'%{strong_start}Hello world%{strong_end}'} | ${'<span class="gl-font-weight-bold">Hello world</span>'}
- ${'%{success_start}Hello world%{success_end}'} | ${'<span class="gl-font-weight-bold gl-text-green-500">Hello world</span>'}
- ${'%{danger_start}Hello world%{danger_end}'} | ${'<span class="gl-font-weight-bold gl-text-red-500">Hello world</span>'}
- ${'%{critical_start}Hello world%{critical_end}'} | ${'<span class="gl-font-weight-bold gl-text-red-800">Hello world</span>'}
- ${'%{same_start}Hello world%{same_end}'} | ${'<span class="gl-font-weight-bold gl-text-gray-700">Hello world</span>'}
- ${'%{small_start}Hello world%{small_end}'} | ${'<span class="gl-font-sm gl-text-gray-700">Hello world</span>'}
- ${'%{strong_start}%{danger_start}Hello world%{danger_end}%{strong_end}'} | ${'<span class="gl-font-weight-bold"><span class="gl-font-weight-bold gl-text-red-500">Hello world</span></span>'}
- ${'%{no_exist_start}Hello world%{no_exist_end}'} | ${'Hello world'}
- ${{ text: 'Hello world', href: 'http://www.example.com' }} | ${'<a class="gl-text-decoration-underline" href="http://www.example.com">Hello world</a>'}
- ${{ prependText: 'Hello', text: 'world', href: 'http://www.example.com' }} | ${'Hello <a class="gl-text-decoration-underline" href="http://www.example.com">world</a>'}
- ${['array']} | ${null}
- `('generates $expectedText from $text', ({ text, expectedText }) => {
- expect(generateText(text)).toBe(expectedText);
- });
-});
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
deleted file mode 100644
index 01fbcb2154f..00000000000
--- a/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js
+++ /dev/null
@@ -1,97 +0,0 @@
-import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import MrCollapsibleSection from '~/vue_merge_request_widget/components/mr_collapsible_extension.vue';
-
-describe('Merge Request Collapsible Extension', () => {
- let wrapper;
- const data = {
- title: 'View artifacts',
- };
-
- const mountComponent = (props) => {
- wrapper = mount(MrCollapsibleSection, {
- propsData: {
- ...props,
- },
- slots: {
- default: '<div class="js-slot">Foo</div>',
- header: '<span data-testid="collapsed-header">hello there</span>',
- },
- });
- };
-
- const findTitle = () => wrapper.find('[data-testid="mr-collapsible-title"]');
- const findErrorMessage = () => wrapper.find('.js-error-state');
- const findIcon = () => wrapper.find(GlIcon);
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('while collapsed', () => {
- beforeEach(() => {
- mountComponent(data);
- });
-
- it('renders provided title', () => {
- expect(findTitle().text()).toBe(data.title);
- });
-
- it('renders the header slot', () => {
- expect(wrapper.find('[data-testid="collapsed-header"]').text()).toBe('hello there');
- });
-
- it('renders chevron-lg-right icon', () => {
- expect(findIcon().props('name')).toBe('chevron-lg-right');
- });
-
- describe('onClick', () => {
- beforeEach(async () => {
- wrapper.find('button').trigger('click');
- await nextTick();
- });
-
- it('rendes the provided slot', () => {
- expect(wrapper.find('.js-slot').isVisible()).toBe(true);
- });
-
- it('renders `Collapse` as the title', () => {
- expect(findTitle().text()).toBe('Collapse');
- });
-
- it('renders chevron-lg-down icon', () => {
- expect(findIcon().props('name')).toBe('chevron-lg-down');
- });
- });
- });
-
- describe('while loading', () => {
- beforeEach(() => {
- mountComponent({ ...data, isLoading: true });
- });
-
- 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');
- });
-
- it('renders loading spinner', () => {
- expect(wrapper.find(GlLoadingIcon).isVisible()).toBe(true);
- });
- });
-
- describe('with error', () => {
- beforeEach(() => {
- mountComponent({ ...data, hasError: true });
- });
-
- it('does not render the buttons', () => {
- expect(wrapper.findAll('button').exists()).toBe(false);
- });
-
- it('renders title message provided', () => {
- expect(findErrorMessage().text()).toBe(data.title);
- });
- });
-});
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
deleted file mode 100644
index 5d923d0383f..00000000000
--- a/spec/frontend/vue_mr_widget/components/mr_widget_alert_message_spec.js
+++ /dev/null
@@ -1,45 +0,0 @@
-import { GlLink, GlAlert } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import MrWidgetAlertMessage from '~/vue_merge_request_widget/components/mr_widget_alert_message.vue';
-
-let wrapper;
-
-function createComponent(propsData = {}) {
- wrapper = shallowMount(MrWidgetAlertMessage, {
- propsData,
- });
-}
-
-describe('MrWidgetAlertMessage', () => {
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('should render a GlAert', () => {
- createComponent({ type: 'danger' });
-
- expect(wrapper.findComponent(GlAlert).exists()).toBe(true);
- expect(wrapper.findComponent(GlAlert).props('variant')).toBe('danger');
- });
-
- describe('when helpPath is not provided', () => {
- it('should not render a help link', () => {
- createComponent({ type: 'info' });
-
- const link = wrapper.findComponent(GlLink);
-
- expect(link.exists()).toBe(false);
- });
- });
-
- describe('when helpPath is provided', () => {
- it('should render a help link', () => {
- createComponent({ type: 'info', helpPath: 'https://gitlab.com' });
-
- const link = wrapper.findComponent(GlLink);
-
- expect(link.exists()).toBe(true);
- expect(link.attributes('href')).toBe('https://gitlab.com');
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_author_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_author_spec.js
deleted file mode 100644
index 8a42e2e2ce7..00000000000
--- a/spec/frontend/vue_mr_widget/components/mr_widget_author_spec.js
+++ /dev/null
@@ -1,62 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import MrWidgetAuthor from '~/vue_merge_request_widget/components/mr_widget_author.vue';
-
-window.gl = window.gl || {};
-
-describe('MrWidgetAuthor', () => {
- let wrapper;
- let oldWindowGl;
- const mockAuthor = {
- name: 'Administrator',
- username: 'root',
- webUrl: 'http://localhost:3000/root',
- avatarUrl: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- };
-
- beforeEach(() => {
- oldWindowGl = window.gl;
- window.gl = {
- mrWidgetData: {
- defaultAvatarUrl: 'no_avatar.png',
- },
- };
- wrapper = shallowMount(MrWidgetAuthor, {
- propsData: {
- author: mockAuthor,
- },
- });
- });
-
- afterEach(() => {
- wrapper.destroy();
- window.gl = oldWindowGl;
- });
-
- it('renders link with the author web url', () => {
- expect(wrapper.attributes('href')).toBe('http://localhost:3000/root');
- });
-
- it('renders image with avatar url', () => {
- expect(wrapper.find('img').attributes('src')).toBe(
- 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- );
- });
-
- it('renders image with default avatar url when no avatarUrl is present in author', async () => {
- wrapper.setProps({
- author: {
- ...mockAuthor,
- avatarUrl: null,
- },
- });
-
- await nextTick();
-
- expect(wrapper.find('img').attributes('src')).toBe('no_avatar.png');
- });
-
- it('renders author name', () => {
- expect(wrapper.find('span').text()).toBe('Administrator');
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_author_time_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_author_time_spec.js
deleted file mode 100644
index 8fd93809e01..00000000000
--- a/spec/frontend/vue_mr_widget/components/mr_widget_author_time_spec.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import MrWidgetAuthor from '~/vue_merge_request_widget/components/mr_widget_author.vue';
-import MrWidgetAuthorTime from '~/vue_merge_request_widget/components/mr_widget_author_time.vue';
-
-describe('MrWidgetAuthorTime', () => {
- let wrapper;
-
- const defaultProps = {
- actionText: 'Merged by',
- author: {
- name: 'Administrator',
- username: 'root',
- webUrl: 'http://localhost:3000/root',
- avatarUrl: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- },
- dateTitle: '2017-03-23T23:02:00.807Z',
- dateReadable: '12 hours ago',
- };
-
- beforeEach(() => {
- wrapper = shallowMount(MrWidgetAuthorTime, {
- propsData: defaultProps,
- });
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders provided action text', () => {
- expect(wrapper.text()).toContain('Merged by');
- });
-
- it('renders author', () => {
- expect(wrapper.find(MrWidgetAuthor).props('author')).toStrictEqual(defaultProps.author);
- });
-
- it('renders provided time', () => {
- expect(wrapper.find('time').attributes('title')).toBe('2017-03-23T23:02:00.807Z');
-
- expect(wrapper.find('time').text().trim()).toBe('12 hours ago');
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_container_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_container_spec.js
deleted file mode 100644
index 4e3e918f7fb..00000000000
--- a/spec/frontend/vue_mr_widget/components/mr_widget_container_spec.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import MrWidgetContainer from '~/vue_merge_request_widget/components/mr_widget_container.vue';
-
-const BODY_HTML = '<div class="test-body">Hello World</div>';
-const FOOTER_HTML = '<div class="test-footer">Goodbye!</div>';
-
-describe('MrWidgetContainer', () => {
- let wrapper;
-
- const factory = (options = {}) => {
- wrapper = shallowMount(MrWidgetContainer, {
- ...options,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('has layout', () => {
- factory();
-
- expect(wrapper.classes()).toContain('mr-widget-heading');
- expect(wrapper.find('.mr-widget-content').exists()).toBe(true);
- });
-
- it('accepts default slot', () => {
- factory({
- slots: {
- default: BODY_HTML,
- },
- });
-
- expect(wrapper.find('.mr-widget-content .test-body').exists()).toBe(true);
- });
-
- it('accepts footer slot', () => {
- factory({
- slots: {
- default: BODY_HTML,
- footer: FOOTER_HTML,
- },
- });
-
- expect(wrapper.find('.mr-widget-content .test-body').exists()).toBe(true);
- expect(wrapper.find('.test-footer').exists()).toBe(true);
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_expandable_section_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_expandable_section_spec.js
deleted file mode 100644
index 631aef412a6..00000000000
--- a/spec/frontend/vue_mr_widget/components/mr_widget_expandable_section_spec.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import { GlButton, GlCollapse, GlIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import MrCollapsibleSection from '~/vue_merge_request_widget/components/mr_widget_expandable_section.vue';
-
-describe('MrWidgetExpanableSection', () => {
- let wrapper;
-
- const findButton = () => wrapper.find(GlButton);
- const findCollapse = () => wrapper.find(GlCollapse);
-
- beforeEach(() => {
- wrapper = shallowMount(MrCollapsibleSection, {
- slots: {
- content: '<span>Collapsable Content</span>',
- header: '<span>Header Content</span>',
- },
- });
- });
-
- it('renders Icon', () => {
- expect(wrapper.find(GlIcon).exists()).toBe(true);
- });
-
- it('renders header slot', () => {
- expect(wrapper.text()).toContain('Header Content');
- });
-
- it('renders content slot', () => {
- expect(wrapper.text()).toContain('Collapsable Content');
- });
-
- describe('when collapse section is closed', () => {
- it('renders button with expand text', () => {
- expect(findButton().text()).toBe('Expand');
- });
-
- it('renders a collpased section with no visibility', () => {
- const collapse = findCollapse();
-
- expect(collapse.exists()).toBe(true);
- expect(collapse.attributes('visible')).toBeUndefined();
- });
- });
-
- describe('when collapse section is open', () => {
- beforeEach(async () => {
- findButton().vm.$emit('click');
- await nextTick();
- });
-
- it('renders button with collapse text', () => {
- const button = findButton();
-
- expect(button.exists()).toBe(true);
- expect(button.text()).toBe('Collapse');
- });
-
- it('renders a collpased section with visible content', () => {
- const collapse = findCollapse();
-
- expect(collapse.exists()).toBe(true);
- expect(collapse.attributes('visible')).toBe('true');
- });
- });
-});
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
deleted file mode 100644
index ebd10f31fa7..00000000000
--- a/spec/frontend/vue_mr_widget/components/mr_widget_icon_spec.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { GlIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import MrWidgetIcon from '~/vue_merge_request_widget/components/mr_widget_icon.vue';
-
-const TEST_ICON = 'commit';
-
-describe('MrWidgetIcon', () => {
- let wrapper;
-
- beforeEach(() => {
- wrapper = shallowMount(MrWidgetIcon, {
- propsData: {
- name: TEST_ICON,
- },
- });
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders icon and container', () => {
- 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
deleted file mode 100644
index f0106914674..00000000000
--- a/spec/frontend/vue_mr_widget/components/mr_widget_memory_usage_spec.js
+++ /dev/null
@@ -1,227 +0,0 @@
-import axios from 'axios';
-import MockAdapter from 'axios-mock-adapter';
-import Vue, { nextTick } from 'vue';
-import waitForPromises from 'helpers/wait_for_promises';
-import MemoryUsage from '~/vue_merge_request_widget/components/deployment/memory_usage.vue';
-import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
-
-const url = '/root/acets-review-apps/environments/15/deployments/1/metrics';
-const monitoringUrl = '/root/acets-review-apps/environments/15/metrics';
-
-const metricsMockData = {
- success: true,
- metrics: {
- memory_before: [
- {
- metric: {},
- value: [1495785220.607, '9572875.906976745'],
- },
- ],
- memory_after: [
- {
- metric: {},
- value: [1495787020.607, '4485853.130206379'],
- },
- ],
- memory_values: [
- {
- metric: {},
- values: [[1493716685, '4.30859375']],
- },
- ],
- },
- last_update: '2017-05-02T12:34:49.628Z',
- deployment_time: 1493718485,
-};
-
-const createComponent = () => {
- const Component = Vue.extend(MemoryUsage);
-
- return new Component({
- el: document.createElement('div'),
- propsData: {
- metricsUrl: url,
- metricsMonitoringUrl: monitoringUrl,
- memoryMetrics: [],
- deploymentTime: 0,
- hasMetrics: false,
- loadFailed: false,
- loadingMetrics: true,
- backOffRequestCounter: 0,
- },
- });
-};
-
-const messages = {
- loadingMetrics: 'Loading deployment statistics',
- hasMetrics: 'Memory usage is unchanged at 0MB',
- loadFailed: 'Failed to load deployment statistics',
- metricsUnavailable: 'Deployment statistics are not available currently',
-};
-
-describe('MemoryUsage', () => {
- let vm;
- let el;
- let mock;
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- mock.onGet(`${url}.json`).reply(200);
-
- vm = createComponent();
- el = vm.$el;
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- describe('data', () => {
- it('should have default data', () => {
- const data = MemoryUsage.data();
-
- expect(Array.isArray(data.memoryMetrics)).toBeTruthy();
- expect(data.memoryMetrics.length).toBe(0);
-
- expect(typeof data.deploymentTime).toBe('number');
- expect(data.deploymentTime).toBe(0);
-
- expect(typeof data.hasMetrics).toBe('boolean');
- expect(data.hasMetrics).toBeFalsy();
-
- expect(typeof data.loadFailed).toBe('boolean');
- expect(data.loadFailed).toBeFalsy();
-
- expect(typeof data.loadingMetrics).toBe('boolean');
- expect(data.loadingMetrics).toBeTruthy();
-
- expect(typeof data.backOffRequestCounter).toBe('number');
- expect(data.backOffRequestCounter).toBe(0);
- });
- });
-
- describe('computed', () => {
- describe('memoryChangeMessage', () => {
- it('should contain "increased" if memoryFrom value is less than memoryTo value', () => {
- vm.memoryFrom = 4.28;
- vm.memoryTo = 9.13;
-
- expect(vm.memoryChangeMessage.indexOf('increased')).not.toEqual('-1');
- });
-
- it('should contain "decreased" if memoryFrom value is less than memoryTo value', () => {
- vm.memoryFrom = 9.13;
- vm.memoryTo = 4.28;
-
- expect(vm.memoryChangeMessage.indexOf('decreased')).not.toEqual('-1');
- });
-
- it('should contain "unchanged" if memoryFrom value equal to memoryTo value', () => {
- vm.memoryFrom = 1;
- vm.memoryTo = 1;
-
- expect(vm.memoryChangeMessage.indexOf('unchanged')).not.toEqual('-1');
- });
- });
- });
-
- describe('methods', () => {
- const { metrics, deployment_time } = metricsMockData;
-
- describe('getMegabytes', () => {
- it('should return Megabytes from provided Bytes value', () => {
- const memoryInBytes = '9572875.906976745';
-
- expect(vm.getMegabytes(memoryInBytes)).toEqual('9.13');
- });
- });
-
- describe('computeGraphData', () => {
- it('should populate sparkline graph', () => {
- // ignore BoostrapVue warnings
- jest.spyOn(console, 'warn').mockImplementation();
-
- vm.computeGraphData(metrics, deployment_time);
- const { hasMetrics, memoryMetrics, deploymentTime, memoryFrom, memoryTo } = vm;
-
- expect(hasMetrics).toBeTruthy();
- expect(memoryMetrics.length).toBeGreaterThan(0);
- expect(deploymentTime).toEqual(deployment_time);
- expect(memoryFrom).toEqual('9.13');
- expect(memoryTo).toEqual('4.28');
- });
- });
-
- describe('loadMetrics', () => {
- it('should load metrics data using MRWidgetService', async () => {
- jest.spyOn(MRWidgetService, 'fetchMetrics').mockResolvedValue({
- data: metricsMockData,
- });
- jest.spyOn(vm, 'computeGraphData').mockImplementation(() => {});
-
- vm.loadMetrics();
-
- await waitForPromises();
-
- expect(MRWidgetService.fetchMetrics).toHaveBeenCalledWith(url);
- expect(vm.computeGraphData).toHaveBeenCalledWith(metrics, deployment_time);
- });
- });
- });
-
- describe('template', () => {
- it('should render template elements correctly', () => {
- expect(el.classList.contains('mr-memory-usage')).toBeTruthy();
- expect(el.querySelector('.js-usage-info')).toBeDefined();
- });
-
- it('should show loading metrics message while metrics are being loaded', async () => {
- vm.loadingMetrics = true;
- vm.hasMetrics = false;
- vm.loadFailed = false;
-
- await nextTick();
-
- expect(el.querySelector('.js-usage-info.usage-info-loading')).toBeDefined();
- expect(el.querySelector('.js-usage-info .usage-info-load-spinner')).toBeDefined();
- expect(el.querySelector('.js-usage-info').innerText).toContain(messages.loadingMetrics);
- });
-
- it('should show deployment memory usage when metrics are loaded', async () => {
- // ignore BoostrapVue warnings
- jest.spyOn(console, 'warn').mockImplementation();
-
- vm.loadingMetrics = false;
- vm.hasMetrics = true;
- vm.loadFailed = false;
- vm.memoryMetrics = metricsMockData.metrics.memory_values[0].values;
-
- await nextTick();
-
- expect(el.querySelector('.memory-graph-container')).toBeDefined();
- expect(el.querySelector('.js-usage-info').innerText).toContain(messages.hasMetrics);
- });
-
- it('should show failure message when metrics loading failed', async () => {
- vm.loadingMetrics = false;
- vm.hasMetrics = false;
- vm.loadFailed = true;
-
- await nextTick();
-
- expect(el.querySelector('.js-usage-info.usage-info-failed')).toBeDefined();
- expect(el.querySelector('.js-usage-info').innerText).toContain(messages.loadFailed);
- });
-
- it('should show metrics unavailable message when metrics loading failed', async () => {
- vm.loadingMetrics = false;
- vm.hasMetrics = false;
- vm.loadFailed = false;
-
- await nextTick();
-
- expect(el.querySelector('.js-usage-info.usage-info-unavailable')).toBeDefined();
- expect(el.querySelector('.js-usage-info').innerText).toContain(messages.metricsUnavailable);
- });
- });
-});
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
deleted file mode 100644
index efe2bf75c3f..00000000000
--- a/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_container_spec.js
+++ /dev/null
@@ -1,131 +0,0 @@
-import { mount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import axios from '~/lib/utils/axios_utils';
-import ArtifactsApp from '~/vue_merge_request_widget/components/artifacts_list_app.vue';
-import DeploymentList from '~/vue_merge_request_widget/components/deployment/deployment_list.vue';
-import MrWidgetPipeline from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue';
-import MrWidgetPipelineContainer from '~/vue_merge_request_widget/components/mr_widget_pipeline_container.vue';
-import { mockStore } from '../mock_data';
-
-describe('MrWidgetPipelineContainer', () => {
- let wrapper;
- let mock;
-
- const factory = (props = {}) => {
- wrapper = extendedWrapper(
- mount(MrWidgetPipelineContainer, {
- propsData: {
- mr: { ...mockStore },
- ...props,
- },
- }),
- );
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- mock.onGet().reply(200, {});
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findDeploymentList = () => wrapper.findComponent(DeploymentList);
- const findCIErrorMessage = () => wrapper.findByTestId('ci-error-message');
-
- describe('when pre merge', () => {
- beforeEach(() => {
- factory();
- });
-
- it('renders pipeline', () => {
- expect(wrapper.find(MrWidgetPipeline).exists()).toBe(true);
- expect(wrapper.find(MrWidgetPipeline).props()).toMatchObject({
- pipeline: mockStore.pipeline,
- pipelineCoverageDelta: mockStore.pipelineCoverageDelta,
- ciStatus: mockStore.ciStatus,
- hasCi: mockStore.hasCI,
- sourceBranch: mockStore.sourceBranch,
- sourceBranchLink: mockStore.sourceBranchLink,
- });
- });
-
- it('renders deployments', () => {
- const expectedProps = mockStore.deployments.map((dep) =>
- expect.objectContaining({
- deployment: dep,
- showMetrics: false,
- }),
- );
-
- const deployments = wrapper.findAll('.mr-widget-extension .js-pre-deployment');
-
- expect(findDeploymentList().exists()).toBe(true);
- expect(findDeploymentList().props('deployments')).toBe(mockStore.deployments);
-
- expect(deployments.wrappers.map((x) => x.props())).toEqual(expectedProps);
- });
- });
-
- describe('when post merge', () => {
- beforeEach(() => {
- factory({
- isPostMerge: true,
- mr: {
- ...mockStore,
- pipeline: {},
- ciStatus: undefined,
- },
- });
- });
-
- it('renders pipeline', () => {
- expect(wrapper.find(MrWidgetPipeline).exists()).toBe(true);
- expect(findCIErrorMessage().exists()).toBe(false);
- expect(wrapper.find(MrWidgetPipeline).props()).toMatchObject({
- pipeline: mockStore.mergePipeline,
- pipelineCoverageDelta: mockStore.pipelineCoverageDelta,
- ciStatus: mockStore.mergePipeline.details.status.text,
- hasCi: mockStore.hasCI,
- sourceBranch: mockStore.targetBranch,
- sourceBranchLink: mockStore.targetBranch,
- });
- });
-
- it('sanitizes the targetBranch', () => {
- factory({
- isPostMerge: true,
- mr: {
- ...mockStore,
- targetBranch: 'Foo<script>alert("XSS")</script>',
- },
- });
- expect(wrapper.find(MrWidgetPipeline).props().sourceBranchLink).toBe('Foo');
- });
-
- it('renders deployments', () => {
- const expectedProps = mockStore.postMergeDeployments.map((dep) =>
- expect.objectContaining({
- deployment: dep,
- showMetrics: true,
- }),
- );
-
- const deployments = wrapper.findAll('.mr-widget-extension .js-post-deployment');
-
- expect(findDeploymentList().exists()).toBe(true);
- expect(findDeploymentList().props('deployments')).toBe(mockStore.postMergeDeployments);
- 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
deleted file mode 100644
index 6347e3c3be3..00000000000
--- a/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_spec.js
+++ /dev/null
@@ -1,282 +0,0 @@
-import { GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount, mount } from '@vue/test-utils';
-import axios from 'axios';
-import MockAdapter from 'axios-mock-adapter';
-import { trimText } from 'helpers/text_helper';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue';
-import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue';
-import PipelineComponent from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue';
-import { SUCCESS } from '~/vue_merge_request_widget/constants';
-import mockData from '../mock_data';
-
-describe('MRWidgetPipeline', () => {
- let wrapper;
-
- const defaultProps = {
- pipeline: mockData.pipeline,
- ciStatus: SUCCESS,
- hasCi: true,
- mrTroubleshootingDocsPath: 'help',
- ciTroubleshootingDocsPath: 'ci-help',
- };
-
- const ciErrorMessage =
- 'Could not retrieve the pipeline status. For troubleshooting steps, read the documentation.';
- const monitoringMessage = 'Checking pipeline status.';
-
- const findCIErrorMessage = () => wrapper.findByTestId('ci-error-message');
- const findPipelineID = () => wrapper.findByTestId('pipeline-id');
- const findPipelineInfoContainer = () => wrapper.findByTestId('pipeline-info-container');
- const findCommitLink = () => wrapper.findByTestId('commit-link');
- const findPipelineFinishedAt = () => wrapper.findByTestId('finished-at');
- const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph);
- const findAllPipelineStages = () => wrapper.findAllComponents(PipelineStage);
- const findPipelineCoverage = () => wrapper.findByTestId('pipeline-coverage');
- const findPipelineCoverageDelta = () => wrapper.findByTestId('pipeline-coverage-delta');
- const findPipelineCoverageTooltipText = () =>
- wrapper.findByTestId('pipeline-coverage-tooltip').text();
- const findPipelineCoverageDeltaTooltipText = () =>
- wrapper.findByTestId('pipeline-coverage-delta-tooltip').text();
- const findMonitoringPipelineMessage = () => wrapper.findByTestId('monitoring-pipeline-message');
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
-
- const mockArtifactsRequest = () => new MockAdapter(axios).onGet().reply(200, []);
-
- const createWrapper = (props = {}, mountFn = shallowMount) => {
- wrapper = extendedWrapper(
- mountFn(PipelineComponent, {
- propsData: {
- ...defaultProps,
- ...props,
- },
- }),
- );
- };
-
- afterEach(() => {
- if (wrapper?.destroy) {
- wrapper.destroy();
- wrapper = null;
- }
- });
-
- it('should render CI error if there is a pipeline, but no status', () => {
- createWrapper({ ciStatus: null }, mount);
- expect(findCIErrorMessage().text()).toBe(ciErrorMessage);
- });
-
- it('should render a loading state when no pipeline is found', () => {
- createWrapper({ pipeline: {} }, mount);
-
- expect(findMonitoringPipelineMessage().text()).toBe(monitoringMessage);
- expect(findLoadingIcon().exists()).toBe(true);
- });
-
- describe('with a pipeline', () => {
- beforeEach(() => {
- mockArtifactsRequest();
-
- createWrapper(
- {
- pipelineCoverageDelta: mockData.pipelineCoverageDelta,
- buildsWithCoverage: mockData.buildsWithCoverage,
- },
- mount,
- );
- });
-
- it('should render 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().attributes('href')).toBe(mockData.pipeline.commit.commit_path);
- });
-
- it('should render pipeline finished timestamp', () => {
- expect(findPipelineFinishedAt().attributes()).toMatchObject({
- title: 'Apr 7, 2017 2:00pm UTC',
- datetime: mockData.pipeline.details.finished_at,
- });
- });
-
- it('should render pipeline graph', () => {
- expect(findPipelineMiniGraph().exists()).toBe(true);
- expect(findAllPipelineStages()).toHaveLength(mockData.pipeline.details.stages.length);
- });
-
- describe('should render pipeline coverage information', () => {
- it('should render coverage percentage', () => {
- expect(findPipelineCoverage().text()).toMatch(
- `Test coverage ${mockData.pipeline.coverage}%`,
- );
- });
-
- it('should render coverage delta', () => {
- expect(findPipelineCoverageDelta().exists()).toBe(true);
- expect(findPipelineCoverageDelta().text()).toBe(`(${mockData.pipelineCoverageDelta}%)`);
- });
-
- it('should render tooltip for jobs contributing to code coverage', () => {
- const tooltipText = findPipelineCoverageTooltipText();
- const expectedDescription = `Test coverage value for this pipeline was calculated by averaging the resulting coverage values of ${mockData.buildsWithCoverage.length} jobs.`;
-
- expect(tooltipText).toContain(expectedDescription);
- });
-
- it.each(mockData.buildsWithCoverage)(
- 'should have name and coverage for build %s listed in tooltip',
- (build) => {
- const tooltipText = findPipelineCoverageTooltipText();
-
- expect(tooltipText).toContain(`${build.name} (${build.coverage}%)`);
- },
- );
-
- describe.each`
- style | coverageState | coverageChangeText | styleClass | pipelineCoverageDelta
- ${'no special'} | ${'the same'} | ${'not change'} | ${''} | ${'0'}
- ${'success'} | ${'increased'} | ${'increase'} | ${'text-success'} | ${'10'}
- ${'danger'} | ${'decreased'} | ${'decrease'} | ${'text-danger'} | ${'-10'}
- `(
- 'if test coverage is $coverageState',
- ({ style, styleClass, coverageChangeText, pipelineCoverageDelta }) => {
- it(`coverage delta should have ${style}`, () => {
- createWrapper({ pipelineCoverageDelta });
- expect(findPipelineCoverageDelta().classes()).toEqual(styleClass ? [styleClass] : []);
- });
-
- it(`coverage delta tooltip should say that the coverage will ${coverageChangeText}`, () => {
- createWrapper({ pipelineCoverageDelta });
- expect(findPipelineCoverageDeltaTooltipText()).toContain(coverageChangeText);
- });
- },
- );
- });
- });
-
- describe('without commit path', () => {
- beforeEach(() => {
- const mockCopy = JSON.parse(JSON.stringify(mockData));
- delete mockCopy.pipeline.commit;
-
- createWrapper({}, mount);
- });
-
- it('should render pipeline ID', () => {
- expect(findPipelineID().text().trim()).toBe(`#${mockData.pipeline.id}`);
- });
-
- it('should render pipeline status', () => {
- expect(findPipelineInfoContainer().text()).toMatch(mockData.pipeline.details.status.label);
- });
-
- it('should render pipeline graph with correct styles', () => {
- const stagesCount = mockData.pipeline.details.stages.length;
-
- expect(findPipelineMiniGraph().exists()).toBe(true);
- expect(findPipelineMiniGraph().findAll('.mr-widget-pipeline-stages')).toHaveLength(
- stagesCount,
- );
-
- expect(findAllPipelineStages()).toHaveLength(stagesCount);
- });
-
- it('should render coverage information', () => {
- expect(findPipelineCoverage().text()).toMatch(`Test coverage ${mockData.pipeline.coverage}%`);
- });
- });
-
- describe('without coverage', () => {
- beforeEach(() => {
- const mockCopy = JSON.parse(JSON.stringify(mockData));
- delete mockCopy.pipeline.coverage;
-
- createWrapper({ pipeline: mockCopy.pipeline });
- });
-
- it('should not render a coverage component', () => {
- expect(findPipelineCoverage().exists()).toBe(false);
- });
- });
-
- describe('without a pipeline graph', () => {
- beforeEach(() => {
- const mockCopy = JSON.parse(JSON.stringify(mockData));
- delete mockCopy.pipeline.details.stages;
-
- createWrapper({
- pipeline: mockCopy.pipeline,
- });
- });
-
- it('should not render a pipeline graph', () => {
- expect(findPipelineMiniGraph().exists()).toBe(false);
- });
- });
-
- describe('for each type of pipeline', () => {
- let pipeline;
-
- beforeEach(() => {
- ({ pipeline } = JSON.parse(JSON.stringify(mockData)));
-
- pipeline.details.name = 'Pipeline';
- pipeline.merge_request_event_type = undefined;
- pipeline.ref.tag = false;
- pipeline.ref.branch = false;
- });
-
- const factory = () => {
- createWrapper({
- pipeline,
- sourceBranchLink: mockData.source_branch_link,
- });
- };
-
- describe('for a branch pipeline', () => {
- it('renders a pipeline widget that reads "Pipeline <ID> <status> for <SHA> on <branch>"', () => {
- pipeline.ref.branch = true;
-
- factory();
-
- const expected = `Pipeline #${pipeline.id} ${pipeline.details.status.label} for ${pipeline.commit.short_id} on ${mockData.source_branch_link}`;
- const actual = trimText(findPipelineInfoContainer().text());
-
- expect(actual).toBe(expected);
- });
- });
-
- describe('for a tag pipeline', () => {
- it('renders a pipeline widget that reads "Pipeline <ID> <status> for <SHA> on <branch>"', () => {
- pipeline.ref.tag = true;
-
- factory();
-
- const expected = `Pipeline #${pipeline.id} ${pipeline.details.status.label} for ${pipeline.commit.short_id}`;
- const actual = trimText(findPipelineInfoContainer().text());
-
- expect(actual).toBe(expected);
- });
- });
-
- describe('for a detached merge request pipeline', () => {
- it('renders a pipeline widget that reads "Detached merge request pipeline <ID> <status> for <SHA>"', () => {
- pipeline.details.name = 'Detached merge request pipeline';
- pipeline.merge_request_event_type = 'detached';
-
- factory();
-
- const expected = `Detached merge request pipeline #${pipeline.id} ${pipeline.details.status.label} for ${pipeline.commit.short_id}`;
- const actual = trimText(findPipelineInfoContainer().text());
-
- expect(actual).toBe(expected);
- });
- });
- });
-});
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
deleted file mode 100644
index 6db82cedd80..00000000000
--- a/spec/frontend/vue_mr_widget/components/mr_widget_rebase_spec.js
+++ /dev/null
@@ -1,292 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import WidgetRebase from '~/vue_merge_request_widget/components/states/mr_widget_rebase.vue';
-import eventHub from '~/vue_merge_request_widget/event_hub';
-import toast from '~/vue_shared/plugins/global_toast';
-
-jest.mock('~/vue_shared/plugins/global_toast');
-
-let wrapper;
-
-function createWrapper(propsData, mergeRequestWidgetGraphql, rebaseWithoutCiUi) {
- wrapper = shallowMount(WidgetRebase, {
- propsData,
- data() {
- return {
- state: {
- rebaseInProgress: propsData.mr.rebaseInProgress,
- targetBranch: propsData.mr.targetBranch,
- userPermissions: {
- pushToSourceBranch: propsData.mr.canPushToSourceBranch,
- },
- },
- };
- },
- provide: { glFeatures: { mergeRequestWidgetGraphql, rebaseWithoutCiUi } },
- mocks: {
- $apollo: {
- queries: {
- state: { loading: false },
- },
- },
- },
- });
-}
-
-describe('Merge request widget rebase component', () => {
- const findRebaseMessage = () => wrapper.find('[data-testid="rebase-message"]');
- const findRebaseMessageText = () => findRebaseMessage().text();
- const findStandardRebaseButton = () => wrapper.find('[data-testid="standard-rebase-button"]');
- const findRebaseWithoutCiButton = () => wrapper.find('[data-testid="rebase-without-ci-button"]');
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- [true, false].forEach((mergeRequestWidgetGraphql) => {
- describe(`widget graphql is ${mergeRequestWidgetGraphql ? 'enabled' : 'disabled'}`, () => {
- describe('while rebasing', () => {
- it('should show progress message', () => {
- createWrapper(
- {
- mr: { rebaseInProgress: true },
- service: {},
- },
- mergeRequestWidgetGraphql,
- );
-
- expect(findRebaseMessageText()).toContain('Rebase in progress');
- });
- });
-
- describe('with permissions', () => {
- const rebaseMock = jest.fn().mockResolvedValue();
- const pollMock = jest.fn().mockResolvedValue({});
-
- it('renders the warning message', () => {
- createWrapper(
- {
- mr: {
- rebaseInProgress: false,
- canPushToSourceBranch: true,
- },
- service: {
- rebase: rebaseMock,
- poll: pollMock,
- },
- },
- mergeRequestWidgetGraphql,
- );
-
- const text = findRebaseMessageText();
-
- expect(text).toContain('Merge blocked');
- expect(text.replace(/\s\s+/g, ' ')).toContain(
- 'the source branch must be rebased onto the target branch',
- );
- });
-
- it('renders an error message when rebasing has failed', async () => {
- createWrapper(
- {
- mr: {
- rebaseInProgress: false,
- canPushToSourceBranch: true,
- },
- service: {
- rebase: rebaseMock,
- poll: pollMock,
- },
- },
- mergeRequestWidgetGraphql,
- );
-
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({ rebasingError: 'Something went wrong!' });
-
- await nextTick();
- expect(findRebaseMessageText()).toContain('Something went wrong!');
- });
-
- describe('Rebase buttons with flag rebaseWithoutCiUi', () => {
- beforeEach(() => {
- createWrapper(
- {
- mr: {
- rebaseInProgress: false,
- canPushToSourceBranch: true,
- },
- service: {
- rebase: rebaseMock,
- poll: pollMock,
- },
- },
- mergeRequestWidgetGraphql,
- { rebaseWithoutCiUi: true },
- );
- });
-
- it('renders both buttons', () => {
- expect(findRebaseWithoutCiButton().exists()).toBe(true);
- expect(findStandardRebaseButton().exists()).toBe(true);
- });
-
- it('starts the rebase when clicking', async () => {
- findStandardRebaseButton().vm.$emit('click');
-
- await nextTick();
-
- expect(rebaseMock).toHaveBeenCalledWith({ skipCi: false });
- });
-
- it('starts the CI-skipping rebase when clicking on "Rebase without CI"', async () => {
- findRebaseWithoutCiButton().vm.$emit('click');
-
- await nextTick();
-
- expect(rebaseMock).toHaveBeenCalledWith({ skipCi: true });
- });
- });
-
- describe('Rebase button with rebaseWithoutCiUI flag disabled', () => {
- beforeEach(() => {
- createWrapper(
- {
- mr: {
- rebaseInProgress: false,
- canPushToSourceBranch: true,
- },
- service: {
- rebase: rebaseMock,
- poll: pollMock,
- },
- },
- mergeRequestWidgetGraphql,
- );
- });
-
- it('standard rebase button is rendered', () => {
- expect(findStandardRebaseButton().exists()).toBe(true);
- expect(findRebaseWithoutCiButton().exists()).toBe(false);
- });
-
- it('calls rebase method with skip_ci false', () => {
- findStandardRebaseButton().vm.$emit('click');
-
- expect(rebaseMock).toHaveBeenCalledWith({ skipCi: false });
- });
- });
- });
-
- describe('without permissions', () => {
- const exampleTargetBranch = 'fake-branch-to-test-with';
-
- describe('UI text', () => {
- beforeEach(() => {
- createWrapper(
- {
- mr: {
- rebaseInProgress: false,
- canPushToSourceBranch: false,
- targetBranch: exampleTargetBranch,
- },
- service: {},
- },
- mergeRequestWidgetGraphql,
- );
- });
-
- it('renders a message explaining user does not have permissions', () => {
- const text = findRebaseMessageText();
-
- expect(text).toContain(
- 'Merge blocked: the source branch must be rebased onto the target branch.',
- );
- expect(text).toContain('the source branch must be rebased');
- });
-
- it('renders the correct target branch name', () => {
- const elem = findRebaseMessage();
-
- expect(elem.text()).toContain(
- 'Merge blocked: the source branch must be rebased onto the target branch.',
- );
- });
- });
-
- it('does not render the "Rebase without pipeline" button with rebaseWithoutCiUI flag enabled', () => {
- createWrapper(
- {
- mr: {
- rebaseInProgress: false,
- canPushToSourceBranch: false,
- targetBranch: exampleTargetBranch,
- },
- service: {},
- },
- mergeRequestWidgetGraphql,
- { rebaseWithoutCiUi: true },
- );
-
- expect(findRebaseWithoutCiButton().exists()).toBe(false);
- });
-
- it('does not render the standard rebase button with rebaseWithoutCiUI flag disabled', () => {
- createWrapper(
- {
- mr: {
- rebaseInProgress: false,
- canPushToSourceBranch: false,
- targetBranch: exampleTargetBranch,
- },
- service: {},
- },
- mergeRequestWidgetGraphql,
- );
-
- expect(findStandardRebaseButton().exists()).toBe(false);
- });
- });
-
- describe('methods', () => {
- it('checkRebaseStatus', async () => {
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- createWrapper(
- {
- mr: {},
- service: {
- rebase() {
- return Promise.resolve();
- },
- poll() {
- return Promise.resolve({
- data: {
- rebase_in_progress: false,
- should_be_rebased: false,
- merge_error: null,
- },
- });
- },
- },
- },
- mergeRequestWidgetGraphql,
- );
-
- 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();
-
- expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetRebaseSuccess');
- expect(toast).toHaveBeenCalledWith('Rebase completed');
- });
- });
- });
- });
-});
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
deleted file mode 100644
index 15522f7ac1d..00000000000
--- a/spec/frontend/vue_mr_widget/components/mr_widget_related_links_spec.js
+++ /dev/null
@@ -1,114 +0,0 @@
-import { GlLink } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import RelatedLinks from '~/vue_merge_request_widget/components/mr_widget_related_links.vue';
-
-describe('MRWidgetRelatedLinks', () => {
- let wrapper;
-
- const createComponent = (propsData = {}) => {
- wrapper = shallowMount(RelatedLinks, { propsData });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('computed', () => {
- describe('closesText', () => {
- it('returns Closes text for open merge request', () => {
- createComponent({ state: 'open', relatedLinks: {} });
-
- expect(wrapper.vm.closesText).toBe('Closes issues');
- });
-
- it('returns correct text for closed merge request', () => {
- createComponent({ state: 'closed', relatedLinks: {} });
-
- expect(wrapper.vm.closesText).toBe('Did not close');
- });
-
- it('returns correct tense for merged request', () => {
- createComponent({ state: 'merged', relatedLinks: {} });
-
- expect(wrapper.vm.closesText).toBe('Closed');
- });
- });
- });
-
- it('should have only have closing issues text', () => {
- createComponent({
- relatedLinks: {
- closing: '<a href="#">#23</a> and <a>#42</a>',
- closingCount: 2,
- },
- });
- const content = wrapper
- .text()
- .replace(/\n(\s)+/g, ' ')
- .trim();
-
- expect(content).toContain('Closes issues #23 and #42');
- expect(content).not.toContain('Mentions');
- });
-
- it('should have only have mentioned issues text', () => {
- createComponent({
- relatedLinks: {
- mentioned: '<a href="#">#7</a>',
- mentionedCount: 1,
- },
- });
-
- const content = wrapper
- .text()
- .replace(/\n(\s)+/g, ' ')
- .trim();
-
- expect(content).toContain('Mentions issue #7');
- expect(content).not.toContain('Closes issues');
- });
-
- it('should have closing and mentioned issues at the same time', () => {
- createComponent({
- relatedLinks: {
- closing: '<a href="#">#7</a>',
- mentioned: '<a href="#">#23</a> and <a>#42</a>',
- closingCount: 1,
- mentionedCount: 2,
- },
- });
- const content = wrapper
- .text()
- .replace(/\n(\s)+/g, ' ')
- .trim();
-
- expect(content).toContain('Closes issue #7');
- expect(content).toContain('Mentions issues #23 and #42');
- });
-
- describe('should have correct assign issues link', () => {
- it.each([
- [1, 'Assign yourself to this issue'],
- [2, 'Assign yourself to these issues'],
- ])('when issue count is %s, link displays correct text', (unassignedCount, text) => {
- const assignToMe = '/assign';
-
- createComponent({
- relatedLinks: { assignToMe, unassignedCount },
- });
-
- const glLinkWrapper = wrapper.findComponent(GlLink);
-
- expect(glLinkWrapper.attributes('href')).toBe(assignToMe);
- expect(glLinkWrapper.text()).toBe(text);
- });
-
- it('when no link is present', () => {
- createComponent({
- relatedLinks: { assignToMe: '#', unassignedCount: 0 },
- });
-
- expect(wrapper.findComponent(GlLink).exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_status_icon_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_status_icon_spec.js
deleted file mode 100644
index c25e10c5249..00000000000
--- a/spec/frontend/vue_mr_widget/components/mr_widget_status_icon_spec.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import { GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount, mount } from '@vue/test-utils';
-import mrStatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
-
-describe('MR widget status icon component', () => {
- let wrapper;
-
- const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
- const findDisabledMergeButton = () => wrapper.find('[data-testid="disabled-merge-button"]');
-
- const createWrapper = (props, mountFn = shallowMount) => {
- wrapper = mountFn(mrStatusIcon, {
- propsData: {
- ...props,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('while loading', () => {
- it('renders loading icon', () => {
- createWrapper({ status: 'loading' });
-
- expect(findLoadingIcon().exists()).toBe(true);
- });
- });
-
- describe('with status icon', () => {
- it('renders success status icon', () => {
- createWrapper({ status: 'success' }, mount);
-
- expect(wrapper.find('[data-testid="status_success-icon"]').exists()).toBe(true);
- });
-
- it('renders failed status icon', () => {
- createWrapper({ status: 'failed' }, mount);
-
- expect(wrapper.find('[data-testid="status_failed-icon"]').exists()).toBe(true);
- });
- });
-
- describe('with disabled button', () => {
- it('renders a disabled button', () => {
- createWrapper({ status: 'failed', showDisabledButton: true });
-
- expect(findDisabledMergeButton().exists()).toBe(true);
- });
- });
-
- describe('without disabled button', () => {
- it('does not render a disabled button', () => {
- createWrapper({ status: 'failed' });
-
- expect(findDisabledMergeButton().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_suggest_pipeline_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_suggest_pipeline_spec.js
deleted file mode 100644
index 352bc1a08ea..00000000000
--- a/spec/frontend/vue_mr_widget/components/mr_widget_suggest_pipeline_spec.js
+++ /dev/null
@@ -1,135 +0,0 @@
-import { GlSprintf } from '@gitlab/ui';
-import { mount, shallowMount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-import { mockTracking, triggerEvent, unmockTracking } from 'helpers/tracking_helper';
-import axios from '~/lib/utils/axios_utils';
-import MrWidgetIcon from '~/vue_merge_request_widget/components/mr_widget_icon.vue';
-import suggestPipelineComponent from '~/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue';
-import {
- SP_TRACK_LABEL,
- SP_SHOW_TRACK_EVENT,
- SP_SHOW_TRACK_VALUE,
- SP_HELP_URL,
-} from '~/vue_merge_request_widget/constants';
-import dismissibleContainer from '~/vue_shared/components/dismissible_container.vue';
-import { suggestProps, iconName } from './pipeline_tour_mock_data';
-
-describe('MRWidgetSuggestPipeline', () => {
- describe('template', () => {
- let wrapper;
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('core functionality', () => {
- const findOkBtn = () => wrapper.find('[data-testid="ok"]');
- let trackingSpy;
- let mockAxios;
-
- const mockTrackingOnWrapper = () => {
- unmockTracking();
- trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
- };
-
- beforeEach(() => {
- mockAxios = new MockAdapter(axios);
- document.body.dataset.page = 'projects:merge_requests:show';
- trackingSpy = mockTracking('_category_', undefined, jest.spyOn);
-
- wrapper = mount(suggestPipelineComponent, {
- propsData: suggestProps,
- stubs: {
- GlSprintf,
- },
- });
- });
-
- afterEach(() => {
- unmockTracking();
- mockAxios.restore();
- });
-
- it('renders the expected text', () => {
- const messageText = /Looks like there's no pipeline here./;
-
- expect(wrapper.text()).toMatch(messageText);
- });
-
- it('renders widget icon', () => {
- const icon = wrapper.find(MrWidgetIcon);
-
- expect(icon.exists()).toBe(true);
- expect(icon.props()).toEqual(
- expect.objectContaining({
- name: iconName,
- }),
- );
- });
-
- it('renders the show me how button', () => {
- const button = findOkBtn();
-
- expect(button.exists()).toBe(true);
- expect(button.classes('btn-confirm')).toEqual(true);
- expect(button.attributes('href')).toBe(suggestProps.pipelinePath);
- });
-
- it('renders the help link', () => {
- const link = wrapper.find('[data-testid="help"]');
-
- expect(link.exists()).toBe(true);
- expect(link.attributes('href')).toBe(SP_HELP_URL);
- });
-
- it('renders the empty pipelines image', () => {
- const image = wrapper.find('[data-testid="pipeline-image"]');
-
- expect(image.exists()).toBe(true);
- expect(image.attributes().src).toBe(suggestProps.pipelineSvgPath);
- });
-
- describe('tracking', () => {
- it('send event for basic view of the suggest pipeline widget', () => {
- const expectedCategory = undefined;
- const expectedAction = undefined;
-
- expect(trackingSpy).toHaveBeenCalledWith(expectedCategory, expectedAction, {
- label: SP_TRACK_LABEL,
- property: suggestProps.humanAccess,
- });
- });
-
- it('send an event when ok button is clicked', () => {
- mockTrackingOnWrapper();
- const okBtn = findOkBtn();
- triggerEvent(okBtn.element);
-
- expect(trackingSpy).toHaveBeenCalledWith('_category_', SP_SHOW_TRACK_EVENT, {
- label: SP_TRACK_LABEL,
- property: suggestProps.humanAccess,
- value: SP_SHOW_TRACK_VALUE.toString(),
- });
- });
- });
- });
-
- describe('dismissible', () => {
- const findDismissContainer = () => wrapper.find(dismissibleContainer);
-
- beforeEach(() => {
- wrapper = shallowMount(suggestPipelineComponent, { propsData: suggestProps });
- });
-
- it('renders the dismissal container', () => {
- expect(findDismissContainer().exists()).toBe(true);
- });
-
- it('emits dismiss upon dismissal button click', () => {
- findDismissContainer().vm.$emit('dismiss');
-
- expect(wrapper.emitted().dismiss).toBeTruthy();
- });
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/pipeline_tour_mock_data.js b/spec/frontend/vue_mr_widget/components/pipeline_tour_mock_data.js
deleted file mode 100644
index eef087d62b8..00000000000
--- a/spec/frontend/vue_mr_widget/components/pipeline_tour_mock_data.js
+++ /dev/null
@@ -1,9 +0,0 @@
-export const suggestProps = {
- pipelinePath: '/foo/bar/add/pipeline/path',
- pipelineSvgPath: 'assets/illustrations/something.svg',
- humanAccess: 'maintainer',
- userCalloutsPath: 'some/callout/path',
- userCalloutFeatureId: 'suggest_pipeline',
-};
-
-export const iconName = 'status_notfound';
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
deleted file mode 100644
index e393b56034d..00000000000
--- a/spec/frontend/vue_mr_widget/components/review_app_link_spec.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { mockTracking, triggerEvent } from 'helpers/tracking_helper';
-import ReviewAppLink from '~/vue_merge_request_widget/components/review_app_link.vue';
-
-describe('review app link', () => {
- const props = {
- link: '/review',
- cssClass: 'js-link',
- display: {
- text: 'View app',
- tooltip: '',
- },
- };
- let wrapper;
-
- beforeEach(() => {
- wrapper = shallowMount(ReviewAppLink, { propsData: props });
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders provided link as href attribute', () => {
- expect(wrapper.attributes('href')).toBe(props.link);
- });
-
- it('renders provided cssClass as class attribute', () => {
- expect(wrapper.classes('js-link')).toBe(true);
- });
-
- it('renders View app text', () => {
- expect(wrapper.text().trim()).toBe('View app');
- });
-
- it('renders svg icon', () => {
- expect(wrapper.find('svg')).not.toBeNull();
- });
-
- it('tracks an event when clicked', () => {
- const spy = mockTracking('_category_', wrapper.element, jest.spyOn);
- triggerEvent(wrapper.element);
-
- expect(spy).toHaveBeenCalledWith('_category_', 'open_review_app', {
- label: 'review_app',
- });
- });
-});
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
deleted file mode 100644
index 56a0218b374..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap
+++ /dev/null
@@ -1,145 +0,0 @@
-// 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"
->
- <gl-icon-stub
- class="gl-text-blue-500 gl-mr-3 gl-mt-1"
- name="status_scheduled"
- size="24"
- />
-
- <div
- class="media-body"
- >
- <h4
- class="gl-display-flex"
- >
- <span
- class="gl-mr-3"
- >
- <gl-sprintf-stub
- data-testid="statusText"
- message="Set by %{merge_author} to be merged automatically when the pipeline succeeds"
- />
- </span>
-
- <gl-button-stub
- buttontextclasses=""
- category="primary"
- class="js-cancel-auto-merge"
- data-qa-selector="cancel_auto_merge_button"
- data-testid="cancelAutomaticMergeButton"
- icon=""
- size="small"
- variant="default"
- >
-
- Cancel auto-merge
-
- </gl-button-stub>
- </h4>
-
- <section
- class="mr-info-list"
- >
- <p
- class="gl-display-flex"
- >
- <span
- class="gl-mr-3"
- >
- Does not delete the source branch
- </span>
-
- <gl-button-stub
- buttontextclasses=""
- category="primary"
- class="js-remove-source-branch"
- data-testid="removeSourceBranchButton"
- icon=""
- size="small"
- variant="default"
- >
-
- Delete source branch
-
- </gl-button-stub>
- </p>
- </section>
- </div>
-</div>
-`;
-
-exports[`MRWidgetAutoMergeEnabled when graphql is enabled template should have correct elements 1`] = `
-<div
- class="mr-widget-body media"
->
- <gl-icon-stub
- class="gl-text-blue-500 gl-mr-3 gl-mt-1"
- name="status_scheduled"
- size="24"
- />
-
- <div
- class="media-body"
- >
- <h4
- class="gl-display-flex"
- >
- <span
- class="gl-mr-3"
- >
- <gl-sprintf-stub
- data-testid="statusText"
- message="Set by %{merge_author} to be merged automatically when the pipeline succeeds"
- />
- </span>
-
- <gl-button-stub
- buttontextclasses=""
- category="primary"
- class="js-cancel-auto-merge"
- data-qa-selector="cancel_auto_merge_button"
- data-testid="cancelAutomaticMergeButton"
- icon=""
- size="small"
- variant="default"
- >
-
- Cancel auto-merge
-
- </gl-button-stub>
- </h4>
-
- <section
- class="mr-info-list"
- >
- <p
- class="gl-display-flex"
- >
- <span
- class="gl-mr-3"
- >
- Does not delete the source branch
- </span>
-
- <gl-button-stub
- buttontextclasses=""
- category="primary"
- class="js-remove-source-branch"
- data-testid="removeSourceBranchButton"
- icon=""
- size="small"
- variant="default"
- >
-
- Delete source branch
-
- </gl-button-stub>
- </p>
- </section>
- </div>
-</div>
-`;
diff --git a/spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_pipeline_failed_spec.js.snap b/spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_pipeline_failed_spec.js.snap
deleted file mode 100644
index 98297630792..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_pipeline_failed_spec.js.snap
+++ /dev/null
@@ -1,24 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`PipelineFailed should render error message with a disabled merge button 1`] = `
-<div
- class="mr-widget-body media"
->
- <status-icon-stub
- showdisabledbutton="true"
- status="warning"
- />
-
- <div
- class="media-body space-children"
- >
- <span
- class="bold"
- >
- <gl-sprintf-stub
- message="Merge blocked: pipeline must succeed. Push a commit that fixes the failure, or %{linkStart}learn about other solutions.%{linkEnd}"
- />
- </span>
- </div>
-</div>
-`;
diff --git a/spec/frontend/vue_mr_widget/components/states/__snapshots__/new_ready_to_merge_spec.js.snap b/spec/frontend/vue_mr_widget/components/states/__snapshots__/new_ready_to_merge_spec.js.snap
deleted file mode 100644
index f9936f22ea3..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/__snapshots__/new_ready_to_merge_spec.js.snap
+++ /dev/null
@@ -1,37 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`New ready to merge state component renders permission text if canMerge (false) is false 1`] = `
-<div
- class="mr-widget-body media"
->
- <status-icon-stub
- status="success"
- />
-
- <p
- class="media-body gl-m-0! gl-font-weight-bold gl-text-gray-900!"
- >
-
- Ready to merge by members who can write to the target branch.
-
- </p>
-</div>
-`;
-
-exports[`New ready to merge state component renders permission text if canMerge (true) is false 1`] = `
-<div
- class="mr-widget-body media"
->
- <status-icon-stub
- status="success"
- />
-
- <p
- class="media-body gl-m-0! gl-font-weight-bold gl-text-gray-900!"
- >
-
- Ready to merge!
-
- </p>
-</div>
-`;
diff --git a/spec/frontend/vue_mr_widget/components/states/commit_edit_spec.js b/spec/frontend/vue_mr_widget/components/states/commit_edit_spec.js
deleted file mode 100644
index c0add94e6ed..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/commit_edit_spec.js
+++ /dev/null
@@ -1,76 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import CommitEdit from '~/vue_merge_request_widget/components/states/commit_edit.vue';
-
-const testCommitMessage = 'Test commit message';
-const testLabel = 'Test label';
-const testInputId = 'test-input-id';
-
-describe('Commits edit component', () => {
- let wrapper;
-
- const createComponent = (slots = {}) => {
- wrapper = shallowMount(CommitEdit, {
- propsData: {
- value: testCommitMessage,
- label: testLabel,
- inputId: testInputId,
- },
- slots: {
- ...slots,
- },
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findTextarea = () => wrapper.find('.form-control');
-
- it('has a correct label', () => {
- const labelElement = wrapper.find('.col-form-label');
-
- expect(labelElement.text()).toBe(testLabel);
- });
-
- describe('textarea', () => {
- it('has a correct ID', () => {
- expect(findTextarea().attributes('id')).toBe(testInputId);
- });
-
- it('has a correct value', () => {
- expect(findTextarea().element.value).toBe(testCommitMessage);
- });
-
- it('emits an input event and receives changed value', async () => {
- const changedCommitMessage = 'Changed commit message';
-
- findTextarea().element.value = changedCommitMessage;
- findTextarea().trigger('input');
-
- await nextTick();
- expect(wrapper.emitted().input[0]).toEqual([changedCommitMessage]);
- expect(findTextarea().element.value).toBe(changedCommitMessage);
- });
- });
-
- describe('when slots are present', () => {
- beforeEach(() => {
- createComponent({
- header: `<div class="test-header">${testCommitMessage}</div>`,
- });
- });
-
- it('renders header slot correctly', () => {
- const headerSlotElement = wrapper.find('.test-header');
-
- expect(headerSlotElement.exists()).toBe(true);
- expect(headerSlotElement.text()).toBe(testCommitMessage);
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/states/merge_checks_failed_spec.js b/spec/frontend/vue_mr_widget/components/states/merge_checks_failed_spec.js
deleted file mode 100644
index 1900b53ac11..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/merge_checks_failed_spec.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import MergeChecksFailed from '~/vue_merge_request_widget/components/states/merge_checks_failed.vue';
-
-let wrapper;
-
-function factory(propsData = {}) {
- wrapper = shallowMount(MergeChecksFailed, {
- propsData,
- });
-}
-
-describe('Merge request widget merge checks failed state component', () => {
- afterEach(() => {
- wrapper.destroy();
- });
-
- it.each`
- mrState | displayText
- ${{ approvals: true, isApproved: false }} | ${'approvalNeeded'}
- ${{ blockingMergeRequests: { total_count: 1 } }} | ${'blockingMergeRequests'}
- `('display $displayText text for $mrState', ({ mrState, displayText }) => {
- factory({ mr: mrState });
-
- expect(wrapper.text()).toContain(MergeChecksFailed.i18n[displayText]);
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/states/merge_failed_pipeline_confirmation_dialog_spec.js b/spec/frontend/vue_mr_widget/components/states/merge_failed_pipeline_confirmation_dialog_spec.js
deleted file mode 100644
index 0e1c38437f0..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/merge_failed_pipeline_confirmation_dialog_spec.js
+++ /dev/null
@@ -1,78 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import MergeFailedPipelineConfirmationDialog from '~/vue_merge_request_widget/components/states/merge_failed_pipeline_confirmation_dialog.vue';
-import { trimText } from 'helpers/text_helper';
-
-describe('MergeFailedPipelineConfirmationDialog', () => {
- let wrapper;
-
- const GlModal = {
- template: `
- <div>
- <slot></slot>
- <slot name="modal-footer"></slot>
- </div>
- `,
- methods: {
- hide: jest.fn(),
- },
- };
-
- const createComponent = () => {
- wrapper = shallowMount(MergeFailedPipelineConfirmationDialog, {
- propsData: {
- visible: true,
- },
- stubs: {
- GlModal,
- },
- attachTo: document.body,
- });
- };
-
- const findModal = () => wrapper.findComponent(GlModal);
- const findMergeBtn = () => wrapper.find('[data-testid="merge-unverified-changes"]');
- const findCancelBtn = () => wrapper.find('[data-testid="merge-cancel-btn"]');
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('should render informational text explaining why merging immediately can be dangerous', () => {
- expect(trimText(wrapper.text())).toContain(
- 'The latest pipeline for this merge request did not succeed. The latest changes are unverified. Are you sure you want to attempt to merge?',
- );
- });
-
- it('should emit the mergeWithFailedPipeline event', () => {
- findMergeBtn().vm.$emit('click');
-
- expect(wrapper.emitted('mergeWithFailedPipeline')).toBeTruthy();
- });
-
- it('when the cancel button is clicked should emit cancel and call hide', () => {
- jest.spyOn(findModal().vm, 'hide');
-
- findCancelBtn().vm.$emit('click');
-
- expect(wrapper.emitted('cancel')).toBeTruthy();
- expect(findModal().vm.hide).toHaveBeenCalled();
- });
-
- it('should emit cancel when the hide event is emitted', () => {
- findModal().vm.$emit('hide');
-
- expect(wrapper.emitted('cancel')).toBeTruthy();
- });
-
- it('when modal is shown it will focus the cancel button', () => {
- jest.spyOn(findCancelBtn().element, 'focus');
-
- findModal().vm.$emit('shown');
-
- expect(findCancelBtn().element.focus).toHaveBeenCalled();
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_archived_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_archived_spec.js
deleted file mode 100644
index f3061d792d0..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_archived_spec.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import Vue from 'vue';
-import mountComponent from 'helpers/vue_mount_component_helper';
-import archivedComponent from '~/vue_merge_request_widget/components/states/mr_widget_archived.vue';
-
-describe('MRWidgetArchived', () => {
- let vm;
-
- beforeEach(() => {
- const Component = Vue.extend(archivedComponent);
- vm = mountComponent(Component);
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('renders a ci status failed icon', () => {
- expect(vm.$el.querySelector('.ci-status-icon')).not.toBeNull();
- });
-
- it('renders a disabled button', () => {
- expect(vm.$el.querySelector('button').getAttribute('disabled')).toEqual('disabled');
- expect(vm.$el.querySelector('button').textContent.trim()).toEqual('Merge');
- });
-
- it('renders information', () => {
- expect(vm.$el.querySelector('.bold').textContent.trim()).toEqual(
- 'Merge unavailable: merge requests are read-only on archived projects.',
- );
- });
-});
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
deleted file mode 100644
index 7387ed2d5e9..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_enabled_spec.js
+++ /dev/null
@@ -1,328 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import { trimText } from 'helpers/text_helper';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import autoMergeEnabledComponent from '~/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue';
-import { MWPS_MERGE_STRATEGY } from '~/vue_merge_request_widget/constants';
-import eventHub from '~/vue_merge_request_widget/event_hub';
-import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
-
-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, stateOverride = {}) {
- let state = {};
-
- if (mergeRequestWidgetGraphqlEnabled) {
- state = { ...convertPropsToGraphqlState(propsData), ...stateOverride };
- }
-
- 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,
-});
-
-const getStatusText = () => wrapper.findByTestId('statusText').attributes('message');
-
-describe('MRWidgetAutoMergeEnabled', () => {
- let oldWindowGl;
-
- beforeEach(() => {
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
-
- oldWindowGl = window.gl;
- window.gl = {
- mrWidgetData: {
- defaultAvatarUrl: 'no_avatar.png',
- },
- };
- });
-
- afterEach(() => {
- window.gl = oldWindowGl;
- wrapper.destroy();
- wrapper = null;
- });
-
- [true, false].forEach((mergeRequestWidgetGraphql) => {
- describe(`when graphql is ${mergeRequestWidgetGraphql ? 'enabled' : 'disabled'}`, () => {
- beforeEach(() => {
- mergeRequestWidgetGraphqlEnabled = mergeRequestWidgetGraphql;
- });
-
- describe('computed', () => {
- describe('canRemoveSourceBranch', () => {
- it('should return true when user is able to remove source branch', () => {
- factory({
- ...defaultMrProps(),
- });
-
- expect(wrapper.findByTestId('removeSourceBranchButton').exists()).toBe(true);
- });
-
- 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 not find "Delete" button when shouldRemoveSourceBranch set to true', () => {
- factory({
- ...defaultMrProps(),
- shouldRemoveSourceBranch: true,
- });
-
- expect(wrapper.findByTestId('removeSourceBranchButton').exists()).toBe(false);
- });
-
- it('should find "Delete" button when shouldRemoveSourceBranch overrides state.forceRemoveSourceBranch', () => {
- factory(
- {
- ...defaultMrProps(),
- shouldRemoveSourceBranch: false,
- },
- {
- forceRemoveSourceBranch: true,
- },
- );
-
- expect(wrapper.findByTestId('removeSourceBranchButton').exists()).toBe(true);
- });
-
- it('should find "Delete" button when shouldRemoveSourceBranch set to false', () => {
- factory({
- ...defaultMrProps(),
- shouldRemoveSourceBranch: false,
- });
-
- expect(wrapper.findByTestId('removeSourceBranchButton').exists()).toBe(true);
- });
-
- it('should return false if user is not able to remove the source branch', () => {
- factory({
- ...defaultMrProps(),
- canRemoveSourceBranch: false,
- });
-
- expect(wrapper.findByTestId('removeSourceBranchButton').exists()).toBe(false);
- });
- });
-
- describe('cancelButtonText', () => {
- it('should return "Cancel" if MWPS is selected', () => {
- factory({
- ...defaultMrProps(),
- autoMergeStrategy: MWPS_MERGE_STRATEGY,
- });
-
- expect(wrapper.findByTestId('cancelAutomaticMergeButton').text()).toBe(
- 'Cancel auto-merge',
- );
- });
- });
- });
-
- describe('methods', () => {
- describe('cancelAutomaticMerge', () => {
- it('should set flag and call service then tell main component to update the widget with data', async () => {
- factory({
- ...defaultMrProps(),
- });
- const mrObj = {
- is_new_mr_data: true,
- };
- jest.spyOn(wrapper.vm.service, 'cancelAutomaticMerge').mockReturnValue(
- new Promise((resolve) => {
- resolve({
- data: mrObj,
- });
- }),
- );
-
- wrapper.vm.cancelAutomaticMerge();
-
- await waitForPromises();
-
- expect(wrapper.vm.isCancellingAutoMerge).toBeTruthy();
- if (mergeRequestWidgetGraphql) {
- expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
- } else {
- expect(eventHub.$emit).toHaveBeenCalledWith('UpdateWidgetData', mrObj);
- }
- });
- });
-
- describe('removeSourceBranch', () => {
- it('should set flag and call service then request main component to update the widget', async () => {
- factory({
- ...defaultMrProps(),
- });
- jest.spyOn(wrapper.vm.service, 'merge').mockReturnValue(
- Promise.resolve({
- data: {
- status: MWPS_MERGE_STRATEGY,
- },
- }),
- );
-
- wrapper.vm.removeSourceBranch();
-
- await waitForPromises();
-
- expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
- expect(wrapper.vm.service.merge).toHaveBeenCalledWith({
- sha,
- auto_merge_strategy: MWPS_MERGE_STRATEGY,
- should_remove_source_branch: true,
- });
- });
- });
- });
-
- describe('template', () => {
- it('should have correct elements', () => {
- factory({
- ...defaultMrProps(),
- });
-
- expect(wrapper.element).toMatchSnapshot();
- });
-
- it('should disable cancel auto merge button when the action is in progress', async () => {
- factory({
- ...defaultMrProps(),
- });
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- isCancellingAutoMerge: true,
- });
-
- await nextTick();
-
- expect(wrapper.find('.js-cancel-auto-merge').props('loading')).toBe(true);
- });
-
- it('should show source branch will be deleted text when it source branch set to remove', () => {
- factory({
- ...defaultMrProps(),
- shouldRemoveSourceBranch: true,
- });
-
- const normalizedText = wrapper.text().replace(/\s+/g, ' ');
-
- expect(normalizedText).toContain('Deletes the source branch');
- expect(normalizedText).not.toContain('Does not delete the source branch');
- });
-
- it('should not show delete source branch button when user not able to delete source branch', () => {
- factory({
- ...defaultMrProps(),
- currentUserId: 4,
- });
-
- expect(wrapper.find('.js-remove-source-branch').exists()).toBe(false);
- });
-
- it('should disable delete source branch button when the action is in progress', async () => {
- factory({
- ...defaultMrProps(),
- });
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- isRemovingSourceBranch: true,
- });
-
- await nextTick();
-
- expect(wrapper.find('.js-remove-source-branch').props('loading')).toBe(true);
- });
-
- it('should render the status text as "...to merged automatically" if MWPS is selected', () => {
- factory({
- ...defaultMrProps(),
- autoMergeStrategy: MWPS_MERGE_STRATEGY,
- });
-
- expect(getStatusText()).toBe(
- 'Set by %{merge_author} to be merged automatically when the pipeline succeeds',
- );
- });
-
- it('should render the cancel button as "Cancel" if MWPS is selected', () => {
- factory({
- ...defaultMrProps(),
- autoMergeStrategy: MWPS_MERGE_STRATEGY,
- });
-
- const cancelButtonText = trimText(wrapper.find('.js-cancel-auto-merge').text());
-
- expect(cancelButtonText).toBe('Cancel auto-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
deleted file mode 100644
index 24198096564..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import { GlLoadingIcon, GlButton } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import AutoMergeFailedComponent from '~/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue';
-import eventHub from '~/vue_merge_request_widget/event_hub';
-
-describe('MRWidgetAutoMergeFailed', () => {
- let wrapper;
- const mergeError = 'This is the merge error';
- const findButton = () => wrapper.find(GlButton);
-
- const createComponent = (props = {}, mergeRequestWidgetGraphql = false) => {
- wrapper = shallowMount(AutoMergeFailedComponent, {
- propsData: { ...props },
- data() {
- if (mergeRequestWidgetGraphql) {
- return { mergeError: props.mr?.mergeError };
- }
-
- return {};
- },
- provide: {
- glFeatures: { mergeRequestWidgetGraphql },
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- [true, false].forEach((mergeRequestWidgetGraphql) => {
- describe(`when graphql is ${mergeRequestWidgetGraphql ? 'enabled' : 'dislabed'}`, () => {
- beforeEach(() => {
- createComponent(
- {
- mr: { mergeError },
- },
- mergeRequestWidgetGraphql,
- );
- });
-
- it('renders failed message', () => {
- expect(wrapper.text()).toContain('This merge request failed to be merged automatically');
- });
-
- 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');
-
- expect(eventHub.$emit.mock.calls[0][0]).toBe('MRWidgetUpdateRequested');
-
- await nextTick();
-
- 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_checking_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_checking_spec.js
deleted file mode 100644
index afe6bd0e767..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_checking_spec.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import Vue from 'vue';
-import mountComponent from 'helpers/vue_mount_component_helper';
-import checkingComponent from '~/vue_merge_request_widget/components/states/mr_widget_checking.vue';
-
-describe('MRWidgetChecking', () => {
- let Component;
- let vm;
-
- beforeEach(() => {
- Component = Vue.extend(checkingComponent);
- vm = mountComponent(Component);
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('renders disabled button', () => {
- expect(vm.$el.querySelector('button').getAttribute('disabled')).toEqual('disabled');
- });
-
- it('renders loading icon', () => {
- expect(vm.$el.querySelector('.mr-widget-icon span').classList).toContain('gl-spinner');
- });
-
- it('renders information about merging', () => {
- expect(vm.$el.querySelector('.media-body').textContent.trim()).toEqual(
- 'Checking if merge request can be merged…',
- );
- });
-});
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
deleted file mode 100644
index 6ae218ce6f8..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_closed_spec.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import Vue from 'vue';
-import mountComponent from 'helpers/vue_mount_component_helper';
-import closedComponent from '~/vue_merge_request_widget/components/states/mr_widget_closed.vue';
-
-describe('MRWidgetClosed', () => {
- let vm;
-
- beforeEach(() => {
- const Component = Vue.extend(closedComponent);
- vm = mountComponent(Component, {
- mr: {
- metrics: {
- mergedBy: {},
- closedBy: {
- name: 'Administrator',
- username: 'root',
- webUrl: 'http://localhost:3000/root',
- avatarUrl:
- 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- },
- mergedAt: 'Jan 24, 2018 1:02pm UTC',
- closedAt: 'Jan 24, 2018 1:02pm UTC',
- readableMergedAt: '',
- readableClosedAt: 'less than a minute ago',
- },
- targetBranchPath: '/twitter/flight/commits/so_long_jquery',
- targetBranch: 'so_long_jquery',
- },
- });
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('renders warning icon', () => {
- expect(vm.$el.querySelector('.js-ci-status-icon-warning')).not.toBeNull();
- });
-
- it('renders closed by information with author and time', () => {
- expect(
- vm.$el.querySelector('.js-mr-widget-author').textContent.trim().replace(/\s\s+/g, ' '),
- ).toContain('Closed by Administrator less than a minute ago');
- });
-
- it('links to the user that closed the MR', () => {
- expect(vm.$el.querySelector('.author-link').getAttribute('href')).toEqual(
- 'http://localhost:3000/root',
- );
- });
-
- it('renders information about the changes not being merged', () => {
- expect(
- vm.$el.querySelector('.mr-info-list').textContent.trim().replace(/\s\s+/g, ' '),
- ).toContain('The changes were not merged into so_long_jquery');
- });
-
- it('renders link for target branch', () => {
- expect(vm.$el.querySelector('.label-branch').getAttribute('href')).toEqual(
- '/twitter/flight/commits/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
deleted file mode 100644
index 663fabb761c..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_commit_message_dropdown_spec.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import { GlDropdownItem } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import CommitMessageDropdown from '~/vue_merge_request_widget/components/states/commit_message_dropdown.vue';
-
-const commits = [
- {
- title: 'Commit 1',
- short_id: '78d5b7',
- message: 'Update test.txt',
- },
- {
- title: 'Commit 2',
- short_id: '34cbe28b',
- message: 'Fixed test',
- },
- {
- title: 'Commit 3',
- short_id: 'fa42932a',
- message: 'Added changelog',
- },
-];
-
-describe('Commits message dropdown component', () => {
- let wrapper;
-
- const createComponent = () => {
- wrapper = shallowMount(CommitMessageDropdown, {
- propsData: {
- commits,
- },
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findDropdownElements = () => wrapper.findAll(GlDropdownItem);
- const findFirstDropdownElement = () => findDropdownElements().at(0);
-
- it('should have 3 elements in dropdown list', () => {
- expect(findDropdownElements().length).toBe(3);
- });
-
- it('should have correct message for the first dropdown list element', () => {
- expect(findFirstDropdownElement().text()).toContain('78d5b7');
- expect(findFirstDropdownElement().text()).toContain('Commit 1');
- });
-
- it('should emit a commit title on selecting commit', async () => {
- findFirstDropdownElement().vm.$emit('click');
-
- await nextTick();
- expect(wrapper.emitted().input[0]).toEqual(['Update test.txt']);
- });
-});
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
deleted file mode 100644
index 2796403b7d0..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_commits_header_spec.js
+++ /dev/null
@@ -1,136 +0,0 @@
-import { mount } from '@vue/test-utils';
-import { GlSprintf } from '@gitlab/ui';
-import { nextTick } from 'vue';
-import CommitsHeader from '~/vue_merge_request_widget/components/states/commits_header.vue';
-
-describe('Commits header component', () => {
- let wrapper;
-
- const createComponent = (props) => {
- wrapper = mount(CommitsHeader, {
- stubs: {
- GlSprintf,
- },
- propsData: {
- isSquashEnabled: false,
- targetBranch: 'main',
- commitsCount: 5,
- isFastForwardEnabled: false,
- ...props,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findHeaderWrapper = () => wrapper.find('.js-mr-widget-commits-count');
- const findCommitToggle = () => wrapper.find('.commit-edit-toggle');
- const findCommitsCountMessage = () => wrapper.find('.commits-count-message');
- const findTargetBranchMessage = () => wrapper.find('.label-branch');
- const findModifyButton = () => wrapper.find('.modify-message-button');
-
- describe('when fast-forward is enabled', () => {
- beforeEach(() => {
- createComponent({
- isFastForwardEnabled: true,
- isSquashEnabled: true,
- });
- });
-
- it('has commits count message showing 1 commit', () => {
- expect(findCommitsCountMessage().text()).toBe('1 commit');
- });
-
- it('has button with modify commit message', () => {
- expect(findModifyButton().text()).toBe('Modify commit message');
- });
-
- it('does not have merge commit part of the message', () => {
- expect(findHeaderWrapper().text()).not.toContain('1 merge commit');
- });
- });
-
- describe('when collapsed', () => {
- it('toggle has aria-label equal to Expand', () => {
- createComponent();
-
- expect(findCommitToggle().attributes('aria-label')).toBe('Expand');
- });
-
- it('has a chevron-right icon', async () => {
- createComponent();
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({ expanded: false });
-
- await nextTick();
- expect(findCommitToggle().props('icon')).toBe('chevron-right');
- });
-
- describe('when squash is disabled', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('has commits count message showing correct amount of commits', () => {
- expect(findCommitsCountMessage().text()).toBe('5 commits');
- });
-
- it('has button with modify merge commit message', () => {
- expect(findModifyButton().text()).toBe('Modify merge commit');
- });
- });
-
- describe('when squash is enabled', () => {
- beforeEach(() => {
- createComponent({ isSquashEnabled: true });
- });
-
- it('has commits count message showing one commit when squash is enabled', () => {
- expect(findCommitsCountMessage().text()).toBe('1 commit');
- });
-
- it('has button with modify commit messages text', () => {
- expect(findModifyButton().text()).toBe('Modify commit messages');
- });
- });
-
- it('has correct target branch displayed', () => {
- createComponent();
-
- expect(findTargetBranchMessage().text()).toBe('main');
- });
-
- it('does has merge commit part of the message', () => {
- createComponent();
-
- expect(findHeaderWrapper().text()).toContain('1 merge commit');
- });
- });
-
- describe('when expanded', () => {
- beforeEach(() => {
- createComponent();
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({ expanded: true });
- });
-
- it('toggle has aria-label equal to collapse', async () => {
- await nextTick();
- expect(findCommitToggle().attributes('aria-label')).toBe('Collapse');
- });
-
- it('has a chevron-down icon', async () => {
- await nextTick();
- expect(findCommitToggle().props('icon')).toBe('chevron-down');
- });
-
- it('has a collapse text', async () => {
- await nextTick();
- expect(findHeaderWrapper().text()).toBe('Collapse');
- });
- });
-});
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
deleted file mode 100644
index 7a92484695c..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js
+++ /dev/null
@@ -1,252 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import { TEST_HOST } from 'helpers/test_constants';
-import { removeBreakLine } from 'helpers/text_helper';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import ConflictsComponent from '~/vue_merge_request_widget/components/states/mr_widget_conflicts.vue';
-
-describe('MRWidgetConflicts', () => {
- let wrapper;
- let mergeRequestWidgetGraphql = null;
- const path = '/conflicts';
-
- const findResolveButton = () => wrapper.findByTestId('resolve-conflicts-button');
- const findMergeLocalButton = () => wrapper.findByTestId('merge-locally-button');
-
- const mergeConflictsText = 'Merge blocked: merge conflicts must be resolved.';
- const fastForwardMergeText =
- 'Merge blocked: fast-forward merge is not possible. To merge this request, first rebase locally.';
- const userCannotMergeText =
- 'Users who can write to the source or target branches can resolve the conflicts.';
- const resolveConflictsBtnText = 'Resolve conflicts';
- const mergeLocallyBtnText = 'Resolve locally';
-
- async function createComponent(propsData = {}) {
- wrapper = extendedWrapper(
- shallowMount(ConflictsComponent, {
- propsData,
- provide: {
- glFeatures: {
- mergeRequestWidgetGraphql,
- },
- },
- mocks: {
- $apollo: {
- queries: {
- userPermissions: { loading: false },
- stateData: { loading: false },
- },
- },
- },
- }),
- );
-
- if (mergeRequestWidgetGraphql) {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- userPermissions: {
- canMerge: propsData.mr.canMerge,
- pushToSourceBranch: propsData.mr.canPushToSourceBranch,
- },
- stateData: {
- shouldBeRebased: propsData.mr.shouldBeRebased,
- sourceBranchProtected: propsData.mr.sourceBranchProtected,
- },
- });
- }
-
- await nextTick();
- }
-
- afterEach(() => {
- mergeRequestWidgetGraphql = null;
- wrapper.destroy();
- });
-
- [false, true].forEach((featureEnabled) => {
- describe(`with GraphQL feature flag ${featureEnabled ? 'enabled' : 'disabled'}`, () => {
- beforeEach(() => {
- mergeRequestWidgetGraphql = featureEnabled;
- });
-
- // There are two permissions we need to consider:
- //
- // 1. Is the user allowed to merge to the target branch?
- // 2. Is the user allowed to push to the source branch?
- //
- // This yields 4 possible permutations that we need to test, and
- // we test them below. A user who can push to the source
- // branch should be allowed to resolve conflicts. This is
- // consistent with what the backend does.
- describe('when allowed to merge but not allowed to push to source branch', () => {
- beforeEach(async () => {
- await createComponent({
- mr: {
- canMerge: true,
- canPushToSourceBranch: false,
- conflictResolutionPath: path,
- conflictsDocsPath: '',
- },
- });
- });
-
- it('should tell you about conflicts without bothering other people', () => {
- expect(wrapper.text()).toContain(mergeConflictsText);
- expect(wrapper.text()).not.toContain(userCannotMergeText);
- });
-
- it('should not allow you to resolve the conflicts', () => {
- expect(wrapper.text()).not.toContain(resolveConflictsBtnText);
- });
-
- it('should have merge buttons', () => {
- expect(findMergeLocalButton().text()).toContain(mergeLocallyBtnText);
- });
- });
-
- describe('when not allowed to merge but allowed to push to source branch', () => {
- beforeEach(async () => {
- await createComponent({
- mr: {
- canMerge: false,
- canPushToSourceBranch: true,
- conflictResolutionPath: path,
- conflictsDocsPath: '',
- },
- });
- });
-
- it('should tell you about conflicts', () => {
- expect(wrapper.text()).toContain(mergeConflictsText);
- expect(wrapper.text()).toContain(userCannotMergeText);
- });
-
- it('should allow you to resolve the conflicts', () => {
- expect(findResolveButton().text()).toContain(resolveConflictsBtnText);
- expect(findResolveButton().attributes('href')).toEqual(path);
- });
-
- it('should not have merge buttons', () => {
- expect(wrapper.text()).not.toContain(mergeLocallyBtnText);
- });
- });
-
- describe('when allowed to merge and push to source branch', () => {
- beforeEach(async () => {
- await createComponent({
- mr: {
- canMerge: true,
- canPushToSourceBranch: true,
- conflictResolutionPath: path,
- conflictsDocsPath: '',
- },
- });
- });
-
- it('should tell you about conflicts without bothering other people', () => {
- expect(wrapper.text()).toContain(mergeConflictsText);
- expect(wrapper.text()).not.toContain(userCannotMergeText);
- });
-
- it('should allow you to resolve the conflicts', () => {
- expect(findResolveButton().text()).toContain(resolveConflictsBtnText);
- expect(findResolveButton().attributes('href')).toEqual(path);
- });
-
- it('should have merge buttons', () => {
- expect(findMergeLocalButton().text()).toContain(mergeLocallyBtnText);
- });
- });
-
- describe('when user does not have permission to push to source branch', () => {
- it('should show proper message', async () => {
- await createComponent({
- mr: {
- canMerge: false,
- canPushToSourceBranch: false,
- conflictsDocsPath: '',
- },
- });
-
- expect(wrapper.text().trim().replace(/\s\s+/g, ' ')).toContain(userCannotMergeText);
- });
-
- it('should not have action buttons', async () => {
- await createComponent({
- mr: {
- canMerge: false,
- canPushToSourceBranch: false,
- conflictsDocsPath: '',
- },
- });
-
- expect(findResolveButton().exists()).toBe(false);
- expect(findMergeLocalButton().exists()).toBe(false);
- });
-
- it('should not have resolve button when no conflict resolution path', async () => {
- await createComponent({
- mr: {
- canMerge: true,
- conflictResolutionPath: null,
- conflictsDocsPath: '',
- },
- });
-
- expect(findResolveButton().exists()).toBe(false);
- });
- });
-
- describe('when fast-forward or semi-linear merge enabled', () => {
- it('should tell you to rebase locally', async () => {
- await createComponent({
- mr: {
- shouldBeRebased: true,
- conflictsDocsPath: '',
- },
- });
-
- expect(removeBreakLine(wrapper.text()).trim()).toContain(fastForwardMergeText);
- });
- });
-
- describe('when source branch protected', () => {
- beforeEach(async () => {
- await createComponent({
- mr: {
- canMerge: true,
- canPushToSourceBranch: true,
- conflictResolutionPath: TEST_HOST,
- sourceBranchProtected: true,
- conflictsDocsPath: '',
- },
- });
- });
-
- it('should allow you to resolve the conflicts', () => {
- expect(findResolveButton().exists()).toBe(true);
- });
- });
-
- describe('when source branch not protected', () => {
- beforeEach(async () => {
- await createComponent({
- mr: {
- canMerge: true,
- canPushToSourceBranch: true,
- conflictResolutionPath: TEST_HOST,
- sourceBranchProtected: false,
- conflictsDocsPath: '',
- },
- });
- });
-
- it('should allow you to resolve the conflicts', () => {
- expect(findResolveButton().text()).toContain(resolveConflictsBtnText);
- expect(findResolveButton().attributes('href')).toEqual(TEST_HOST);
- });
- });
- });
- });
-});
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
deleted file mode 100644
index 6d8e7056366..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js
+++ /dev/null
@@ -1,158 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import StatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
-import MrWidgetFailedToMerge from '~/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue';
-import eventHub from '~/vue_merge_request_widget/event_hub';
-
-describe('MRWidgetFailedToMerge', () => {
- const dummyIntervalId = 1337;
- let wrapper;
-
- const createComponent = (props = {}, data = {}) => {
- wrapper = shallowMount(MrWidgetFailedToMerge, {
- propsData: {
- mr: {
- mergeError: 'Merge error happened',
- },
- ...props,
- },
- data() {
- return data;
- },
- });
- };
-
- beforeEach(() => {
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- jest.spyOn(window, 'setInterval').mockReturnValue(dummyIntervalId);
- jest.spyOn(window, 'clearInterval').mockImplementation();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('interval', () => {
- it('sets interval to refresh', () => {
- createComponent();
-
- expect(window.setInterval).toHaveBeenCalledWith(wrapper.vm.updateTimer, 1000);
- expect(wrapper.vm.intervalId).toBe(dummyIntervalId);
- });
-
- it('clears interval when destroying ', () => {
- createComponent();
- wrapper.destroy();
-
- expect(window.clearInterval).toHaveBeenCalledWith(dummyIntervalId);
- });
- });
-
- describe('mergeError', () => {
- it('removes forced line breaks', async () => {
- createComponent({ mr: { mergeError: 'contains<br />line breaks<br />' } });
-
- await nextTick();
-
- expect(wrapper.vm.mergeError).toBe('contains line breaks.');
- });
- });
-
- describe('created', () => {
- it('should disable polling', () => {
- createComponent();
-
- expect(eventHub.$emit).toHaveBeenCalledWith('DisablePolling');
- });
- });
-
- describe('methods', () => {
- describe('refresh', () => {
- it('should emit event to request component refresh', () => {
- createComponent();
-
- expect(wrapper.vm.isRefreshing).toBe(false);
-
- wrapper.vm.refresh();
-
- expect(wrapper.vm.isRefreshing).toBe(true);
- expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
- expect(eventHub.$emit).toHaveBeenCalledWith('EnablePolling');
- });
- });
-
- describe('updateTimer', () => {
- it('should update timer and emit event when timer end', () => {
- createComponent();
-
- jest.spyOn(wrapper.vm, 'refresh').mockImplementation(() => {});
-
- expect(wrapper.vm.timer).toEqual(10);
-
- for (let i = 0; i < 10; i += 1) {
- expect(wrapper.vm.timer).toEqual(10 - i);
- wrapper.vm.updateTimer();
- }
-
- expect(wrapper.vm.refresh).toHaveBeenCalled();
- });
- });
- });
-
- describe('while it is refreshing', () => {
- it('renders Refresing now', async () => {
- createComponent({}, { isRefreshing: true });
-
- await nextTick();
-
- expect(wrapper.find('.js-refresh-label').text().trim()).toBe('Refreshing now');
- });
- });
-
- describe('while it is not regresing', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders warning icon and disabled merge button', () => {
- expect(wrapper.find('.js-ci-status-icon-warning')).not.toBeNull();
- expect(wrapper.find(StatusIcon).props('showDisabledButton')).toBe(true);
- });
-
- it('renders given error', () => {
- expect(wrapper.find('.has-error-message').text().trim()).toBe('Merge error happened.');
- });
-
- it('renders refresh button', () => {
- expect(
- wrapper.find('[data-testid="merge-request-failed-refresh-button"]').text().trim(),
- ).toBe('Refresh now');
- });
-
- it('renders remaining time', () => {
- expect(wrapper.find('.has-custom-error').text().trim()).toBe(
- 'Refreshing in 10 seconds to show the updated status...',
- );
- });
- });
-
- it('should just generic merge failed message if merge_error is not available', async () => {
- createComponent({ mr: { mergeError: null } });
-
- await nextTick();
-
- expect(wrapper.text().trim()).toContain('Merge failed.');
- expect(wrapper.text().trim()).not.toContain('Merge error happened.');
- });
-
- it('should show refresh label when refresh requested', async () => {
- createComponent();
-
- wrapper.vm.refresh();
-
- await nextTick();
-
- expect(wrapper.text().trim()).not.toContain('Merge failed. Refreshing');
- expect(wrapper.text().trim()).toContain('Refreshing now');
- });
-});
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
deleted file mode 100644
index 29ee7e0010f..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_merged_spec.js
+++ /dev/null
@@ -1,235 +0,0 @@
-import { getByRole } from '@testing-library/dom';
-import Vue, { nextTick } from 'vue';
-import mountComponent from 'helpers/vue_mount_component_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { OPEN_REVERT_MODAL, OPEN_CHERRY_PICK_MODAL } from '~/projects/commit/constants';
-import modalEventHub from '~/projects/commit/event_hub';
-import mergedComponent from '~/vue_merge_request_widget/components/states/mr_widget_merged.vue';
-import eventHub from '~/vue_merge_request_widget/event_hub';
-
-describe('MRWidgetMerged', () => {
- let vm;
- const targetBranch = 'foo';
- const selectors = {
- get copyMergeShaButton() {
- return vm.$el.querySelector('button.js-mr-merged-copy-sha');
- },
- get mergeCommitShaLink() {
- return vm.$el.querySelector('a.js-mr-merged-commit-sha');
- },
- };
-
- beforeEach(() => {
- jest.spyOn(document, 'dispatchEvent');
- const Component = Vue.extend(mergedComponent);
- const mr = {
- isRemovingSourceBranch: false,
- cherryPickInForkPath: false,
- canCherryPickInCurrentMR: true,
- revertInForkPath: false,
- canRevertInCurrentMR: true,
- canRemoveSourceBranch: true,
- sourceBranchRemoved: true,
- metrics: {
- mergedBy: {
- name: 'Administrator',
- username: 'root',
- webUrl: 'http://localhost:3000/root',
- avatarUrl:
- 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- },
- mergedAt: 'Jan 24, 2018 1:02pm UTC',
- readableMergedAt: '',
- closedBy: {},
- closedAt: 'Jan 24, 2018 1:02pm UTC',
- readableClosedAt: '',
- },
- updatedAt: 'mergedUpdatedAt',
- shortMergeCommitSha: '958c0475',
- mergeCommitSha: '958c047516e182dfc52317f721f696e8a1ee85ed',
- mergeCommitPath:
- 'http://localhost:3000/root/nautilus/commit/f7ce827c314c9340b075657fd61c789fb01cf74d',
- sourceBranch: 'bar',
- targetBranch,
- };
-
- const service = {
- removeSourceBranch() {},
- };
-
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
-
- vm = mountComponent(Component, { mr, service });
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('computed', () => {
- describe('shouldShowRemoveSourceBranch', () => {
- it('returns true when sourceBranchRemoved is false', () => {
- vm.mr.sourceBranchRemoved = false;
-
- expect(vm.shouldShowRemoveSourceBranch).toEqual(true);
- });
-
- it('returns false when sourceBranchRemoved is true', () => {
- vm.mr.sourceBranchRemoved = true;
-
- expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
- });
-
- it('returns false when canRemoveSourceBranch is false', () => {
- vm.mr.sourceBranchRemoved = false;
- vm.mr.canRemoveSourceBranch = false;
-
- expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
- });
-
- it('returns false when is making request', () => {
- vm.mr.canRemoveSourceBranch = true;
- vm.isMakingRequest = true;
-
- expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
- });
-
- it('returns true when all are true', () => {
- vm.mr.isRemovingSourceBranch = true;
- vm.mr.canRemoveSourceBranch = true;
- vm.isMakingRequest = true;
-
- expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
- });
- });
-
- describe('shouldShowSourceBranchRemoving', () => {
- it('should correct value when fields changed', () => {
- vm.mr.sourceBranchRemoved = false;
-
- expect(vm.shouldShowSourceBranchRemoving).toEqual(false);
-
- vm.mr.sourceBranchRemoved = true;
-
- expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
-
- vm.mr.sourceBranchRemoved = false;
- vm.isMakingRequest = true;
-
- expect(vm.shouldShowSourceBranchRemoving).toEqual(true);
-
- vm.isMakingRequest = false;
- vm.mr.isRemovingSourceBranch = true;
-
- expect(vm.shouldShowSourceBranchRemoving).toEqual(true);
- });
- });
- });
-
- describe('methods', () => {
- describe('removeSourceBranch', () => {
- it('should set flag and call service then request main component to update the widget', async () => {
- jest.spyOn(vm.service, 'removeSourceBranch').mockReturnValue(
- new Promise((resolve) => {
- resolve({
- data: {
- message: 'Branch was deleted',
- },
- });
- }),
- );
-
- vm.removeSourceBranch();
-
- await waitForPromises();
-
- const args = eventHub.$emit.mock.calls[0];
-
- expect(vm.isMakingRequest).toEqual(true);
- expect(args[0]).toEqual('MRWidgetUpdateRequested');
- expect(args[1]).not.toThrow();
- });
- });
- });
-
- it('calls dispatchDocumentEvent to load in the modal component', () => {
- expect(document.dispatchEvent).toHaveBeenCalledWith(new CustomEvent('merged:UpdateActions'));
- });
-
- it('emits event to open the revert modal on revert button click', () => {
- const eventHubSpy = jest.spyOn(modalEventHub, '$emit');
-
- getByRole(vm.$el, 'button', { name: /Revert/i }).click();
-
- expect(eventHubSpy).toHaveBeenCalledWith(OPEN_REVERT_MODAL);
- });
-
- it('emits event to open the cherry-pick modal on cherry-pick button click', () => {
- const eventHubSpy = jest.spyOn(modalEventHub, '$emit');
-
- getByRole(vm.$el, 'button', { name: /Cherry-pick/i }).click();
-
- expect(eventHubSpy).toHaveBeenCalledWith(OPEN_CHERRY_PICK_MODAL);
- });
-
- it('has merged by information', () => {
- expect(vm.$el.textContent).toContain('Merged by');
- expect(vm.$el.textContent).toContain('Administrator');
- });
-
- it('renders branch information', () => {
- expect(vm.$el.textContent).toContain('The changes were merged into');
- expect(vm.$el.textContent).toContain(targetBranch);
- });
-
- it('renders information about branch being deleted', () => {
- expect(vm.$el.textContent).toContain('The source branch has been deleted');
- });
-
- it('shows revert and cherry-pick buttons', () => {
- expect(vm.$el.textContent).toContain('Revert');
- expect(vm.$el.textContent).toContain('Cherry-pick');
- });
-
- it('shows button to copy commit SHA to clipboard', () => {
- expect(selectors.copyMergeShaButton).not.toBe(null);
- expect(selectors.copyMergeShaButton.dataset.clipboardText).toBe(vm.mr.mergeCommitSha);
- });
-
- it('hides button to copy commit SHA if SHA does not exist', async () => {
- vm.mr.mergeCommitSha = null;
-
- await nextTick();
-
- expect(selectors.copyMergeShaButton).toBe(null);
- expect(vm.$el.querySelector('.mr-info-list').innerText).not.toContain('with');
- });
-
- it('shows merge commit SHA link', () => {
- expect(selectors.mergeCommitShaLink).not.toBe(null);
- expect(selectors.mergeCommitShaLink.text).toContain(vm.mr.shortMergeCommitSha);
- expect(selectors.mergeCommitShaLink.href).toBe(vm.mr.mergeCommitPath);
- });
-
- it('should not show source branch deleted text', async () => {
- vm.mr.sourceBranchRemoved = false;
-
- await nextTick();
-
- expect(vm.$el.innerText).not.toContain('The source branch has been deleted');
- });
-
- it('should show source branch deleting text', async () => {
- vm.mr.isRemovingSourceBranch = true;
- vm.mr.sourceBranchRemoved = false;
-
- await nextTick();
-
- expect(vm.$el.innerText).toContain('The source branch is being deleted');
- expect(vm.$el.innerText).not.toContain('The source branch has been deleted');
- });
-
- it('should use mergedEvent mergedAt as tooltip title', () => {
- expect(vm.$el.querySelector('time').getAttribute('title')).toBe('Jan 24, 2018 1:02pm UTC');
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_merging_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_merging_spec.js
deleted file mode 100644
index e16c897a49b..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_merging_spec.js
+++ /dev/null
@@ -1,74 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import simplePoll from '~/lib/utils/simple_poll';
-import MrWidgetMerging from '~/vue_merge_request_widget/components/states/mr_widget_merging.vue';
-
-jest.mock('~/lib/utils/simple_poll', () =>
- jest.fn().mockImplementation(jest.requireActual('~/lib/utils/simple_poll').default),
-);
-
-describe('MRWidgetMerging', () => {
- let wrapper;
-
- const GlEmoji = { template: '<img />' };
- beforeEach(() => {
- wrapper = shallowMount(MrWidgetMerging, {
- propsData: {
- mr: {
- targetBranchPath: '/branch-path',
- targetBranch: 'branch',
- transitionStateMachine() {},
- },
- service: {
- poll: jest.fn().mockResolvedValue(),
- },
- },
- stubs: {
- GlEmoji,
- },
- });
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders information about merge request being merged', () => {
- expect(
- wrapper
- .find('.media-body')
- .text()
- .trim()
- .replace(/\s\s+/g, ' ')
- .replace(/[\r\n]+/g, ' '),
- ).toContain('Merging!');
- });
-
- it('renders branch information', () => {
- expect(
- wrapper
- .find('.mr-info-list')
- .text()
- .trim()
- .replace(/\s\s+/g, ' ')
- .replace(/[\r\n]+/g, ' '),
- ).toEqual('Merges changes into branch');
-
- expect(wrapper.find('a').attributes('href')).toBe('/branch-path');
- });
-
- describe('initiateMergePolling', () => {
- it('should call simplePoll', () => {
- wrapper.vm.initiateMergePolling();
-
- expect(simplePoll).toHaveBeenCalledWith(expect.any(Function), { timeout: 0 });
- });
-
- it('should call handleMergePolling', () => {
- jest.spyOn(wrapper.vm, 'handleMergePolling').mockImplementation(() => {});
-
- wrapper.vm.initiateMergePolling();
-
- expect(wrapper.vm.handleMergePolling).toHaveBeenCalled();
- });
- });
-});
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
deleted file mode 100644
index ddce07954ab..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import MissingBranchComponent from '~/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue';
-
-let wrapper;
-
-async function factory(sourceBranchRemoved, mergeRequestWidgetGraphql) {
- wrapper = shallowMount(MissingBranchComponent, {
- propsData: {
- mr: { sourceBranchRemoved },
- },
- provide: {
- glFeatures: { mergeRequestWidgetGraphql },
- },
- });
-
- if (mergeRequestWidgetGraphql) {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({ state: { sourceBranchExists: !sourceBranchRemoved } });
- }
-
- await nextTick();
-}
-
-describe('MRWidgetMissingBranch', () => {
- afterEach(() => {
- wrapper.destroy();
- });
-
- [true, false].forEach((mergeRequestWidgetGraphql) => {
- describe(`widget GraphQL feature flag is ${
- mergeRequestWidgetGraphql ? 'enabled' : 'disabled'
- }`, () => {
- it.each`
- sourceBranchRemoved | branchName
- ${true} | ${'source'}
- ${false} | ${'target'}
- `(
- 'should set missing branch name as $branchName when sourceBranchRemoved is $sourceBranchRemoved',
- async ({ sourceBranchRemoved, branchName }) => {
- await factory(sourceBranchRemoved, mergeRequestWidgetGraphql);
-
- expect(wrapper.find('[data-testid="widget-content"]').text()).toContain(branchName);
- },
- );
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_not_allowed_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_not_allowed_spec.js
deleted file mode 100644
index 63e93074857..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_not_allowed_spec.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import Vue from 'vue';
-import mountComponent from 'helpers/vue_mount_component_helper';
-import notAllowedComponent from '~/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue';
-
-describe('MRWidgetNotAllowed', () => {
- let vm;
- beforeEach(() => {
- const Component = Vue.extend(notAllowedComponent);
- vm = mountComponent(Component);
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('renders success icon', () => {
- expect(vm.$el.querySelector('.ci-status-icon-success')).not.toBe(null);
- });
-
- it('renders informative text', () => {
- expect(vm.$el.innerText).toContain('Ready to be merged automatically.');
- expect(vm.$el.innerText).toContain(
- 'Ask someone with write access to this repository to merge this request',
- );
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_nothing_to_merge_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_nothing_to_merge_spec.js
deleted file mode 100644
index c7c0b69425d..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_nothing_to_merge_spec.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import Vue, { nextTick } from 'vue';
-import NothingToMerge from '~/vue_merge_request_widget/components/states/nothing_to_merge.vue';
-
-describe('NothingToMerge', () => {
- describe('template', () => {
- const Component = Vue.extend(NothingToMerge);
- const newBlobPath = '/foo';
- const vm = new Component({
- el: document.createElement('div'),
- propsData: {
- mr: { newBlobPath },
- },
- });
-
- it('should have correct elements', () => {
- expect(vm.$el.classList.contains('mr-widget-body')).toBeTruthy();
- expect(vm.$el.querySelector('[data-testid="createFileButton"]').href).toContain(newBlobPath);
- expect(vm.$el.innerText).toContain('Use merge requests to propose changes to your project');
- });
-
- it('should not show new blob link if there is no link available', () => {
- vm.mr.newBlobPath = null;
- nextTick(() => {
- expect(vm.$el.querySelector('[data-testid="createFileButton"]')).toEqual(null);
- });
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_pipeline_blocked_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_pipeline_blocked_spec.js
deleted file mode 100644
index 9b10b078e89..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_pipeline_blocked_spec.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import { shallowMount, mount } from '@vue/test-utils';
-import PipelineBlockedComponent from '~/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue';
-
-describe('MRWidgetPipelineBlocked', () => {
- let wrapper;
-
- const createWrapper = (mountFn = shallowMount) => {
- wrapper = mountFn(PipelineBlockedComponent);
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders warning icon', () => {
- createWrapper(mount);
-
- expect(wrapper.find('.ci-status-icon-warning').exists()).toBe(true);
- });
-
- it('renders information text', () => {
- createWrapper();
-
- expect(wrapper.text()).toBe(
- "Merge blocked: pipeline must succeed. It's waiting for a manual action to continue.",
- );
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_pipeline_failed_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_pipeline_failed_spec.js
deleted file mode 100644
index 3e0840fef4e..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_pipeline_failed_spec.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import statusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
-import PipelineFailed from '~/vue_merge_request_widget/components/states/pipeline_failed.vue';
-
-describe('PipelineFailed', () => {
- let wrapper;
-
- const createComponent = () => {
- wrapper = shallowMount(PipelineFailed);
- };
-
- const findStatusIcon = () => wrapper.find(statusIcon);
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- it('should render error message with a disabled merge button', () => {
- expect(wrapper.element).toMatchSnapshot();
- });
-
- it('merge button should be disabled', () => {
- expect(findStatusIcon().props('showDisabledButton')).toBe(true);
- });
-});
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
deleted file mode 100644
index 46d90ddc83c..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
+++ /dev/null
@@ -1,905 +0,0 @@
-import { createLocalVue, shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import { GlSprintf } from '@gitlab/ui';
-import VueApollo from 'vue-apollo';
-import produce from 'immer';
-import readyToMergeResponse from 'test_fixtures/graphql/merge_requests/states/ready_to_merge.query.graphql.json';
-import waitForPromises from 'helpers/wait_for_promises';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import readyToMergeQuery from 'ee_else_ce/vue_merge_request_widget/queries/states/ready_to_merge.query.graphql';
-import simplePoll from '~/lib/utils/simple_poll';
-import CommitEdit from '~/vue_merge_request_widget/components/states/commit_edit.vue';
-import CommitMessageDropdown from '~/vue_merge_request_widget/components/states/commit_message_dropdown.vue';
-import CommitsHeader from '~/vue_merge_request_widget/components/states/commits_header.vue';
-import ReadyToMerge from '~/vue_merge_request_widget/components/states/ready_to_merge.vue';
-import SquashBeforeMerge from '~/vue_merge_request_widget/components/states/squash_before_merge.vue';
-import MergeFailedPipelineConfirmationDialog from '~/vue_merge_request_widget/components/states/merge_failed_pipeline_confirmation_dialog.vue';
-import { MWPS_MERGE_STRATEGY } from '~/vue_merge_request_widget/constants';
-import eventHub from '~/vue_merge_request_widget/event_hub';
-
-jest.mock('~/lib/utils/simple_poll', () =>
- jest.fn().mockImplementation(jest.requireActual('~/lib/utils/simple_poll').default),
-);
-jest.mock('~/commons/nav/user_merge_requests', () => ({
- refreshUserMergeRequestCounts: jest.fn(),
-}));
-
-const commitMessage = readyToMergeResponse.data.project.mergeRequest.defaultMergeCommitMessage;
-const squashCommitMessage =
- readyToMergeResponse.data.project.mergeRequest.defaultSquashCommitMessage;
-const commitMessageWithDescription =
- readyToMergeResponse.data.project.mergeRequest.defaultMergeCommitMessageWithDescription;
-const createTestMr = (customConfig) => {
- const mr = {
- isPipelineActive: false,
- pipeline: null,
- isPipelineFailed: false,
- isPipelinePassing: false,
- isMergeAllowed: true,
- isApproved: true,
- onlyAllowMergeIfPipelineSucceeds: false,
- ffOnlyEnabled: false,
- hasCI: false,
- ciStatus: null,
- sha: '12345678',
- squash: false,
- squashIsEnabledByDefault: false,
- squashIsReadonly: false,
- squashIsSelected: false,
- commitMessage,
- squashCommitMessage,
- commitMessageWithDescription,
- defaultMergeCommitMessage: commitMessage,
- defaultSquashCommitMessage: squashCommitMessage,
- shouldRemoveSourceBranch: true,
- canRemoveSourceBranch: false,
- targetBranch: 'main',
- preferredAutoMergeStrategy: MWPS_MERGE_STRATEGY,
- availableAutoMergeStrategies: [MWPS_MERGE_STRATEGY],
- mergeImmediatelyDocsPath: 'path/to/merge/immediately/docs',
- transitionStateMachine: (transition) => eventHub.$emit('StateMachineValueChanged', transition),
- translateStateToMachine: () => this.transitionStateMachine(),
- state: 'open',
- };
-
- Object.assign(mr, customConfig.mr);
-
- return mr;
-};
-
-const createTestService = () => ({
- merge: jest.fn(),
- poll: jest.fn().mockResolvedValue(),
-});
-const localVue = createLocalVue();
-localVue.use(VueApollo);
-
-let wrapper;
-let readyToMergeResponseSpy;
-
-const findMergeButton = () => wrapper.find('[data-testid="merge-button"]');
-const findPipelineFailedConfirmModal = () =>
- wrapper.findComponent(MergeFailedPipelineConfirmationDialog);
-
-const createReadyToMergeResponse = (customMr) => {
- return produce(readyToMergeResponse, (draft) => {
- Object.assign(draft.data.project.mergeRequest, customMr);
- });
-};
-
-const createComponent = (
- customConfig = {},
- mergeRequestWidgetGraphql = false,
- restructuredMrWidget = false,
-) => {
- wrapper = shallowMount(ReadyToMerge, {
- localVue,
- propsData: {
- mr: createTestMr(customConfig),
- service: createTestService(),
- },
- provide: {
- glFeatures: {
- mergeRequestWidgetGraphql,
- restructuredMrWidget,
- },
- },
- stubs: {
- CommitEdit,
- },
- apolloProvider: createMockApollo([[readyToMergeQuery, readyToMergeResponseSpy]]),
- });
-};
-
-const findCheckboxElement = () => wrapper.find(SquashBeforeMerge);
-const findCommitsHeaderElement = () => wrapper.find(CommitsHeader);
-const findCommitEditElements = () => wrapper.findAll(CommitEdit);
-const findCommitDropdownElement = () => wrapper.find(CommitMessageDropdown);
-const findFirstCommitEditLabel = () => findCommitEditElements().at(0).props('label');
-const findTipLink = () => wrapper.find(GlSprintf);
-const findCommitEditWithInputId = (inputId) =>
- findCommitEditElements().wrappers.find((x) => x.props('inputId') === inputId);
-const findMergeCommitMessage = () => findCommitEditWithInputId('merge-message-edit').props('value');
-const findSquashCommitMessage = () =>
- findCommitEditWithInputId('squash-message-edit').props('value');
-
-const triggerApprovalUpdated = () => eventHub.$emit('ApprovalUpdated');
-
-describe('ReadyToMerge', () => {
- beforeEach(() => {
- readyToMergeResponseSpy = jest.fn().mockResolvedValueOnce(readyToMergeResponse);
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('computed', () => {
- describe('isAutoMergeAvailable', () => {
- it('should return true when at least one merge strategy is available', () => {
- createComponent();
-
- expect(wrapper.vm.isAutoMergeAvailable).toBe(true);
- });
-
- it('should return false when no merge strategies are available', () => {
- createComponent({ mr: { availableAutoMergeStrategies: [] } });
-
- expect(wrapper.vm.isAutoMergeAvailable).toBe(false);
- });
- });
-
- describe('status', () => {
- it('defaults to success', () => {
- createComponent({ mr: { pipeline: true, availableAutoMergeStrategies: [] } });
-
- expect(wrapper.vm.status).toEqual('success');
- });
-
- it('returns failed when MR has CI but also has an unknown status', () => {
- createComponent({ mr: { hasCI: true } });
-
- expect(wrapper.vm.status).toEqual('failed');
- });
-
- it('returns default when MR has no pipeline', () => {
- createComponent({ mr: { availableAutoMergeStrategies: [] } });
-
- expect(wrapper.vm.status).toEqual('success');
- });
-
- it('returns pending when pipeline is active', () => {
- createComponent({ mr: { pipeline: {}, isPipelineActive: true } });
-
- expect(wrapper.vm.status).toEqual('pending');
- });
-
- it('returns failed when pipeline is failed', () => {
- createComponent({
- mr: { pipeline: {}, isPipelineFailed: true, availableAutoMergeStrategies: [] },
- });
-
- expect(wrapper.vm.status).toEqual('failed');
- });
- });
-
- describe('Merge Button Variant', () => {
- it('defaults to confirm class', () => {
- createComponent({
- mr: { availableAutoMergeStrategies: [] },
- });
-
- expect(findMergeButton().attributes('variant')).toBe('confirm');
- });
- });
-
- describe('status icon', () => {
- it('defaults to tick icon', () => {
- createComponent();
-
- expect(wrapper.vm.iconClass).toEqual('success');
- });
-
- it('shows tick for success status', () => {
- createComponent({ mr: { pipeline: true } });
-
- expect(wrapper.vm.iconClass).toEqual('success');
- });
-
- it('shows tick for pending status', () => {
- createComponent({ mr: { pipeline: {}, isPipelineActive: true } });
-
- expect(wrapper.vm.iconClass).toEqual('success');
- });
- });
-
- describe('mergeButtonText', () => {
- it('should return "Merge" when no auto merge strategies are available', () => {
- createComponent({ mr: { availableAutoMergeStrategies: [] } });
-
- expect(wrapper.vm.mergeButtonText).toEqual('Merge');
- });
-
- it('should return "Merge in progress"', async () => {
- createComponent();
-
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({ isMergingImmediately: true });
-
- await nextTick();
-
- expect(wrapper.vm.mergeButtonText).toEqual('Merge in progress');
- });
-
- it('should return "Merge when pipeline succeeds" when the MWPS auto merge strategy is available', () => {
- createComponent({
- mr: { isMergingImmediately: false, preferredAutoMergeStrategy: MWPS_MERGE_STRATEGY },
- });
-
- expect(wrapper.vm.mergeButtonText).toEqual('Merge when pipeline succeeds');
- });
- });
-
- describe('autoMergeText', () => {
- it('should return Merge when pipeline succeeds', () => {
- createComponent({ mr: { preferredAutoMergeStrategy: MWPS_MERGE_STRATEGY } });
-
- expect(wrapper.vm.autoMergeText).toEqual('Merge when pipeline succeeds');
- });
- });
-
- describe('shouldShowMergeImmediatelyDropdown', () => {
- it('should return false if no pipeline is active', () => {
- createComponent({
- mr: { isPipelineActive: false, onlyAllowMergeIfPipelineSucceeds: false },
- });
-
- expect(wrapper.vm.shouldShowMergeImmediatelyDropdown).toBe(false);
- });
-
- it('should return false if "Pipelines must succeed" is enabled for the current project', () => {
- createComponent({ mr: { isPipelineActive: true, onlyAllowMergeIfPipelineSucceeds: true } });
-
- expect(wrapper.vm.shouldShowMergeImmediatelyDropdown).toBe(false);
- });
- });
-
- describe('isMergeButtonDisabled', () => {
- it('should return false with initial data', () => {
- createComponent({ mr: { isMergeAllowed: true } });
-
- expect(wrapper.vm.isMergeButtonDisabled).toBe(false);
- });
-
- it('should return true when there is no commit message', () => {
- createComponent({ mr: { isMergeAllowed: true, commitMessage: '' } });
-
- expect(wrapper.vm.isMergeButtonDisabled).toBe(true);
- });
-
- it('should return true if merge is not allowed', () => {
- createComponent({
- mr: {
- isMergeAllowed: false,
- availableAutoMergeStrategies: [],
- onlyAllowMergeIfPipelineSucceeds: true,
- },
- });
-
- expect(wrapper.vm.isMergeButtonDisabled).toBe(true);
- });
-
- it('should return true when the vm instance is making request', async () => {
- createComponent({ mr: { isMergeAllowed: true } });
-
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({ isMakingRequest: true });
-
- await nextTick();
-
- expect(wrapper.vm.isMergeButtonDisabled).toBe(true);
- });
- });
- });
-
- describe('methods', () => {
- describe('handleMergeButtonClick', () => {
- const response = (status) => ({
- data: {
- status,
- },
- });
-
- beforeEach(() => {
- readyToMergeResponseSpy = jest
- .fn()
- .mockResolvedValueOnce(createReadyToMergeResponse({ squash: true, squashOnMerge: true }))
- .mockResolvedValue(
- createReadyToMergeResponse({
- squash: true,
- squashOnMerge: true,
- defaultMergeCommitMessage: '',
- defaultSquashCommitMessage: '',
- }),
- );
- });
-
- it('should handle merge when pipeline succeeds', async () => {
- createComponent();
-
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- jest
- .spyOn(wrapper.vm.service, 'merge')
- .mockResolvedValue(response('merge_when_pipeline_succeeds'));
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({ removeSourceBranch: false });
-
- wrapper.vm.handleMergeButtonClick(true);
-
- await waitForPromises();
-
- expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
- expect(eventHub.$emit).toHaveBeenCalledWith('StateMachineValueChanged', {
- transition: 'start-auto-merge',
- });
-
- const params = wrapper.vm.service.merge.mock.calls[0][0];
-
- expect(params).toEqual(
- expect.objectContaining({
- sha: wrapper.vm.mr.sha,
- commit_message: wrapper.vm.mr.commitMessage,
- should_remove_source_branch: false,
- auto_merge_strategy: 'merge_when_pipeline_succeeds',
- }),
- );
- });
-
- it('should handle merge failed', async () => {
- createComponent();
-
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- jest.spyOn(wrapper.vm.service, 'merge').mockResolvedValue(response('failed'));
- wrapper.vm.handleMergeButtonClick(false, true);
-
- await waitForPromises();
-
- expect(eventHub.$emit).toHaveBeenCalledWith('FailedToMerge', undefined);
-
- const params = wrapper.vm.service.merge.mock.calls[0][0];
-
- expect(params.should_remove_source_branch).toBeTruthy();
- expect(params.auto_merge_strategy).toBeUndefined();
- });
-
- it('should handle merge action accepted case', async () => {
- createComponent();
-
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- jest.spyOn(wrapper.vm.service, 'merge').mockResolvedValue(response('success'));
- jest.spyOn(wrapper.vm.mr, 'transitionStateMachine');
- wrapper.vm.handleMergeButtonClick();
-
- expect(eventHub.$emit).toHaveBeenCalledWith('StateMachineValueChanged', {
- transition: 'start-merge',
- });
-
- await waitForPromises();
-
- expect(wrapper.vm.mr.transitionStateMachine).toHaveBeenCalledWith({
- transition: 'start-merge',
- });
-
- const params = wrapper.vm.service.merge.mock.calls[0][0];
-
- expect(params.should_remove_source_branch).toBeTruthy();
- expect(params.auto_merge_strategy).toBeUndefined();
- });
-
- it('hides edit commit message', async () => {
- createComponent({}, true, true);
-
- await waitForPromises();
-
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- jest.spyOn(wrapper.vm.service, 'merge').mockResolvedValue(response('success'));
-
- await wrapper
- .findComponent('[data-testid="widget_edit_commit_message"]')
- .vm.$emit('input', true);
-
- expect(wrapper.findComponent('[data-testid="edit_commit_message"]').exists()).toBe(true);
-
- wrapper.vm.handleMergeButtonClick();
-
- await waitForPromises();
-
- expect(wrapper.findComponent('[data-testid="edit_commit_message"]').exists()).toBe(false);
- });
- });
-
- describe('initiateRemoveSourceBranchPolling', () => {
- it('should emit event and call simplePoll', () => {
- createComponent();
-
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
-
- wrapper.vm.initiateRemoveSourceBranchPolling();
-
- expect(eventHub.$emit).toHaveBeenCalledWith('SetBranchRemoveFlag', [true]);
- expect(simplePoll).toHaveBeenCalled();
- });
- });
-
- describe('handleRemoveBranchPolling', () => {
- const response = (state) => ({
- data: {
- source_branch_exists: state,
- },
- });
-
- it('should call start and stop polling when MR merged', async () => {
- createComponent();
-
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- jest.spyOn(wrapper.vm.service, 'poll').mockResolvedValue(response(false));
-
- let cpc = false; // continuePollingCalled
- let spc = false; // stopPollingCalled
-
- wrapper.vm.handleRemoveBranchPolling(
- () => {
- cpc = true;
- },
- () => {
- spc = true;
- },
- );
-
- await waitForPromises();
-
- expect(wrapper.vm.service.poll).toHaveBeenCalled();
-
- const args = eventHub.$emit.mock.calls[0];
-
- expect(args[0]).toEqual('MRWidgetUpdateRequested');
- expect(args[1]).toBeDefined();
- args[1]();
-
- expect(eventHub.$emit).toHaveBeenCalledWith('SetBranchRemoveFlag', [false]);
-
- expect(cpc).toBeFalsy();
- expect(spc).toBeTruthy();
- });
-
- it('should continue polling until MR is merged', async () => {
- createComponent();
-
- jest.spyOn(wrapper.vm.service, 'poll').mockResolvedValue(response(true));
-
- let cpc = false; // continuePollingCalled
- let spc = false; // stopPollingCalled
-
- wrapper.vm.handleRemoveBranchPolling(
- () => {
- cpc = true;
- },
- () => {
- spc = true;
- },
- );
-
- await waitForPromises();
-
- expect(cpc).toBeTruthy();
- expect(spc).toBeFalsy();
- });
- });
- });
-
- describe('Remove source branch checkbox', () => {
- describe('when user can merge but cannot delete branch', () => {
- it('should be disabled in the rendered output', () => {
- createComponent();
-
- expect(wrapper.find('#remove-source-branch-input').exists()).toBe(false);
- });
- });
-
- describe('when user can merge and can delete branch', () => {
- beforeEach(() => {
- createComponent({
- mr: { canRemoveSourceBranch: true },
- });
- });
-
- it('isRemoveSourceBranchButtonDisabled should be false', () => {
- expect(wrapper.find('#remove-source-branch-input').props('disabled')).toBe(undefined);
- });
- });
- });
-
- describe('render children components', () => {
- describe('squash checkbox', () => {
- it('should be rendered when squash before merge is enabled and there is more than 1 commit', () => {
- createComponent({
- mr: { commitsCount: 2, enableSquashBeforeMerge: true },
- });
-
- expect(findCheckboxElement().exists()).toBeTruthy();
- });
-
- it('should not be rendered when squash before merge is disabled', () => {
- createComponent({ mr: { commitsCount: 2, enableSquashBeforeMerge: false } });
-
- expect(findCheckboxElement().exists()).toBeFalsy();
- });
-
- it('should be rendered when there is only 1 commit', () => {
- createComponent({ mr: { commitsCount: 1, enableSquashBeforeMerge: true } });
-
- expect(findCheckboxElement().exists()).toBe(true);
- });
-
- describe('squash options', () => {
- it.each`
- squashState | state | prop | expectation
- ${'squashIsReadonly'} | ${'enabled'} | ${'isDisabled'} | ${false}
- ${'squashIsSelected'} | ${'selected'} | ${'value'} | ${false}
- ${'squashIsSelected'} | ${'unselected'} | ${'value'} | ${false}
- `(
- 'is $state when squashIsReadonly returns $expectation ',
- ({ squashState, prop, expectation }) => {
- createComponent({
- mr: { commitsCount: 2, enableSquashBeforeMerge: true, [squashState]: expectation },
- });
-
- expect(findCheckboxElement().props(prop)).toBe(expectation);
- },
- );
-
- it('is not rendered for "Do not allow" option', () => {
- createComponent({
- mr: {
- commitsCount: 2,
- enableSquashBeforeMerge: true,
- squashIsReadonly: true,
- squashIsSelected: false,
- },
- });
-
- expect(findCheckboxElement().exists()).toBe(false);
- });
- });
- });
-
- describe('commits count collapsible header', () => {
- it('should be rendered when fast-forward is disabled', () => {
- createComponent();
-
- expect(findCommitsHeaderElement().exists()).toBeTruthy();
- });
-
- describe('when fast-forward is enabled', () => {
- it('should be rendered if squash and squash before are enabled and there is more than 1 commit', () => {
- createComponent({
- mr: {
- ffOnlyEnabled: true,
- enableSquashBeforeMerge: true,
- squashIsSelected: true,
- commitsCount: 2,
- },
- });
-
- expect(findCommitsHeaderElement().exists()).toBeTruthy();
- });
-
- it('should not be rendered if squash before merge is disabled', () => {
- createComponent({
- mr: {
- ffOnlyEnabled: true,
- enableSquashBeforeMerge: false,
- squash: true,
- commitsCount: 2,
- },
- });
-
- expect(findCommitsHeaderElement().exists()).toBeFalsy();
- });
-
- it('should not be rendered if squash is disabled', () => {
- createComponent({
- mr: {
- ffOnlyEnabled: true,
- squash: false,
- enableSquashBeforeMerge: true,
- commitsCount: 2,
- },
- });
-
- expect(findCommitsHeaderElement().exists()).toBeFalsy();
- });
-
- it('should not be rendered if commits count is 1', () => {
- createComponent({
- mr: {
- ffOnlyEnabled: true,
- squash: true,
- enableSquashBeforeMerge: true,
- commitsCount: 1,
- },
- });
-
- expect(findCommitsHeaderElement().exists()).toBeFalsy();
- });
- });
- });
-
- describe('commits edit components', () => {
- describe('when fast-forward merge is enabled', () => {
- it('should not be rendered if squash is disabled', () => {
- createComponent({
- mr: {
- ffOnlyEnabled: true,
- squash: false,
- enableSquashBeforeMerge: true,
- commitsCount: 2,
- },
- });
-
- expect(findCommitEditElements().length).toBe(0);
- });
-
- it('should not be rendered if squash before merge is disabled', () => {
- createComponent({
- mr: {
- ffOnlyEnabled: true,
- squash: true,
- enableSquashBeforeMerge: false,
- commitsCount: 2,
- },
- });
-
- expect(findCommitEditElements().length).toBe(0);
- });
-
- it('should not be rendered if there is only one commit', () => {
- createComponent({
- mr: {
- ffOnlyEnabled: true,
- squash: true,
- enableSquashBeforeMerge: true,
- commitsCount: 1,
- },
- });
-
- expect(findCommitEditElements().length).toBe(0);
- });
-
- it('should have one edit component if squash is enabled and there is more than 1 commit', () => {
- createComponent({
- mr: {
- ffOnlyEnabled: true,
- squashIsSelected: true,
- enableSquashBeforeMerge: true,
- commitsCount: 2,
- },
- });
-
- expect(findCommitEditElements().length).toBe(1);
- expect(findFirstCommitEditLabel()).toBe('Squash commit message');
- });
- });
-
- it('should have one edit component when squash is disabled', () => {
- createComponent();
-
- expect(findCommitEditElements().length).toBe(1);
- });
-
- it('should have two edit components when squash is enabled and there is more than 1 commit', () => {
- createComponent({
- mr: {
- commitsCount: 2,
- squashIsSelected: true,
- enableSquashBeforeMerge: true,
- },
- });
-
- expect(findCommitEditElements().length).toBe(2);
- });
-
- it('should have two edit components when squash is enabled and there is more than 1 commit and mergeRequestWidgetGraphql is enabled', async () => {
- createComponent(
- {
- mr: {
- commitsCount: 2,
- squashIsSelected: true,
- enableSquashBeforeMerge: true,
- },
- },
- true,
- );
-
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- loading: false,
- state: {
- ...createTestMr({}),
- userPermissions: {},
- squash: true,
- mergeable: true,
- commitCount: 2,
- commitsWithoutMergeCommits: {},
- },
- });
- await nextTick();
-
- expect(findCommitEditElements().length).toBe(2);
- });
-
- it('should have one edit components when squash is enabled and there is 1 commit only', () => {
- createComponent({
- mr: {
- commitsCount: 1,
- squash: true,
- enableSquashBeforeMerge: true,
- },
- });
-
- expect(findCommitEditElements().length).toBe(1);
- });
-
- it('should have correct edit merge commit label', () => {
- createComponent();
-
- expect(findFirstCommitEditLabel()).toBe('Merge commit message');
- });
-
- it('should have correct edit squash commit label', () => {
- createComponent({
- mr: {
- commitsCount: 2,
- squashIsSelected: true,
- enableSquashBeforeMerge: true,
- },
- });
-
- expect(findFirstCommitEditLabel()).toBe('Squash commit message');
- });
- });
-
- describe('commits dropdown', () => {
- it('should not be rendered if squash is disabled', () => {
- createComponent();
-
- expect(findCommitDropdownElement().exists()).toBeFalsy();
- });
-
- it('should be rendered if squash is enabled and there is more than 1 commit', () => {
- createComponent({
- mr: { enableSquashBeforeMerge: true, squashIsSelected: true, commitsCount: 2 },
- });
-
- expect(findCommitDropdownElement().exists()).toBeTruthy();
- });
- });
-
- it('renders a tip including a link to docs on templates', () => {
- createComponent();
-
- expect(findTipLink().exists()).toBe(true);
- });
- });
-
- describe('Merge request project settings', () => {
- describe('when the merge commit merge method is enabled', () => {
- beforeEach(() => {
- createComponent({
- mr: { ffOnlyEnabled: false },
- });
- });
-
- it('should not show fast forward message', () => {
- expect(wrapper.find('.mr-fast-forward-message').exists()).toBe(false);
- });
- });
-
- describe('when the fast-forward merge method is enabled', () => {
- beforeEach(() => {
- createComponent({
- mr: { ffOnlyEnabled: true },
- });
- });
-
- it('should show fast forward message', () => {
- expect(wrapper.find('.mr-fast-forward-message').exists()).toBe(true);
- });
- });
- });
-
- describe('Merge button when pipeline has failed', () => {
- beforeEach(() => {
- createComponent({
- mr: { pipeline: {}, isPipelineFailed: true, availableAutoMergeStrategies: [] },
- });
- });
-
- it('should display the correct merge text', () => {
- expect(findMergeButton().text()).toBe('Merge...');
- });
-
- it('should display confirmation modal when merge button is clicked', async () => {
- expect(findPipelineFailedConfirmModal().props()).toEqual({ visible: false });
-
- await findMergeButton().vm.$emit('click');
-
- expect(findPipelineFailedConfirmModal().props()).toEqual({ visible: true });
- });
- });
-
- describe('updating graphql data triggers commit message update when default changed', () => {
- const UPDATED_MERGE_COMMIT_MESSAGE = 'New merge message from BE';
- const UPDATED_SQUASH_COMMIT_MESSAGE = 'New squash message from BE';
- const USER_COMMIT_MESSAGE = 'Merge message provided manually by user';
-
- const createDefaultGqlComponent = () =>
- createComponent({ mr: { commitsCount: 2, enableSquashBeforeMerge: true } }, true);
-
- beforeEach(() => {
- readyToMergeResponseSpy = jest
- .fn()
- .mockResolvedValueOnce(createReadyToMergeResponse({ squash: true, squashOnMerge: true }))
- .mockResolvedValue(
- createReadyToMergeResponse({
- squash: true,
- squashOnMerge: true,
- defaultMergeCommitMessage: UPDATED_MERGE_COMMIT_MESSAGE,
- defaultSquashCommitMessage: UPDATED_SQUASH_COMMIT_MESSAGE,
- }),
- );
- });
-
- describe.each`
- desc | finderFn | initialValue | updatedValue | inputId
- ${'merge commit message'} | ${findMergeCommitMessage} | ${commitMessage} | ${UPDATED_MERGE_COMMIT_MESSAGE} | ${'#merge-message-edit'}
- ${'squash commit message'} | ${findSquashCommitMessage} | ${squashCommitMessage} | ${UPDATED_SQUASH_COMMIT_MESSAGE} | ${'#squash-message-edit'}
- `('with $desc', ({ finderFn, initialValue, updatedValue, inputId }) => {
- it('should have initial value', async () => {
- createDefaultGqlComponent();
-
- await waitForPromises();
-
- expect(finderFn()).toBe(initialValue);
- });
-
- it('should have updated value after graphql refetch', async () => {
- createDefaultGqlComponent();
- await waitForPromises();
-
- triggerApprovalUpdated();
- await waitForPromises();
-
- expect(finderFn()).toBe(updatedValue);
- });
-
- it('should not update if user has touched', async () => {
- createDefaultGqlComponent();
- await waitForPromises();
-
- const input = wrapper.find(inputId);
- input.element.value = USER_COMMIT_MESSAGE;
- input.trigger('input');
-
- triggerApprovalUpdated();
- await waitForPromises();
-
- expect(finderFn()).toBe(USER_COMMIT_MESSAGE);
- });
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_sha_mismatch_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_sha_mismatch_spec.js
deleted file mode 100644
index 2a343997cf5..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_sha_mismatch_spec.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import { mount } from '@vue/test-utils';
-import ShaMismatch from '~/vue_merge_request_widget/components/states/sha_mismatch.vue';
-import { I18N_SHA_MISMATCH } from '~/vue_merge_request_widget/i18n';
-
-function createComponent({ path = '' } = {}) {
- return mount(ShaMismatch, {
- propsData: {
- mr: {
- mergeRequestDiffsPath: path,
- },
- },
- });
-}
-
-describe('ShaMismatch', () => {
- let wrapper;
- const findActionButton = () => wrapper.find('[data-testid="action-button"]');
-
- beforeEach(() => {
- wrapper = createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('should render warning message', () => {
- expect(wrapper.element.innerText).toContain(I18N_SHA_MISMATCH.warningMessage);
- });
-
- it('action button should have correct label', () => {
- expect(findActionButton().text()).toBe(I18N_SHA_MISMATCH.actionButtonLabel);
- });
-
- it('action button should link to the diff path', () => {
- const DIFF_PATH = '/gitlab-org/gitlab-test/-/merge_requests/6/diffs';
-
- wrapper = createComponent({ path: DIFF_PATH });
-
- expect(findActionButton().attributes('href')).toBe(DIFF_PATH);
- });
-});
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
deleted file mode 100644
index 6ea2e8675d3..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js
+++ /dev/null
@@ -1,104 +0,0 @@
-import { GlFormCheckbox, GlLink } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import SquashBeforeMerge from '~/vue_merge_request_widget/components/states/squash_before_merge.vue';
-import { SQUASH_BEFORE_MERGE } from '~/vue_merge_request_widget/i18n';
-
-describe('Squash before merge component', () => {
- let wrapper;
-
- const createComponent = (props) => {
- wrapper = shallowMount(SquashBeforeMerge, {
- propsData: {
- ...props,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findCheckbox = () => wrapper.find(GlFormCheckbox);
-
- describe('checkbox', () => {
- it('is unchecked if passed value prop is false', () => {
- createComponent({
- value: false,
- });
-
- expect(findCheckbox().vm.$attrs.checked).toBe(false);
- });
-
- it('is checked if passed value prop is true', () => {
- createComponent({
- value: true,
- });
-
- expect(findCheckbox().vm.$attrs.checked).toBe(true);
- });
-
- it('is disabled if isDisabled prop is true', () => {
- createComponent({
- value: false,
- isDisabled: true,
- });
-
- expect(findCheckbox().vm.$attrs.disabled).toBe(true);
- });
- });
-
- describe('tooltip', () => {
- const tooltipTitle = () => findCheckbox().attributes('title');
-
- it('does not render when isDisabled is false', () => {
- createComponent({
- value: true,
- isDisabled: false,
- });
- expect(tooltipTitle()).toBeUndefined();
- });
-
- it('display message when when isDisabled is true', () => {
- createComponent({
- value: true,
- isDisabled: true,
- });
-
- expect(tooltipTitle()).toBe(SQUASH_BEFORE_MERGE.tooltipTitle);
- });
- });
-
- describe('about link', () => {
- it('is not rendered if no help path is passed', () => {
- createComponent({
- value: false,
- });
-
- const aboutLink = wrapper.findComponent(GlLink);
-
- expect(aboutLink.exists()).toBe(false);
- });
-
- it('is rendered if help path is passed', () => {
- createComponent({
- value: false,
- helpPath: 'test-path',
- });
-
- const aboutLink = wrapper.findComponent(GlLink);
-
- expect(aboutLink.exists()).toBe(true);
- });
-
- it('should have a correct help path if passed', () => {
- createComponent({
- value: false,
- helpPath: 'test-path',
- });
-
- const aboutLink = wrapper.findComponent(GlLink);
-
- expect(aboutLink.attributes('href')).toEqual('test-path');
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_unresolved_discussions_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_unresolved_discussions_spec.js
deleted file mode 100644
index e2d79c61b9b..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_unresolved_discussions_spec.js
+++ /dev/null
@@ -1,64 +0,0 @@
-import { mount } from '@vue/test-utils';
-import { TEST_HOST } from 'helpers/test_constants';
-import notesEventHub from '~/notes/event_hub';
-import UnresolvedDiscussions from '~/vue_merge_request_widget/components/states/unresolved_discussions.vue';
-
-function createComponent({ path = '' } = {}) {
- return mount(UnresolvedDiscussions, {
- propsData: {
- mr: {
- createIssueToResolveDiscussionsPath: path,
- },
- },
- });
-}
-
-describe('UnresolvedDiscussions', () => {
- let wrapper;
-
- beforeEach(() => {
- wrapper = createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('triggers the correct notes event when the jump to first unresolved discussion button is clicked', () => {
- jest.spyOn(notesEventHub, '$emit');
-
- wrapper.find('[data-testid="jump-to-first"]').trigger('click');
-
- expect(notesEventHub.$emit).toHaveBeenCalledWith('jumpToFirstUnresolvedDiscussion');
- });
-
- describe('with threads path', () => {
- beforeEach(() => {
- wrapper = createComponent({ path: TEST_HOST });
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('should have correct elements', () => {
- expect(wrapper.element.innerText).toContain(`Merge blocked: all threads must be resolved.`);
-
- expect(wrapper.element.innerText).toContain('Jump to first unresolved thread');
- expect(wrapper.element.innerText).toContain('Create issue to resolve all threads');
- expect(wrapper.element.querySelector('.js-create-issue').getAttribute('href')).toEqual(
- TEST_HOST,
- );
- });
- });
-
- describe('without threads path', () => {
- it('should not show create issue link if user cannot create issue', () => {
- expect(wrapper.element.innerText).toContain(`Merge blocked: all threads must be resolved.`);
-
- expect(wrapper.element.innerText).toContain('Jump to first unresolved thread');
- expect(wrapper.element.innerText).not.toContain('Create issue to resolve all threads');
- expect(wrapper.element.querySelector('.js-create-issue')).toEqual(null);
- });
- });
-});
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
deleted file mode 100644
index 4998147c6b6..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_wip_spec.js
+++ /dev/null
@@ -1,103 +0,0 @@
-import Vue, { nextTick } from 'vue';
-import waitForPromises from 'helpers/wait_for_promises';
-import WorkInProgress from '~/vue_merge_request_widget/components/states/work_in_progress.vue';
-import toast from '~/vue_shared/plugins/global_toast';
-import eventHub from '~/vue_merge_request_widget/event_hub';
-
-jest.mock('~/vue_shared/plugins/global_toast');
-
-const createComponent = () => {
- const Component = Vue.extend(WorkInProgress);
- const mr = {
- title: 'The best MR ever',
- removeWIPPath: '/path/to/remove/wip',
- };
- const service = {
- removeWIP() {},
- };
- return new Component({
- el: document.createElement('div'),
- propsData: { mr, service },
- });
-};
-
-describe('Wip', () => {
- describe('props', () => {
- it('should have props', () => {
- const { mr, service } = WorkInProgress.props;
-
- expect(mr.type instanceof Object).toBeTruthy();
- expect(mr.required).toBeTruthy();
-
- expect(service.type instanceof Object).toBeTruthy();
- expect(service.required).toBeTruthy();
- });
- });
-
- describe('data', () => {
- it('should have default data', () => {
- const vm = createComponent();
-
- expect(vm.isMakingRequest).toBeFalsy();
- });
- });
-
- describe('methods', () => {
- const mrObj = {
- is_new_mr_data: true,
- };
-
- describe('handleRemoveDraft', () => {
- it('should make a request to service and handle response', async () => {
- const vm = createComponent();
-
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- jest.spyOn(vm.service, 'removeWIP').mockReturnValue(
- new Promise((resolve) => {
- resolve({
- data: mrObj,
- });
- }),
- );
-
- vm.handleRemoveDraft();
-
- await waitForPromises();
-
- expect(vm.isMakingRequest).toBeTruthy();
- expect(eventHub.$emit).toHaveBeenCalledWith('UpdateWidgetData', mrObj);
- expect(toast).toHaveBeenCalledWith('Marked as ready. Merging is now allowed.');
- });
- });
- });
-
- describe('template', () => {
- let vm;
- let el;
-
- beforeEach(() => {
- vm = createComponent();
- el = vm.$el;
- });
-
- it('should have correct elements', () => {
- expect(el.classList.contains('mr-widget-body')).toBeTruthy();
- expect(el.innerText).toContain(
- "Merge blocked: merge request must be marked as ready. It's still marked as draft.",
- );
- expect(el.querySelector('button').getAttribute('disabled')).toBeTruthy();
- expect(el.querySelector('button').innerText).toContain('Merge');
- expect(el.querySelector('.js-remove-draft').innerText.replace(/\s\s+/g, ' ')).toContain(
- 'Mark as ready',
- );
- });
-
- it('should not show removeWIP button is user cannot update MR', async () => {
- vm.mr.removeWIPPath = '';
-
- await nextTick();
-
- expect(el.querySelector('.js-remove-draft')).toEqual(null);
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/states/new_ready_to_merge_spec.js b/spec/frontend/vue_mr_widget/components/states/new_ready_to_merge_spec.js
deleted file mode 100644
index 5ec9654a4af..00000000000
--- a/spec/frontend/vue_mr_widget/components/states/new_ready_to_merge_spec.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import ReadyToMerge from '~/vue_merge_request_widget/components/states/new_ready_to_merge.vue';
-
-let wrapper;
-
-function factory({ canMerge }) {
- wrapper = shallowMount(ReadyToMerge, {
- propsData: {
- mr: {},
- },
- data() {
- return { canMerge };
- },
- });
-}
-
-describe('New ready to merge state component', () => {
- afterEach(() => {
- wrapper.destroy();
- });
-
- it.each`
- canMerge
- ${true}
- ${false}
- `('renders permission text if canMerge ($canMerge) is false', ({ canMerge }) => {
- factory({ canMerge });
-
- expect(wrapper.element).toMatchSnapshot();
- });
-});
diff --git a/spec/frontend/vue_mr_widget/components/terraform/mock_data.js b/spec/frontend/vue_mr_widget/components/terraform/mock_data.js
deleted file mode 100644
index 8e46af5dfd6..00000000000
--- a/spec/frontend/vue_mr_widget/components/terraform/mock_data.js
+++ /dev/null
@@ -1,31 +0,0 @@
-export const invalidPlanWithName = {
- job_name: 'Invalid Plan',
- job_path: '/path/to/ci/logs/3',
- tf_report_error: 'api_error',
-};
-
-export const invalidPlanWithoutName = {
- tf_report_error: 'invalid_json_format',
-};
-
-export const validPlanWithName = {
- create: 10,
- update: 20,
- delete: 30,
- job_name: 'Valid Plan',
- job_path: '/path/to/ci/logs/1',
-};
-
-export const validPlanWithoutName = {
- create: 10,
- update: 20,
- delete: 30,
- job_path: '/path/to/ci/logs/2',
-};
-
-export const plans = {
- invalid_plan_one: invalidPlanWithName,
- invalid_plan_two: invalidPlanWithoutName,
- valid_plan_one: validPlanWithName,
- valid_plan_two: validPlanWithoutName,
-};
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
deleted file mode 100644
index 8f20d6a8fc9..00000000000
--- a/spec/frontend/vue_mr_widget/components/terraform/mr_widget_terraform_container_spec.js
+++ /dev/null
@@ -1,174 +0,0 @@
-import { GlSkeletonLoader, GlSprintf } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-import { nextTick } from 'vue';
-import axios from '~/lib/utils/axios_utils';
-import Poll from '~/lib/utils/poll';
-import MrWidgetExpanableSection from '~/vue_merge_request_widget/components/mr_widget_expandable_section.vue';
-import MrWidgetTerraformContainer from '~/vue_merge_request_widget/components/terraform/mr_widget_terraform_container.vue';
-import TerraformPlan from '~/vue_merge_request_widget/components/terraform/terraform_plan.vue';
-import { invalidPlanWithName, plans, validPlanWithName } from './mock_data';
-
-describe('MrWidgetTerraformConainer', () => {
- let mock;
- let wrapper;
-
- 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 mockPollingApi = (response, body, header) => {
- mock.onGet(propsData.endpoint).reply(response, body, header);
- };
-
- const mountWrapper = () => {
- wrapper = shallowMount(MrWidgetTerraformContainer, {
- propsData,
- stubs: { MrWidgetExpanableSection, GlSprintf },
- });
- return axios.waitForAll();
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- wrapper.destroy();
- mock.restore();
- });
-
- describe('when data is loading', () => {
- beforeEach(async () => {
- mockPollingApi(200, plans, {});
-
- await mountWrapper();
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({ loading: true });
- await nextTick();
- });
-
- it('diplays loading skeleton', () => {
- expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(true);
- expect(wrapper.find(MrWidgetExpanableSection).exists()).toBe(false);
- });
- });
-
- describe('when data has finished loading', () => {
- beforeEach(() => {
- mockPollingApi(200, plans, {});
- return mountWrapper();
- });
-
- it('displays terraform content', () => {
- expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(false);
- expect(wrapper.find(MrWidgetExpanableSection).exists()).toBe(true);
- expect(findPlans()).toEqual(Object.values(plans));
- });
-
- describe('when data includes one invalid plan', () => {
- beforeEach(() => {
- const invalidPlanGroup = { bad_plan: invalidPlanWithName };
- mockPollingApi(200, invalidPlanGroup, {});
- return mountWrapper();
- });
-
- it('displays header text for one invalid plan', () => {
- expect(findHeader().text()).toBe('1 Terraform report failed to generate');
- });
- });
-
- describe('when data includes multiple invalid plans', () => {
- beforeEach(() => {
- const invalidPlanGroup = {
- bad_plan_one: invalidPlanWithName,
- bad_plan_two: invalidPlanWithName,
- };
-
- mockPollingApi(200, invalidPlanGroup, {});
- return mountWrapper();
- });
-
- it('displays header text for multiple invalid plans', () => {
- expect(findHeader().text()).toBe('2 Terraform reports failed to generate');
- });
- });
-
- describe('when data includes one valid plan', () => {
- beforeEach(() => {
- const validPlanGroup = { valid_plan: validPlanWithName };
- mockPollingApi(200, validPlanGroup, {});
- return mountWrapper();
- });
-
- it('displays header text for one valid plans', () => {
- expect(findHeader().text()).toBe('1 Terraform report was generated in your pipelines');
- });
- });
-
- describe('when data includes multiple valid plans', () => {
- beforeEach(() => {
- const validPlanGroup = {
- valid_plan_one: validPlanWithName,
- valid_plan_two: validPlanWithName,
- };
- mockPollingApi(200, validPlanGroup, {});
- return mountWrapper();
- });
-
- it('displays header text for multiple valid plans', () => {
- expect(findHeader().text()).toBe('2 Terraform reports were generated in your pipelines');
- });
- });
- });
-
- describe('polling', () => {
- let pollRequest;
- let pollStop;
-
- beforeEach(() => {
- pollRequest = jest.spyOn(Poll.prototype, 'makeRequest');
- pollStop = jest.spyOn(Poll.prototype, 'stop');
- });
-
- afterEach(() => {
- pollRequest.mockRestore();
- pollStop.mockRestore();
- });
-
- describe('successful poll', () => {
- beforeEach(() => {
- mockPollingApi(200, plans, {});
-
- return mountWrapper();
- });
-
- it('does not make additional requests after poll is successful', () => {
- expect(pollRequest).toHaveBeenCalledTimes(1);
- expect(pollStop).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('polling fails', () => {
- beforeEach(() => {
- mockPollingApi(500, null, {});
- return mountWrapper();
- });
-
- it('stops loading', () => {
- expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(false);
- });
-
- it('generates one broken plan', () => {
- expect(findPlans()).toEqual([{ tf_report_error: 'api_error' }]);
- });
-
- it('does not make additional requests after poll is unsuccessful', () => {
- expect(pollRequest).toHaveBeenCalledTimes(1);
- expect(pollStop).toHaveBeenCalledTimes(1);
- });
- });
- });
-});
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
deleted file mode 100644
index 3c9f6c2e165..00000000000
--- a/spec/frontend/vue_mr_widget/components/terraform/terraform_plan_spec.js
+++ /dev/null
@@ -1,93 +0,0 @@
-import { GlLink, GlSprintf } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import TerraformPlan from '~/vue_merge_request_widget/components/terraform/terraform_plan.vue';
-import {
- invalidPlanWithName,
- invalidPlanWithoutName,
- validPlanWithName,
- validPlanWithoutName,
-} from './mock_data';
-
-describe('TerraformPlan', () => {
- let wrapper;
-
- const findIcon = () => wrapper.find('[data-testid="change-type-icon"]');
- const findLogButton = () => wrapper.find('[data-testid="terraform-report-link"]');
-
- const mountWrapper = (propsData) => {
- wrapper = shallowMount(TerraformPlan, { stubs: { GlLink, GlSprintf }, propsData });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('valid plan with job_name', () => {
- beforeEach(() => {
- mountWrapper({ plan: validPlanWithName });
- });
-
- it('displays a document icon', () => {
- expect(findIcon().attributes('name')).toBe('doc-changes');
- });
-
- it('diplays the header text with a name', () => {
- expect(wrapper.text()).toContain(`The job ${validPlanWithName.job_name} generated a report.`);
- });
-
- it('diplays the reported changes', () => {
- expect(wrapper.text()).toContain(
- `Reported Resource Changes: ${validPlanWithName.create} to add, ${validPlanWithName.update} to change, ${validPlanWithName.delete} to delete`,
- );
- });
-
- it('renders button when url is found', () => {
- expect(findLogButton().exists()).toBe(true);
- expect(findLogButton().text()).toEqual('View full log');
- });
- });
-
- describe('valid plan without job_name', () => {
- beforeEach(() => {
- mountWrapper({ plan: validPlanWithoutName });
- });
-
- it('diplays the header text without a name', () => {
- expect(wrapper.text()).toContain('A report was generated in your pipelines.');
- });
- });
-
- describe('invalid plan with job_name', () => {
- beforeEach(() => {
- mountWrapper({ plan: invalidPlanWithName });
- });
-
- it('displays a warning icon', () => {
- expect(findIcon().attributes('name')).toBe('warning');
- });
-
- it('diplays the header text with a name', () => {
- expect(wrapper.text()).toContain(
- `The job ${invalidPlanWithName.job_name} failed to generate a report.`,
- );
- });
-
- it('diplays generic error since report values are missing', () => {
- expect(wrapper.text()).toContain('Generating the report caused an error.');
- });
- });
-
- describe('invalid plan with out job_name', () => {
- beforeEach(() => {
- mountWrapper({ plan: invalidPlanWithoutName });
- });
-
- it('diplays the header text without a name', () => {
- expect(wrapper.text()).toContain('A report failed to generate.');
- });
-
- it('does not render button because url is missing', () => {
- expect(findLogButton().exists()).toBe(false);
- });
- });
-});