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')
-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
-rw-r--r--spec/frontend/vue_mr_widget/deployment/deployment_action_button_spec.js125
-rw-r--r--spec/frontend/vue_mr_widget/deployment/deployment_actions_spec.js234
-rw-r--r--spec/frontend/vue_mr_widget/deployment/deployment_list_spec.js95
-rw-r--r--spec/frontend/vue_mr_widget/deployment/deployment_mock_data.js76
-rw-r--r--spec/frontend/vue_mr_widget/deployment/deployment_spec.js187
-rw-r--r--spec/frontend/vue_mr_widget/deployment/deployment_view_button_spec.js109
-rw-r--r--spec/frontend/vue_mr_widget/extensions/test_report/index_spec.js269
-rw-r--r--spec/frontend/vue_mr_widget/extensions/test_report/utils_spec.js242
-rw-r--r--spec/frontend/vue_mr_widget/extentions/accessibility/index_spec.js127
-rw-r--r--spec/frontend/vue_mr_widget/extentions/accessibility/mock_data.js137
-rw-r--r--spec/frontend/vue_mr_widget/extentions/code_quality/index_spec.js145
-rw-r--r--spec/frontend/vue_mr_widget/extentions/code_quality/mock_data.js87
-rw-r--r--spec/frontend/vue_mr_widget/extentions/terraform/index_spec.js192
-rw-r--r--spec/frontend/vue_mr_widget/mock_data.js358
-rw-r--r--spec/frontend/vue_mr_widget/mr_widget_how_to_merge_modal_spec.js70
-rw-r--r--spec/frontend/vue_mr_widget/mr_widget_options_spec.js1266
-rw-r--r--spec/frontend/vue_mr_widget/stores/artifacts_list/actions_spec.js158
-rw-r--r--spec/frontend/vue_mr_widget/stores/artifacts_list/getters_spec.js32
-rw-r--r--spec/frontend/vue_mr_widget/stores/artifacts_list/mutations_spec.js78
-rw-r--r--spec/frontend/vue_mr_widget/stores/get_state_key_spec.js126
-rw-r--r--spec/frontend/vue_mr_widget/stores/mr_widget_store_spec.js180
-rw-r--r--spec/frontend/vue_mr_widget/test_extensions.js192
81 files changed, 0 insertions, 10712 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);
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/deployment/deployment_action_button_spec.js b/spec/frontend/vue_mr_widget/deployment/deployment_action_button_spec.js
deleted file mode 100644
index 7e7438bcc0f..00000000000
--- a/spec/frontend/vue_mr_widget/deployment/deployment_action_button_spec.js
+++ /dev/null
@@ -1,125 +0,0 @@
-import { GlIcon, GlLoadingIcon, GlButton } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import {
- CREATED,
- RUNNING,
- DEPLOYING,
- REDEPLOYING,
-} from '~/vue_merge_request_widget/components/deployment/constants';
-import DeploymentActionButton from '~/vue_merge_request_widget/components/deployment/deployment_action_button.vue';
-import { actionButtonMocks } from './deployment_mock_data';
-
-const baseProps = {
- actionsConfiguration: actionButtonMocks[DEPLOYING],
- actionInProgress: null,
- computedDeploymentStatus: CREATED,
- icon: 'play',
-};
-
-describe('Deployment action button', () => {
- let wrapper;
-
- const factory = (options = {}) => {
- wrapper = mount(DeploymentActionButton, {
- ...options,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('when passed only icon via props', () => {
- beforeEach(() => {
- factory({
- propsData: baseProps,
- slots: {},
- stubs: {
- 'gl-icon': GlIcon,
- },
- });
- });
-
- it('renders prop icon correctly', () => {
- expect(wrapper.find(GlIcon).exists()).toBe(true);
- });
- });
-
- describe('when passed multiple items', () => {
- beforeEach(() => {
- factory({
- propsData: baseProps,
- slots: {
- default: [`<span>${actionButtonMocks[DEPLOYING]}</span>`],
- },
- stubs: {
- 'gl-icon': GlIcon,
- },
- });
- });
-
- it('renders slot and icon prop correctly', () => {
- expect(wrapper.find(GlIcon).exists()).toBe(true);
- expect(wrapper.text()).toContain(actionButtonMocks[DEPLOYING].toString());
- });
- });
-
- describe('when its action is in progress', () => {
- beforeEach(() => {
- factory({
- propsData: {
- ...baseProps,
- actionInProgress: actionButtonMocks[DEPLOYING].actionName,
- },
- });
- });
-
- it('is disabled and shows the loading icon', () => {
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
- expect(wrapper.find(GlButton).props('disabled')).toBe(true);
- });
- });
-
- describe('when another action is in progress', () => {
- beforeEach(() => {
- factory({
- propsData: {
- ...baseProps,
- actionInProgress: actionButtonMocks[REDEPLOYING].actionName,
- },
- });
- });
- it('is disabled and does not show the loading icon', () => {
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
- expect(wrapper.find(GlButton).props('disabled')).toBe(true);
- });
- });
-
- describe('when action status is running', () => {
- beforeEach(() => {
- factory({
- propsData: {
- ...baseProps,
- actionInProgress: actionButtonMocks[REDEPLOYING].actionName,
- computedDeploymentStatus: RUNNING,
- },
- });
- });
- it('is disabled and does not show the loading icon', () => {
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
- expect(wrapper.find(GlButton).props('disabled')).toBe(true);
- });
- });
-
- describe('when no action is in progress', () => {
- beforeEach(() => {
- factory({
- propsData: baseProps,
- });
- });
- it('is not disabled nor does it show the loading icon', () => {
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
- expect(wrapper.find(GlButton).props('disabled')).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/deployment/deployment_actions_spec.js b/spec/frontend/vue_mr_widget/deployment/deployment_actions_spec.js
deleted file mode 100644
index a285d26f404..00000000000
--- a/spec/frontend/vue_mr_widget/deployment/deployment_actions_spec.js
+++ /dev/null
@@ -1,234 +0,0 @@
-import { mount } from '@vue/test-utils';
-import waitForPromises from 'helpers/wait_for_promises';
-import createFlash from '~/flash';
-import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
-import { visitUrl } from '~/lib/utils/url_utility';
-import {
- CREATED,
- MANUAL_DEPLOY,
- FAILED,
- DEPLOYING,
- REDEPLOYING,
- STOPPING,
-} from '~/vue_merge_request_widget/components/deployment/constants';
-import DeploymentActions from '~/vue_merge_request_widget/components/deployment/deployment_actions.vue';
-import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
-import {
- actionButtonMocks,
- deploymentMockData,
- playDetails,
- retryDetails,
-} from './deployment_mock_data';
-
-jest.mock('~/flash');
-jest.mock('~/lib/utils/url_utility');
-jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal', () => {
- return {
- confirmAction: jest.fn(),
- };
-});
-
-describe('DeploymentAction component', () => {
- let wrapper;
- let executeActionSpy;
-
- const factory = (options = {}) => {
- // This destroys any wrappers created before a nested call to factory reassigns it
- if (wrapper && wrapper.destroy) {
- wrapper.destroy();
- }
-
- wrapper = mount(DeploymentActions, options);
- };
-
- const findStopButton = () => wrapper.find('.js-stop-env');
- const findDeployButton = () => wrapper.find('.js-manual-deploy-action');
- const findRedeployButton = () => wrapper.find('.js-manual-redeploy-action');
-
- beforeEach(() => {
- executeActionSpy = jest.spyOn(MRWidgetService, 'executeInlineAction');
-
- factory({
- propsData: {
- computedDeploymentStatus: CREATED,
- deployment: deploymentMockData,
- },
- });
- });
-
- afterEach(() => {
- wrapper.destroy();
- confirmAction.mockReset();
- });
-
- describe('actions do not appear when conditions are unmet', () => {
- describe('when there is no stop_url', () => {
- beforeEach(() => {
- factory({
- propsData: {
- computedDeploymentStatus: CREATED,
- deployment: {
- ...deploymentMockData,
- stop_url: null,
- },
- },
- });
- });
-
- it('the stop button does not appear', () => {
- expect(findStopButton().exists()).toBe(false);
- });
- });
-
- describe('when there is no play_path in details', () => {
- it('the manual deploy button does not appear', () => {
- expect(findDeployButton().exists()).toBe(false);
- });
- });
-
- describe('when there is no retry_path in details', () => {
- it('the manual redeploy button does not appear', () => {
- expect(findRedeployButton().exists()).toBe(false);
- });
- });
- });
-
- describe('when conditions are met', () => {
- describe.each`
- configConst | computedDeploymentStatus | displayConditionChanges | finderFn | endpoint
- ${STOPPING} | ${CREATED} | ${{}} | ${findStopButton} | ${deploymentMockData.stop_url}
- ${DEPLOYING} | ${MANUAL_DEPLOY} | ${playDetails} | ${findDeployButton} | ${playDetails.playable_build.play_path}
- ${REDEPLOYING} | ${FAILED} | ${retryDetails} | ${findRedeployButton} | ${retryDetails.playable_build.retry_path}
- `(
- '$configConst action',
- ({ configConst, computedDeploymentStatus, displayConditionChanges, finderFn, endpoint }) => {
- describe(`${configConst} action`, () => {
- beforeEach(() => {
- factory({
- propsData: {
- computedDeploymentStatus,
- deployment: {
- ...deploymentMockData,
- details: displayConditionChanges,
- },
- },
- });
- });
-
- it('the button is rendered', () => {
- expect(finderFn().exists()).toBe(true);
- });
-
- describe('when clicked', () => {
- describe('should show a confirm dialog but not call executeInlineAction when declined', () => {
- beforeEach(() => {
- executeActionSpy.mockResolvedValueOnce();
- confirmAction.mockResolvedValueOnce(false);
- finderFn().trigger('click');
- });
-
- it('should show the confirm dialog', () => {
- expect(confirmAction).toHaveBeenCalled();
- expect(confirmAction).toHaveBeenCalledWith(
- actionButtonMocks[configConst].confirmMessage,
- {
- primaryBtnVariant: actionButtonMocks[configConst].buttonVariant,
- primaryBtnText: actionButtonMocks[configConst].buttonText,
- },
- );
- });
-
- it('should not execute the action', () => {
- expect(MRWidgetService.executeInlineAction).not.toHaveBeenCalled();
- });
- });
-
- describe('should show a confirm dialog and call executeInlineAction when accepted', () => {
- beforeEach(() => {
- executeActionSpy.mockResolvedValueOnce();
- confirmAction.mockResolvedValueOnce(true);
- finderFn().trigger('click');
- });
-
- it('should show the confirm dialog', () => {
- expect(confirmAction).toHaveBeenCalled();
- expect(confirmAction).toHaveBeenCalledWith(
- actionButtonMocks[configConst].confirmMessage,
- {
- primaryBtnVariant: actionButtonMocks[configConst].buttonVariant,
- primaryBtnText: actionButtonMocks[configConst].buttonText,
- },
- );
- });
-
- it('should execute the action with expected URL', () => {
- expect(MRWidgetService.executeInlineAction).toHaveBeenCalled();
- expect(MRWidgetService.executeInlineAction).toHaveBeenCalledWith(endpoint);
- });
-
- it('should not throw an error', () => {
- expect(createFlash).not.toHaveBeenCalled();
- });
-
- describe('response includes redirect_url', () => {
- const url = '/root/example';
- beforeEach(async () => {
- executeActionSpy.mockResolvedValueOnce({
- data: { redirect_url: url },
- });
-
- await waitForPromises();
-
- confirmAction.mockResolvedValueOnce(true);
- finderFn().trigger('click');
- });
-
- it('calls visit url with the redirect_url', () => {
- expect(visitUrl).toHaveBeenCalled();
- expect(visitUrl).toHaveBeenCalledWith(url);
- });
- });
-
- describe('it should call the executeAction method ', () => {
- beforeEach(async () => {
- jest.spyOn(wrapper.vm, 'executeAction').mockImplementation();
-
- await waitForPromises();
-
- confirmAction.mockResolvedValueOnce(true);
- finderFn().trigger('click');
- });
-
- it('calls with the expected arguments', () => {
- expect(wrapper.vm.executeAction).toHaveBeenCalled();
- expect(wrapper.vm.executeAction).toHaveBeenCalledWith(
- endpoint,
- actionButtonMocks[configConst],
- );
- });
- });
-
- describe('when executeInlineAction errors', () => {
- beforeEach(async () => {
- executeActionSpy.mockRejectedValueOnce();
-
- await waitForPromises();
-
- confirmAction.mockResolvedValueOnce(true);
- finderFn().trigger('click');
- });
-
- it('should call createFlash with error message', () => {
- expect(createFlash).toHaveBeenCalled();
- expect(createFlash).toHaveBeenCalledWith({
- message: actionButtonMocks[configConst].errorMessage,
- });
- });
- });
- });
- });
- });
- },
- );
- });
-});
diff --git a/spec/frontend/vue_mr_widget/deployment/deployment_list_spec.js b/spec/frontend/vue_mr_widget/deployment/deployment_list_spec.js
deleted file mode 100644
index 948d7ebab5e..00000000000
--- a/spec/frontend/vue_mr_widget/deployment/deployment_list_spec.js
+++ /dev/null
@@ -1,95 +0,0 @@
-import { mount } from '@vue/test-utils';
-import { zip } from 'lodash';
-import { trimText } from 'helpers/text_helper';
-import Deployment from '~/vue_merge_request_widget/components/deployment/deployment.vue';
-import DeploymentList from '~/vue_merge_request_widget/components/deployment/deployment_list.vue';
-import MrCollapsibleExtension from '~/vue_merge_request_widget/components/mr_collapsible_extension.vue';
-import { mockStore } from '../mock_data';
-
-const DEFAULT_PROPS = {
- hasDeploymentMetrics: false,
- deploymentClass: 'js-pre-deployment',
-};
-
-describe('~/vue_merge_request_widget/components/deployment/deployment_list.vue', () => {
- let wrapper;
- let propsData;
-
- const factory = (props = {}) => {
- propsData = {
- ...DEFAULT_PROPS,
- deployments: mockStore.deployments,
- ...props,
- };
- wrapper = mount(DeploymentList, {
- propsData,
- });
- };
-
- afterEach(() => {
- wrapper?.destroy?.();
- wrapper = null;
- });
-
- describe('with few deployments', () => {
- beforeEach(() => {
- factory();
- });
-
- it('shows all deployments', () => {
- const deploymentWrappers = wrapper.findAllComponents(Deployment);
- expect(wrapper.findComponent(MrCollapsibleExtension).exists()).toBe(false);
- expect(deploymentWrappers).toHaveLength(propsData.deployments.length);
-
- zip(deploymentWrappers.wrappers, propsData.deployments).forEach(
- ([deploymentWrapper, deployment]) => {
- expect(deploymentWrapper.props('deployment')).toEqual(deployment);
- expect(deploymentWrapper.props()).toMatchObject({
- showMetrics: DEFAULT_PROPS.hasDeploymentMetrics,
- });
- expect(deploymentWrapper.classes(DEFAULT_PROPS.deploymentClass)).toBe(true);
- expect(deploymentWrapper.text()).toEqual(expect.any(String));
- expect(deploymentWrapper.text()).not.toBe('');
- },
- );
- });
- });
- describe('with many deployments', () => {
- let deployments;
- let collapsibleExtension;
-
- beforeEach(() => {
- deployments = [
- ...mockStore.deployments,
- ...mockStore.deployments.map((deployment) => ({
- ...deployment,
- id: deployment.id + mockStore.deployments.length,
- })),
- ];
- factory({ deployments });
-
- collapsibleExtension = wrapper.findComponent(MrCollapsibleExtension);
- });
-
- it('shows collapsed deployments', () => {
- expect(collapsibleExtension.exists()).toBe(true);
- expect(trimText(collapsibleExtension.text())).toBe(
- `${deployments.length} environments impacted. View all environments.`,
- );
- });
- it('shows all deployments on click', async () => {
- await collapsibleExtension.find('button').trigger('click');
- const deploymentWrappers = wrapper.findAllComponents(Deployment);
- expect(deploymentWrappers).toHaveLength(deployments.length);
-
- zip(deploymentWrappers.wrappers, propsData.deployments).forEach(
- ([deploymentWrapper, deployment]) => {
- expect(deploymentWrapper.props('deployment')).toEqual(deployment);
- expect(deploymentWrapper.classes(DEFAULT_PROPS.deploymentClass)).toBe(true);
- expect(deploymentWrapper.text()).toEqual(expect.any(String));
- expect(deploymentWrapper.text()).not.toBe('');
- },
- );
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/deployment/deployment_mock_data.js b/spec/frontend/vue_mr_widget/deployment/deployment_mock_data.js
deleted file mode 100644
index e98b1160ae4..00000000000
--- a/spec/frontend/vue_mr_widget/deployment/deployment_mock_data.js
+++ /dev/null
@@ -1,76 +0,0 @@
-import {
- DEPLOYING,
- REDEPLOYING,
- SUCCESS,
- STOPPING,
-} from '~/vue_merge_request_widget/components/deployment/constants';
-
-const actionButtonMocks = {
- [STOPPING]: {
- actionName: STOPPING,
- buttonText: 'Stop environment',
- buttonVariant: 'danger',
- busyText: 'This environment is being deployed',
- confirmMessage: 'Are you sure you want to stop this environment?',
- errorMessage: 'Something went wrong while stopping this environment. Please try again.',
- },
- [DEPLOYING]: {
- actionName: DEPLOYING,
- buttonText: 'Deploy',
- buttonVariant: 'confirm',
- busyText: 'This environment is being deployed',
- confirmMessage: 'Are you sure you want to deploy this environment?',
- errorMessage: 'Something went wrong while deploying this environment. Please try again.',
- },
- [REDEPLOYING]: {
- actionName: REDEPLOYING,
- buttonText: 'Re-deploy',
- buttonVariant: 'confirm',
- busyText: 'This environment is being re-deployed',
- confirmMessage: 'Are you sure you want to re-deploy this environment?',
- errorMessage: 'Something went wrong while deploying this environment. Please try again.',
- },
-};
-
-const deploymentMockData = {
- id: 15,
- name: 'review/diplo',
- url: '/root/review-apps/environments/15',
- stop_url: '/root/review-apps/environments/15/stop',
- metrics_url: '/root/review-apps/environments/15/deployments/1/metrics',
- metrics_monitoring_url: '/root/review-apps/environments/15/metrics',
- external_url: 'http://gitlab.com.',
- external_url_formatted: 'gitlab',
- deployed_at: '2017-03-22T22:44:42.258Z',
- deployed_at_formatted: 'Mar 22, 2017 10:44pm',
- details: {},
- status: SUCCESS,
- changes: [
- {
- path: 'index.html',
- external_url: 'http://root-main-patch-91341.volatile-watch.surge.sh/index.html',
- },
- {
- path: 'imgs/gallery.html',
- external_url: 'http://root-main-patch-91341.volatile-watch.surge.sh/imgs/gallery.html',
- },
- {
- path: 'about/',
- external_url: 'http://root-main-patch-91341.volatile-watch.surge.sh/about/',
- },
- ],
-};
-
-const playDetails = {
- playable_build: {
- play_path: '/root/test-deployments/-/jobs/1131/play',
- },
-};
-
-const retryDetails = {
- playable_build: {
- retry_path: '/root/test-deployments/-/jobs/1131/retry',
- },
-};
-
-export { actionButtonMocks, deploymentMockData, playDetails, retryDetails };
diff --git a/spec/frontend/vue_mr_widget/deployment/deployment_spec.js b/spec/frontend/vue_mr_widget/deployment/deployment_spec.js
deleted file mode 100644
index c27cbd8b781..00000000000
--- a/spec/frontend/vue_mr_widget/deployment/deployment_spec.js
+++ /dev/null
@@ -1,187 +0,0 @@
-import { mount } from '@vue/test-utils';
-import {
- CREATED,
- RUNNING,
- SUCCESS,
- FAILED,
- CANCELED,
- SKIPPED,
-} from '~/vue_merge_request_widget/components/deployment/constants';
-import DeploymentComponent from '~/vue_merge_request_widget/components/deployment/deployment.vue';
-import DeploymentInfo from '~/vue_merge_request_widget/components/deployment/deployment_info.vue';
-import DeploymentViewButton from '~/vue_merge_request_widget/components/deployment/deployment_view_button.vue';
-import { deploymentMockData, playDetails, retryDetails } from './deployment_mock_data';
-
-describe('Deployment component', () => {
- let wrapper;
-
- const factory = (options = {}) => {
- // This destroys any wrappers created before a nested call to factory reassigns it
- if (wrapper && wrapper.destroy) {
- wrapper.destroy();
- }
- wrapper = mount(DeploymentComponent, options);
- };
-
- beforeEach(() => {
- factory({
- propsData: {
- deployment: deploymentMockData,
- showMetrics: false,
- },
- });
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('always renders DeploymentInfo', () => {
- expect(wrapper.find(DeploymentInfo).exists()).toBe(true);
- });
-
- describe('status message and buttons', () => {
- const noActions = [];
- const noDetails = { isManual: false };
- const deployDetail = {
- ...playDetails,
- isManual: true,
- };
-
- const retryDetail = {
- ...retryDetails,
- isManual: true,
- };
- const defaultGroup = ['.js-deploy-url', '.js-stop-env'];
- const manualDeployGroup = ['.js-manual-deploy-action', ...defaultGroup];
- const manualRedeployGroup = ['.js-manual-redeploy-action', ...defaultGroup];
-
- describe.each`
- status | previous | deploymentDetails | text | actionButtons
- ${CREATED} | ${true} | ${deployDetail} | ${'Can be manually deployed to'} | ${manualDeployGroup}
- ${CREATED} | ${true} | ${noDetails} | ${'Will deploy to'} | ${defaultGroup}
- ${CREATED} | ${false} | ${deployDetail} | ${'Can be manually deployed to'} | ${noActions}
- ${CREATED} | ${false} | ${noDetails} | ${'Will deploy to'} | ${noActions}
- ${RUNNING} | ${true} | ${deployDetail} | ${'Deploying to'} | ${defaultGroup}
- ${RUNNING} | ${true} | ${noDetails} | ${'Deploying to'} | ${defaultGroup}
- ${RUNNING} | ${false} | ${deployDetail} | ${'Deploying to'} | ${noActions}
- ${RUNNING} | ${false} | ${noDetails} | ${'Deploying to'} | ${noActions}
- ${SUCCESS} | ${true} | ${deployDetail} | ${'Deployed to'} | ${defaultGroup}
- ${SUCCESS} | ${true} | ${noDetails} | ${'Deployed to'} | ${defaultGroup}
- ${SUCCESS} | ${false} | ${deployDetail} | ${'Deployed to'} | ${defaultGroup}
- ${SUCCESS} | ${false} | ${noDetails} | ${'Deployed to'} | ${defaultGroup}
- ${FAILED} | ${true} | ${retryDetail} | ${'Failed to deploy to'} | ${manualRedeployGroup}
- ${FAILED} | ${true} | ${noDetails} | ${'Failed to deploy to'} | ${defaultGroup}
- ${FAILED} | ${false} | ${retryDetail} | ${'Failed to deploy to'} | ${noActions}
- ${FAILED} | ${false} | ${noDetails} | ${'Failed to deploy to'} | ${noActions}
- ${CANCELED} | ${true} | ${deployDetail} | ${'Canceled deployment to'} | ${defaultGroup}
- ${CANCELED} | ${true} | ${noDetails} | ${'Canceled deployment to'} | ${defaultGroup}
- ${CANCELED} | ${false} | ${deployDetail} | ${'Canceled deployment to'} | ${noActions}
- ${CANCELED} | ${false} | ${noDetails} | ${'Canceled deployment to'} | ${noActions}
- ${SKIPPED} | ${true} | ${deployDetail} | ${'Skipped deployment to'} | ${defaultGroup}
- ${SKIPPED} | ${true} | ${noDetails} | ${'Skipped deployment to'} | ${defaultGroup}
- ${SKIPPED} | ${false} | ${deployDetail} | ${'Skipped deployment to'} | ${noActions}
- ${SKIPPED} | ${false} | ${noDetails} | ${'Skipped deployment to'} | ${noActions}
- `(
- '$status + previous: $previous + manual: $deploymentDetails.isManual',
- ({ status, previous, deploymentDetails, text, actionButtons }) => {
- beforeEach(() => {
- const previousOrSuccess = Boolean(previous || status === SUCCESS);
- const updatedDeploymentData = {
- status,
- deployed_at: previous ? deploymentMockData.deployed_at : null,
- deployed_at_formatted: previous ? deploymentMockData.deployed_at_formatted : null,
- external_url: previousOrSuccess ? deploymentMockData.external_url : null,
- external_url_formatted: previousOrSuccess
- ? deploymentMockData.external_url_formatted
- : null,
- stop_url: previousOrSuccess ? deploymentMockData.stop_url : null,
- details: deploymentDetails,
- };
-
- factory({
- propsData: {
- showMetrics: false,
- deployment: {
- ...deploymentMockData,
- ...updatedDeploymentData,
- },
- },
- });
- });
-
- it(`renders the text: ${text}`, () => {
- expect(wrapper.find(DeploymentInfo).text()).toContain(text);
- });
-
- if (actionButtons.length > 0) {
- describe('renders the expected button group', () => {
- actionButtons.forEach((button) => {
- it(`renders ${button}`, () => {
- expect(wrapper.find(button).exists()).toBe(true);
- });
- });
- });
- }
-
- if (actionButtons.length === 0) {
- describe('does not render the button group', () => {
- defaultGroup.forEach((button) => {
- it(`does not render ${button}`, () => {
- expect(wrapper.find(button).exists()).toBe(false);
- });
- });
- });
- }
-
- if (actionButtons.includes(DeploymentViewButton)) {
- it('renders the View button with expected text', () => {
- if (status === SUCCESS) {
- expect(wrapper.find(DeploymentViewButton).text()).toContain('View app');
- } else {
- expect(wrapper.find(DeploymentViewButton).text()).toContain('View latest app');
- }
- });
- }
- },
- );
- });
-
- describe('hasExternalUrls', () => {
- describe('when deployment has both external_url_formatted and external_url', () => {
- it('should render the View Button', () => {
- expect(wrapper.find(DeploymentViewButton).exists()).toBe(true);
- });
- });
-
- describe('when deployment has no external_url_formatted', () => {
- beforeEach(() => {
- factory({
- propsData: {
- deployment: { ...deploymentMockData, external_url_formatted: null },
- showMetrics: false,
- },
- });
- });
-
- it('should not render the View Button', () => {
- expect(wrapper.find(DeploymentViewButton).exists()).toBe(false);
- });
- });
-
- describe('when deployment has no external_url', () => {
- beforeEach(() => {
- factory({
- propsData: {
- deployment: { ...deploymentMockData, external_url: null },
- showMetrics: false,
- },
- });
- });
-
- it('should not render the View Button', () => {
- expect(wrapper.find(DeploymentViewButton).exists()).toBe(false);
- });
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/deployment/deployment_view_button_spec.js b/spec/frontend/vue_mr_widget/deployment/deployment_view_button_spec.js
deleted file mode 100644
index eb6e3711e2e..00000000000
--- a/spec/frontend/vue_mr_widget/deployment/deployment_view_button_spec.js
+++ /dev/null
@@ -1,109 +0,0 @@
-import { GlDropdown, GlLink } from '@gitlab/ui';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import DeploymentViewButton from '~/vue_merge_request_widget/components/deployment/deployment_view_button.vue';
-import ReviewAppLink from '~/vue_merge_request_widget/components/review_app_link.vue';
-import { deploymentMockData } from './deployment_mock_data';
-
-const appButtonText = {
- text: 'View app',
- tooltip: 'View the latest successful deployment to this environment',
-};
-
-describe('Deployment View App button', () => {
- let wrapper;
-
- const createComponent = (options = {}) => {
- wrapper = mountExtended(DeploymentViewButton, {
- ...options,
- });
- };
-
- beforeEach(() => {
- createComponent({
- propsData: {
- deployment: deploymentMockData,
- appButtonText,
- },
- });
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findReviewAppLink = () => wrapper.findComponent(ReviewAppLink);
- const findMrWigdetDeploymentDropdown = () => wrapper.findComponent(GlDropdown);
- const findMrWigdetDeploymentDropdownIcon = () =>
- wrapper.findByTestId('mr-wigdet-deployment-dropdown-icon');
- const findDeployUrlMenuItems = () => wrapper.findAllComponents(GlLink);
-
- describe('text', () => {
- it('renders text as passed', () => {
- expect(findReviewAppLink().props().display.text).toBe(appButtonText.text);
- });
- });
-
- describe('without changes', () => {
- beforeEach(() => {
- createComponent({
- propsData: {
- deployment: { ...deploymentMockData, changes: null },
- appButtonText,
- },
- });
- });
-
- it('renders the link to the review app without dropdown', () => {
- expect(findMrWigdetDeploymentDropdown().exists()).toBe(false);
- });
- });
-
- describe('with a single change', () => {
- beforeEach(() => {
- createComponent({
- propsData: {
- deployment: { ...deploymentMockData, changes: [deploymentMockData.changes[0]] },
- appButtonText,
- },
- });
- });
-
- it('renders the link to the review app without dropdown', () => {
- expect(findMrWigdetDeploymentDropdown().exists()).toBe(false);
- expect(findMrWigdetDeploymentDropdownIcon().exists()).toBe(false);
- });
-
- it('renders the link to the review app linked to to the first change', () => {
- const expectedUrl = deploymentMockData.changes[0].external_url;
-
- expect(findReviewAppLink().attributes('href')).toBe(expectedUrl);
- });
- });
-
- describe('with multiple changes', () => {
- beforeEach(() => {
- createComponent({
- propsData: {
- deployment: deploymentMockData,
- appButtonText,
- },
- });
- });
-
- it('renders the link to the review app with dropdown', () => {
- expect(findMrWigdetDeploymentDropdown().exists()).toBe(true);
- expect(findMrWigdetDeploymentDropdownIcon().exists()).toBe(true);
- });
-
- it('renders all the links to the review apps', () => {
- const allUrls = findDeployUrlMenuItems().wrappers;
- const expectedUrls = deploymentMockData.changes.map((change) => change.external_url);
-
- expectedUrls.forEach((expectedUrl, idx) => {
- const deployUrl = allUrls[idx];
-
- expect(deployUrl.attributes('href')).toBe(expectedUrl);
- });
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/extensions/test_report/index_spec.js b/spec/frontend/vue_mr_widget/extensions/test_report/index_spec.js
deleted file mode 100644
index 5c1d3c8e8e8..00000000000
--- a/spec/frontend/vue_mr_widget/extensions/test_report/index_spec.js
+++ /dev/null
@@ -1,269 +0,0 @@
-import { nextTick } from 'vue';
-import MockAdapter from 'axios-mock-adapter';
-import testReportExtension from '~/vue_merge_request_widget/extensions/test_report';
-import { i18n } from '~/vue_merge_request_widget/extensions/test_report/constants';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import { trimText } from 'helpers/text_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import axios from '~/lib/utils/axios_utils';
-import extensionsContainer from '~/vue_merge_request_widget/components/extensions/container';
-import { registerExtension } from '~/vue_merge_request_widget/components/extensions';
-import httpStatusCodes from '~/lib/utils/http_status';
-import TestCaseDetails from '~/pipelines/components/test_reports/test_case_details.vue';
-
-import { failedReport } from 'jest/reports/mock_data/mock_data';
-import mixedResultsTestReports from 'jest/reports/mock_data/new_and_fixed_failures_report.json';
-import newErrorsTestReports from 'jest/reports/mock_data/new_errors_report.json';
-import newFailedTestReports from 'jest/reports/mock_data/new_failures_report.json';
-import successTestReports from 'jest/reports/mock_data/no_failures_report.json';
-import resolvedFailures from 'jest/reports/mock_data/resolved_failures.json';
-import recentFailures from 'jest/reports/mock_data/recent_failures_report.json';
-
-const reportWithParsingErrors = failedReport;
-reportWithParsingErrors.suites[0].suite_errors = {
- head: 'JUnit XML parsing failed: 2:24: FATAL: attributes construct error',
- base: 'JUnit data parsing failed: string not matched',
-};
-
-describe('Test report extension', () => {
- let wrapper;
- let mock;
-
- registerExtension(testReportExtension);
-
- const endpoint = '/root/repo/-/merge_requests/4/test_reports.json';
-
- const mockApi = (statusCode, data = mixedResultsTestReports) => {
- mock.onGet(endpoint).reply(statusCode, data);
- };
-
- const findToggleCollapsedButton = () => wrapper.findByTestId('toggle-button');
- const findFullReportLink = () => wrapper.findByTestId('full-report-link');
- const findCopyFailedSpecsBtn = () => wrapper.findByTestId('copy-failed-specs-btn');
- const findAllExtensionListItems = () => wrapper.findAllByTestId('extension-list-item');
- const findModal = () => wrapper.find(TestCaseDetails);
-
- const createComponent = () => {
- wrapper = mountExtended(extensionsContainer, {
- propsData: {
- mr: {
- testResultsPath: endpoint,
- headBlobPath: 'head/blob/path',
- pipeline: { path: 'pipeline/path' },
- },
- },
- });
- };
-
- const createExpandedWidgetWithData = async (data = mixedResultsTestReports) => {
- mockApi(httpStatusCodes.OK, data);
- createComponent();
- await waitForPromises();
- findToggleCollapsedButton().trigger('click');
- await waitForPromises();
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- wrapper.destroy();
- mock.restore();
- });
-
- describe('summary', () => {
- it('displays loading state initially', () => {
- mockApi(httpStatusCodes.OK);
- createComponent();
-
- expect(wrapper.text()).toContain(i18n.loading);
- });
-
- it('with a 204 response, continues to display loading state', async () => {
- mockApi(httpStatusCodes.NO_CONTENT, '');
- createComponent();
-
- await waitForPromises();
-
- expect(wrapper.text()).toContain(i18n.loading);
- });
-
- it('with an error response, displays failed to load text', async () => {
- mockApi(httpStatusCodes.INTERNAL_SERVER_ERROR);
- createComponent();
-
- await waitForPromises();
-
- expect(wrapper.text()).toContain(i18n.error);
- });
-
- it.each`
- description | mockData | expectedResult
- ${'mixed test results'} | ${mixedResultsTestReports} | ${'Test summary: 2 failed and 2 fixed test results, 11 total tests'}
- ${'unchanged test results'} | ${successTestReports} | ${'Test summary: no changed test results, 11 total tests'}
- ${'tests with errors'} | ${newErrorsTestReports} | ${'Test summary: 2 errors, 11 total tests'}
- ${'failed test results'} | ${newFailedTestReports} | ${'Test summary: 2 failed, 11 total tests'}
- ${'resolved failures'} | ${resolvedFailures} | ${'Test summary: 4 fixed test results, 11 total tests'}
- `('displays summary text for $description', async ({ mockData, expectedResult }) => {
- mockApi(httpStatusCodes.OK, mockData);
- createComponent();
-
- await waitForPromises();
-
- expect(wrapper.text()).toContain(expectedResult);
- });
-
- it('displays report level recently failed count', async () => {
- mockApi(httpStatusCodes.OK, recentFailures);
- createComponent();
-
- await waitForPromises();
-
- expect(wrapper.text()).toContain(
- '2 out of 3 failed tests have failed more than once in the last 14 days',
- );
- });
-
- it('displays a link to the full report', async () => {
- mockApi(httpStatusCodes.OK);
- createComponent();
-
- await waitForPromises();
-
- expect(findFullReportLink().text()).toBe('Full report');
- expect(findFullReportLink().attributes('href')).toBe('pipeline/path/test_report');
- });
-
- it('hides copy failed tests button when there are no failing tests', async () => {
- mockApi(httpStatusCodes.OK);
- createComponent();
-
- await waitForPromises();
-
- expect(findCopyFailedSpecsBtn().exists()).toBe(false);
- });
-
- it('displays copy failed tests button when there are failing tests', async () => {
- mockApi(httpStatusCodes.OK, newFailedTestReports);
- createComponent();
-
- await waitForPromises();
-
- expect(findCopyFailedSpecsBtn().exists()).toBe(true);
- expect(findCopyFailedSpecsBtn().text()).toBe(i18n.copyFailedSpecs);
- expect(findCopyFailedSpecsBtn().attributes('data-clipboard-text')).toBe(
- 'spec/file_1.rb spec/file_2.rb',
- );
- });
-
- it('copy failed tests button updates tooltip text when clicked', async () => {
- mockApi(httpStatusCodes.OK, newFailedTestReports);
- createComponent();
-
- await waitForPromises();
-
- // original tooltip shows up
- expect(findCopyFailedSpecsBtn().attributes()).toMatchObject({
- title: i18n.copyFailedSpecsTooltip,
- });
-
- await findCopyFailedSpecsBtn().trigger('click');
-
- // tooltip text is replaced for 1 second
- expect(findCopyFailedSpecsBtn().attributes()).toMatchObject({
- title: 'Copied',
- });
-
- jest.runAllTimers();
- await nextTick();
-
- // tooltip reverts back to original string
- expect(findCopyFailedSpecsBtn().attributes()).toMatchObject({
- title: i18n.copyFailedSpecsTooltip,
- });
- });
-
- it('shows an error when a suite has a parsing error', async () => {
- mockApi(httpStatusCodes.OK, reportWithParsingErrors);
- createComponent();
-
- await waitForPromises();
-
- expect(wrapper.text()).toContain(i18n.error);
- });
- });
-
- describe('expanded data', () => {
- it('displays summary for each suite', async () => {
- await createExpandedWidgetWithData();
-
- expect(trimText(findAllExtensionListItems().at(0).text())).toContain(
- 'rspec:pg: 1 failed and 2 fixed test results, 8 total tests',
- );
- expect(trimText(findAllExtensionListItems().at(1).text())).toContain(
- 'java ant: 1 failed, 3 total tests',
- );
- });
-
- it('displays suite parsing errors', async () => {
- await createExpandedWidgetWithData(reportWithParsingErrors);
-
- const suiteText = trimText(findAllExtensionListItems().at(0).text());
-
- expect(suiteText).toContain(
- 'Head report parsing error: JUnit XML parsing failed: 2:24: FATAL: attributes construct error',
- );
- expect(suiteText).toContain(
- 'Base report parsing error: JUnit data parsing failed: string not matched',
- );
- });
-
- it('displays suite level recently failed count', async () => {
- await createExpandedWidgetWithData(recentFailures);
-
- expect(trimText(findAllExtensionListItems().at(0).text())).toContain(
- '1 out of 2 failed tests has failed more than once in the last 14 days',
- );
- expect(trimText(findAllExtensionListItems().at(1).text())).toContain(
- '1 out of 1 failed test has failed more than once in the last 14 days',
- );
- });
-
- it('displays the list of failed and fixed tests', async () => {
- await createExpandedWidgetWithData();
-
- const firstSuite = trimText(findAllExtensionListItems().at(0).text());
- const secondSuite = trimText(findAllExtensionListItems().at(1).text());
-
- expect(firstSuite).toContain('Test#subtract when a is 2 and b is 1 returns correct result');
- expect(firstSuite).toContain('Test#sum when a is 1 and b is 2 returns summary');
- expect(firstSuite).toContain('Test#sum when a is 100 and b is 200 returns summary');
-
- expect(secondSuite).toContain('sumTest');
- });
-
- it('displays the test level recently failed count', async () => {
- await createExpandedWidgetWithData(recentFailures);
-
- expect(trimText(findAllExtensionListItems().at(0).text())).toContain(
- 'Failed 8 times in main in the last 14 days',
- );
- });
- });
-
- describe('modal link', () => {
- beforeEach(async () => {
- await createExpandedWidgetWithData();
-
- wrapper.findByTestId('modal-link').trigger('click');
- });
-
- it('opens a modal to display test case details', () => {
- expect(findModal().exists()).toBe(true);
- expect(findModal().props('testCase')).toMatchObject(
- mixedResultsTestReports.suites[0].new_failures[0],
- );
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/extensions/test_report/utils_spec.js b/spec/frontend/vue_mr_widget/extensions/test_report/utils_spec.js
deleted file mode 100644
index 69ea70549fe..00000000000
--- a/spec/frontend/vue_mr_widget/extensions/test_report/utils_spec.js
+++ /dev/null
@@ -1,242 +0,0 @@
-import * as utils from '~/vue_merge_request_widget/extensions/test_report/utils';
-
-describe('test report widget extension utils', () => {
- describe('summaryTextbuilder', () => {
- it('should render text for no changed results in multiple tests', () => {
- const name = 'Test summary';
- const data = { total: 10 };
- const result = utils.summaryTextBuilder(name, data);
-
- expect(result).toBe(
- 'Test summary: %{strong_start}no%{strong_end} changed test results, %{strong_start}10%{strong_end} total tests',
- );
- });
-
- it('should render text for no changed results in one test', () => {
- const name = 'Test summary';
- const data = { total: 1 };
- const result = utils.summaryTextBuilder(name, data);
-
- expect(result).toBe(
- 'Test summary: %{strong_start}no%{strong_end} changed test results, %{strong_start}1%{strong_end} total test',
- );
- });
-
- it('should render text for multiple failed results', () => {
- const name = 'Test summary';
- const data = { failed: 3, total: 10 };
- const result = utils.summaryTextBuilder(name, data);
-
- expect(result).toBe(
- 'Test summary: %{strong_start}3%{strong_end} failed, %{strong_start}10%{strong_end} total tests',
- );
- });
-
- it('should render text for multiple errored results', () => {
- const name = 'Test summary';
- const data = { errored: 7, total: 10 };
- const result = utils.summaryTextBuilder(name, data);
-
- expect(result).toBe(
- 'Test summary: %{strong_start}7%{strong_end} errors, %{strong_start}10%{strong_end} total tests',
- );
- });
-
- it('should render text for multiple fixed results', () => {
- const name = 'Test summary';
- const data = { resolved: 4, total: 10 };
- const result = utils.summaryTextBuilder(name, data);
-
- expect(result).toBe(
- 'Test summary: %{strong_start}4%{strong_end} fixed test results, %{strong_start}10%{strong_end} total tests',
- );
- });
-
- it('should render text for multiple fixed, and multiple failed results', () => {
- const name = 'Test summary';
- const data = { failed: 3, resolved: 4, total: 10 };
- const result = utils.summaryTextBuilder(name, data);
-
- expect(result).toBe(
- 'Test summary: %{strong_start}3%{strong_end} failed and %{strong_start}4%{strong_end} fixed test results, %{strong_start}10%{strong_end} total tests',
- );
- });
-
- it('should render text for a singular fixed, and a singular failed result', () => {
- const name = 'Test summary';
- const data = { failed: 1, resolved: 1, total: 10 };
- const result = utils.summaryTextBuilder(name, data);
-
- expect(result).toBe(
- 'Test summary: %{strong_start}1%{strong_end} failed and %{strong_start}1%{strong_end} fixed test result, %{strong_start}10%{strong_end} total tests',
- );
- });
-
- it('should render text for singular failed, errored, and fixed results', () => {
- const name = 'Test summary';
- const data = { failed: 1, errored: 1, resolved: 1, total: 10 };
- const result = utils.summaryTextBuilder(name, data);
-
- expect(result).toBe(
- 'Test summary: %{strong_start}1%{strong_end} failed, %{strong_start}1%{strong_end} error and %{strong_start}1%{strong_end} fixed test result, %{strong_start}10%{strong_end} total tests',
- );
- });
-
- it('should render text for multiple failed, errored, and fixed results', () => {
- const name = 'Test summary';
- const data = { failed: 2, errored: 3, resolved: 4, total: 10 };
- const result = utils.summaryTextBuilder(name, data);
-
- expect(result).toBe(
- 'Test summary: %{strong_start}2%{strong_end} failed, %{strong_start}3%{strong_end} errors and %{strong_start}4%{strong_end} fixed test results, %{strong_start}10%{strong_end} total tests',
- );
- });
- });
-
- describe('reportTextBuilder', () => {
- const name = 'Rspec';
-
- it('should render text for no changed results in multiple tests', () => {
- const data = { name, summary: { total: 10 } };
- const result = utils.reportTextBuilder(data);
-
- expect(result).toBe('Rspec: no changed test results, 10 total tests');
- });
-
- it('should render text for no changed results in one test', () => {
- const data = { name, summary: { total: 1 } };
- const result = utils.reportTextBuilder(data);
-
- expect(result).toBe('Rspec: no changed test results, 1 total test');
- });
-
- it('should render text for multiple failed results', () => {
- const data = { name, summary: { failed: 3, total: 10 } };
- const result = utils.reportTextBuilder(data);
-
- expect(result).toBe('Rspec: 3 failed, 10 total tests');
- });
-
- it('should render text for multiple errored results', () => {
- const data = { name, summary: { errored: 7, total: 10 } };
- const result = utils.reportTextBuilder(data);
-
- expect(result).toBe('Rspec: 7 errors, 10 total tests');
- });
-
- it('should render text for multiple fixed results', () => {
- const data = { name, summary: { resolved: 4, total: 10 } };
- const result = utils.reportTextBuilder(data);
-
- expect(result).toBe('Rspec: 4 fixed test results, 10 total tests');
- });
-
- it('should render text for multiple fixed, and multiple failed results', () => {
- const data = { name, summary: { failed: 3, resolved: 4, total: 10 } };
- const result = utils.reportTextBuilder(data);
-
- expect(result).toBe('Rspec: 3 failed and 4 fixed test results, 10 total tests');
- });
-
- it('should render text for a singular fixed, and a singular failed result', () => {
- const data = { name, summary: { failed: 1, resolved: 1, total: 10 } };
- const result = utils.reportTextBuilder(data);
-
- expect(result).toBe('Rspec: 1 failed and 1 fixed test result, 10 total tests');
- });
-
- it('should render text for singular failed, errored, and fixed results', () => {
- const data = { name, summary: { failed: 1, errored: 1, resolved: 1, total: 10 } };
- const result = utils.reportTextBuilder(data);
-
- expect(result).toBe('Rspec: 1 failed, 1 error and 1 fixed test result, 10 total tests');
- });
-
- it('should render text for multiple failed, errored, and fixed results', () => {
- const data = { name, summary: { failed: 2, errored: 3, resolved: 4, total: 10 } };
- const result = utils.reportTextBuilder(data);
-
- expect(result).toBe('Rspec: 2 failed, 3 errors and 4 fixed test results, 10 total tests');
- });
- });
-
- describe('recentFailuresTextBuilder', () => {
- it.each`
- recentlyFailed | failed | expected
- ${0} | ${1} | ${''}
- ${1} | ${1} | ${'1 out of 1 failed test has failed more than once in the last 14 days'}
- ${1} | ${2} | ${'1 out of 2 failed tests has failed more than once in the last 14 days'}
- ${2} | ${3} | ${'2 out of 3 failed tests have failed more than once in the last 14 days'}
- `(
- 'should render summary for $recentlyFailed out of $failed failures',
- ({ recentlyFailed, failed, expected }) => {
- const result = utils.recentFailuresTextBuilder({ recentlyFailed, failed });
-
- expect(result).toBe(expected);
- },
- );
- });
-
- describe('countRecentlyFailedTests', () => {
- it('counts tests with more than one recent failure in a report', () => {
- const report = {
- new_failures: [{ recent_failures: { count: 2 } }],
- existing_failures: [{ recent_failures: { count: 1 } }],
- resolved_failures: [{ recent_failures: { count: 20 } }, { recent_failures: { count: 5 } }],
- };
- const result = utils.countRecentlyFailedTests(report);
-
- expect(result).toBe(3);
- });
-
- it('counts tests with more than one recent failure in an array of reports', () => {
- const reports = [
- {
- new_failures: [{ recent_failures: { count: 2 } }],
- existing_failures: [
- { recent_failures: { count: 20 } },
- { recent_failures: { count: 5 } },
- ],
- resolved_failures: [{ recent_failures: { count: 2 } }],
- },
- {
- new_failures: [{ recent_failures: { count: 8 } }, { recent_failures: { count: 14 } }],
- existing_failures: [{ recent_failures: { count: 1 } }],
- resolved_failures: [{ recent_failures: { count: 7 } }, { recent_failures: { count: 5 } }],
- },
- ];
- const result = utils.countRecentlyFailedTests(reports);
-
- expect(result).toBe(8);
- });
-
- it.each([
- [],
- {},
- null,
- undefined,
- { new_failures: undefined },
- [{ existing_failures: null }],
- { resolved_failures: [{}] },
- [{ new_failures: [{ recent_failures: {} }] }],
- ])('returns 0 when subject is %s', (subject) => {
- const result = utils.countRecentlyFailedTests(subject);
-
- expect(result).toBe(0);
- });
- });
-
- describe('formatFilePath', () => {
- it.each`
- file | expected
- ${'./test.js'} | ${'test.js'}
- ${'/test.js'} | ${'test.js'}
- ${'.//////////////test.js'} | ${'test.js'}
- ${'test.js'} | ${'test.js'}
- ${'mock/path./test.js'} | ${'mock/path./test.js'}
- ${'./mock/path./test.js'} | ${'mock/path./test.js'}
- `('should format $file to be $expected', ({ file, expected }) => {
- expect(utils.formatFilePath(file)).toBe(expected);
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/extentions/accessibility/index_spec.js b/spec/frontend/vue_mr_widget/extentions/accessibility/index_spec.js
deleted file mode 100644
index a06ad930abe..00000000000
--- a/spec/frontend/vue_mr_widget/extentions/accessibility/index_spec.js
+++ /dev/null
@@ -1,127 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import { trimText } from 'helpers/text_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import axios from '~/lib/utils/axios_utils';
-import extensionsContainer from '~/vue_merge_request_widget/components/extensions/container';
-import { registerExtension } from '~/vue_merge_request_widget/components/extensions';
-import accessibilityExtension from '~/vue_merge_request_widget/extensions/accessibility';
-import httpStatusCodes from '~/lib/utils/http_status';
-import { accessibilityReportResponseErrors, accessibilityReportResponseSuccess } from './mock_data';
-
-describe('Accessibility extension', () => {
- let wrapper;
- let mock;
-
- registerExtension(accessibilityExtension);
-
- const endpoint = '/root/repo/-/merge_requests/4/accessibility_reports.json';
-
- const mockApi = (statusCode, data) => {
- mock.onGet(endpoint).reply(statusCode, data);
- };
-
- const findToggleCollapsedButton = () => wrapper.findByTestId('toggle-button');
- const findAllExtensionListItems = () => wrapper.findAllByTestId('extension-list-item');
-
- const createComponent = () => {
- wrapper = mountExtended(extensionsContainer, {
- propsData: {
- mr: {
- accessibilityReportPath: endpoint,
- },
- },
- });
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- wrapper.destroy();
- mock.restore();
- });
-
- describe('summary', () => {
- it('displays loading text', () => {
- mockApi(httpStatusCodes.OK, accessibilityReportResponseErrors);
-
- createComponent();
-
- expect(wrapper.text()).toBe('Accessibility scanning results are being parsed');
- });
-
- it('displays failed loading text', async () => {
- mockApi(httpStatusCodes.INTERNAL_SERVER_ERROR);
-
- createComponent();
-
- await waitForPromises();
-
- expect(wrapper.text()).toBe('Accessibility scanning failed loading results');
- });
-
- it('displays detected errors and is expandable', async () => {
- mockApi(httpStatusCodes.OK, accessibilityReportResponseErrors);
-
- createComponent();
-
- await waitForPromises();
-
- expect(wrapper.text()).toBe(
- 'Accessibility scanning detected 8 issues for the source branch only',
- );
- expect(findToggleCollapsedButton().exists()).toBe(true);
- });
-
- it('displays no detected errors and is not expandable', async () => {
- mockApi(httpStatusCodes.OK, accessibilityReportResponseSuccess);
-
- createComponent();
-
- await waitForPromises();
-
- expect(wrapper.text()).toBe(
- 'Accessibility scanning detected no issues for the source branch only',
- );
- expect(findToggleCollapsedButton().exists()).toBe(false);
- });
- });
-
- describe('expanded data', () => {
- beforeEach(async () => {
- mockApi(httpStatusCodes.OK, accessibilityReportResponseErrors);
-
- createComponent();
-
- await waitForPromises();
-
- findToggleCollapsedButton().trigger('click');
-
- await waitForPromises();
- });
-
- it('displays all report list items in viewport', async () => {
- expect(findAllExtensionListItems()).toHaveLength(7);
- });
-
- it('displays report list item formatted', () => {
- const text = {
- newError: trimText(findAllExtensionListItems().at(0).text()),
- resolvedError: trimText(findAllExtensionListItems().at(3).text()),
- existingError: trimText(findAllExtensionListItems().at(6).text()),
- };
-
- expect(text.newError).toBe(
- 'New The accessibility scanning found an error of the following type: WCAG2AA.Principle2.Guideline2_4.2_4_1.H64.1 Learn more Message: Iframe element requires a non-empty title attribute that identifies the frame.',
- );
- expect(text.resolvedError).toBe(
- 'The accessibility scanning found an error of the following type: WCAG2AA.Principle1.Guideline1_1.1_1_1.H30.2 Learn more Message: Img element is the only content of the link, but is missing alt text. The alt text should describe the purpose of the link.',
- );
- expect(text.existingError).toBe(
- 'The accessibility scanning found an error of the following type: WCAG2AA.Principle1.Guideline1_1.1_1_1.H37 Learn more Message: Img element missing an alt attribute. Use the alt attribute to specify a short text alternative.',
- );
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/extentions/accessibility/mock_data.js b/spec/frontend/vue_mr_widget/extentions/accessibility/mock_data.js
deleted file mode 100644
index 06dc93d101f..00000000000
--- a/spec/frontend/vue_mr_widget/extentions/accessibility/mock_data.js
+++ /dev/null
@@ -1,137 +0,0 @@
-export const accessibilityReportResponseErrors = {
- status: 'failed',
- new_errors: [
- {
- code: 'WCAG2AA.Principle2.Guideline2_4.2_4_1.H64.1',
- type: 'error',
- type_code: 1,
- message: 'Iframe element requires a non-empty title attribute that identifies the frame.',
- context:
- '<iframe height="0" width="0" style="display: none; visibility: hidden;" src="//10421980.fls.doubleclick.net/activityi;src=10421980;type=count0;cat=globa0;ord=6271888671448;gtm=2wg1c0;auiddc=40010797.1642181125;u1=undefined;u2=undefined;u3=undefined;u...',
- selector: 'html > body > iframe:nth-child(42)',
- runner: 'htmlcs',
- runner_extras: {},
- },
- {
- code: 'WCAG2AA.Principle3.Guideline3_2.3_2_2.H32.2',
- type: 'error',
- type_code: 1,
- message:
- 'This form does not contain a submit button, which creates issues for those who cannot submit the form using the keyboard. Submit buttons are INPUT elements with type attribute "submit" or "image", or BUTTON elements with type "submit" or omitted/invalid.',
- context:
- '<form class="challenge-form" id="challenge-form" action="/users/sign_in?__cf_chl_jschl_tk__=xoagAHj9DXTTDveypAmMkakkNQgeWc6LmZA53YyDeSg-1642181129-0-gaNycGzNB1E" method="POST" enctype="application/x-www-form-urlencoded">\n <input type="hidden" name...',
- selector: '#challenge-form',
- runner: 'htmlcs',
- runner_extras: {},
- },
- {
- code: 'WCAG2AA.Principle2.Guideline2_4.2_4_1.H64.1',
- type: 'error',
- type_code: 1,
- message: 'Iframe element requires a non-empty title attribute that identifies the frame.',
- context: '<iframe style="display: none;"></iframe>',
- selector: 'html > body > iframe',
- runner: 'htmlcs',
- runner_extras: {},
- },
- ],
- resolved_errors: [
- {
- code: 'WCAG2AA.Principle2.Guideline2_4.2_4_1.H64.1',
- type: 'error',
- type_code: 1,
- message: 'Iframe element requires a non-empty title attribute that identifies the frame.',
- context:
- '<iframe height="0" width="0" style="display: none; visibility: hidden;" src="//10421980.fls.doubleclick.net/activityi;src=10421980;type=count0;cat=globa0;ord=6722452746146;gtm=2wg1a0;auiddc=716711306.1642082367;u1=undefined;u2=undefined;u3=undefined;...',
- selector: 'html > body > iframe:nth-child(42)',
- runner: 'htmlcs',
- runner_extras: {},
- },
- {
- code: 'WCAG2AA.Principle3.Guideline3_2.3_2_2.H32.2',
- type: 'error',
- type_code: 1,
- message:
- 'This form does not contain a submit button, which creates issues for those who cannot submit the form using the keyboard. Submit buttons are INPUT elements with type attribute "submit" or "image", or BUTTON elements with type "submit" or omitted/invalid.',
- context:
- '<form class="challenge-form" id="challenge-form" action="/users/sign_in?__cf_chl_jschl_tk__=vDKZT2hjxWCstlWz2wtxsLdqLF79rM4IsoxzMgY6Lfw-1642082370-0-gaNycGzNB2U" method="POST" enctype="application/x-www-form-urlencoded">\n <input type="hidden" name...',
- selector: '#challenge-form',
- runner: 'htmlcs',
- runner_extras: {},
- },
- ],
- existing_errors: [
- {
- code: 'WCAG2AA.Principle1.Guideline1_1.1_1_1.H30.2',
- type: 'error',
- type_code: 1,
- message:
- 'Img element is the only content of the link, but is missing alt text. The alt text should describe the purpose of the link.',
- context: '<a href="/" data-nav="logo">\n<img src="/images/icons/logos/...</a>',
- selector: '#navigation-mobile > header > a',
- runner: 'htmlcs',
- runner_extras: {},
- },
- {
- code: 'WCAG2AA.Principle1.Guideline1_1.1_1_1.H37',
- type: 'error',
- type_code: 1,
- message:
- 'Img element missing an alt attribute. Use the alt attribute to specify a short text alternative.',
- context: '<img src="/images/icons/slp-hamburger.svg" class="slp-inline-block slp-mr-8">',
- selector: '#slpMobileNavActive > img',
- runner: 'htmlcs',
- runner_extras: {},
- },
- {
- code: 'WCAG2AA.Principle1.Guideline1_1.1_1_1.H37',
- type: 'error',
- type_code: 1,
- message:
- 'Img element missing an alt attribute. Use the alt attribute to specify a short text alternative.',
- context: '<img src="/images/icons/slp-caret-down.svg">',
- selector: '#navigation-mobile > div:nth-child(2) > div:nth-child(2) > button > div > img',
- runner: 'htmlcs',
- runner_extras: {},
- },
- {
- code: 'WCAG2AA.Principle1.Guideline1_1.1_1_1.H37',
- type: 'error',
- type_code: 1,
- message:
- 'Img element missing an alt attribute. Use the alt attribute to specify a short text alternative.',
- context: '<img src="/images/icons/slp-caret-down.svg">',
- selector: '#navigation-mobile > div:nth-child(2) > div:nth-child(3) > button > div > img',
- runner: 'htmlcs',
- runner_extras: {},
- },
- {
- code: 'WCAG2AA.Principle1.Guideline1_1.1_1_1.H37',
- type: 'error',
- type_code: 1,
- message:
- 'Img element missing an alt attribute. Use the alt attribute to specify a short text alternative.',
- context: '<img src="/images/icons/slp-caret-down.svg">',
- selector: '#navigation-mobile > div:nth-child(2) > div:nth-child(4) > button > div > img',
- runner: 'htmlcs',
- runner_extras: {},
- },
- ],
- summary: {
- total: 8,
- resolved: 2,
- errored: 8,
- },
-};
-
-export const accessibilityReportResponseSuccess = {
- status: 'success',
- new_errors: [],
- resolved_errors: [],
- existing_errors: [],
- summary: {
- total: 0,
- resolved: 0,
- errored: 0,
- },
-};
diff --git a/spec/frontend/vue_mr_widget/extentions/code_quality/index_spec.js b/spec/frontend/vue_mr_widget/extentions/code_quality/index_spec.js
deleted file mode 100644
index 9a72e4a086b..00000000000
--- a/spec/frontend/vue_mr_widget/extentions/code_quality/index_spec.js
+++ /dev/null
@@ -1,145 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import { trimText } from 'helpers/text_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import axios from '~/lib/utils/axios_utils';
-import extensionsContainer from '~/vue_merge_request_widget/components/extensions/container';
-import { registerExtension } from '~/vue_merge_request_widget/components/extensions';
-import codeQualityExtension from '~/vue_merge_request_widget/extensions/code_quality';
-import httpStatusCodes from '~/lib/utils/http_status';
-import {
- codeQualityResponseNewErrors,
- codeQualityResponseResolvedErrors,
- codeQualityResponseResolvedAndNewErrors,
- codeQualityResponseNoErrors,
-} from './mock_data';
-
-describe('Code Quality extension', () => {
- let wrapper;
- let mock;
-
- registerExtension(codeQualityExtension);
-
- const endpoint = '/root/repo/-/merge_requests/4/accessibility_reports.json';
-
- const mockApi = (statusCode, data) => {
- mock.onGet(endpoint).reply(statusCode, data);
- };
-
- const findToggleCollapsedButton = () => wrapper.findByTestId('toggle-button');
- const findAllExtensionListItems = () => wrapper.findAllByTestId('extension-list-item');
-
- const createComponent = () => {
- wrapper = mountExtended(extensionsContainer, {
- propsData: {
- mr: {
- codeQuality: endpoint,
- blobPath: {
- head_path: 'example/path',
- base_path: 'example/path',
- },
- },
- },
- });
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- wrapper.destroy();
- mock.restore();
- });
-
- describe('summary', () => {
- it('displays loading text', () => {
- mockApi(httpStatusCodes.OK, codeQualityResponseNewErrors);
-
- createComponent();
-
- expect(wrapper.text()).toBe('Code Quality test metrics results are being parsed');
- });
-
- it('displays failed loading text', async () => {
- mockApi(httpStatusCodes.INTERNAL_SERVER_ERROR);
-
- createComponent();
-
- await waitForPromises();
- expect(wrapper.text()).toBe('Code Quality failed loading results');
- });
-
- it('displays quality degradation', async () => {
- mockApi(httpStatusCodes.OK, codeQualityResponseNewErrors);
-
- createComponent();
-
- await waitForPromises();
-
- expect(wrapper.text()).toBe('Code Quality degraded on 2 points.');
- });
-
- it('displays quality improvement', async () => {
- mockApi(httpStatusCodes.OK, codeQualityResponseResolvedErrors);
-
- createComponent();
-
- await waitForPromises();
-
- expect(wrapper.text()).toBe('Code Quality improved on 2 points.');
- });
-
- it('displays quality improvement and degradation', async () => {
- mockApi(httpStatusCodes.OK, codeQualityResponseResolvedAndNewErrors);
-
- createComponent();
-
- await waitForPromises();
-
- expect(wrapper.text()).toBe('Code Quality improved on 1 point and degraded on 1 point.');
- });
-
- it('displays no detected errors', async () => {
- mockApi(httpStatusCodes.OK, codeQualityResponseNoErrors);
-
- createComponent();
-
- await waitForPromises();
-
- expect(wrapper.text()).toBe('No changes to Code Quality.');
- });
- });
-
- describe('expanded data', () => {
- beforeEach(async () => {
- mockApi(httpStatusCodes.OK, codeQualityResponseResolvedAndNewErrors);
-
- createComponent();
-
- await waitForPromises();
-
- findToggleCollapsedButton().trigger('click');
-
- await waitForPromises();
- });
-
- it('displays all report list items in viewport', async () => {
- expect(findAllExtensionListItems()).toHaveLength(2);
- });
-
- it('displays report list item formatted', () => {
- const text = {
- newError: trimText(findAllExtensionListItems().at(0).text().replace(/\s+/g, ' ').trim()),
- resolvedError: findAllExtensionListItems().at(1).text().replace(/\s+/g, ' ').trim(),
- };
-
- expect(text.newError).toContain(
- "Minor - Parsing error: 'return' outside of function in index.js:12",
- );
- expect(text.resolvedError).toContain(
- "Minor - Parsing error: 'return' outside of function in index.js:12",
- );
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/extentions/code_quality/mock_data.js b/spec/frontend/vue_mr_widget/extentions/code_quality/mock_data.js
deleted file mode 100644
index f5ad0ce7377..00000000000
--- a/spec/frontend/vue_mr_widget/extentions/code_quality/mock_data.js
+++ /dev/null
@@ -1,87 +0,0 @@
-export const codeQualityResponseNewErrors = {
- status: 'failed',
- new_errors: [
- {
- description: "Parsing error: 'return' outside of function",
- severity: 'minor',
- file_path: 'index.js',
- line: 12,
- },
- {
- description: 'TODO found',
- severity: 'minor',
- file_path: '.gitlab-ci.yml',
- line: 73,
- },
- ],
- resolved_errors: [],
- existing_errors: [],
- summary: {
- total: 2,
- resolved: 0,
- errored: 2,
- },
-};
-
-export const codeQualityResponseResolvedErrors = {
- status: 'failed',
- new_errors: [],
- resolved_errors: [
- {
- description: "Parsing error: 'return' outside of function",
- severity: 'minor',
- file_path: 'index.js',
- line: 12,
- },
- {
- description: 'TODO found',
- severity: 'minor',
- file_path: '.gitlab-ci.yml',
- line: 73,
- },
- ],
- existing_errors: [],
- summary: {
- total: 2,
- resolved: 2,
- errored: 0,
- },
-};
-
-export const codeQualityResponseResolvedAndNewErrors = {
- status: 'failed',
- new_errors: [
- {
- description: "Parsing error: 'return' outside of function",
- severity: 'minor',
- file_path: 'index.js',
- line: 12,
- },
- ],
- resolved_errors: [
- {
- description: "Parsing error: 'return' outside of function",
- severity: 'minor',
- file_path: 'index.js',
- line: 12,
- },
- ],
- existing_errors: [],
- summary: {
- total: 2,
- resolved: 1,
- errored: 1,
- },
-};
-
-export const codeQualityResponseNoErrors = {
- status: 'failed',
- new_errors: [],
- resolved_errors: [],
- existing_errors: [],
- summary: {
- total: 0,
- resolved: 0,
- errored: 0,
- },
-};
diff --git a/spec/frontend/vue_mr_widget/extentions/terraform/index_spec.js b/spec/frontend/vue_mr_widget/extentions/terraform/index_spec.js
deleted file mode 100644
index d9faa7b2d25..00000000000
--- a/spec/frontend/vue_mr_widget/extentions/terraform/index_spec.js
+++ /dev/null
@@ -1,192 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import api from '~/api';
-import axios from '~/lib/utils/axios_utils';
-import Poll from '~/lib/utils/poll';
-import extensionsContainer from '~/vue_merge_request_widget/components/extensions/container';
-import { registerExtension } from '~/vue_merge_request_widget/components/extensions';
-import terraformExtension from '~/vue_merge_request_widget/extensions/terraform';
-import {
- plans,
- validPlanWithName,
- validPlanWithoutName,
- invalidPlanWithName,
- invalidPlanWithoutName,
-} from '../../components/terraform/mock_data';
-
-jest.mock('~/api.js');
-
-describe('Terraform extension', () => {
- let wrapper;
- let mock;
-
- const endpoint = '/path/to/terraform/report.json';
- const successStatusCode = 200;
- const errorStatusCode = 500;
-
- const findListItem = (at) => wrapper.findAllByTestId('extension-list-item').at(at);
-
- registerExtension(terraformExtension);
-
- const mockPollingApi = (response, body, header) => {
- mock.onGet(endpoint).reply(response, body, header);
- };
-
- const createComponent = () => {
- wrapper = mountExtended(extensionsContainer, {
- propsData: {
- mr: {
- terraformReportsPath: endpoint,
- },
- },
- });
- return axios.waitForAll();
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- wrapper.destroy();
- mock.restore();
- });
-
- describe('summary', () => {
- describe('while loading', () => {
- const loadingText = 'Loading Terraform reports...';
- it('should render loading text', async () => {
- mockPollingApi(successStatusCode, plans, {});
- createComponent();
-
- expect(wrapper.text()).toContain(loadingText);
- await waitForPromises();
- expect(wrapper.text()).not.toContain(loadingText);
- });
- });
-
- describe('when the fetching fails', () => {
- beforeEach(() => {
- mockPollingApi(errorStatusCode, null, {});
- return createComponent();
- });
-
- it('should generate one invalid plan and render correct summary text', () => {
- expect(wrapper.text()).toContain('1 Terraform report failed to generate');
- });
- });
-
- describe('when the fetching succeeds', () => {
- describe.each`
- responseType | response | summaryTitle | summarySubtitle
- ${'1 invalid report'} | ${{ 0: invalidPlanWithName }} | ${'1 Terraform report failed to generate'} | ${''}
- ${'2 valid reports'} | ${{ 0: validPlanWithName, 1: validPlanWithName }} | ${'2 Terraform reports were generated in your pipelines'} | ${''}
- ${'1 valid and 2 invalid reports'} | ${{ 0: validPlanWithName, 1: invalidPlanWithName, 2: invalidPlanWithName }} | ${'Terraform report was generated in your pipelines'} | ${'2 Terraform reports failed to generate'}
- `('and received $responseType', ({ response, summaryTitle, summarySubtitle }) => {
- beforeEach(async () => {
- mockPollingApi(successStatusCode, response, {});
- return createComponent();
- });
-
- it(`should render correct summary text`, () => {
- expect(wrapper.text()).toContain(summaryTitle);
-
- if (summarySubtitle) {
- expect(wrapper.text()).toContain(summarySubtitle);
- }
- });
- });
- });
- });
-
- describe('expanded data', () => {
- beforeEach(async () => {
- mockPollingApi(successStatusCode, plans, {});
- await createComponent();
-
- wrapper.findByTestId('toggle-button').trigger('click');
- });
-
- describe.each`
- reportType | title | subtitle | logLink | lineNumber
- ${'a valid report with name'} | ${`The job ${validPlanWithName.job_name} generated a report.`} | ${`Reported Resource Changes: ${validPlanWithName.create} to add, ${validPlanWithName.update} to change, ${validPlanWithName.delete} to delete`} | ${validPlanWithName.job_path} | ${0}
- ${'a valid report without name'} | ${'A Terraform report was generated in your pipelines.'} | ${`Reported Resource Changes: ${validPlanWithoutName.create} to add, ${validPlanWithoutName.update} to change, ${validPlanWithoutName.delete} to delete`} | ${validPlanWithoutName.job_path} | ${1}
- ${'an invalid report with name'} | ${`The job ${invalidPlanWithName.job_name} failed to generate a report.`} | ${'Generating the report caused an error.'} | ${invalidPlanWithName.job_path} | ${2}
- ${'an invalid report without name'} | ${'A Terraform report failed to generate.'} | ${'Generating the report caused an error.'} | ${invalidPlanWithoutName.job_path} | ${3}
- `('renders correct text for $reportType', ({ title, subtitle, logLink, lineNumber }) => {
- it('renders correct text', () => {
- expect(findListItem(lineNumber).text()).toContain(title);
- expect(findListItem(lineNumber).text()).toContain(subtitle);
- });
-
- it(`${logLink ? 'renders' : "doesn't render"} the log link`, () => {
- const logText = 'Full log';
- if (logLink) {
- expect(
- findListItem(lineNumber)
- .find('[data-testid="extension-actions-button"]')
- .attributes('href'),
- ).toBe(logLink);
- } else {
- expect(findListItem(lineNumber).text()).not.toContain(logText);
- }
- });
- });
-
- it('responds with the correct telemetry when the deeply nested "Full log" link is clicked', () => {
- api.trackRedisHllUserEvent.mockClear();
- api.trackRedisCounterEvent.mockClear();
-
- findListItem(0).find('[data-testid="extension-actions-button"]').trigger('click');
-
- expect(api.trackRedisHllUserEvent).toHaveBeenCalledTimes(1);
- expect(api.trackRedisHllUserEvent).toHaveBeenCalledWith(
- 'i_code_review_merge_request_widget_terraform_click_full_report',
- );
- expect(api.trackRedisCounterEvent).toHaveBeenCalledTimes(1);
- expect(api.trackRedisCounterEvent).toHaveBeenCalledWith(
- 'i_code_review_merge_request_widget_terraform_count_click_full_report',
- );
- });
- });
-
- describe('polling', () => {
- let pollRequest;
-
- beforeEach(() => {
- pollRequest = jest.spyOn(Poll.prototype, 'makeRequest');
- });
-
- afterEach(() => {
- pollRequest.mockRestore();
- });
-
- describe('successful poll', () => {
- beforeEach(() => {
- mockPollingApi(successStatusCode, plans, {});
-
- return createComponent();
- });
-
- it('does not make additional requests after poll is successful', () => {
- expect(pollRequest).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('polling fails', () => {
- beforeEach(() => {
- mockPollingApi(errorStatusCode, null, {});
- return createComponent();
- });
-
- it('generates one broken plan', () => {
- expect(wrapper.text()).toContain('1 Terraform report failed to generate');
- });
-
- it('does not make additional requests after poll is unsuccessful', () => {
- expect(pollRequest).toHaveBeenCalledTimes(1);
- });
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/mock_data.js b/spec/frontend/vue_mr_widget/mock_data.js
deleted file mode 100644
index 20d00a116bb..00000000000
--- a/spec/frontend/vue_mr_widget/mock_data.js
+++ /dev/null
@@ -1,358 +0,0 @@
-import { SUCCESS } from '~/vue_merge_request_widget/components/deployment/constants';
-
-export const artifacts = [
- {
- text: 'result.txt',
- url: 'bar',
- job_name: 'generate-artifact',
- job_path: 'bar',
- },
- {
- text: 'foo.txt',
- url: 'foo',
- job_name: 'foo-artifact',
- job_path: 'foo',
- },
-];
-
-export default {
- id: 132,
- iid: 22,
- assignee_id: null,
- author_id: 1,
- description: '',
- lock_version: null,
- milestone_id: null,
- position: 0,
- state: 'merged',
- title: 'Update README.md',
- updated_by_id: null,
- created_at: '2017-04-07T12:27:26.718Z',
- updated_at: '2017-04-07T15:39:25.852Z',
- time_estimate: 0,
- total_time_spent: 0,
- human_access: 'Maintainer',
- human_time_estimate: null,
- human_total_time_spent: null,
- in_progress_merge_commit_sha: null,
- merge_commit_sha: '53027d060246c8f47e4a9310fb332aa52f221775',
- short_merge_commit_sha: '53027d06',
- merge_error: null,
- merge_params: {
- force_remove_source_branch: null,
- },
- merge_status: 'can_be_merged',
- merge_user_id: null,
- pipelines_empty_svg_path: '/path/to/svg',
- source_branch: 'daaaa',
- source_branch_link: 'daaaa',
- source_project_id: 19,
- source_project_full_path: '/group1/project1',
- target_branch: 'main',
- target_project_id: 19,
- target_project_full_path: '/group2/project2',
- merge_request_add_ci_config_path: '/root/group2/project2/-/ci/editor',
- is_dismissed_suggest_pipeline: false,
- user_callouts_path: 'some/callout/path',
- suggest_pipeline_feature_id: 'suggest_pipeline',
- new_project_pipeline_path: '/group2/project2/pipelines/new',
- source_project_default_url: '/gitlab-org/html5-boilerplate.git',
- metrics: {
- merged_by: {
- name: 'Administrator',
- username: 'root',
- id: 1,
- state: 'active',
- avatar_url:
- 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- web_url: 'http://localhost:3000/root',
- },
- merged_at: '2017-04-07T15:39:25.696Z',
- closed_by: null,
- closed_at: null,
- },
- author: {
- name: 'Administrator',
- username: 'root',
- id: 1,
- state: 'active',
- avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- web_url: 'http://localhost:3000/root',
- },
- merge_user: null,
- diff_head_sha: '104096c51715e12e7ae41f9333e9fa35b73f385d',
- diff_head_commit_short_id: '104096c5',
- default_merge_commit_message:
- "Merge branch 'daaaa' into 'main'\n\nUpdate README.md\n\nSee merge request !22",
- pipeline: {
- id: 172,
- user: {
- name: 'Administrator',
- username: 'root',
- id: 1,
- state: 'active',
- avatar_url:
- 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- web_url: 'http://localhost:3000/root',
- },
- active: false,
- coverage: '92.16',
- path: '/root/acets-app/pipelines/172',
- details: {
- artifacts,
- status: {
- icon: 'status_success',
- favicon: 'favicon_status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- has_details: true,
- details_path: '/root/acets-app/pipelines/172',
- },
- duration: null,
- finished_at: '2017-04-07T14:00:14.256Z',
- stages: [
- {
- name: 'build',
- title: 'build: failed',
- status: {
- icon: 'status_failed',
- favicon: 'favicon_status_failed',
- text: 'failed',
- label: 'failed',
- group: 'failed',
- has_details: true,
- details_path: '/root/acets-app/pipelines/172#build',
- },
- path: '/root/acets-app/pipelines/172#build',
- dropdown_path: '/root/acets-app/pipelines/172/stage.json?stage=build',
- },
- {
- name: 'review',
- title: 'review: skipped',
- status: {
- icon: 'status_skipped',
- favicon: 'favicon_status_skipped',
- text: 'skipped',
- label: 'skipped',
- group: 'skipped',
- has_details: true,
- details_path: '/root/acets-app/pipelines/172#review',
- },
- path: '/root/acets-app/pipelines/172#review',
- dropdown_path: '/root/acets-app/pipelines/172/stage.json?stage=review',
- },
- ],
- manual_actions: [
- {
- name: 'stop_review',
- path: '/root/acets-app/builds/1427/play',
- playable: false,
- },
- ],
- },
- flags: {
- latest: false,
- triggered: false,
- stuck: false,
- yaml_errors: false,
- retryable: true,
- cancelable: false,
- merge_request_pipeline: false,
- detached_merge_request_pipeline: true,
- },
- ref: {
- name: 'daaaa',
- path: '/root/acets-app/tree/daaaa',
- tag: false,
- branch: true,
- },
- merge_request: {
- iid: 1,
- path: '/root/detached-merge-request-pipelines/-/merge_requests/1',
- title: 'Update README.md',
- source_branch: 'feature-1',
- source_branch_path: '/root/detached-merge-request-pipelines/branches/feature-1',
- target_branch: 'main',
- target_branch_path: '/root/detached-merge-request-pipelines/branches/main',
- },
- commit: {
- id: '104096c51715e12e7ae41f9333e9fa35b73f385d',
- short_id: '104096c5',
- title: 'Update README.md',
- created_at: '2017-04-07T15:27:18.000+03:00',
- parent_ids: ['2396536178668d8930c29d904e53bd4d06228b32'],
- message: 'Update README.md',
- author_name: 'Administrator',
- author_email: 'admin@example.com',
- authored_date: '2017-04-07T15:27:18.000+03:00',
- committer_name: 'Administrator',
- committer_email: 'admin@example.com',
- committed_date: '2017-04-07T15:27:18.000+03:00',
- author: {
- name: 'Administrator',
- username: 'root',
- id: 1,
- state: 'active',
- avatar_url:
- 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- web_url: 'http://localhost:3000/root',
- },
- author_gravatar_url:
- 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- commit_url:
- 'http://localhost:3000/root/acets-app/commit/104096c51715e12e7ae41f9333e9fa35b73f385d',
- commit_path: '/root/acets-app/commit/104096c51715e12e7ae41f9333e9fa35b73f385d',
- },
- retry_path: '/root/acets-app/pipelines/172/retry',
- created_at: '2017-04-07T12:27:19.520Z',
- updated_at: '2017-04-07T15:28:44.800Z',
- },
- pipelineCoverageDelta: '15.25',
- buildsWithCoverage: [
- { name: 'karma', coverage: '40.2' },
- { name: 'rspec', coverage: '80.4' },
- ],
- work_in_progress: false,
- source_branch_exists: false,
- mergeable_discussions_state: true,
- conflicts_can_be_resolved_in_ui: false,
- branch_missing: true,
- commits_count: 1,
- has_conflicts: false,
- can_be_merged: true,
- has_ci: true,
- ci_status: 'success',
- pipeline_status_path: '/root/acets-app/-/merge_requests/22/pipeline_status',
- issues_links: {
- closing: '',
- mentioned_but_not_closing: '',
- },
- current_user: {
- can_resolve_conflicts: true,
- can_remove_source_branch: false,
- can_revert_on_current_merge_request: true,
- can_cherry_pick_on_current_merge_request: true,
- },
- blob_path: {
- base_path: 'blob_path',
- head_path: 'blob_path',
- },
- codequality_reports_path: 'codequality_reports.json',
- codequality_help_path: 'code_quality.html',
- target_branch_path: '/root/acets-app/branches/main',
- source_branch_path: '/root/acets-app/branches/daaaa',
- conflict_resolution_ui_path: '/root/acets-app/-/merge_requests/22/conflicts',
- remove_wip_path: '/root/acets-app/-/merge_requests/22/remove_wip',
- cancel_auto_merge_path: '/root/acets-app/-/merge_requests/22/cancel_auto_merge',
- create_issue_to_resolve_discussions_path:
- '/root/acets-app/-/issues/new?merge_request_to_resolve_discussions_of=22',
- merge_path: '/root/acets-app/-/merge_requests/22/merge',
- cherry_pick_in_fork_path:
- '/root/acets-app/forks?continue%5Bnotice%5D=You%27re+not+allowed+to+make+changes+to+this+project+directly.+A+fork+of+this+project+has+been+created+that+you+can+make+changes+in%2C+so+you+can+submit+a+merge+request.+Try+to+revert+this+commit+again.&continue%5Bnotice_now%5D=You%27re+not+allowed+to+make+changes+to+this+project+directly.+A+fork+of+this+project+is+being+created+that+you+can+make+changes+in%2C+so+you+can+submit+a+merge+request.&continue%5Bto%5D=%2Froot%2Facets-app%2Fmerge_requests%2F22&namespace_key=1',
- revert_in_fork_path:
- '/root/acets-app/forks?continue%5Bnotice%5D=You%27re+not+allowed+to+make+changes+to+this+project+directly.+A+fork+of+this+project+has+been+created+that+you+can+make+changes+in%2C+so+you+can+submit+a+merge+request.+Try+to+cherry-pick+this+commit+again.&continue%5Bnotice_now%5D=You%27re+not+allowed+to+make+changes+to+this+project+directly.+A+fork+of+this+project+is+being+created+that+you+can+make+changes+in%2C+so+you+can+submit+a+merge+request.&continue%5Bto%5D=%2Froot%2Facets-app%2Fmerge_requests%2F22&namespace_key=1',
- email_patches_path: '/root/acets-app/-/merge_requests/22.patch',
- plain_diff_path: '/root/acets-app/-/merge_requests/22.diff',
- merge_request_basic_path: '/root/acets-app/-/merge_requests/22.json?serializer=basic',
- merge_request_widget_path: '/root/acets-app/-/merge_requests/22/widget.json',
- merge_request_cached_widget_path: '/cached.json',
- merge_check_path: '/root/acets-app/-/merge_requests/22/merge_check',
- ci_environments_status_url: '/root/acets-app/-/merge_requests/22/ci_environments_status',
- project_archived: false,
- default_merge_commit_message_with_description:
- "Merge branch 'daaaa' into 'main'\n\nUpdate README.md\n\nSee merge request !22",
- default_squash_commit_message: 'Test squash commit message',
- diverged_commits_count: 0,
- only_allow_merge_if_pipeline_succeeds: false,
- commit_change_content_path: '/root/acets-app/-/merge_requests/22/commit_change_content',
- merge_commit_path:
- 'http://localhost:3000/root/acets-app/commit/53027d060246c8f47e4a9310fb332aa52f221775',
- mr_troubleshooting_docs_path: 'help',
- ci_troubleshooting_docs_path: 'help2',
- merge_request_pipelines_docs_path: '/help/ci/pipelines/merge_request_pipelines.md',
- squash: true,
- visual_review_app_available: true,
- merge_trains_enabled: true,
- merge_trains_count: 3,
- merge_train_index: 1,
- security_reports_docs_path: 'security-reports-docs-path',
- sast_comparison_path: '/sast_comparison_path',
- secret_detection_comparison_path: '/secret_detection_comparison_path',
- gitpod_enabled: true,
- show_gitpod_button: true,
- gitpod_url: 'http://gitpod.localhost',
- user_preferences_gitpod_path: '/-/profile/preferences#user_gitpod_enabled',
- user_profile_enable_gitpod_path: '/-/profile?user%5Bgitpod_enabled%5D=true',
-};
-
-export const mockStore = {
- pipeline: {
- id: 0,
- details: {
- artifacts,
- status: {
- details_path: '/root/review-app-tester/pipelines/66',
- favicon:
- '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2. png',
- group: 'success-with-warnings',
- has_details: true,
- icon: 'status_warning',
- illustration: null,
- label: 'passed with warnings',
- text: 'passed',
- tooltip: 'passed',
- },
- },
- flags: {},
- ref: {},
- },
- mergePipeline: {
- id: 1,
- details: {
- artifacts,
- status: {
- details_path: '/root/review-app-tester/pipelines/66',
- favicon:
- '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2. png',
- group: 'success-with-warnings',
- has_details: true,
- icon: 'status_warning',
- illustration: null,
- label: 'passed with warnings',
- text: 'passed',
- tooltip: 'passed',
- },
- },
- flags: {},
- ref: {},
- },
- targetBranch: 'target-branch',
- sourceBranch: 'source-branch',
- sourceBranchLink: 'source-branch-link',
- deployments: [
- {
- id: 0,
- name: 'bogus',
- external_url: 'https://fake.com',
- external_url_formatted: 'https://fake.com',
- status: SUCCESS,
- },
- {
- id: 1,
- name: 'bogus-docs',
- external_url: 'https://fake.com',
- external_url_formatted: 'https://fake.com',
- status: SUCCESS,
- },
- ],
- postMergeDeployments: [
- { id: 0, name: 'prod', status: SUCCESS },
- { id: 1, name: 'prod-docs', status: SUCCESS },
- ],
- mrTroubleshootingDocsPath: 'mr-troubleshooting-docs-path',
- ciTroubleshootingDocsPath: 'ci-troubleshooting-docs-path',
- ciStatus: 'ci-status',
- hasCI: true,
- exposedArtifactsPath: 'exposed_artifacts.json',
-};
diff --git a/spec/frontend/vue_mr_widget/mr_widget_how_to_merge_modal_spec.js b/spec/frontend/vue_mr_widget/mr_widget_how_to_merge_modal_spec.js
deleted file mode 100644
index 295b9df30b9..00000000000
--- a/spec/frontend/vue_mr_widget/mr_widget_how_to_merge_modal_spec.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import { GlModal } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import MrWidgetHowToMergeModal from '~/vue_merge_request_widget/components/mr_widget_how_to_merge_modal.vue';
-
-describe('MRWidgetHowToMerge', () => {
- let wrapper;
-
- function mountComponent({ data = {}, props = {} } = {}) {
- wrapper = shallowMount(MrWidgetHowToMergeModal, {
- data() {
- return data;
- },
- propsData: props,
- stubs: {},
- });
- }
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- beforeEach(() => {
- mountComponent();
- });
-
- const findModal = () => wrapper.find(GlModal);
- const findInstructionsFields = () =>
- wrapper.findAll('[ data-testid="how-to-merge-instructions"]');
- const findTipLink = () => wrapper.find("[data-testid='docs-tip']");
-
- it('renders a modal', () => {
- expect(findModal().exists()).toBe(true);
- });
-
- it('renders a selection of markdown fields', () => {
- expect(findInstructionsFields().length).toBe(3);
- });
-
- it('renders a tip including a link to docs when a valid link is present', () => {
- mountComponent({ props: { reviewingDocsPath: '/gitlab-org/help' } });
- expect(findTipLink().exists()).toBe(true);
- });
-
- it('should not render a tip including a link to docs when a valid link is not present', () => {
- expect(findTipLink().exists()).toBe(false);
- });
-
- it('should render different instructions based on if the user can merge', () => {
- mountComponent({ props: { canMerge: true } });
- expect(findInstructionsFields().at(2).text()).toContain('git push origin');
- });
-
- it('should render different instructions based on if the merge is based off a fork', () => {
- mountComponent({ props: { isFork: true } });
- expect(findInstructionsFields().at(0).text()).toContain('FETCH_HEAD');
- });
-
- it('escapes the target branch name shell-secure', () => {
- mountComponent({ props: { targetBranch: '";echo$IFS"you_shouldnt_run_this' } });
-
- expect(findInstructionsFields().at(1).text()).toContain('\'";echo$IFS"you_shouldnt_run_this\'');
- });
-
- it('escapes the source branch name shell-secure', () => {
- mountComponent({ props: { sourceBranch: 'branch-of-$USER' } });
-
- expect(findInstructionsFields().at(0).text()).toContain("'branch-of-$USER'");
- });
-});
diff --git a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js
deleted file mode 100644
index b3af5eba364..00000000000
--- a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js
+++ /dev/null
@@ -1,1266 +0,0 @@
-import { GlBadge, GlLink, GlIcon, GlButton, GlDropdown } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-import Vue, { nextTick } from 'vue';
-import VueApollo from 'vue-apollo';
-import * as Sentry from '@sentry/browser';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { securityReportMergeRequestDownloadPathsQueryResponse } from 'jest/vue_shared/security_reports/mock_data';
-import api from '~/api';
-import axios from '~/lib/utils/axios_utils';
-import Poll from '~/lib/utils/poll';
-import { setFaviconOverlay } from '~/lib/utils/favicon';
-import notify from '~/lib/utils/notify';
-import SmartInterval from '~/smart_interval';
-import {
- registerExtension,
- registeredExtensions,
-} from '~/vue_merge_request_widget/components/extensions';
-import { SUCCESS } from '~/vue_merge_request_widget/components/deployment/constants';
-import eventHub from '~/vue_merge_request_widget/event_hub';
-import MrWidgetOptions from '~/vue_merge_request_widget/mr_widget_options.vue';
-import { stateKey } from '~/vue_merge_request_widget/stores/state_maps';
-import StatusIcon from '~/vue_merge_request_widget/components/extensions/status_icon.vue';
-import securityReportMergeRequestDownloadPathsQuery from '~/vue_shared/security_reports/graphql/queries/security_report_merge_request_download_paths.query.graphql';
-import { faviconDataUrl, overlayDataUrl } from '../lib/utils/mock_data';
-import mockData from './mock_data';
-import {
- workingExtension,
- collapsedDataErrorExtension,
- fullDataErrorExtension,
- fullReportExtension,
- noTelemetryExtension,
- pollingExtension,
- pollingFullDataExtension,
- pollingErrorExtension,
- multiPollingExtension,
-} from './test_extensions';
-
-jest.mock('~/api.js');
-
-jest.mock('~/smart_interval');
-
-jest.mock('~/lib/utils/favicon');
-
-jest.mock('@sentry/browser', () => ({
- setExtra: jest.fn(),
- setExtras: jest.fn(),
- captureMessage: jest.fn(),
- captureException: jest.fn(),
-}));
-
-Vue.use(VueApollo);
-
-describe('MrWidgetOptions', () => {
- let wrapper;
- let mock;
-
- const COLLABORATION_MESSAGE = 'Members who can merge are allowed to add commits';
- const findExtensionToggleButton = () =>
- wrapper.find('[data-testid="widget-extension"] [data-testid="toggle-button"]');
- const findExtensionLink = (linkHref) =>
- wrapper.find(`[data-testid="widget-extension"] [href="${linkHref}"]`);
-
- beforeEach(() => {
- gl.mrWidgetData = { ...mockData };
- gon.features = { asyncMrWidget: true };
-
- mock = new MockAdapter(axios);
- mock.onGet(mockData.merge_request_widget_path).reply(() => [200, { ...mockData }]);
- mock.onGet(mockData.merge_request_cached_widget_path).reply(() => [200, { ...mockData }]);
- });
-
- afterEach(() => {
- mock.restore();
- wrapper.destroy();
-
- gl.mrWidgetData = {};
- gon.features = {};
- });
-
- const createComponent = (mrData = mockData, options = {}) => {
- wrapper = mount(MrWidgetOptions, {
- propsData: {
- mrData: { ...mrData },
- },
- ...options,
- });
-
- return axios.waitForAll();
- };
-
- const findSuggestPipeline = () => wrapper.find('[data-testid="mr-suggest-pipeline"]');
- const findSuggestPipelineButton = () => findSuggestPipeline().find('button');
- const findSecurityMrWidget = () => wrapper.find('[data-testid="security-mr-widget"]');
-
- describe('default', () => {
- beforeEach(() => {
- jest.spyOn(document, 'dispatchEvent');
- return createComponent();
- });
-
- describe('data', () => {
- it('should instantiate Store and Service', () => {
- expect(wrapper.vm.mr).toBeDefined();
- expect(wrapper.vm.service).toBeDefined();
- });
- });
-
- describe('computed', () => {
- describe('componentName', () => {
- it.each`
- state | componentName
- ${'merged'} | ${'mr-widget-merged'}
- ${'conflicts'} | ${'mr-widget-conflicts'}
- ${'shaMismatch'} | ${'sha-mismatch'}
- `('should translate $state into $componentName', ({ state, componentName }) => {
- wrapper.vm.mr.state = state;
-
- expect(wrapper.vm.componentName).toEqual(componentName);
- });
- });
-
- describe('shouldRenderPipelines', () => {
- it('should return true when hasCI is true', () => {
- wrapper.vm.mr.hasCI = true;
-
- expect(wrapper.vm.shouldRenderPipelines).toBeTruthy();
- });
-
- it('should return false when hasCI is false', () => {
- wrapper.vm.mr.hasCI = false;
-
- expect(wrapper.vm.shouldRenderPipelines).toBeFalsy();
- });
- });
-
- describe('shouldRenderRelatedLinks', () => {
- it('should return false for the initial data', () => {
- expect(wrapper.vm.shouldRenderRelatedLinks).toBeFalsy();
- });
-
- it('should return true if there is relatedLinks in MR', () => {
- Vue.set(wrapper.vm.mr, 'relatedLinks', {});
-
- expect(wrapper.vm.shouldRenderRelatedLinks).toBeTruthy();
- });
- });
-
- describe('shouldRenderSourceBranchRemovalStatus', () => {
- beforeEach(() => {
- wrapper.vm.mr.state = 'readyToMerge';
- });
-
- it('should return true when cannot remove source branch and branch will be removed', () => {
- wrapper.vm.mr.canRemoveSourceBranch = false;
- wrapper.vm.mr.shouldRemoveSourceBranch = true;
-
- expect(wrapper.vm.shouldRenderSourceBranchRemovalStatus).toEqual(true);
- });
-
- it('should return false when can remove source branch and branch will be removed', () => {
- wrapper.vm.mr.canRemoveSourceBranch = true;
- wrapper.vm.mr.shouldRemoveSourceBranch = true;
-
- expect(wrapper.vm.shouldRenderSourceBranchRemovalStatus).toEqual(false);
- });
-
- it('should return false when cannot remove source branch and branch will not be removed', () => {
- wrapper.vm.mr.canRemoveSourceBranch = false;
- wrapper.vm.mr.shouldRemoveSourceBranch = false;
-
- expect(wrapper.vm.shouldRenderSourceBranchRemovalStatus).toEqual(false);
- });
-
- it('should return false when in merged state', () => {
- wrapper.vm.mr.canRemoveSourceBranch = false;
- wrapper.vm.mr.shouldRemoveSourceBranch = true;
- wrapper.vm.mr.state = 'merged';
-
- expect(wrapper.vm.shouldRenderSourceBranchRemovalStatus).toEqual(false);
- });
-
- it('should return false when in nothing to merge state', () => {
- wrapper.vm.mr.canRemoveSourceBranch = false;
- wrapper.vm.mr.shouldRemoveSourceBranch = true;
- wrapper.vm.mr.state = 'nothingToMerge';
-
- expect(wrapper.vm.shouldRenderSourceBranchRemovalStatus).toEqual(false);
- });
- });
-
- describe('shouldRenderCollaborationStatus', () => {
- describe('when collaboration is allowed', () => {
- beforeEach(() => {
- wrapper.vm.mr.allowCollaboration = true;
- });
-
- describe('when merge request is opened', () => {
- beforeEach(() => {
- wrapper.vm.mr.isOpen = true;
- return nextTick();
- });
-
- it('should render collaboration status', () => {
- expect(wrapper.text()).toContain(COLLABORATION_MESSAGE);
- });
- });
-
- describe('when merge request is not opened', () => {
- beforeEach(() => {
- wrapper.vm.mr.isOpen = false;
- return nextTick();
- });
-
- it('should not render collaboration status', () => {
- expect(wrapper.text()).not.toContain(COLLABORATION_MESSAGE);
- });
- });
- });
-
- describe('when collaboration is not allowed', () => {
- beforeEach(() => {
- wrapper.vm.mr.allowCollaboration = false;
- });
-
- describe('when merge request is opened', () => {
- beforeEach(() => {
- wrapper.vm.mr.isOpen = true;
- return nextTick();
- });
-
- it('should not render collaboration status', () => {
- expect(wrapper.text()).not.toContain(COLLABORATION_MESSAGE);
- });
- });
- });
- });
-
- describe('showMergePipelineForkWarning', () => {
- describe('when the source project and target project are the same', () => {
- beforeEach(() => {
- Vue.set(wrapper.vm.mr, 'mergePipelinesEnabled', true);
- Vue.set(wrapper.vm.mr, 'sourceProjectId', 1);
- Vue.set(wrapper.vm.mr, 'targetProjectId', 1);
- return nextTick();
- });
-
- it('should be false', () => {
- expect(wrapper.vm.showMergePipelineForkWarning).toEqual(false);
- });
- });
-
- describe('when merge pipelines are not enabled', () => {
- beforeEach(() => {
- Vue.set(wrapper.vm.mr, 'mergePipelinesEnabled', false);
- Vue.set(wrapper.vm.mr, 'sourceProjectId', 1);
- Vue.set(wrapper.vm.mr, 'targetProjectId', 2);
- return nextTick();
- });
-
- it('should be false', () => {
- expect(wrapper.vm.showMergePipelineForkWarning).toEqual(false);
- });
- });
-
- describe('when merge pipelines are enabled _and_ the source project and target project are different', () => {
- beforeEach(() => {
- Vue.set(wrapper.vm.mr, 'mergePipelinesEnabled', true);
- Vue.set(wrapper.vm.mr, 'sourceProjectId', 1);
- Vue.set(wrapper.vm.mr, 'targetProjectId', 2);
- return nextTick();
- });
-
- it('should be true', () => {
- expect(wrapper.vm.showMergePipelineForkWarning).toEqual(true);
- });
- });
- });
-
- describe('formattedHumanAccess', () => {
- it('when user is a tool admin but not a member of project', () => {
- wrapper.vm.mr.humanAccess = null;
-
- expect(wrapper.vm.formattedHumanAccess).toEqual('');
- });
-
- it('when user a member of the project', () => {
- wrapper.vm.mr.humanAccess = 'Owner';
-
- expect(wrapper.vm.formattedHumanAccess).toEqual('owner');
- });
- });
- });
-
- describe('methods', () => {
- describe('checkStatus', () => {
- let cb;
- let isCbExecuted;
-
- beforeEach(() => {
- jest.spyOn(wrapper.vm.service, 'checkStatus').mockResolvedValue({ data: mockData });
- jest.spyOn(wrapper.vm.mr, 'setData').mockImplementation(() => {});
- jest.spyOn(wrapper.vm, 'handleNotification').mockImplementation(() => {});
-
- isCbExecuted = false;
- cb = () => {
- isCbExecuted = true;
- };
- });
-
- it('should tell service to check status if document is visible', () => {
- wrapper.vm.checkStatus(cb);
-
- return nextTick().then(() => {
- expect(wrapper.vm.service.checkStatus).toHaveBeenCalled();
- expect(wrapper.vm.mr.setData).toHaveBeenCalled();
- expect(wrapper.vm.handleNotification).toHaveBeenCalledWith(mockData);
- expect(isCbExecuted).toBeTruthy();
- });
- });
- });
-
- describe('initPolling', () => {
- it('should call SmartInterval', () => {
- wrapper.vm.initPolling();
-
- expect(SmartInterval).toHaveBeenCalledWith(
- expect.objectContaining({
- callback: wrapper.vm.checkStatus,
- }),
- );
- });
- });
-
- describe('initDeploymentsPolling', () => {
- it('should call SmartInterval', () => {
- wrapper.vm.initDeploymentsPolling();
-
- expect(SmartInterval).toHaveBeenCalledWith(
- expect.objectContaining({
- callback: wrapper.vm.fetchPreMergeDeployments,
- }),
- );
- });
- });
-
- describe('fetchDeployments', () => {
- it('should fetch deployments', () => {
- jest
- .spyOn(wrapper.vm.service, 'fetchDeployments')
- .mockResolvedValue({ data: [{ id: 1, status: SUCCESS }] });
-
- wrapper.vm.fetchPreMergeDeployments();
-
- return nextTick().then(() => {
- expect(wrapper.vm.service.fetchDeployments).toHaveBeenCalled();
- expect(wrapper.vm.mr.deployments.length).toEqual(1);
- expect(wrapper.vm.mr.deployments[0].id).toBe(1);
- });
- });
- });
-
- describe('fetchActionsContent', () => {
- it('should fetch content of Cherry Pick and Revert modals', () => {
- jest
- .spyOn(wrapper.vm.service, 'fetchMergeActionsContent')
- .mockResolvedValue({ data: 'hello world' });
-
- wrapper.vm.fetchActionsContent();
-
- return nextTick().then(() => {
- expect(wrapper.vm.service.fetchMergeActionsContent).toHaveBeenCalled();
- expect(document.body.textContent).toContain('hello world');
- expect(document.dispatchEvent).toHaveBeenCalledWith(
- new CustomEvent('merged:UpdateActions'),
- );
- });
- });
- });
-
- describe('bindEventHubListeners', () => {
- it.each`
- event | method | methodArgs
- ${'MRWidgetUpdateRequested'} | ${'checkStatus'} | ${(x) => [x]}
- ${'MRWidgetRebaseSuccess'} | ${'checkStatus'} | ${(x) => [x, true]}
- ${'FetchActionsContent'} | ${'fetchActionsContent'} | ${() => []}
- ${'EnablePolling'} | ${'resumePolling'} | ${() => []}
- ${'DisablePolling'} | ${'stopPolling'} | ${() => []}
- `('should bind to $event', ({ event, method, methodArgs }) => {
- jest.spyOn(wrapper.vm, method).mockImplementation();
-
- const eventArg = {};
- eventHub.$emit(event, eventArg);
-
- expect(wrapper.vm[method]).toHaveBeenCalledWith(...methodArgs(eventArg));
- });
-
- it('should bind to SetBranchRemoveFlag', () => {
- expect(wrapper.vm.mr.isRemovingSourceBranch).toBe(false);
-
- eventHub.$emit('SetBranchRemoveFlag', [true]);
-
- expect(wrapper.vm.mr.isRemovingSourceBranch).toBe(true);
- });
-
- it('should bind to FailedToMerge', () => {
- wrapper.vm.mr.state = '';
- wrapper.vm.mr.mergeError = '';
-
- const mergeError = 'Something bad happened!';
- eventHub.$emit('FailedToMerge', mergeError);
-
- expect(wrapper.vm.mr.state).toBe('failedToMerge');
- expect(wrapper.vm.mr.mergeError).toBe(mergeError);
- });
-
- it('should bind to UpdateWidgetData', () => {
- jest.spyOn(wrapper.vm.mr, 'setData').mockImplementation();
-
- const data = { ...mockData };
- eventHub.$emit('UpdateWidgetData', data);
-
- expect(wrapper.vm.mr.setData).toHaveBeenCalledWith(data);
- });
- });
-
- describe('setFavicon', () => {
- let faviconElement;
-
- beforeEach(() => {
- const favicon = document.createElement('link');
- favicon.setAttribute('id', 'favicon');
- favicon.dataset.originalHref = faviconDataUrl;
- document.body.appendChild(favicon);
-
- faviconElement = document.getElementById('favicon');
- });
-
- afterEach(() => {
- document.body.removeChild(document.getElementById('favicon'));
- });
-
- it('should call setFavicon method', async () => {
- wrapper.vm.mr.ciStatusFaviconPath = overlayDataUrl;
-
- await wrapper.vm.setFaviconHelper();
-
- expect(setFaviconOverlay).toHaveBeenCalledWith(overlayDataUrl);
- });
-
- it('should not call setFavicon when there is no ciStatusFaviconPath', async () => {
- wrapper.vm.mr.ciStatusFaviconPath = null;
- await wrapper.vm.setFaviconHelper();
- expect(faviconElement.getAttribute('href')).toEqual(null);
- });
- });
-
- describe('handleNotification', () => {
- const data = {
- ci_status: 'running',
- title: 'title',
- pipeline: { details: { status: { label: 'running-label' } } },
- };
-
- beforeEach(() => {
- jest.spyOn(notify, 'notifyMe').mockImplementation(() => {});
-
- wrapper.vm.mr.ciStatus = 'failed';
- wrapper.vm.mr.gitlabLogo = 'logo.png';
- });
-
- it('should call notifyMe', () => {
- wrapper.vm.handleNotification(data);
-
- expect(notify.notifyMe).toHaveBeenCalledWith(
- 'Pipeline running-label',
- 'Pipeline running-label for "title"',
- 'logo.png',
- );
- });
-
- it('should not call notifyMe if the status has not changed', () => {
- wrapper.vm.mr.ciStatus = data.ci_status;
-
- wrapper.vm.handleNotification(data);
-
- expect(notify.notifyMe).not.toHaveBeenCalled();
- });
-
- it('should not notify if no pipeline provided', () => {
- wrapper.vm.handleNotification({
- ...data,
- pipeline: undefined,
- });
-
- expect(notify.notifyMe).not.toHaveBeenCalled();
- });
- });
-
- describe('resumePolling', () => {
- it('should call stopTimer on pollingInterval', () => {
- jest.spyOn(wrapper.vm.pollingInterval, 'resume').mockImplementation(() => {});
-
- wrapper.vm.resumePolling();
-
- expect(wrapper.vm.pollingInterval.resume).toHaveBeenCalled();
- });
- });
-
- describe('stopPolling', () => {
- it('should call stopTimer on pollingInterval', () => {
- jest.spyOn(wrapper.vm.pollingInterval, 'stopTimer').mockImplementation(() => {});
-
- wrapper.vm.stopPolling();
-
- expect(wrapper.vm.pollingInterval.stopTimer).toHaveBeenCalled();
- });
- });
- });
-
- describe('rendering relatedLinks', () => {
- beforeEach(() => {
- return createComponent({
- ...mockData,
- issues_links: {
- closing: `
- <a class="close-related-link" href="#">
- Close
- </a>
- `,
- },
- });
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders if there are relatedLinks', () => {
- expect(wrapper.find('.close-related-link').exists()).toBe(true);
- });
-
- it('does not render if state is nothingToMerge', async () => {
- wrapper.vm.mr.state = stateKey.nothingToMerge;
- await nextTick();
- expect(wrapper.find('.close-related-link').exists()).toBe(false);
- });
- });
-
- describe('rendering source branch removal status', () => {
- it('renders when user cannot remove branch and branch should be removed', async () => {
- wrapper.vm.mr.canRemoveSourceBranch = false;
- wrapper.vm.mr.shouldRemoveSourceBranch = true;
- wrapper.vm.mr.state = 'readyToMerge';
-
- await nextTick();
- const tooltip = wrapper.find('[data-testid="question-o-icon"]');
-
- expect(wrapper.text()).toContain('Deletes the source branch');
- expect(tooltip.attributes('title')).toBe(
- 'A user with write access to the source branch selected this option',
- );
- });
-
- it('does not render in merged state', async () => {
- wrapper.vm.mr.canRemoveSourceBranch = false;
- wrapper.vm.mr.shouldRemoveSourceBranch = true;
- wrapper.vm.mr.state = 'merged';
-
- await nextTick();
- expect(wrapper.text()).toContain('The source branch has been deleted');
- expect(wrapper.text()).not.toContain('Deletes the source branch');
- });
- });
-
- describe('rendering deployments', () => {
- const changes = [
- {
- path: 'index.html',
- external_url: 'http://root-main-patch-91341.volatile-watch.surge.sh/index.html',
- },
- {
- path: 'imgs/gallery.html',
- external_url: 'http://root-main-patch-91341.volatile-watch.surge.sh/imgs/gallery.html',
- },
- {
- path: 'about/',
- external_url: 'http://root-main-patch-91341.volatile-watch.surge.sh/about/',
- },
- ];
- const deploymentMockData = {
- id: 15,
- name: 'review/diplo',
- url: '/root/acets-review-apps/environments/15',
- stop_url: '/root/acets-review-apps/environments/15/stop',
- metrics_url: '/root/acets-review-apps/environments/15/deployments/1/metrics',
- metrics_monitoring_url: '/root/acets-review-apps/environments/15/metrics',
- external_url: 'http://diplo.',
- external_url_formatted: 'diplo.',
- deployed_at: '2017-03-22T22:44:42.258Z',
- deployed_at_formatted: 'Mar 22, 2017 10:44pm',
- changes,
- status: SUCCESS,
- };
-
- beforeEach(() => {
- wrapper.vm.mr.deployments.push(
- {
- ...deploymentMockData,
- },
- {
- ...deploymentMockData,
- id: deploymentMockData.id + 1,
- },
- );
-
- return nextTick();
- });
-
- it('renders multiple deployments', () => {
- expect(wrapper.findAll('.deploy-heading').length).toBe(2);
- });
-
- it('renders dropdpown with multiple file changes', () => {
- expect(
- wrapper.find('.js-mr-wigdet-deployment-dropdown').findAll('.js-filtered-dropdown-result')
- .length,
- ).toEqual(changes.length);
- });
- });
-
- describe('code quality widget', () => {
- beforeEach(() => {
- jest.spyOn(document, 'dispatchEvent');
- });
- it('renders the component when refactorCodeQualityExtension is false', () => {
- createComponent(mockData, {}, { refactorCodeQualityExtension: false });
- expect(wrapper.find('.js-codequality-widget').exists()).toBe(true);
- });
-
- it('does not render the component when refactorCodeQualityExtension is true', () => {
- createComponent(mockData, {}, { refactorCodeQualityExtension: true });
- expect(wrapper.find('.js-codequality-widget').exists()).toBe(true);
- });
- });
-
- describe('pipeline for target branch after merge', () => {
- describe('with information for target branch pipeline', () => {
- beforeEach(() => {
- wrapper.vm.mr.state = 'merged';
- wrapper.vm.mr.mergePipeline = {
- id: 127,
- user: {
- id: 1,
- name: 'Administrator',
- username: 'root',
- state: 'active',
- avatar_url: null,
- web_url: 'http://localhost:3000/root',
- status_tooltip_html: null,
- path: '/root',
- },
- active: true,
- coverage: null,
- source: 'push',
- created_at: '2018-10-22T11:41:35.186Z',
- updated_at: '2018-10-22T11:41:35.433Z',
- path: '/root/ci-web-terminal/pipelines/127',
- flags: {
- latest: true,
- stuck: true,
- auto_devops: false,
- yaml_errors: false,
- retryable: false,
- cancelable: true,
- failure_reason: false,
- },
- details: {
- status: {
- icon: 'status_pending',
- text: 'pending',
- label: 'pending',
- group: 'pending',
- tooltip: 'pending',
- has_details: true,
- details_path: '/root/ci-web-terminal/pipelines/127',
- illustration: null,
- favicon:
- '/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png',
- },
- duration: null,
- finished_at: null,
- stages: [
- {
- name: 'test',
- title: 'test: pending',
- status: {
- icon: 'status_pending',
- text: 'pending',
- label: 'pending',
- group: 'pending',
- tooltip: 'pending',
- has_details: true,
- details_path: '/root/ci-web-terminal/pipelines/127#test',
- illustration: null,
- favicon:
- '/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png',
- },
- path: '/root/ci-web-terminal/pipelines/127#test',
- dropdown_path: '/root/ci-web-terminal/pipelines/127/stage.json?stage=test',
- },
- ],
- artifacts: [],
- manual_actions: [],
- scheduled_actions: [],
- },
- ref: {
- name: 'main',
- path: '/root/ci-web-terminal/commits/main',
- tag: false,
- branch: true,
- },
- commit: {
- id: 'aa1939133d373c94879becb79d91828a892ee319',
- short_id: 'aa193913',
- title: "Merge branch 'main-test' into 'main'",
- created_at: '2018-10-22T11:41:33.000Z',
- parent_ids: [
- '4622f4dd792468993003caf2e3be978798cbe096',
- '76598df914cdfe87132d0c3c40f80db9fa9396a4',
- ],
- message:
- "Merge branch 'main-test' into 'main'\n\nUpdate .gitlab-ci.yml\n\nSee merge request root/ci-web-terminal!1",
- author_name: 'Administrator',
- author_email: 'admin@example.com',
- authored_date: '2018-10-22T11:41:33.000Z',
- committer_name: 'Administrator',
- committer_email: 'admin@example.com',
- committed_date: '2018-10-22T11:41:33.000Z',
- author: {
- id: 1,
- name: 'Administrator',
- username: 'root',
- state: 'active',
- avatar_url: null,
- web_url: 'http://localhost:3000/root',
- status_tooltip_html: null,
- path: '/root',
- },
- author_gravatar_url: null,
- commit_url:
- 'http://localhost:3000/root/ci-web-terminal/commit/aa1939133d373c94879becb79d91828a892ee319',
- commit_path: '/root/ci-web-terminal/commit/aa1939133d373c94879becb79d91828a892ee319',
- },
- cancel_path: '/root/ci-web-terminal/pipelines/127/cancel',
- };
- return nextTick();
- });
-
- it('renders pipeline block', () => {
- expect(wrapper.find('.js-post-merge-pipeline').exists()).toBe(true);
- });
-
- describe('with post merge deployments', () => {
- beforeEach(() => {
- wrapper.vm.mr.postMergeDeployments = [
- {
- id: 15,
- name: 'review/diplo',
- url: '/root/acets-review-apps/environments/15',
- stop_url: '/root/acets-review-apps/environments/15/stop',
- metrics_url: '/root/acets-review-apps/environments/15/deployments/1/metrics',
- metrics_monitoring_url: '/root/acets-review-apps/environments/15/metrics',
- external_url: 'http://diplo.',
- external_url_formatted: 'diplo.',
- deployed_at: '2017-03-22T22:44:42.258Z',
- deployed_at_formatted: 'Mar 22, 2017 10:44pm',
- changes: [
- {
- path: 'index.html',
- external_url: 'http://root-main-patch-91341.volatile-watch.surge.sh/index.html',
- },
- {
- path: 'imgs/gallery.html',
- external_url:
- 'http://root-main-patch-91341.volatile-watch.surge.sh/imgs/gallery.html',
- },
- {
- path: 'about/',
- external_url: 'http://root-main-patch-91341.volatile-watch.surge.sh/about/',
- },
- ],
- status: 'success',
- },
- ];
-
- return nextTick();
- });
-
- it('renders post deployment information', () => {
- expect(wrapper.find('.js-post-deployment').exists()).toBe(true);
- });
- });
- });
-
- describe('without information for target branch pipeline', () => {
- beforeEach(() => {
- wrapper.vm.mr.state = 'merged';
-
- return nextTick();
- });
-
- it('does not render pipeline block', () => {
- expect(wrapper.find('.js-post-merge-pipeline').exists()).toBe(false);
- });
- });
-
- describe('when state is not merged', () => {
- beforeEach(() => {
- wrapper.vm.mr.state = 'archived';
-
- return nextTick();
- });
-
- it('does not render pipeline block', () => {
- expect(wrapper.find('.js-post-merge-pipeline').exists()).toBe(false);
- });
-
- it('does not render post deployment information', () => {
- expect(wrapper.find('.js-post-deployment').exists()).toBe(false);
- });
- });
- });
-
- it('should not suggest pipelines when feature flag is not present', () => {
- expect(findSuggestPipeline().exists()).toBe(false);
- });
- });
-
- describe('security widget', () => {
- describe.each`
- context | hasPipeline | shouldRender
- ${'there is a pipeline'} | ${true} | ${true}
- ${'no pipeline'} | ${false} | ${false}
- `('given $context', ({ hasPipeline, shouldRender }) => {
- beforeEach(() => {
- const mrData = {
- ...mockData,
- ...(hasPipeline ? {} : { pipeline: null }),
- };
-
- // Override top-level mocked requests, which always use a fresh copy of
- // mockData, which always includes the full pipeline object.
- mock.onGet(mockData.merge_request_widget_path).reply(() => [200, mrData]);
- mock.onGet(mockData.merge_request_cached_widget_path).reply(() => [200, mrData]);
-
- return createComponent(mrData, {
- apolloProvider: createMockApollo([
- [
- securityReportMergeRequestDownloadPathsQuery,
- async () => ({ data: securityReportMergeRequestDownloadPathsQueryResponse }),
- ],
- ]),
- });
- });
-
- it(shouldRender ? 'renders' : 'does not render', () => {
- expect(findSecurityMrWidget().exists()).toBe(shouldRender);
- });
- });
- });
-
- describe('suggestPipeline', () => {
- beforeEach(() => {
- mock.onAny().reply(200);
- });
-
- describe('given feature flag is enabled', () => {
- beforeEach(async () => {
- await createComponent();
-
- wrapper.vm.mr.hasCI = false;
- });
-
- it('should suggest pipelines when none exist', () => {
- expect(findSuggestPipeline().exists()).toBe(true);
- });
-
- it.each([
- { isDismissedSuggestPipeline: true },
- { mergeRequestAddCiConfigPath: null },
- { hasCI: true },
- ])('with %s, should not suggest pipeline', async (obj) => {
- Object.assign(wrapper.vm.mr, obj);
-
- await nextTick();
-
- expect(findSuggestPipeline().exists()).toBe(false);
- });
-
- it('should allow dismiss of the suggest pipeline message', async () => {
- await findSuggestPipelineButton().trigger('click');
-
- expect(findSuggestPipeline().exists()).toBe(false);
- });
- });
- });
-
- describe('merge error', () => {
- it.each`
- state | show | showText
- ${'closed'} | ${false} | ${'hides'}
- ${'merged'} | ${true} | ${'shows'}
- ${'open'} | ${true} | ${'shows'}
- `('it $showText merge error when state is $state', ({ state, show }) => {
- createComponent({ ...mockData, state, merge_error: 'Error!' });
-
- expect(wrapper.find('[data-testid="merge_error"]').exists()).toBe(show);
- });
- });
-
- describe('mock extension', () => {
- let pollRequest;
-
- beforeEach(() => {
- pollRequest = jest.spyOn(Poll.prototype, 'makeRequest');
-
- registerExtension(workingExtension());
-
- createComponent();
- });
-
- afterEach(() => {
- registeredExtensions.extensions = [];
- });
-
- it('renders collapsed data', async () => {
- await waitForPromises();
-
- expect(wrapper.text()).toContain('Test extension summary count: 1');
- });
-
- it('renders full data', async () => {
- await waitForPromises();
-
- findExtensionToggleButton().trigger('click');
-
- await nextTick();
-
- expect(
- wrapper.find('[data-testid="widget-extension-top-level"]').find(GlDropdown).exists(),
- ).toBe(false);
-
- await nextTick();
-
- const collapsedSection = wrapper.find('[data-testid="widget-extension-collapsed-section"]');
- expect(collapsedSection.exists()).toBe(true);
- expect(collapsedSection.text()).toContain('Hello world');
-
- // Renders icon in the row
- expect(collapsedSection.find(GlIcon).exists()).toBe(true);
- expect(collapsedSection.find(GlIcon).props('name')).toBe('status-failed');
-
- // Renders badge in the row
- expect(collapsedSection.find(GlBadge).exists()).toBe(true);
- expect(collapsedSection.find(GlBadge).text()).toBe('Closed');
-
- // Renders a link in the row
- expect(collapsedSection.find(GlLink).exists()).toBe(true);
- expect(collapsedSection.find(GlLink).text()).toBe('GitLab.com');
-
- expect(collapsedSection.find(GlButton).exists()).toBe(true);
- expect(collapsedSection.find(GlButton).text()).toBe('Full report');
- });
-
- it('extension polling is not called if enablePolling flag is not passed', () => {
- // called one time due to parent component polling (mount)
- expect(pollRequest).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('expansion', () => {
- it('hides collapse button', async () => {
- registerExtension(workingExtension(false));
- await createComponent();
-
- expect(findExtensionToggleButton().exists()).toBe(false);
- });
-
- it('shows collapse button', async () => {
- registerExtension(workingExtension(true));
- await createComponent();
-
- expect(findExtensionToggleButton().exists()).toBe(true);
- });
- });
-
- describe('mock polling extension', () => {
- let pollRequest;
-
- const findWidgetTestExtension = () => wrapper.find('[data-testid="widget-extension"]');
-
- beforeEach(() => {
- pollRequest = jest.spyOn(Poll.prototype, 'makeRequest');
-
- registeredExtensions.extensions = [];
- });
-
- afterEach(() => {
- registeredExtensions.extensions = [];
- });
-
- describe('success - multi polling', () => {
- it('sets data when polling is complete', async () => {
- registerExtension(
- multiPollingExtension([
- () =>
- Promise.resolve({
- headers: { 'poll-interval': 0 },
- status: 200,
- data: { reports: 'parsed' },
- }),
- () =>
- Promise.resolve({
- status: 200,
- data: { reports: 'parsed' },
- }),
- ]),
- );
-
- await createComponent();
- expect(findWidgetTestExtension().html()).toContain(
- 'Multi polling test extension reports: parsed, count: 2',
- );
- });
-
- it('shows loading state until polling is complete', async () => {
- registerExtension(
- multiPollingExtension([
- () =>
- Promise.resolve({
- headers: { 'poll-interval': 1 },
- status: 204,
- }),
- () =>
- Promise.resolve({
- status: 200,
- data: { reports: 'parsed' },
- }),
- ]),
- );
-
- await createComponent();
- expect(findWidgetTestExtension().html()).toContain('Test extension loading...');
- });
- });
-
- describe('success', () => {
- it('does not make additional requests after poll is successful', async () => {
- registerExtension(pollingExtension);
-
- await createComponent();
-
- expect(pollRequest).toHaveBeenCalledTimes(6);
- });
- });
-
- describe('success - full data polling', () => {
- it('sets data when polling is complete', async () => {
- registerExtension(pollingFullDataExtension);
-
- await createComponent();
-
- api.trackRedisHllUserEvent.mockClear();
- api.trackRedisCounterEvent.mockClear();
-
- findExtensionToggleButton().trigger('click');
-
- // The default working extension is a "warning" type, which generates a second - more specific - telemetry event for expansions
- expect(api.trackRedisHllUserEvent).toHaveBeenCalledTimes(2);
- expect(api.trackRedisHllUserEvent).toHaveBeenCalledWith(
- 'i_code_review_merge_request_widget_test_extension_expand',
- );
- expect(api.trackRedisHllUserEvent).toHaveBeenCalledWith(
- 'i_code_review_merge_request_widget_test_extension_expand_warning',
- );
- expect(api.trackRedisCounterEvent).toHaveBeenCalledTimes(2);
- expect(api.trackRedisCounterEvent).toHaveBeenCalledWith(
- 'i_code_review_merge_request_widget_test_extension_count_expand',
- );
- expect(api.trackRedisCounterEvent).toHaveBeenCalledWith(
- 'i_code_review_merge_request_widget_test_extension_count_expand_warning',
- );
- });
- });
-
- describe('error', () => {
- it('does not make additional requests after poll has failed', async () => {
- registerExtension(pollingErrorExtension);
- await createComponent();
-
- expect(pollRequest).toHaveBeenCalledTimes(6);
- });
-
- it('captures sentry error and displays error when poll has failed', async () => {
- registerExtension(pollingErrorExtension);
- await createComponent();
-
- expect(Sentry.captureException).toHaveBeenCalledTimes(5);
- expect(Sentry.captureException).toHaveBeenCalledWith(new Error('Fetch error'));
- expect(wrapper.findComponent(StatusIcon).props('iconName')).toBe('failed');
- });
- });
- });
-
- describe('mock extension errors', () => {
- afterEach(() => {
- registeredExtensions.extensions = [];
- });
-
- it('handles collapsed data fetch errors', async () => {
- registerExtension(collapsedDataErrorExtension);
- await createComponent();
-
- expect(
- wrapper.find('[data-testid="widget-extension"] [data-testid="toggle-button"]').exists(),
- ).toBe(false);
- expect(Sentry.captureException).toHaveBeenCalledTimes(5);
- expect(Sentry.captureException).toHaveBeenCalledWith(new Error('Fetch error'));
- expect(wrapper.findComponent(StatusIcon).props('iconName')).toBe('failed');
- });
-
- it('handles full data fetch errors', async () => {
- registerExtension(fullDataErrorExtension);
- await createComponent();
-
- expect(wrapper.findComponent(StatusIcon).props('iconName')).not.toBe('error');
- wrapper
- .find('[data-testid="widget-extension"] [data-testid="toggle-button"]')
- .trigger('click');
-
- await nextTick();
- await waitForPromises();
-
- expect(Sentry.captureException).toHaveBeenCalledTimes(1);
- expect(Sentry.captureException).toHaveBeenCalledWith(new Error('Fetch error'));
- expect(wrapper.findComponent(StatusIcon).props('iconName')).toBe('failed');
- });
- });
-
- describe('telemetry', () => {
- afterEach(() => {
- registeredExtensions.extensions = [];
- });
-
- it('triggers view events when mounted', () => {
- registerExtension(workingExtension());
- createComponent();
-
- expect(api.trackRedisHllUserEvent).toHaveBeenCalledTimes(1);
- expect(api.trackRedisHllUserEvent).toHaveBeenCalledWith(
- 'i_code_review_merge_request_widget_test_extension_view',
- );
- expect(api.trackRedisCounterEvent).toHaveBeenCalledTimes(1);
- expect(api.trackRedisCounterEvent).toHaveBeenCalledWith(
- 'i_code_review_merge_request_widget_test_extension_count_view',
- );
- });
-
- describe('expand button', () => {
- it('triggers expand events when clicked', async () => {
- registerExtension(workingExtension());
- createComponent();
-
- await waitForPromises();
-
- api.trackRedisHllUserEvent.mockClear();
- api.trackRedisCounterEvent.mockClear();
-
- findExtensionToggleButton().trigger('click');
-
- // The default working extension is a "warning" type, which generates a second - more specific - telemetry event for expansions
- expect(api.trackRedisHllUserEvent).toHaveBeenCalledTimes(2);
- expect(api.trackRedisHllUserEvent).toHaveBeenCalledWith(
- 'i_code_review_merge_request_widget_test_extension_expand',
- );
- expect(api.trackRedisHllUserEvent).toHaveBeenCalledWith(
- 'i_code_review_merge_request_widget_test_extension_expand_warning',
- );
- expect(api.trackRedisCounterEvent).toHaveBeenCalledTimes(2);
- expect(api.trackRedisCounterEvent).toHaveBeenCalledWith(
- 'i_code_review_merge_request_widget_test_extension_count_expand',
- );
- expect(api.trackRedisCounterEvent).toHaveBeenCalledWith(
- 'i_code_review_merge_request_widget_test_extension_count_expand_warning',
- );
- });
-
- it.each`
- widgetName | nonStandardEvent
- ${'WidgetCodeQuality'} | ${'i_testing_code_quality_widget_total'}
- ${'WidgetTerraform'} | ${'i_testing_terraform_widget_total'}
- ${'WidgetIssues'} | ${'i_testing_load_performance_widget_total'}
- ${'WidgetTestReport'} | ${'i_testing_summary_widget_total'}
- `(
- "sends non-standard events for the '$widgetName' widget",
- async ({ widgetName, nonStandardEvent }) => {
- const definition = {
- ...workingExtension(),
- name: widgetName,
- };
-
- registerExtension(definition);
- createComponent();
-
- await waitForPromises();
-
- api.trackRedisHllUserEvent.mockClear();
-
- findExtensionToggleButton().trigger('click');
-
- expect(api.trackRedisHllUserEvent).toHaveBeenCalledWith(nonStandardEvent);
- },
- );
- });
-
- it('triggers the "full report clicked" events when the appropriate button is clicked', () => {
- registerExtension(fullReportExtension);
- createComponent();
-
- api.trackRedisHllUserEvent.mockClear();
- api.trackRedisCounterEvent.mockClear();
-
- findExtensionLink('testref').trigger('click');
-
- expect(api.trackRedisHllUserEvent).toHaveBeenCalledTimes(1);
- expect(api.trackRedisHllUserEvent).toHaveBeenCalledWith(
- 'i_code_review_merge_request_widget_test_extension_click_full_report',
- );
- expect(api.trackRedisCounterEvent).toHaveBeenCalledTimes(1);
- expect(api.trackRedisCounterEvent).toHaveBeenCalledWith(
- 'i_code_review_merge_request_widget_test_extension_count_click_full_report',
- );
- });
-
- describe('when disabled', () => {
- afterEach(() => {
- registeredExtensions.extensions = [];
- });
-
- it("doesn't emit any telemetry events", async () => {
- registerExtension(noTelemetryExtension);
- createComponent();
-
- await waitForPromises();
-
- findExtensionToggleButton().trigger('click');
- findExtensionLink('testref').trigger('click'); // The "full report" link
-
- expect(api.trackRedisHllUserEvent).not.toHaveBeenCalled();
- expect(api.trackRedisCounterEvent).not.toHaveBeenCalled();
- });
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/stores/artifacts_list/actions_spec.js b/spec/frontend/vue_mr_widget/stores/artifacts_list/actions_spec.js
deleted file mode 100644
index 22562bb4ddb..00000000000
--- a/spec/frontend/vue_mr_widget/stores/artifacts_list/actions_spec.js
+++ /dev/null
@@ -1,158 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import { TEST_HOST } from 'helpers/test_constants';
-import testAction from 'helpers/vuex_action_helper';
-import axios from '~/lib/utils/axios_utils';
-import {
- setEndpoint,
- requestArtifacts,
- clearEtagPoll,
- stopPolling,
- fetchArtifacts,
- receiveArtifactsSuccess,
- receiveArtifactsError,
-} from '~/vue_merge_request_widget/stores/artifacts_list/actions';
-import * as types from '~/vue_merge_request_widget/stores/artifacts_list/mutation_types';
-import state from '~/vue_merge_request_widget/stores/artifacts_list/state';
-
-describe('Artifacts App Store Actions', () => {
- let mockedState;
-
- beforeEach(() => {
- mockedState = state();
- });
-
- describe('setEndpoint', () => {
- it('should commit SET_ENDPOINT mutation', () => {
- return testAction(
- setEndpoint,
- 'endpoint.json',
- mockedState,
- [{ type: types.SET_ENDPOINT, payload: 'endpoint.json' }],
- [],
- );
- });
- });
-
- describe('requestArtifacts', () => {
- it('should commit REQUEST_ARTIFACTS mutation', () => {
- return testAction(
- requestArtifacts,
- null,
- mockedState,
- [{ type: types.REQUEST_ARTIFACTS }],
- [],
- );
- });
- });
-
- describe('fetchArtifacts', () => {
- let mock;
-
- beforeEach(() => {
- mockedState.endpoint = `${TEST_HOST}/endpoint.json`;
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- mock.restore();
- stopPolling();
- clearEtagPoll();
- });
-
- describe('success', () => {
- it('dispatches requestArtifacts and receiveArtifactsSuccess ', () => {
- mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, [
- {
- text: 'result.txt',
- url: 'asda',
- job_name: 'generate-artifact',
- job_path: 'asda',
- },
- ]);
-
- return testAction(
- fetchArtifacts,
- null,
- mockedState,
- [],
- [
- {
- type: 'requestArtifacts',
- },
- {
- payload: {
- data: [
- {
- text: 'result.txt',
- url: 'asda',
- job_name: 'generate-artifact',
- job_path: 'asda',
- },
- ],
- status: 200,
- },
- type: 'receiveArtifactsSuccess',
- },
- ],
- );
- });
- });
-
- describe('error', () => {
- beforeEach(() => {
- mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500);
- });
-
- it('dispatches requestArtifacts and receiveArtifactsError ', () => {
- return testAction(
- fetchArtifacts,
- null,
- mockedState,
- [],
- [
- {
- type: 'requestArtifacts',
- },
- {
- type: 'receiveArtifactsError',
- },
- ],
- );
- });
- });
- });
-
- describe('receiveArtifactsSuccess', () => {
- it('should commit RECEIVE_ARTIFACTS_SUCCESS mutation with 200', () => {
- return testAction(
- receiveArtifactsSuccess,
- { data: { summary: {} }, status: 200 },
- mockedState,
- [{ type: types.RECEIVE_ARTIFACTS_SUCCESS, payload: { summary: {} } }],
- [],
- );
- });
-
- it('should not commit RECEIVE_ARTIFACTS_SUCCESS mutation with 204', () => {
- return testAction(
- receiveArtifactsSuccess,
- { data: { summary: {} }, status: 204 },
- mockedState,
- [],
- [],
- );
- });
- });
-
- describe('receiveArtifactsError', () => {
- it('should commit RECEIVE_ARTIFACTS_ERROR mutation', () => {
- return testAction(
- receiveArtifactsError,
- null,
- mockedState,
- [{ type: types.RECEIVE_ARTIFACTS_ERROR }],
- [],
- );
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/stores/artifacts_list/getters_spec.js b/spec/frontend/vue_mr_widget/stores/artifacts_list/getters_spec.js
deleted file mode 100644
index dc90fef63c6..00000000000
--- a/spec/frontend/vue_mr_widget/stores/artifacts_list/getters_spec.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import { title } from '~/vue_merge_request_widget/stores/artifacts_list/getters';
-import state from '~/vue_merge_request_widget/stores/artifacts_list/state';
-import { artifacts } from '../../mock_data';
-
-describe('Artifacts Store Getters', () => {
- let localState;
-
- beforeEach(() => {
- localState = state();
- });
-
- describe('title', () => {
- describe('when is loading', () => {
- it('returns loading message', () => {
- localState.isLoading = true;
- expect(title(localState)).toBe('Loading artifacts');
- });
- });
- describe('when has error', () => {
- it('returns error message', () => {
- localState.hasError = true;
- expect(title(localState)).toBe('An error occurred while fetching the artifacts');
- });
- });
- describe('when it has artifacts', () => {
- it('returns artifacts message', () => {
- localState.artifacts = artifacts;
- expect(title(localState)).toBe('View 2 exposed artifacts');
- });
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/stores/artifacts_list/mutations_spec.js b/spec/frontend/vue_mr_widget/stores/artifacts_list/mutations_spec.js
deleted file mode 100644
index a4e6788c7f6..00000000000
--- a/spec/frontend/vue_mr_widget/stores/artifacts_list/mutations_spec.js
+++ /dev/null
@@ -1,78 +0,0 @@
-import * as types from '~/vue_merge_request_widget/stores/artifacts_list/mutation_types';
-import mutations from '~/vue_merge_request_widget/stores/artifacts_list/mutations';
-import state from '~/vue_merge_request_widget/stores/artifacts_list/state';
-
-describe('Artifacts Store Mutations', () => {
- let stateCopy;
-
- beforeEach(() => {
- stateCopy = state();
- });
-
- describe('SET_ENDPOINT', () => {
- it('should set endpoint', () => {
- mutations[types.SET_ENDPOINT](stateCopy, 'endpoint.json');
-
- expect(stateCopy.endpoint).toEqual('endpoint.json');
- });
- });
-
- describe('REQUEST_ARTIFACTS', () => {
- it('should set isLoading to true', () => {
- mutations[types.REQUEST_ARTIFACTS](stateCopy);
-
- expect(stateCopy.isLoading).toEqual(true);
- });
- });
-
- describe('REECEIVE_ARTIFACTS_SUCCESS', () => {
- const artifacts = [
- {
- text: 'result.txt',
- url: 'asda',
- job_name: 'generate-artifact',
- job_path: 'asda',
- },
- {
- text: 'file.txt',
- url: 'asda',
- job_name: 'generate-artifact',
- job_path: 'asda',
- },
- ];
-
- beforeEach(() => {
- mutations[types.RECEIVE_ARTIFACTS_SUCCESS](stateCopy, artifacts);
- });
-
- it('should set isLoading to false', () => {
- expect(stateCopy.isLoading).toEqual(false);
- });
-
- it('should set hasError to false', () => {
- expect(stateCopy.hasError).toEqual(false);
- });
-
- it('should set list of artifacts', () => {
- expect(stateCopy.artifacts).toEqual(artifacts);
- });
- });
-
- describe('RECEIVE_ARTIFACTS_ERROR', () => {
- beforeEach(() => {
- mutations[types.RECEIVE_ARTIFACTS_ERROR](stateCopy);
- });
-
- it('should set isLoading to false', () => {
- expect(stateCopy.isLoading).toEqual(false);
- });
-
- it('should set hasError to true', () => {
- expect(stateCopy.hasError).toEqual(true);
- });
-
- it('should set list of artifacts as empty array', () => {
- expect(stateCopy.artifacts).toEqual([]);
- });
- });
-});
diff --git a/spec/frontend/vue_mr_widget/stores/get_state_key_spec.js b/spec/frontend/vue_mr_widget/stores/get_state_key_spec.js
deleted file mode 100644
index fc760f5c5be..00000000000
--- a/spec/frontend/vue_mr_widget/stores/get_state_key_spec.js
+++ /dev/null
@@ -1,126 +0,0 @@
-import getStateKey from '~/vue_merge_request_widget/stores/get_state_key';
-
-describe('getStateKey', () => {
- it('should return proper state name', () => {
- const context = {
- mergeStatus: 'checked',
- autoMergeEnabled: false,
- canMerge: true,
- onlyAllowMergeIfPipelineSucceeds: false,
- isPipelineFailed: false,
- hasMergeableDiscussionsState: false,
- isPipelineBlocked: false,
- canBeMerged: false,
- projectArchived: false,
- branchMissing: false,
- commitsCount: 2,
- hasConflicts: false,
- draft: false,
- };
- const bound = getStateKey.bind(context);
-
- expect(bound()).toEqual(null);
-
- context.canBeMerged = true;
-
- expect(bound()).toEqual('readyToMerge');
-
- context.canMerge = false;
-
- expect(bound()).toEqual('notAllowedToMerge');
-
- context.autoMergeEnabled = true;
- context.hasMergeableDiscussionsState = true;
-
- expect(bound()).toEqual('autoMergeEnabled');
-
- context.canMerge = true;
- context.isSHAMismatch = true;
-
- expect(bound()).toEqual('shaMismatch');
-
- context.canMerge = false;
- context.isPipelineBlocked = true;
-
- expect(bound()).toEqual('pipelineBlocked');
-
- context.hasMergeableDiscussionsState = true;
- context.autoMergeEnabled = false;
-
- expect(bound()).toEqual('unresolvedDiscussions');
-
- context.draft = true;
-
- expect(bound()).toEqual('draft');
-
- context.onlyAllowMergeIfPipelineSucceeds = true;
- context.isPipelineFailed = true;
-
- expect(bound()).toEqual('pipelineFailed');
-
- context.shouldBeRebased = true;
-
- expect(bound()).toEqual('rebase');
-
- context.hasConflicts = true;
-
- expect(bound()).toEqual('conflicts');
-
- context.mergeStatus = 'unchecked';
-
- expect(bound()).toEqual('checking');
-
- context.commitsCount = 0;
-
- expect(bound()).toEqual('nothingToMerge');
-
- context.commitsCount = 1;
- context.branchMissing = true;
-
- expect(bound()).toEqual('missingBranch');
-
- context.projectArchived = true;
-
- expect(bound()).toEqual('archived');
- });
-
- it('returns rebased state key', () => {
- const context = {
- mergeStatus: 'checked',
- autoMergeEnabled: false,
- canMerge: true,
- onlyAllowMergeIfPipelineSucceeds: true,
- isPipelineFailed: true,
- hasMergeableDiscussionsState: false,
- isPipelineBlocked: false,
- canBeMerged: false,
- shouldBeRebased: true,
- projectArchived: false,
- branchMissing: false,
- commitsCount: 2,
- hasConflicts: false,
- draft: false,
- };
- const bound = getStateKey.bind(context);
-
- expect(bound()).toEqual('rebase');
- });
-
- it.each`
- canMerge | isSHAMismatch | stateKey
- ${true} | ${true} | ${'shaMismatch'}
- ${false} | ${true} | ${'notAllowedToMerge'}
- ${false} | ${false} | ${'notAllowedToMerge'}
- `(
- 'returns $stateKey when canMerge is $canMerge and isSHAMismatch is $isSHAMismatch',
- ({ canMerge, isSHAMismatch, stateKey }) => {
- const bound = getStateKey.bind({
- canMerge,
- isSHAMismatch,
- commitsCount: 2,
- });
-
- expect(bound()).toEqual(stateKey);
- },
- );
-});
diff --git a/spec/frontend/vue_mr_widget/stores/mr_widget_store_spec.js b/spec/frontend/vue_mr_widget/stores/mr_widget_store_spec.js
deleted file mode 100644
index 3cdb4265ef0..00000000000
--- a/spec/frontend/vue_mr_widget/stores/mr_widget_store_spec.js
+++ /dev/null
@@ -1,180 +0,0 @@
-import { convertToCamelCase } from '~/lib/utils/text_utility';
-import MergeRequestStore from '~/vue_merge_request_widget/stores/mr_widget_store';
-import { stateKey } from '~/vue_merge_request_widget/stores/state_maps';
-import mockData from '../mock_data';
-
-describe('MergeRequestStore', () => {
- let store;
-
- beforeEach(() => {
- store = new MergeRequestStore(mockData);
- });
-
- it('should initialize gitpod attributes', () => {
- expect(store).toMatchObject({
- gitpodEnabled: mockData.gitpod_enabled,
- showGitpodButton: mockData.show_gitpod_button,
- gitpodUrl: mockData.gitpod_url,
- userPreferencesGitpodPath: mockData.user_preferences_gitpod_path,
- userProfileEnableGitpodPath: mockData.user_profile_enable_gitpod_path,
- });
- });
-
- describe('setData', () => {
- it('should set isSHAMismatch when the diff SHA changes', () => {
- store.setData({ ...mockData, diff_head_sha: 'a-different-string' });
-
- expect(store.isSHAMismatch).toBe(true);
- });
-
- it('should not set isSHAMismatch when other data changes', () => {
- store.setData({ ...mockData, work_in_progress: !mockData.work_in_progress });
-
- expect(store.isSHAMismatch).toBe(false);
- });
-
- it('should update cached sha after rebasing', () => {
- store.setData({ ...mockData, diff_head_sha: 'abc123' }, true);
-
- expect(store.isSHAMismatch).toBe(false);
- expect(store.sha).toBe('abc123');
- });
-
- describe('isPipelinePassing', () => {
- it('is true when the CI status is `success`', () => {
- store.setData({ ...mockData, ci_status: 'success' });
-
- expect(store.isPipelinePassing).toBe(true);
- });
-
- it('is true when the CI status is `success-with-warnings`', () => {
- store.setData({ ...mockData, ci_status: 'success-with-warnings' });
-
- expect(store.isPipelinePassing).toBe(true);
- });
-
- it('is false when the CI status is `failed`', () => {
- store.setData({ ...mockData, ci_status: 'failed' });
-
- expect(store.isPipelinePassing).toBe(false);
- });
-
- it('is false when the CI status is anything except `success`', () => {
- store.setData({ ...mockData, ci_status: 'foobarbaz' });
-
- expect(store.isPipelinePassing).toBe(false);
- });
- });
-
- describe('isPipelineSkipped', () => {
- it('should set isPipelineSkipped=true when the CI status is `skipped`', () => {
- store.setData({ ...mockData, ci_status: 'skipped' });
-
- expect(store.isPipelineSkipped).toBe(true);
- });
-
- it('should set isPipelineSkipped=false when the CI status is anything except `skipped`', () => {
- store.setData({ ...mockData, ci_status: 'foobarbaz' });
-
- expect(store.isPipelineSkipped).toBe(false);
- });
- });
-
- describe('isPipelineBlocked', () => {
- const pipelineWaitingForManualAction = {
- details: {
- status: {
- group: 'manual',
- },
- },
- };
-
- it('should be `false` when the pipeline status is missing', () => {
- store.setData({ ...mockData, pipeline: undefined });
-
- expect(store.isPipelineBlocked).toBe(false);
- });
-
- it('should be `false` when the pipeline is waiting for manual action', () => {
- store.setData({ ...mockData, pipeline: pipelineWaitingForManualAction });
-
- expect(store.isPipelineBlocked).toBe(false);
- });
-
- it('should be `true` when the pipeline is waiting for manual action and the pipeline must succeed', () => {
- store.setData({
- ...mockData,
- pipeline: pipelineWaitingForManualAction,
- only_allow_merge_if_pipeline_succeeds: true,
- });
-
- expect(store.isPipelineBlocked).toBe(true);
- });
- });
-
- describe('isNothingToMergeState', () => {
- it('returns true when nothingToMerge', () => {
- store.state = stateKey.nothingToMerge;
-
- expect(store.isNothingToMergeState).toBe(true);
- });
-
- it('returns false when not nothingToMerge', () => {
- store.state = 'state';
-
- expect(store.isNothingToMergeState).toBe(false);
- });
- });
- });
-
- describe('setPaths', () => {
- it('should set the add ci config path', () => {
- store.setPaths({ ...mockData });
-
- expect(store.mergeRequestAddCiConfigPath).toBe('/root/group2/project2/-/ci/editor');
- });
-
- it('should set humanAccess=Maintainer when user has that role', () => {
- store.setPaths({ ...mockData });
-
- expect(store.humanAccess).toBe('Maintainer');
- });
-
- it('should set pipelinesEmptySvgPath', () => {
- store.setPaths({ ...mockData });
-
- expect(store.pipelinesEmptySvgPath).toBe('/path/to/svg');
- });
-
- it('should set newPipelinePath', () => {
- store.setPaths({ ...mockData });
-
- expect(store.newPipelinePath).toBe('/group2/project2/pipelines/new');
- });
-
- it('should set sourceProjectDefaultUrl', () => {
- store.setPaths({ ...mockData });
-
- expect(store.sourceProjectDefaultUrl).toBe('/gitlab-org/html5-boilerplate.git');
- });
-
- it('should set securityReportsDocsPath', () => {
- store.setPaths({ ...mockData });
-
- expect(store.securityReportsDocsPath).toBe('security-reports-docs-path');
- });
-
- it.each(['sast_comparison_path', 'secret_detection_comparison_path'])(
- 'should set %s path',
- (property) => {
- // Ensure something is set in the mock data
- expect(property in mockData).toBe(true);
- const expectedValue = mockData[property];
-
- store.setPaths({ ...mockData });
-
- expect(store[convertToCamelCase(property)]).toBe(expectedValue);
- },
- );
- });
-});
diff --git a/spec/frontend/vue_mr_widget/test_extensions.js b/spec/frontend/vue_mr_widget/test_extensions.js
deleted file mode 100644
index 1977f550577..00000000000
--- a/spec/frontend/vue_mr_widget/test_extensions.js
+++ /dev/null
@@ -1,192 +0,0 @@
-import { EXTENSION_ICONS } from '~/vue_merge_request_widget/constants';
-
-export const workingExtension = (shouldCollapse = true) => ({
- name: 'WidgetTestExtension',
- props: ['targetProjectFullPath'],
- expandEvent: 'test_expand_event',
- i18n: {
- loading: 'Test extension loading...',
- },
- computed: {
- summary({ count, targetProjectFullPath } = {}) {
- return `Test extension summary count: ${count} & ${targetProjectFullPath}`;
- },
- statusIcon({ count } = {}) {
- return count > 0 ? EXTENSION_ICONS.warning : EXTENSION_ICONS.success;
- },
- shouldCollapse() {
- return shouldCollapse;
- },
- },
- methods: {
- fetchCollapsedData({ targetProjectFullPath }) {
- return Promise.resolve({ targetProjectFullPath, count: 1 });
- },
- fetchFullData() {
- return Promise.resolve([
- {
- id: 1,
- text: 'Hello world',
- icon: {
- name: EXTENSION_ICONS.failed,
- },
- badge: {
- text: 'Closed',
- },
- link: {
- href: 'https://gitlab.com',
- text: 'GitLab.com',
- },
- actions: [{ text: 'Full report', href: 'https://gitlab.com', target: '_blank' }],
- },
- ]);
- },
- },
-});
-
-export const collapsedDataErrorExtension = {
- name: 'WidgetTestCollapsedErrorExtension',
- props: ['targetProjectFullPath'],
- expandEvent: 'test_expand_event',
- computed: {
- summary({ count, targetProjectFullPath }) {
- return `Test extension summary count: ${count} & ${targetProjectFullPath}`;
- },
- statusIcon({ count }) {
- return count > 0 ? EXTENSION_ICONS.warning : EXTENSION_ICONS.success;
- },
- },
- methods: {
- fetchCollapsedData() {
- return Promise.reject(new Error('Fetch error'));
- },
- fetchFullData() {
- return Promise.resolve([
- {
- id: 1,
- text: 'Hello world',
- icon: {
- name: EXTENSION_ICONS.failed,
- },
- badge: {
- text: 'Closed',
- },
- link: {
- href: 'https://gitlab.com',
- text: 'GitLab.com',
- },
- actions: [{ text: 'Full report', href: 'https://gitlab.com', target: '_blank' }],
- },
- ]);
- },
- },
-};
-
-export const fullDataErrorExtension = {
- name: 'WidgetTestCollapsedErrorExtension',
- props: ['targetProjectFullPath'],
- expandEvent: 'test_expand_event',
- computed: {
- summary({ count, targetProjectFullPath }) {
- return `Test extension summary count: ${count} & ${targetProjectFullPath}`;
- },
- statusIcon({ count }) {
- return count > 0 ? EXTENSION_ICONS.warning : EXTENSION_ICONS.success;
- },
- },
- methods: {
- fetchCollapsedData({ targetProjectFullPath }) {
- return Promise.resolve({ targetProjectFullPath, count: 1 });
- },
- fetchFullData() {
- return Promise.reject(new Error('Fetch error'));
- },
- },
-};
-
-export const pollingExtension = {
- ...workingExtension(),
- enablePolling: true,
-};
-
-export const pollingFullDataExtension = {
- ...workingExtension(),
- enableExpandedPolling: true,
- methods: {
- fetchCollapsedData({ targetProjectFullPath }) {
- return Promise.resolve({ targetProjectFullPath, count: 1 });
- },
- fetchFullData() {
- return Promise.resolve([
- {
- headers: { 'poll-interval': 0 },
- status: 200,
- data: {
- id: 1,
- text: 'Hello world',
- icon: {
- name: EXTENSION_ICONS.failed,
- },
- badge: {
- text: 'Closed',
- },
- link: {
- href: 'https://gitlab.com',
- text: 'GitLab.com',
- },
- actions: [{ text: 'Full report', href: 'https://gitlab.com', target: '_blank' }],
- },
- },
- ]);
- },
- },
-};
-
-export const fullReportExtension = {
- ...workingExtension(),
- computed: {
- ...workingExtension().computed,
- tertiaryButtons() {
- return [
- {
- text: 'test',
- href: `testref`,
- target: '_blank',
- fullReport: true,
- },
- ];
- },
- },
-};
-
-export const noTelemetryExtension = {
- ...fullReportExtension,
- telemetry: false,
-};
-
-export const multiPollingExtension = (endpointsToBePolled) => ({
- name: 'WidgetTestMultiPollingExtension',
- props: [],
- i18n: {
- loading: 'Test extension loading...',
- },
- computed: {
- summary(data) {
- return `Multi polling test extension reports: ${data?.[0]?.reports}, count: ${data.length}`;
- },
- statusIcon(data) {
- return data?.[0]?.reports === 'parsed' ? EXTENSION_ICONS.success : EXTENSION_ICONS.warning;
- },
- },
- enablePolling: true,
- methods: {
- fetchMultiData() {
- return endpointsToBePolled;
- },
- },
-});
-
-export const pollingErrorExtension = {
- ...collapsedDataErrorExtension,
- enablePolling: true,
-};