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
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-06-22 21:09:45 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-06-22 21:09:45 +0300
commit06f12476c7962ba59579b3a08d187a22325d9039 (patch)
tree6ebee8dbf9f31ca8fe2a3fe1b75de9e93a2adb82 /spec
parent3a8d221b7e3dc909876fe60ac267e63d1ffffdd7 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/factories/organizations/organization_users.rb8
-rw-r--r--spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb141
-rw-r--r--spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb157
-rw-r--r--spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb55
-rw-r--r--spec/frontend/admin/topics/components/topic_select_spec.js4
-rw-r--r--spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js2
-rw-r--r--spec/frontend/blob_edit/edit_blob_spec.js1
-rw-r--r--spec/frontend/boards/board_card_inner_spec.js1
-rw-r--r--spec/frontend/boards/components/board_settings_sidebar_spec.js4
-rw-r--r--spec/frontend/contribution_events/components/contribution_event/contribution_event_approved_spec.js26
-rw-r--r--spec/frontend/contribution_events/components/contribution_event/contribution_event_base_spec.js79
-rw-r--r--spec/frontend/contribution_events/components/contribution_event/contribution_event_expired_spec.js21
-rw-r--r--spec/frontend/contribution_events/components/contribution_event/contribution_event_joined_spec.js21
-rw-r--r--spec/frontend/contribution_events/components/contribution_event/contribution_event_left_spec.js21
-rw-r--r--spec/frontend/contribution_events/components/contribution_events_spec.js8
-rw-r--r--spec/frontend/contribution_events/components/resource_parent_link_spec.js46
-rw-r--r--spec/frontend/contribution_events/components/target_link_spec.js43
-rw-r--r--spec/frontend/contribution_events/utils.js12
-rw-r--r--spec/frontend/design_management/components/design_presentation_spec.js4
-rw-r--r--spec/frontend/design_management/components/design_todo_button_spec.js4
-rw-r--r--spec/frontend/drawio/drawio_editor_spec.js1
-rw-r--r--spec/frontend/editor/source_editor_extension_base_spec.js1
-rw-r--r--spec/frontend/editor/source_editor_markdown_livepreview_ext_spec.js7
-rw-r--r--spec/frontend/editor/source_editor_yaml_ext_spec.js4
-rw-r--r--spec/frontend/header_search/init_spec.js2
-rw-r--r--spec/frontend/ide/components/repo_editor_spec.js1
-rw-r--r--spec/frontend/jira_connect/branches/components/project_dropdown_spec.js1
-rw-r--r--spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js1
-rw-r--r--spec/frontend/lib/utils/downloader_spec.js4
-rw-r--r--spec/frontend/notes/mixins/discussion_navigation_spec.js1
-rw-r--r--spec/frontend/pipeline_wizard/components/commit_spec.js8
-rw-r--r--spec/frontend/pipelines/graph_shared/links_inner_spec.js1
-rw-r--r--spec/frontend/projects/settings/branch_rules/components/view/index_spec.js1
-rw-r--r--spec/frontend/sidebar/components/todo_toggle/todo_button_spec.js1
-rw-r--r--spec/frontend/sidebar/sidebar_mediator_spec.js2
-rw-r--r--spec/frontend/super_sidebar/components/sidebar_peek_behavior_spec.js12
-rw-r--r--spec/frontend/super_sidebar/components/super_sidebar_spec.js15
-rw-r--r--spec/frontend/super_sidebar/components/super_sidebar_toggle_spec.js15
-rw-r--r--spec/frontend/super_sidebar/super_sidebar_collapsed_state_manager_spec.js32
-rw-r--r--spec/frontend/tracking/tracking_spec.js1
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js39
-rw-r--r--spec/frontend/vue_shared/components/blob_viewers/rich_viewer_spec.js11
-rw-r--r--spec/frontend/vue_shared/components/source_viewer/components/chunk_new_spec.js2
-rw-r--r--spec/frontend/work_items/components/notes/work_item_note_actions_spec.js27
-rw-r--r--spec/frontend/work_items/components/notes/work_item_note_awards_list_spec.js147
-rw-r--r--spec/frontend/work_items/components/notes/work_item_note_spec.js19
-rw-r--r--spec/frontend/work_items/mock_data.js54
-rw-r--r--spec/frontend/work_items/notes/award_utils_spec.js109
-rw-r--r--spec/models/organizations/organization_spec.rb2
-rw-r--r--spec/models/organizations/organization_user_spec.rb10
-rw-r--r--spec/models/user_spec.rb9
-rw-r--r--spec/support/formatters/json_formatter.rb3
52 files changed, 598 insertions, 603 deletions
diff --git a/spec/factories/organizations/organization_users.rb b/spec/factories/organizations/organization_users.rb
new file mode 100644
index 00000000000..761f260ccb3
--- /dev/null
+++ b/spec/factories/organizations/organization_users.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :organization_user, class: 'Organizations::OrganizationUser' do
+ user
+ organization
+ end
+end
diff --git a/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
index 19b5ad0fa84..a4c03dc4e73 100644
--- a/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
@@ -13,22 +13,6 @@ RSpec.describe 'Merge request > User merges only if pipeline succeeds', :js, fea
context 'project does not have CI enabled' do
it 'allows MR to be merged' do
- stub_feature_flags(auto_merge_labels_mr_widget: false)
-
- visit project_merge_request_path(project, merge_request)
-
- wait_for_requests
-
- page.within('.mr-state-widget') do
- expect(page).to have_button 'Merge'
- end
- end
- end
-
- context 'project does not have CI enabled and auto_merge_labels_mr_widget on' do
- it 'allows MR to be merged' do
- stub_feature_flags(auto_merge_labels_mr_widget: true)
-
visit project_merge_request_path(project, merge_request)
wait_for_requests
@@ -51,79 +35,6 @@ RSpec.describe 'Merge request > User merges only if pipeline succeeds', :js, fea
context 'when merge requests can only be merged if the pipeline succeeds' do
before do
project.update_attribute(:only_allow_merge_if_pipeline_succeeds, true)
-
- stub_feature_flags(auto_merge_labels_mr_widget: false)
- end
-
- context 'when CI is running' do
- let(:status) { :running }
-
- it 'does not allow to merge immediately' do
- visit project_merge_request_path(project, merge_request)
-
- wait_for_requests
-
- expect(page).to have_button 'Merge when pipeline succeeds'
- expect(page).not_to have_button '.js-merge-moment'
- end
- end
-
- context 'when CI failed' do
- let(:status) { :failed }
-
- it 'does not allow MR to be merged' do
- visit project_merge_request_path(project, merge_request)
-
- wait_for_requests
-
- expect(page).not_to have_button('Merge', exact: true)
- expect(page).to have_content('Merge blocked: pipeline must succeed. Push a commit that fixes the failure or learn about other solutions.')
- end
- end
-
- context 'when CI canceled' do
- let(:status) { :canceled }
-
- it 'does not allow MR to be merged' do
- visit project_merge_request_path(project, merge_request)
-
- wait_for_requests
-
- expect(page).not_to have_button('Merge', exact: true)
- expect(page).to have_content('Merge blocked: pipeline must succeed. Push a commit that fixes the failure or learn about other solutions.')
- end
- end
-
- context 'when CI succeeded' do
- let(:status) { :success }
-
- it 'allows MR to be merged' do
- visit project_merge_request_path(project, merge_request)
-
- wait_for_requests
-
- expect(page).to have_button('Merge', exact: true)
- end
- end
-
- context 'when CI skipped' do
- let(:status) { :skipped }
-
- it 'does not allow MR to be merged' do
- visit project_merge_request_path(project, merge_request)
-
- wait_for_requests
-
- expect(page).not_to have_button('Merge', exact: true)
- end
- end
- end
-
- context 'when merge requests can only be merged if the pipeline succeeds with auto_merge_labels_mr_widget on' do
- before do
- project.update_attribute(:only_allow_merge_if_pipeline_succeeds, true)
-
- stub_feature_flags(auto_merge_labels_mr_widget: true)
end
context 'when CI is running' do
@@ -193,58 +104,6 @@ RSpec.describe 'Merge request > User merges only if pipeline succeeds', :js, fea
context 'when merge requests can be merged when the build failed' do
before do
project.update_attribute(:only_allow_merge_if_pipeline_succeeds, false)
-
- stub_feature_flags(auto_merge_labels_mr_widget: false)
- end
-
- context 'when CI is running' do
- let(:status) { :running }
-
- it 'allows MR to be merged immediately' do
- visit project_merge_request_path(project, merge_request)
-
- wait_for_requests
-
- expect(page).to have_button 'Merge when pipeline succeeds'
-
- page.find('.js-merge-moment').click
- expect(page).to have_content 'Merge immediately'
- end
- end
-
- context 'when CI failed' do
- let(:status) { :failed }
-
- it 'allows MR to be merged' do
- visit project_merge_request_path(project, merge_request)
-
- wait_for_requests
- page.within('.mr-state-widget') do
- expect(page).to have_button 'Merge'
- end
- end
- end
-
- context 'when CI succeeded' do
- let(:status) { :success }
-
- it 'allows MR to be merged' do
- visit project_merge_request_path(project, merge_request)
-
- wait_for_requests
-
- page.within('.mr-state-widget') do
- expect(page).to have_button 'Merge'
- end
- end
- end
- end
-
- context 'when merge requests can be merged when the build failed with auto_merge_labels_mr_widget on' do
- before do
- project.update_attribute(:only_allow_merge_if_pipeline_succeeds, false)
-
- stub_feature_flags(auto_merge_labels_mr_widget: true)
end
context 'when CI is running' do
diff --git a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
index e42e4735ee2..9a8384bfc9f 100644
--- a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
@@ -26,83 +26,6 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js, featur
context 'when there is active pipeline for merge request' do
before do
create(:ci_build, pipeline: pipeline)
- stub_feature_flags(auto_merge_labels_mr_widget: false)
-
- sign_in(user)
- visit project_merge_request_path(project, merge_request)
- end
-
- describe 'enabling Merge when pipeline succeeds' do
- shared_examples 'Merge when pipeline succeeds activator' do
- it 'activates the Merge when pipeline succeeds feature', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/410105' do
- click_button "Merge when pipeline succeeds"
-
- expect(page).to have_content "Set by #{user.name} to be merged automatically when the pipeline succeeds"
- expect(page).to have_content "Source branch will not be deleted"
- expect(page).to have_selector ".js-cancel-auto-merge"
- visit project_merge_request_path(project, merge_request) # Needed to refresh the page
- expect(page).to have_content /enabled an automatic merge when the pipeline for \h{8} succeeds/i
- end
- end
-
- context "when enabled immediately" do
- it_behaves_like 'Merge when pipeline succeeds activator'
- end
-
- context 'when enabled after pipeline status changed', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/258667' do
- before do
- pipeline.run!
-
- # We depend on merge request widget being reloaded
- # so we have to wait for asynchronous call to reload it
- # and have_content expectation handles that.
- #
- expect(page).to have_content "Pipeline ##{pipeline.id} running"
- end
-
- it_behaves_like 'Merge when pipeline succeeds activator'
- end
-
- context 'when enabled after it was previously canceled' do
- before do
- click_button "Merge when pipeline succeeds"
-
- wait_for_requests
-
- click_button "Cancel auto-merge"
-
- wait_for_requests
-
- expect(page).to have_content 'Merge when pipeline succeeds'
- end
-
- it_behaves_like 'Merge when pipeline succeeds activator'
- end
-
- context 'when it was enabled and then canceled' do
- let(:merge_request) do
- create(:merge_request_with_diffs,
- :merge_when_pipeline_succeeds,
- source_project: project,
- title: 'Bug NS-04',
- author: user,
- merge_user: user)
- end
-
- before do
- merge_request.merge_params['force_remove_source_branch'] = '0'
- merge_request.save!
- click_button "Cancel auto-merge"
- end
-
- it_behaves_like 'Merge when pipeline succeeds activator'
- end
- end
- end
-
- context 'when there is active pipeline for merge request with auto_merge_labels_mr_widget on' do
- before do
- create(:ci_build, pipeline: pipeline)
stub_feature_flags(auto_merge_labels_mr_widget: true)
sign_in(user)
@@ -166,86 +89,6 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js, featur
context 'when merge when pipeline succeeds is enabled' do
let(:merge_request) do
create(:merge_request_with_diffs, :simple, :merge_when_pipeline_succeeds,
- source_project: project,
- author: user,
- merge_user: user,
- title: 'MepMep')
- end
-
- let!(:build) do
- create(:ci_build, pipeline: pipeline)
- end
-
- before do
- stub_feature_flags(auto_merge_labels_mr_widget: false)
- sign_in user
- visit project_merge_request_path(project, merge_request)
- end
-
- it 'allows to cancel the automatic merge', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/412416' do
- click_button "Cancel auto-merge"
-
- expect(page).to have_button "Merge when pipeline succeeds"
-
- refresh
-
- expect(page).to have_content "canceled the automatic merge"
- end
-
- context 'when pipeline succeeds' do
- before do
- build.success
- refresh
- end
-
- it 'merges merge request', :sidekiq_might_not_need_inline do
- expect(page).to have_content 'Changes merged'
- expect(merge_request.reload).to be_merged
- end
- end
-
- context 'view merge request with MWPS enabled but automatically merge fails' do
- before do
- merge_request.update!(
- merge_user: merge_request.author,
- merge_error: 'Something went wrong'
- )
- refresh
- end
-
- it 'shows information about the merge error' do
- # Wait for the `ci_status` and `merge_check` requests
- wait_for_requests
-
- page.within('.mr-state-widget') do
- expect(page).to have_content('Something went wrong. Try again.')
- end
- end
- end
-
- context 'view merge request with MWPS enabled but automatically merge fails' do
- before do
- merge_request.update!(
- merge_user: merge_request.author,
- merge_error: 'Something went wrong.'
- )
- refresh
- end
-
- it 'shows information about the merge error' do
- # Wait for the `ci_status` and `merge_check` requests
- wait_for_requests
-
- page.within('.mr-state-widget') do
- expect(page).to have_content('Something went wrong. Try again.')
- end
- end
- end
- end
-
- context 'when merge when pipeline succeeds is enabled and auto_merge_labels_mr_widget on' do
- let(:merge_request) do
- create(:merge_request_with_diffs, :simple, :merge_when_pipeline_succeeds,
source_project: project,
author: user,
merge_user: user,
diff --git a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
index fba25b41b83..4ea36286768 100644
--- a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
@@ -58,8 +58,6 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
end
before do
- stub_feature_flags(auto_merge_labels_mr_widget: false)
-
visit project_merge_request_path(project, merge_request)
page.within('.merge-request-tabs') do
@@ -146,53 +144,8 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
end
end
- context 'when a user merges a merge request in the parent project', :sidekiq_might_not_need_inline do
- before do
- click_link 'Overview'
- click_button 'Merge when pipeline succeeds'
-
- wait_for_requests
- end
-
- context 'when detached merge request pipeline is pending' do
- it 'waits the head pipeline' do
- expect(page).to have_content('to be merged automatically when the pipeline succeeds')
- expect(page).to have_button('Cancel auto-merge')
- end
- end
-
- context 'when detached merge request pipeline succeeds' do
- before do
- detached_merge_request_pipeline.reload.succeed!
-
- wait_for_requests
- end
-
- it 'merges the merge request' do
- expect(page).to have_content('Merged by')
- expect(page).to have_button('Revert')
- end
- end
-
- context 'when branch pipeline succeeds' do
- before do
- click_link 'Overview'
- push_pipeline.reload.succeed!
-
- wait_for_requests
- end
-
- it 'waits the head pipeline' do
- expect(page).to have_content('to be merged automatically when the pipeline succeeds')
- expect(page).to have_button('Cancel auto-merge')
- end
- end
- end
-
- context 'when a user created a merge request in the parent project with auto_merge_labels_mr_widget on' do
+ context 'when a user created a merge request in the parent project' do
before do
- stub_feature_flags(auto_merge_labels_mr_widget: true)
-
visit project_merge_request_path(project, merge_request)
page.within('.merge-request-tabs') do
@@ -408,10 +361,10 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
project.update!(only_allow_merge_if_pipeline_succeeds: true)
end
- it 'shows MWPS button' do
+ it 'shows Set to auto-merge button' do
visit project_merge_request_path(project, merge_request)
- expect(page).to have_button('Merge when pipeline succeeds')
+ expect(page).to have_button('Set to auto-merge')
end
end
end
@@ -421,7 +374,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
before do
click_link("Overview")
- click_button 'Merge when pipeline succeeds'
+ click_button 'Set to auto-merge'
wait_for_requests
end
diff --git a/spec/frontend/admin/topics/components/topic_select_spec.js b/spec/frontend/admin/topics/components/topic_select_spec.js
index 113a0e3d404..5b7e6365606 100644
--- a/spec/frontend/admin/topics/components/topic_select_spec.js
+++ b/spec/frontend/admin/topics/components/topic_select_spec.js
@@ -58,10 +58,6 @@ describe('TopicSelect', () => {
});
}
- afterEach(() => {
- jest.clearAllMocks();
- });
-
it('mounts', () => {
createComponent();
diff --git a/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js b/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js
index 521bbf06b02..de4db23bae2 100644
--- a/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js
+++ b/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js
@@ -239,8 +239,6 @@ describe('Batch comments store actions', () => {
params = { note: { id: 1 }, noteText: 'test' };
});
- afterEach(() => jest.clearAllMocks());
-
it('commits RECEIVE_DRAFT_UPDATE_SUCCESS with returned data', () => {
return actions.updateDraft(context, { ...params, callback() {} }).then(() => {
expect(commit).toHaveBeenCalledWith('RECEIVE_DRAFT_UPDATE_SUCCESS', { id: 1 });
diff --git a/spec/frontend/blob_edit/edit_blob_spec.js b/spec/frontend/blob_edit/edit_blob_spec.js
index 9ab20fc2cd7..1bdc54723ce 100644
--- a/spec/frontend/blob_edit/edit_blob_spec.js
+++ b/spec/frontend/blob_edit/edit_blob_spec.js
@@ -61,7 +61,6 @@ describe('Blob Editing', () => {
});
afterEach(() => {
mock.restore();
- jest.clearAllMocks();
unuseMock.mockClear();
useMock.mockClear();
resetHTMLFixture();
diff --git a/spec/frontend/boards/board_card_inner_spec.js b/spec/frontend/boards/board_card_inner_spec.js
index a925f752f5e..47e6a305447 100644
--- a/spec/frontend/boards/board_card_inner_spec.js
+++ b/spec/frontend/boards/board_card_inner_spec.js
@@ -111,7 +111,6 @@ describe('Board card component', () => {
afterEach(() => {
store = null;
- jest.clearAllMocks();
});
it('renders issue title', () => {
diff --git a/spec/frontend/boards/components/board_settings_sidebar_spec.js b/spec/frontend/boards/components/board_settings_sidebar_spec.js
index b1e14be8ceb..affe1260c66 100644
--- a/spec/frontend/boards/components/board_settings_sidebar_spec.js
+++ b/spec/frontend/boards/components/board_settings_sidebar_spec.js
@@ -90,10 +90,6 @@ describe('BoardSettingsSidebar', () => {
const findModal = () => wrapper.findComponent(GlModal);
const findRemoveButton = () => wrapper.findComponent(GlButton);
- afterEach(() => {
- jest.restoreAllMocks();
- });
-
it('finds a MountingPortal component', () => {
createComponent();
diff --git a/spec/frontend/contribution_events/components/contribution_event/contribution_event_approved_spec.js b/spec/frontend/contribution_events/components/contribution_event/contribution_event_approved_spec.js
index fb27e160897..5bce0ca3746 100644
--- a/spec/frontend/contribution_events/components/contribution_event/contribution_event_approved_spec.js
+++ b/spec/frontend/contribution_events/components/contribution_event/contribution_event_approved_spec.js
@@ -1,20 +1,17 @@
-import events from 'test_fixtures/controller/users/activity.json';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ContributionEventApproved from '~/contribution_events/components/contribution_event/contribution_event_approved.vue';
import ContributionEventBase from '~/contribution_events/components/contribution_event/contribution_event_base.vue';
-import TargetLink from '~/contribution_events/components/target_link.vue';
-import ResourceParentLink from '~/contribution_events/components/resource_parent_link.vue';
import { eventApproved } from '../../utils';
const defaultPropsData = {
- event: eventApproved(events),
+ event: eventApproved(),
};
describe('ContributionEventApproved', () => {
let wrapper;
const createComponent = () => {
- wrapper = mountExtended(ContributionEventApproved, {
+ wrapper = shallowMountExtended(ContributionEventApproved, {
propsData: defaultPropsData,
});
};
@@ -28,22 +25,7 @@ describe('ContributionEventApproved', () => {
event: defaultPropsData.event,
iconName: 'approval-solid',
iconClass: 'gl-text-green-500',
+ message: ContributionEventApproved.i18n.message,
});
});
-
- it('renders message', () => {
- expect(wrapper.findByTestId('event-body').text()).toBe(
- `Approved merge request ${defaultPropsData.event.target.reference_link_text} in ${defaultPropsData.event.resource_parent.full_name}.`,
- );
- });
-
- it('renders target link', () => {
- expect(wrapper.findComponent(TargetLink).props('event')).toEqual(defaultPropsData.event);
- });
-
- it('renders resource parent link', () => {
- expect(wrapper.findComponent(ResourceParentLink).props('event')).toEqual(
- defaultPropsData.event,
- );
- });
});
diff --git a/spec/frontend/contribution_events/components/contribution_event/contribution_event_base_spec.js b/spec/frontend/contribution_events/components/contribution_event/contribution_event_base_spec.js
index 8c951e20bed..310966243d1 100644
--- a/spec/frontend/contribution_events/components/contribution_event/contribution_event_base_spec.js
+++ b/spec/frontend/contribution_events/components/contribution_event/contribution_event_base_spec.js
@@ -1,23 +1,27 @@
import { GlAvatarLabeled, GlAvatarLink, GlIcon } from '@gitlab/ui';
-import events from 'test_fixtures/controller/users/activity.json';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
import ContributionEventBase from '~/contribution_events/components/contribution_event/contribution_event_base.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
-
-const [event] = events;
+import TargetLink from '~/contribution_events/components/target_link.vue';
+import ResourceParentLink from '~/contribution_events/components/resource_parent_link.vue';
+import { eventApproved } from '../../utils';
describe('ContributionEventBase', () => {
let wrapper;
const defaultPropsData = {
- event,
+ event: eventApproved(),
iconName: 'approval-solid',
iconClass: 'gl-text-green-500',
+ message: 'Approved merge request %{targetLink} in %{resourceParentLink}.',
};
- const createComponent = () => {
- wrapper = shallowMountExtended(ContributionEventBase, {
- propsData: defaultPropsData,
+ const createComponent = ({ propsData = {} } = {}) => {
+ wrapper = mountExtended(ContributionEventBase, {
+ propsData: {
+ ...defaultPropsData,
+ ...propsData,
+ },
scopedSlots: {
default: '<div data-testid="default-slot"></div>',
'additional-info': '<div data-testid="additional-info-slot"></div>',
@@ -25,38 +29,75 @@ describe('ContributionEventBase', () => {
});
};
- beforeEach(() => {
+ it('renders avatar', () => {
createComponent();
- });
- it('renders avatar', () => {
const avatarLink = wrapper.findComponent(GlAvatarLink);
+ const avatarLabeled = avatarLink.findComponent(GlAvatarLabeled);
- expect(avatarLink.attributes('href')).toBe(event.author.web_url);
- expect(avatarLink.findComponent(GlAvatarLabeled).attributes()).toMatchObject({
- label: event.author.name,
- sublabel: `@${event.author.username}`,
- src: event.author.avatar_url,
+ expect(avatarLink.attributes('href')).toBe(defaultPropsData.event.author.web_url);
+ expect(avatarLabeled.attributes()).toMatchObject({
+ src: defaultPropsData.event.author.avatar_url,
size: '32',
});
+ expect(avatarLabeled.props()).toMatchObject({
+ label: defaultPropsData.event.author.name,
+ subLabel: `@${defaultPropsData.event.author.username}`,
+ });
});
it('renders time ago tooltip', () => {
- expect(wrapper.findComponent(TimeAgoTooltip).props('time')).toBe(event.created_at);
+ createComponent();
+
+ expect(wrapper.findComponent(TimeAgoTooltip).props('time')).toBe(
+ defaultPropsData.event.created_at,
+ );
});
it('renders icon', () => {
+ createComponent();
+
const icon = wrapper.findComponent(GlIcon);
expect(icon.props('name')).toBe(defaultPropsData.iconName);
expect(icon.classes()).toContain(defaultPropsData.iconClass);
});
- it('renders `default` slot', () => {
- expect(wrapper.findByTestId('default-slot').exists()).toBe(true);
+ describe('when `message` prop is passed', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders message', () => {
+ expect(wrapper.findByTestId('event-body').text()).toBe(
+ `Approved merge request ${defaultPropsData.event.target.reference_link_text} in ${defaultPropsData.event.resource_parent.full_name}.`,
+ );
+ });
+
+ it('renders target link', () => {
+ expect(wrapper.findComponent(TargetLink).props('event')).toEqual(defaultPropsData.event);
+ });
+
+ it('renders resource parent link', () => {
+ expect(wrapper.findComponent(ResourceParentLink).props('event')).toEqual(
+ defaultPropsData.event,
+ );
+ });
+ });
+
+ describe('when `message` prop is not passed', () => {
+ beforeEach(() => {
+ createComponent({ propsData: { message: '' } });
+ });
+
+ it('renders `default` slot', () => {
+ expect(wrapper.findByTestId('default-slot').exists()).toBe(true);
+ });
});
it('renders `additional-info` slot', () => {
+ createComponent();
+
expect(wrapper.findByTestId('additional-info-slot').exists()).toBe(true);
});
});
diff --git a/spec/frontend/contribution_events/components/contribution_event/contribution_event_expired_spec.js b/spec/frontend/contribution_events/components/contribution_event/contribution_event_expired_spec.js
index 90cb7161952..c58fca1ad12 100644
--- a/spec/frontend/contribution_events/components/contribution_event/contribution_event_expired_spec.js
+++ b/spec/frontend/contribution_events/components/contribution_event/contribution_event_expired_spec.js
@@ -1,19 +1,17 @@
-import events from 'test_fixtures/controller/users/activity.json';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ContributionEventExpired from '~/contribution_events/components/contribution_event/contribution_event_expired.vue';
import ContributionEventBase from '~/contribution_events/components/contribution_event/contribution_event_base.vue';
-import ResourceParentLink from '~/contribution_events/components/resource_parent_link.vue';
import { eventExpired } from '../../utils';
const defaultPropsData = {
- event: eventExpired(events),
+ event: eventExpired(),
};
describe('ContributionEventExpired', () => {
let wrapper;
const createComponent = () => {
- wrapper = mountExtended(ContributionEventExpired, {
+ wrapper = shallowMountExtended(ContributionEventExpired, {
propsData: defaultPropsData,
});
};
@@ -26,18 +24,7 @@ describe('ContributionEventExpired', () => {
expect(wrapper.findComponent(ContributionEventBase).props()).toMatchObject({
event: defaultPropsData.event,
iconName: 'expire',
+ message: ContributionEventExpired.i18n.message,
});
});
-
- it('renders message', () => {
- expect(wrapper.findByTestId('event-body').text()).toBe(
- `Removed due to membership expiration from ${defaultPropsData.event.resource_parent.full_name}.`,
- );
- });
-
- it('renders resource parent link', () => {
- expect(wrapper.findComponent(ResourceParentLink).props('event')).toEqual(
- defaultPropsData.event,
- );
- });
});
diff --git a/spec/frontend/contribution_events/components/contribution_event/contribution_event_joined_spec.js b/spec/frontend/contribution_events/components/contribution_event/contribution_event_joined_spec.js
index 511972e35dd..56688e2ef27 100644
--- a/spec/frontend/contribution_events/components/contribution_event/contribution_event_joined_spec.js
+++ b/spec/frontend/contribution_events/components/contribution_event/contribution_event_joined_spec.js
@@ -1,19 +1,17 @@
-import events from 'test_fixtures/controller/users/activity.json';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ContributionEventJoined from '~/contribution_events/components/contribution_event/contribution_event_joined.vue';
import ContributionEventBase from '~/contribution_events/components/contribution_event/contribution_event_base.vue';
-import ResourceParentLink from '~/contribution_events/components/resource_parent_link.vue';
import { eventJoined } from '../../utils';
const defaultPropsData = {
- event: eventJoined(events),
+ event: eventJoined(),
};
describe('ContributionEventJoined', () => {
let wrapper;
const createComponent = () => {
- wrapper = mountExtended(ContributionEventJoined, {
+ wrapper = shallowMountExtended(ContributionEventJoined, {
propsData: defaultPropsData,
});
};
@@ -26,18 +24,7 @@ describe('ContributionEventJoined', () => {
expect(wrapper.findComponent(ContributionEventBase).props()).toMatchObject({
event: defaultPropsData.event,
iconName: 'users',
+ message: ContributionEventJoined.i18n.message,
});
});
-
- it('renders message', () => {
- expect(wrapper.findByTestId('event-body').text()).toBe(
- `Joined project ${defaultPropsData.event.resource_parent.full_name}.`,
- );
- });
-
- it('renders resource parent link', () => {
- expect(wrapper.findComponent(ResourceParentLink).props('event')).toEqual(
- defaultPropsData.event,
- );
- });
});
diff --git a/spec/frontend/contribution_events/components/contribution_event/contribution_event_left_spec.js b/spec/frontend/contribution_events/components/contribution_event/contribution_event_left_spec.js
index 2e82addcda2..58cb8714d03 100644
--- a/spec/frontend/contribution_events/components/contribution_event/contribution_event_left_spec.js
+++ b/spec/frontend/contribution_events/components/contribution_event/contribution_event_left_spec.js
@@ -1,19 +1,17 @@
-import events from 'test_fixtures/controller/users/activity.json';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ContributionEventLeft from '~/contribution_events/components/contribution_event/contribution_event_left.vue';
import ContributionEventBase from '~/contribution_events/components/contribution_event/contribution_event_base.vue';
-import ResourceParentLink from '~/contribution_events/components/resource_parent_link.vue';
import { eventLeft } from '../../utils';
const defaultPropsData = {
- event: eventLeft(events),
+ event: eventLeft(),
};
describe('ContributionEventLeft', () => {
let wrapper;
const createComponent = () => {
- wrapper = mountExtended(ContributionEventLeft, {
+ wrapper = shallowMountExtended(ContributionEventLeft, {
propsData: defaultPropsData,
});
};
@@ -26,18 +24,7 @@ describe('ContributionEventLeft', () => {
expect(wrapper.findComponent(ContributionEventBase).props()).toMatchObject({
event: defaultPropsData.event,
iconName: 'leave',
+ message: ContributionEventLeft.i18n.message,
});
});
-
- it('renders message', () => {
- expect(wrapper.findByTestId('event-body').text()).toBe(
- `Left project ${defaultPropsData.event.resource_parent.full_name}.`,
- );
- });
-
- it('renders resource parent link', () => {
- expect(wrapper.findComponent(ResourceParentLink).props('event')).toEqual(
- defaultPropsData.event,
- );
- });
});
diff --git a/spec/frontend/contribution_events/components/contribution_events_spec.js b/spec/frontend/contribution_events/components/contribution_events_spec.js
index dcea9bacb78..064799d4a82 100644
--- a/spec/frontend/contribution_events/components/contribution_events_spec.js
+++ b/spec/frontend/contribution_events/components/contribution_events_spec.js
@@ -20,10 +20,10 @@ describe('ContributionEvents', () => {
it.each`
expectedComponent | expectedEvent
- ${ContributionEventApproved} | ${eventApproved(events)}
- ${ContributionEventExpired} | ${eventExpired(events)}
- ${ContributionEventJoined} | ${eventJoined(events)}
- ${ContributionEventLeft} | ${eventLeft(events)}
+ ${ContributionEventApproved} | ${eventApproved()}
+ ${ContributionEventExpired} | ${eventExpired()}
+ ${ContributionEventJoined} | ${eventJoined()}
+ ${ContributionEventLeft} | ${eventLeft()}
`(
'renders `$expectedComponent.name` component and passes expected event',
({ expectedComponent, expectedEvent }) => {
diff --git a/spec/frontend/contribution_events/components/resource_parent_link_spec.js b/spec/frontend/contribution_events/components/resource_parent_link_spec.js
index 8d586db2a30..815a1b751cf 100644
--- a/spec/frontend/contribution_events/components/resource_parent_link_spec.js
+++ b/spec/frontend/contribution_events/components/resource_parent_link_spec.js
@@ -1,30 +1,52 @@
import { GlLink } from '@gitlab/ui';
-import events from 'test_fixtures/controller/users/activity.json';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { EVENT_TYPE_APPROVED } from '~/contribution_events/constants';
import ResourceParentLink from '~/contribution_events/components/resource_parent_link.vue';
-
-const eventApproved = events.find((event) => event.action === EVENT_TYPE_APPROVED);
+import { EVENT_TYPE_PRIVATE } from '~/contribution_events/constants';
+import { eventApproved } from '../utils';
describe('ResourceParentLink', () => {
let wrapper;
- const createComponent = () => {
+ const defaultPropsData = {
+ event: eventApproved(),
+ };
+
+ const createComponent = ({ propsData = {} } = {}) => {
wrapper = shallowMountExtended(ResourceParentLink, {
propsData: {
- event: eventApproved,
+ ...defaultPropsData,
+ ...propsData,
},
});
};
- beforeEach(() => {
- createComponent();
+ describe('when resource parent is defined', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders link', () => {
+ const link = wrapper.findComponent(GlLink);
+ const { web_url, full_name } = defaultPropsData.event.resource_parent;
+
+ expect(link.attributes('href')).toBe(web_url);
+ expect(link.text()).toBe(full_name);
+ });
});
- it('renders link', () => {
- const link = wrapper.findComponent(GlLink);
+ describe('when resource parent is not defined', () => {
+ beforeEach(() => {
+ createComponent({
+ propsData: {
+ event: {
+ type: EVENT_TYPE_PRIVATE,
+ },
+ },
+ });
+ });
- expect(link.attributes('href')).toBe(eventApproved.resource_parent.web_url);
- expect(link.text()).toBe(eventApproved.resource_parent.full_name);
+ it('renders nothing', () => {
+ expect(wrapper.html()).toBe('');
+ });
});
});
diff --git a/spec/frontend/contribution_events/components/target_link_spec.js b/spec/frontend/contribution_events/components/target_link_spec.js
index 7944375487b..b71d6eff432 100644
--- a/spec/frontend/contribution_events/components/target_link_spec.js
+++ b/spec/frontend/contribution_events/components/target_link_spec.js
@@ -1,33 +1,48 @@
import { GlLink } from '@gitlab/ui';
-import events from 'test_fixtures/controller/users/activity.json';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { EVENT_TYPE_APPROVED } from '~/contribution_events/constants';
import TargetLink from '~/contribution_events/components/target_link.vue';
-
-const eventApproved = events.find((event) => event.action === EVENT_TYPE_APPROVED);
+import { eventApproved, eventJoined } from '../utils';
describe('TargetLink', () => {
let wrapper;
- const createComponent = () => {
+ const defaultPropsData = {
+ event: eventApproved(),
+ };
+
+ const createComponent = ({ propsData = {} } = {}) => {
wrapper = shallowMountExtended(TargetLink, {
propsData: {
- event: eventApproved,
+ ...defaultPropsData,
+ ...propsData,
},
});
};
- beforeEach(() => {
- createComponent();
+ describe('when target is defined', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders link', () => {
+ const link = wrapper.findComponent(GlLink);
+ const { web_url: webUrl, title, reference_link_text } = defaultPropsData.event.target;
+
+ expect(link.attributes()).toMatchObject({
+ href: webUrl,
+ title,
+ });
+ expect(link.text()).toBe(reference_link_text);
+ });
});
- it('renders link', () => {
- const link = wrapper.findComponent(GlLink);
+ describe('when target is not defined', () => {
+ beforeEach(() => {
+ createComponent({ propsData: { event: eventJoined() } });
+ });
- expect(link.attributes()).toMatchObject({
- href: eventApproved.target.web_url,
- title: eventApproved.target.title,
+ it('renders nothing', () => {
+ expect(wrapper.html()).toBe('');
});
- expect(link.text()).toBe(eventApproved.target.reference_link_text);
});
});
diff --git a/spec/frontend/contribution_events/utils.js b/spec/frontend/contribution_events/utils.js
index 0887a178e5c..736479fce04 100644
--- a/spec/frontend/contribution_events/utils.js
+++ b/spec/frontend/contribution_events/utils.js
@@ -1,3 +1,4 @@
+import events from 'test_fixtures/controller/users/activity.json';
import {
EVENT_TYPE_APPROVED,
EVENT_TYPE_EXPIRED,
@@ -5,11 +6,12 @@ import {
EVENT_TYPE_LEFT,
} from '~/contribution_events/constants';
-export const eventApproved = (events) =>
- events.find((event) => event.action === EVENT_TYPE_APPROVED);
+const findEventByAction = (action) => events.find((event) => event.action === action);
-export const eventExpired = (events) => events.find((event) => event.action === EVENT_TYPE_EXPIRED);
+export const eventApproved = () => findEventByAction(EVENT_TYPE_APPROVED);
-export const eventJoined = (events) => events.find((event) => event.action === EVENT_TYPE_JOINED);
+export const eventExpired = () => findEventByAction(EVENT_TYPE_EXPIRED);
-export const eventLeft = (events) => events.find((event) => event.action === EVENT_TYPE_LEFT);
+export const eventJoined = () => findEventByAction(EVENT_TYPE_JOINED);
+
+export const eventLeft = () => findEventByAction(EVENT_TYPE_LEFT);
diff --git a/spec/frontend/design_management/components/design_presentation_spec.js b/spec/frontend/design_management/components/design_presentation_spec.js
index fdcea6d88c0..e64dec14461 100644
--- a/spec/frontend/design_management/components/design_presentation_spec.js
+++ b/spec/frontend/design_management/components/design_presentation_spec.js
@@ -220,10 +220,6 @@ describe('Design management design presentation component', () => {
);
});
- afterEach(() => {
- jest.clearAllMocks();
- });
-
it('sets overlay position correctly when overlay is smaller than viewport', () => {
jest.spyOn(wrapper.vm.$refs.presentationViewport, 'offsetWidth', 'get').mockReturnValue(200);
jest.spyOn(wrapper.vm.$refs.presentationViewport, 'offsetHeight', 'get').mockReturnValue(200);
diff --git a/spec/frontend/design_management/components/design_todo_button_spec.js b/spec/frontend/design_management/components/design_todo_button_spec.js
index 698535d8937..2262e5fdd83 100644
--- a/spec/frontend/design_management/components/design_todo_button_spec.js
+++ b/spec/frontend/design_management/components/design_todo_button_spec.js
@@ -50,10 +50,6 @@ describe('Design management design todo button', () => {
createComponent();
});
- afterEach(() => {
- jest.clearAllMocks();
- });
-
it('renders TodoButton component', () => {
expect(wrapper.findComponent(TodoButton).exists()).toBe(true);
});
diff --git a/spec/frontend/drawio/drawio_editor_spec.js b/spec/frontend/drawio/drawio_editor_spec.js
index 4d93908b757..5a77b9d4689 100644
--- a/spec/frontend/drawio/drawio_editor_spec.js
+++ b/spec/frontend/drawio/drawio_editor_spec.js
@@ -66,7 +66,6 @@ describe('drawio/drawio_editor', () => {
});
afterEach(() => {
- jest.clearAllMocks();
findDrawioIframe()?.remove();
});
diff --git a/spec/frontend/editor/source_editor_extension_base_spec.js b/spec/frontend/editor/source_editor_extension_base_spec.js
index 70bc1dee0ee..c820d6ac63d 100644
--- a/spec/frontend/editor/source_editor_extension_base_spec.js
+++ b/spec/frontend/editor/source_editor_extension_base_spec.js
@@ -56,7 +56,6 @@ describe('The basis for an Source Editor extension', () => {
});
afterEach(() => {
- jest.clearAllMocks();
resetHTMLFixture();
});
diff --git a/spec/frontend/editor/source_editor_markdown_livepreview_ext_spec.js b/spec/frontend/editor/source_editor_markdown_livepreview_ext_spec.js
index 512b298bbbd..d9e1a22d60d 100644
--- a/spec/frontend/editor/source_editor_markdown_livepreview_ext_spec.js
+++ b/spec/frontend/editor/source_editor_markdown_livepreview_ext_spec.js
@@ -182,10 +182,6 @@ describe('Markdown Live Preview Extension for Source Editor', () => {
instance.togglePreview();
});
- afterEach(() => {
- jest.clearAllMocks();
- });
-
it('does not do anything if there is no model', () => {
instance.setModel(null);
@@ -199,9 +195,6 @@ describe('Markdown Live Preview Extension for Source Editor', () => {
mockAxios.onPost().reply(HTTP_STATUS_OK, { body: responseData });
await togglePreview();
});
- afterEach(() => {
- jest.clearAllMocks();
- });
it('removes the registered buttons from the toolbar', () => {
expect(instance.toolbar.removeItems).not.toHaveBeenCalled();
diff --git a/spec/frontend/editor/source_editor_yaml_ext_spec.js b/spec/frontend/editor/source_editor_yaml_ext_spec.js
index 14ec7f8b93f..4b1ed0fbb42 100644
--- a/spec/frontend/editor/source_editor_yaml_ext_spec.js
+++ b/spec/frontend/editor/source_editor_yaml_ext_spec.js
@@ -368,10 +368,6 @@ abc: def
let highlightLinesSpy;
let removeHighlightsSpy;
- afterEach(() => {
- jest.clearAllMocks();
- });
-
it.each`
highlightPathOnSetup | path | keepOnNotFound | expectHighlightLinesToBeCalled | withLines | expectRemoveHighlightsToBeCalled | storedHighlightPath
${null} | ${undefined} | ${false} | ${false} | ${undefined} | ${true} | ${null}
diff --git a/spec/frontend/header_search/init_spec.js b/spec/frontend/header_search/init_spec.js
index baf3c6f08b2..459ca33ee66 100644
--- a/spec/frontend/header_search/init_spec.js
+++ b/spec/frontend/header_search/init_spec.js
@@ -5,7 +5,6 @@ import initHeaderSearch, { eventHandler, cleanEventListeners } from '~/header_se
describe('Header Search EventListener', () => {
beforeEach(() => {
jest.resetModules();
- jest.restoreAllMocks();
setHTMLFixture(`
<div class="js-header-content">
<div class="header-search-form" id="js-header-search" data-autocomplete-path="/search/autocomplete" data-issues-path="/dashboard/issues" data-mr-path="/dashboard/merge_requests" data-search-context="{}" data-search-path="/search">
@@ -16,7 +15,6 @@ describe('Header Search EventListener', () => {
afterEach(() => {
resetHTMLFixture();
- jest.clearAllMocks();
});
it('attached event listener', () => {
diff --git a/spec/frontend/ide/components/repo_editor_spec.js b/spec/frontend/ide/components/repo_editor_spec.js
index 6747ec97050..aa99b1cacef 100644
--- a/spec/frontend/ide/components/repo_editor_spec.js
+++ b/spec/frontend/ide/components/repo_editor_spec.js
@@ -158,7 +158,6 @@ describe('RepoEditor', () => {
});
afterEach(() => {
- jest.clearAllMocks();
// create a new model each time, otherwise tests conflict with each other
// because of same model being used in multiple tests
monacoEditor.getModels().forEach((model) => model.dispose());
diff --git a/spec/frontend/jira_connect/branches/components/project_dropdown_spec.js b/spec/frontend/jira_connect/branches/components/project_dropdown_spec.js
index 0a887efee4b..f4f4936a134 100644
--- a/spec/frontend/jira_connect/branches/components/project_dropdown_spec.js
+++ b/spec/frontend/jira_connect/branches/components/project_dropdown_spec.js
@@ -137,7 +137,6 @@ describe('ProjectDropdown', () => {
describe('when searching branches', () => {
it('triggers a refetch', async () => {
createComponent({ mountFn: mount });
- jest.clearAllMocks();
const mockSearchTerm = 'gitl';
await findDropdown().vm.$emit('search', mockSearchTerm);
diff --git a/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js b/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js
index a3bc8e861b2..cf2dacb50d8 100644
--- a/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js
+++ b/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js
@@ -104,7 +104,6 @@ describe('SourceBranchDropdown', () => {
it('triggers a refetch', async () => {
createComponent({ mountFn: mount, props: { selectedProject: mockSelectedProject } });
await waitForPromises();
- jest.clearAllMocks();
const mockSearchTerm = 'mai';
await findListbox().vm.$emit('search', mockSearchTerm);
diff --git a/spec/frontend/lib/utils/downloader_spec.js b/spec/frontend/lib/utils/downloader_spec.js
index c14cba3a62b..a95b46d1440 100644
--- a/spec/frontend/lib/utils/downloader_spec.js
+++ b/spec/frontend/lib/utils/downloader_spec.js
@@ -8,10 +8,6 @@ describe('Downloader', () => {
jest.spyOn(document, 'createElement').mockImplementation(() => a);
});
- afterEach(() => {
- jest.clearAllMocks();
- });
-
describe('when inline file content is provided', () => {
const fileData = 'inline content';
const fileName = 'test.csv';
diff --git a/spec/frontend/notes/mixins/discussion_navigation_spec.js b/spec/frontend/notes/mixins/discussion_navigation_spec.js
index b6a2b318ec3..bef8ed8e659 100644
--- a/spec/frontend/notes/mixins/discussion_navigation_spec.js
+++ b/spec/frontend/notes/mixins/discussion_navigation_spec.js
@@ -74,7 +74,6 @@ describe('Discussion navigation mixin', () => {
});
afterEach(() => {
- jest.clearAllMocks();
resetHTMLFixture();
});
diff --git a/spec/frontend/pipeline_wizard/components/commit_spec.js b/spec/frontend/pipeline_wizard/components/commit_spec.js
index 7095525e948..bb9a4b85e0e 100644
--- a/spec/frontend/pipeline_wizard/components/commit_spec.js
+++ b/spec/frontend/pipeline_wizard/components/commit_spec.js
@@ -141,10 +141,6 @@ describe('Pipeline Wizard - Commit Page', () => {
it('emits a done event', () => {
expect(wrapper.emitted().done.length).toBe(1);
});
-
- afterEach(() => {
- jest.clearAllMocks();
- });
});
describe('failed commit', () => {
@@ -167,10 +163,6 @@ describe('Pipeline Wizard - Commit Page', () => {
it('will not emit a done event', () => {
expect(wrapper.emitted().done?.length).toBeUndefined();
});
-
- afterEach(() => {
- jest.clearAllMocks();
- });
});
});
diff --git a/spec/frontend/pipelines/graph_shared/links_inner_spec.js b/spec/frontend/pipelines/graph_shared/links_inner_spec.js
index 50f754393fe..b4ffd2658fe 100644
--- a/spec/frontend/pipelines/graph_shared/links_inner_spec.js
+++ b/spec/frontend/pipelines/graph_shared/links_inner_spec.js
@@ -80,7 +80,6 @@ describe('Links Inner component', () => {
};
afterEach(() => {
- jest.restoreAllMocks();
resetHTMLFixture();
});
diff --git a/spec/frontend/projects/settings/branch_rules/components/view/index_spec.js b/spec/frontend/projects/settings/branch_rules/components/view/index_spec.js
index 077995ab6e4..76d45692a63 100644
--- a/spec/frontend/projects/settings/branch_rules/components/view/index_spec.js
+++ b/spec/frontend/projects/settings/branch_rules/components/view/index_spec.js
@@ -91,7 +91,6 @@ describe('View branch rules', () => {
expect(findBranchName().text()).toBe(I18N.allBranches);
expect(findBranchTitle().text()).toBe(I18N.targetBranch);
- jest.restoreAllMocks();
});
it('renders the correct branch title', () => {
diff --git a/spec/frontend/sidebar/components/todo_toggle/todo_button_spec.js b/spec/frontend/sidebar/components/todo_toggle/todo_button_spec.js
index 472a89e9b21..4385db43a4a 100644
--- a/spec/frontend/sidebar/components/todo_toggle/todo_button_spec.js
+++ b/spec/frontend/sidebar/components/todo_toggle/todo_button_spec.js
@@ -23,7 +23,6 @@ describe('Todo Button', () => {
afterEach(() => {
dispatchEventSpy = null;
- jest.clearAllMocks();
});
it('renders GlButton', () => {
diff --git a/spec/frontend/sidebar/sidebar_mediator_spec.js b/spec/frontend/sidebar/sidebar_mediator_spec.js
index f2003aee96e..9c12088216b 100644
--- a/spec/frontend/sidebar/sidebar_mediator_spec.js
+++ b/spec/frontend/sidebar/sidebar_mediator_spec.js
@@ -25,8 +25,6 @@ describe('Sidebar mediator', () => {
SidebarService.singleton = null;
SidebarStore.singleton = null;
SidebarMediator.singleton = null;
-
- jest.clearAllMocks();
});
it('assigns yourself', () => {
diff --git a/spec/frontend/super_sidebar/components/sidebar_peek_behavior_spec.js b/spec/frontend/super_sidebar/components/sidebar_peek_behavior_spec.js
index 047dc9a6599..abd9c1dc44d 100644
--- a/spec/frontend/super_sidebar/components/sidebar_peek_behavior_spec.js
+++ b/spec/frontend/super_sidebar/components/sidebar_peek_behavior_spec.js
@@ -9,6 +9,7 @@ import SidebarPeek, {
STATE_OPEN,
STATE_WILL_CLOSE,
} from '~/super_sidebar/components/sidebar_peek_behavior.vue';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
// These are measured at runtime in the browser, but statically defined here
// since Jest does not do layout/styling.
@@ -32,6 +33,7 @@ jest.mock('~/lib/utils/css_utils', () => ({
describe('SidebarPeek component', () => {
let wrapper;
+ let trackingSpy = null;
const createComponent = () => {
wrapper = mount(SidebarPeek);
@@ -54,6 +56,11 @@ describe('SidebarPeek component', () => {
beforeEach(() => {
createComponent();
+ trackingSpy = mockTracking(undefined, undefined, jest.spyOn);
+ });
+
+ afterEach(() => {
+ unmockTracking();
});
it('begins in the closed state', () => {
@@ -87,6 +94,11 @@ describe('SidebarPeek component', () => {
jest.advanceTimersByTime(1);
expect(lastNChangeEvents(2)).toEqual([STATE_WILL_OPEN, STATE_OPEN]);
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'nav_peek', {
+ label: 'nav_hover',
+ property: 'nav_sidebar',
+ });
});
it('cancels transition will-open -> open if mouse out of peek region', () => {
diff --git a/spec/frontend/super_sidebar/components/super_sidebar_spec.js b/spec/frontend/super_sidebar/components/super_sidebar_spec.js
index b76c637caf4..0c785109b5e 100644
--- a/spec/frontend/super_sidebar/components/super_sidebar_spec.js
+++ b/spec/frontend/super_sidebar/components/super_sidebar_spec.js
@@ -19,6 +19,7 @@ import {
isCollapsed,
} from '~/super_sidebar/super_sidebar_collapsed_state_manager';
import { stubComponent } from 'helpers/stub_component';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { sidebarData as mockSidebarData } from '../mock_data';
const initialSidebarState = { ...sidebarState };
@@ -49,6 +50,7 @@ describe('SuperSidebar component', () => {
const findTrialStatusWidget = () => wrapper.findByTestId(trialStatusWidgetStubTestId);
const findTrialStatusPopover = () => wrapper.findByTestId(trialStatusPopoverStubTestId);
const findSidebarMenu = () => wrapper.findComponent(SidebarMenu);
+ let trackingSpy = null;
const createWrapper = ({
provide = {},
@@ -77,6 +79,11 @@ describe('SuperSidebar component', () => {
beforeEach(() => {
Object.assign(sidebarState, initialSidebarState);
+ trackingSpy = mockTracking(undefined, undefined, jest.spyOn);
+ });
+
+ afterEach(() => {
+ unmockTracking();
});
describe('default', () => {
@@ -143,12 +150,20 @@ describe('SuperSidebar component', () => {
expect(toggleSuperSidebarCollapsed).toHaveBeenCalledTimes(1);
expect(toggleSuperSidebarCollapsed).toHaveBeenCalledWith(true, true);
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'nav_hide', {
+ label: 'nav_toggle_keyboard_shortcut',
+ property: 'nav_sidebar',
+ });
isCollapsed.mockReturnValue(true);
Mousetrap.trigger('mod+\\');
expect(toggleSuperSidebarCollapsed).toHaveBeenCalledTimes(2);
expect(toggleSuperSidebarCollapsed).toHaveBeenCalledWith(false, true);
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'nav_show', {
+ label: 'nav_toggle_keyboard_shortcut',
+ property: 'nav_sidebar',
+ });
jest.spyOn(Mousetrap, 'unbind');
diff --git a/spec/frontend/super_sidebar/components/super_sidebar_toggle_spec.js b/spec/frontend/super_sidebar/components/super_sidebar_toggle_spec.js
index 8bb20186e16..23b735c2773 100644
--- a/spec/frontend/super_sidebar/components/super_sidebar_toggle_spec.js
+++ b/spec/frontend/super_sidebar/components/super_sidebar_toggle_spec.js
@@ -7,6 +7,7 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { JS_TOGGLE_COLLAPSE_CLASS, JS_TOGGLE_EXPAND_CLASS } from '~/super_sidebar/constants';
import SuperSidebarToggle from '~/super_sidebar/components/super_sidebar_toggle.vue';
import { toggleSuperSidebarCollapsed } from '~/super_sidebar/super_sidebar_collapsed_state_manager';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
jest.mock('~/super_sidebar/super_sidebar_collapsed_state_manager.js', () => ({
toggleSuperSidebarCollapsed: jest.fn(),
@@ -61,7 +62,7 @@ describe('SuperSidebarToggle component', () => {
});
});
- describe('toolip', () => {
+ describe('tooltip', () => {
it('displays collapse when expanded', () => {
createWrapper();
expect(getTooltip().title).toBe(__('Hide sidebar'));
@@ -74,15 +75,19 @@ describe('SuperSidebarToggle component', () => {
});
describe('toggle', () => {
+ let trackingSpy = null;
+
beforeEach(() => {
setHTMLFixture(`
<button class="${JS_TOGGLE_COLLAPSE_CLASS}">Hide</button>
<button class="${JS_TOGGLE_EXPAND_CLASS}">Show</button>
`);
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
});
afterEach(() => {
resetHTMLFixture();
+ unmockTracking();
});
it('collapses the sidebar and focuses the other toggle', async () => {
@@ -93,6 +98,10 @@ describe('SuperSidebarToggle component', () => {
expect(document.activeElement).toEqual(
document.querySelector(`.${JS_TOGGLE_COLLAPSE_CLASS}`),
);
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'nav_hide', {
+ label: 'nav_toggle',
+ property: 'nav_sidebar',
+ });
});
it('expands the sidebar and focuses the other toggle', async () => {
@@ -101,6 +110,10 @@ describe('SuperSidebarToggle component', () => {
await nextTick();
expect(toggleSuperSidebarCollapsed).toHaveBeenCalledWith(false, true);
expect(document.activeElement).toEqual(document.querySelector(`.${JS_TOGGLE_EXPAND_CLASS}`));
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'nav_show', {
+ label: 'nav_toggle',
+ property: 'nav_sidebar',
+ });
});
});
});
diff --git a/spec/frontend/super_sidebar/super_sidebar_collapsed_state_manager_spec.js b/spec/frontend/super_sidebar/super_sidebar_collapsed_state_manager_spec.js
index 771d1f07fea..9388d837186 100644
--- a/spec/frontend/super_sidebar/super_sidebar_collapsed_state_manager_spec.js
+++ b/spec/frontend/super_sidebar/super_sidebar_collapsed_state_manager_spec.js
@@ -11,8 +11,10 @@ import {
findPage,
bindSuperSidebarCollapsedEvents,
} from '~/super_sidebar/super_sidebar_collapsed_state_manager';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
const { xl, sm } = breakpoints;
+let trackingSpy = null;
jest.mock('~/lib/utils/common_utils', () => ({
getCookie: jest.fn(),
@@ -27,6 +29,15 @@ const pageHasCollapsedClass = (hasClass) => {
}
};
+const tracksCollapse = (shouldTrack) => {
+ if (shouldTrack) {
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'nav_hide', {
+ label: 'browser_resize',
+ property: 'nav_sidebar',
+ });
+ }
+};
+
describe('Super Sidebar Collapsed State Manager', () => {
beforeEach(() => {
setHTMLFixture(`
@@ -34,10 +45,12 @@ describe('Super Sidebar Collapsed State Manager', () => {
<aside class="super-sidebar"></aside>
</div>
`);
+ trackingSpy = mockTracking(undefined, undefined, jest.spyOn);
});
afterEach(() => {
resetHTMLFixture();
+ unmockTracking();
});
describe('toggleSuperSidebarCollapsed', () => {
@@ -109,14 +122,20 @@ describe('Super Sidebar Collapsed State Manager', () => {
});
it.each`
- initialWindowWidth | updatedWindowWidth | hasClassBeforeResize | hasClassAfterResize
- ${xl} | ${sm} | ${false} | ${true}
- ${sm} | ${xl} | ${true} | ${false}
- ${xl} | ${xl} | ${false} | ${false}
- ${sm} | ${sm} | ${true} | ${true}
+ initialWindowWidth | updatedWindowWidth | hasClassBeforeResize | hasClassAfterResize | sendsTrackingEvent
+ ${xl} | ${sm} | ${false} | ${true} | ${true}
+ ${sm} | ${xl} | ${true} | ${false} | ${false}
+ ${xl} | ${xl} | ${false} | ${false} | ${false}
+ ${sm} | ${sm} | ${true} | ${true} | ${false}
`(
'when changing width from $initialWindowWidth to $updatedWindowWidth expect page to have collapsed class before resize to be $hasClassBeforeResize and after resize to be $hasClassAfterResize',
- ({ initialWindowWidth, updatedWindowWidth, hasClassBeforeResize, hasClassAfterResize }) => {
+ ({
+ initialWindowWidth,
+ updatedWindowWidth,
+ hasClassBeforeResize,
+ hasClassAfterResize,
+ sendsTrackingEvent,
+ }) => {
getCookie.mockReturnValue(undefined);
window.innerWidth = initialWindowWidth;
initSuperSidebarCollapsedState();
@@ -129,6 +148,7 @@ describe('Super Sidebar Collapsed State Manager', () => {
window.dispatchEvent(new Event('resize'));
pageHasCollapsedClass(hasClassAfterResize);
+ tracksCollapse(sendsTrackingEvent);
},
);
});
diff --git a/spec/frontend/tracking/tracking_spec.js b/spec/frontend/tracking/tracking_spec.js
index c23790bb589..55ce8039399 100644
--- a/spec/frontend/tracking/tracking_spec.js
+++ b/spec/frontend/tracking/tracking_spec.js
@@ -59,7 +59,6 @@ describe('Tracking', () => {
window.doNotTrack = undefined;
navigator.doNotTrack = undefined;
navigator.msDoNotTrack = undefined;
- jest.clearAllMocks();
});
it('tracks to snowplow (our current tracking system)', () => {
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js
index 07fc0be9e51..123ff7ded63 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js
@@ -113,11 +113,6 @@ const createComponent = (customConfig = {}, createState = true) => {
GlSprintf,
},
apolloProvider: createMockApollo([[readyToMergeQuery, readyToMergeResponseSpy]]),
- provide: {
- glFeatures: {
- autoMergeLabelsMrWidget: false,
- },
- },
});
};
@@ -144,6 +139,7 @@ const findDeleteSourceBranchCheckbox = () =>
const triggerApprovalUpdated = () => eventHub.$emit('ApprovalUpdated');
const triggerEditCommitInput = () =>
wrapper.find('[data-testid="widget_edit_commit_message"]').vm.$emit('input', true);
+const findMergeHelperText = () => wrapper.find('[data-testid="auto-merge-helper-text"]');
describe('ReadyToMerge', () => {
beforeEach(() => {
@@ -185,26 +181,6 @@ describe('ReadyToMerge', () => {
expect(wrapper.vm.status).toEqual('failed');
});
});
-
- describe('status icon', () => {
- it('defaults to tick icon', () => {
- createComponent({ mr: { mergeable: true } });
-
- expect(wrapper.vm.iconClass).toEqual('success');
- });
-
- it('shows tick for success status', () => {
- createComponent({ mr: { pipeline: { status: 'SUCCESS' }, mergeable: true } });
-
- expect(wrapper.vm.iconClass).toEqual('success');
- });
-
- it('shows tick for pending status', () => {
- createComponent({ mr: { pipeline: { active: true }, mergeable: true } });
-
- expect(wrapper.vm.iconClass).toEqual('success');
- });
- });
});
describe('merge button text', () => {
@@ -214,18 +190,11 @@ describe('ReadyToMerge', () => {
expect(findMergeButton().text()).toBe('Merge');
});
- it('should return "Merge when pipeline succeeds" when the MWPS auto merge strategy is available', () => {
- createComponent({
- mr: { preferredAutoMergeStrategy: MWPS_MERGE_STRATEGY },
- });
-
- expect(findMergeButton().text()).toBe('Merge when pipeline succeeds');
- });
-
- it('should return Merge when pipeline succeeds', () => {
+ it('should return Set to auto-merge in the button and Merge when pipeline succeeds in the helper text', () => {
createComponent({ mr: { preferredAutoMergeStrategy: MWPS_MERGE_STRATEGY } });
- expect(findMergeButton().text()).toBe('Merge when pipeline succeeds');
+ expect(findMergeButton().text()).toBe('Set to auto-merge');
+ expect(findMergeHelperText().text()).toBe('Merge when pipeline succeeds');
});
});
diff --git a/spec/frontend/vue_shared/components/blob_viewers/rich_viewer_spec.js b/spec/frontend/vue_shared/components/blob_viewers/rich_viewer_spec.js
index 6acd1f51a86..1f3029435ee 100644
--- a/spec/frontend/vue_shared/components/blob_viewers/rich_viewer_spec.js
+++ b/spec/frontend/vue_shared/components/blob_viewers/rich_viewer_spec.js
@@ -1,3 +1,4 @@
+import { nextTick } from 'vue';
import { shallowMount } from '@vue/test-utils';
import { handleBlobRichViewer } from '~/blob/viewer';
import RichViewer from '~/vue_shared/components/blob_viewers/rich_viewer.vue';
@@ -21,16 +22,24 @@ describe('Blob Rich Viewer component', () => {
}
beforeEach(() => {
+ const execImmediately = (callback) => callback();
+ jest.spyOn(window, 'requestIdleCallback').mockImplementation(execImmediately);
+
createComponent();
});
+ it('listens to requestIdleCallback', () => {
+ expect(window.requestIdleCallback).toHaveBeenCalled();
+ });
+
it('renders the passed content without transformations', () => {
expect(wrapper.html()).toContain(content);
});
- it('renders the richViewer if one is present', () => {
+ it('renders the richViewer if one is present', async () => {
const richViewer = '<div class="js-pdf-viewer"></div>';
createComponent('pdf', richViewer);
+ await nextTick();
expect(wrapper.html()).toContain(richViewer);
});
diff --git a/spec/frontend/vue_shared/components/source_viewer/components/chunk_new_spec.js b/spec/frontend/vue_shared/components/source_viewer/components/chunk_new_spec.js
index 919abc26e05..1154c930e5d 100644
--- a/spec/frontend/vue_shared/components/source_viewer/components/chunk_new_spec.js
+++ b/spec/frontend/vue_shared/components/source_viewer/components/chunk_new_spec.js
@@ -40,8 +40,6 @@ describe('Chunk component', () => {
describe('rendering', () => {
it('does not register window.requestIdleCallback for the first chunk, renders content immediately', () => {
- jest.clearAllMocks();
-
expect(window.requestIdleCallback).not.toHaveBeenCalled();
expect(findContent().text()).toBe(CHUNK_1.highlightedContent);
});
diff --git a/spec/frontend/work_items/components/notes/work_item_note_actions_spec.js b/spec/frontend/work_items/components/notes/work_item_note_actions_spec.js
index 18d7b2397c9..e4180b2d178 100644
--- a/spec/frontend/work_items/components/notes/work_item_note_actions_spec.js
+++ b/spec/frontend/work_items/components/notes/work_item_note_actions_spec.js
@@ -5,8 +5,6 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { createMockDirective } from 'helpers/vue_mock_directive';
import { stubComponent } from 'helpers/stub_component';
-import EmojiPicker from '~/emoji/components/picker.vue';
-import waitForPromises from 'helpers/wait_for_promises';
import ReplyButton from '~/notes/components/note_actions/reply_button.vue';
import WorkItemNoteActions from '~/work_items/components/notes/work_item_note_actions.vue';
import addAwardEmojiMutation from '~/work_items/graphql/notes/work_item_note_add_award_emoji.mutation.graphql';
@@ -36,11 +34,6 @@ describe('Work Item Note Actions', () => {
},
});
- const EmojiPickerStub = {
- props: EmojiPicker.props,
- template: '<div></div>',
- };
-
const createComponent = ({
showReply = true,
showEdit = true,
@@ -57,6 +50,8 @@ describe('Work Item Note Actions', () => {
propsData: {
showReply,
showEdit,
+ workItemIid: '1',
+ note: {},
noteId,
showAwardEmoji,
showAssignUnassign,
@@ -68,12 +63,12 @@ describe('Work Item Note Actions', () => {
projectName,
},
provide: {
+ fullPath: 'gitlab-org',
glFeatures: {
workItemsMvc2: true,
},
},
stubs: {
- EmojiPicker: EmojiPickerStub,
GlDisclosureDropdown: stubComponent(GlDisclosureDropdown, {
methods: { close: showSpy },
}),
@@ -136,22 +131,6 @@ describe('Work Item Note Actions', () => {
expect(findEmojiButton().exists()).toBe(false);
});
-
- it('commits mutation on click', async () => {
- const awardName = 'carrot';
-
- createComponent();
-
- findEmojiButton().vm.$emit('click', awardName);
-
- await waitForPromises();
-
- expect(findEmojiButton().emitted('errors')).toEqual(undefined);
- expect(addEmojiMutationResolver).toHaveBeenCalledWith({
- awardableId: noteId,
- name: awardName,
- });
- });
});
describe('delete note', () => {
diff --git a/spec/frontend/work_items/components/notes/work_item_note_awards_list_spec.js b/spec/frontend/work_items/components/notes/work_item_note_awards_list_spec.js
new file mode 100644
index 00000000000..d425f1e50dc
--- /dev/null
+++ b/spec/frontend/work_items/components/notes/work_item_note_awards_list_spec.js
@@ -0,0 +1,147 @@
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import mockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { __ } from '~/locale';
+import AwardsList from '~/vue_shared/components/awards_list.vue';
+import WorkItemNoteAwardsList from '~/work_items/components/notes/work_item_note_awards_list.vue';
+import addAwardEmojiMutation from '~/work_items/graphql/notes/work_item_note_add_award_emoji.mutation.graphql';
+import removeAwardEmojiMutation from '~/work_items/graphql/notes/work_item_note_remove_award_emoji.mutation.graphql';
+import workItemNotesByIidQuery from '~/work_items/graphql/notes/work_item_notes_by_iid.query.graphql';
+import {
+ mockWorkItemNotesResponseWithComments,
+ mockAwardEmojiThumbsUp,
+} from 'jest/work_items/mock_data';
+import { EMOJI_THUMBSUP, EMOJI_THUMBSDOWN } from '~/work_items/constants';
+
+Vue.use(VueApollo);
+
+describe('Work Item Note Awards List', () => {
+ let wrapper;
+ const workItem = mockWorkItemNotesResponseWithComments.data.workspace.workItems.nodes[0];
+ const firstNote = workItem.widgets.find((w) => w.type === 'NOTES').discussions.nodes[0].notes
+ .nodes[0];
+ const fullPath = 'test-project-path';
+ const workItemIid = workItem.iid;
+ const currentUserId = getIdFromGraphQLId(mockAwardEmojiThumbsUp.user.id);
+
+ const addAwardEmojiMutationSuccessHandler = jest.fn().mockResolvedValue({
+ data: {
+ awardEmojiAdd: {
+ errors: [],
+ },
+ },
+ });
+ const removeAwardEmojiMutationSuccessHandler = jest.fn().mockResolvedValue({
+ data: {
+ awardEmojiRemove: {
+ errors: [],
+ },
+ },
+ });
+
+ const findAwardsList = () => wrapper.findComponent(AwardsList);
+
+ const createComponent = ({
+ note = firstNote,
+ addAwardEmojiMutationHandler = addAwardEmojiMutationSuccessHandler,
+ removeAwardEmojiMutationHandler = removeAwardEmojiMutationSuccessHandler,
+ } = {}) => {
+ const apolloProvider = mockApollo([
+ [addAwardEmojiMutation, addAwardEmojiMutationHandler],
+ [removeAwardEmojiMutation, removeAwardEmojiMutationHandler],
+ ]);
+
+ apolloProvider.clients.defaultClient.writeQuery({
+ query: workItemNotesByIidQuery,
+ variables: { fullPath, iid: workItemIid },
+ ...mockWorkItemNotesResponseWithComments,
+ });
+
+ wrapper = shallowMount(WorkItemNoteAwardsList, {
+ provide: {
+ fullPath,
+ },
+ propsData: {
+ workItemIid,
+ note,
+ isModal: false,
+ },
+ apolloProvider,
+ });
+ };
+
+ beforeEach(() => {
+ window.gon.current_user_id = currentUserId;
+ });
+
+ describe('when not editing', () => {
+ it.each([true, false])('passes emoji permission to awards-list', (hasAwardEmojiPermission) => {
+ const note = {
+ ...firstNote,
+ userPermissions: {
+ ...firstNote.userPermissions,
+ awardEmoji: hasAwardEmojiPermission,
+ },
+ };
+ createComponent({ note });
+
+ expect(findAwardsList().props('canAwardEmoji')).toBe(hasAwardEmojiPermission);
+ });
+
+ it('adds award if not already awarded', async () => {
+ createComponent();
+ await waitForPromises();
+
+ findAwardsList().vm.$emit('award', EMOJI_THUMBSUP);
+
+ expect(addAwardEmojiMutationSuccessHandler).toHaveBeenCalledWith({
+ awardableId: firstNote.id,
+ name: EMOJI_THUMBSUP,
+ });
+ });
+
+ it('emits error if awarding emoji fails', async () => {
+ createComponent({
+ addAwardEmojiMutationHandler: jest.fn().mockRejectedValue('oh no'),
+ });
+ await waitForPromises();
+
+ findAwardsList().vm.$emit('award', EMOJI_THUMBSUP);
+
+ await waitForPromises();
+
+ expect(wrapper.emitted('error')).toEqual([[__('Failed to add emoji. Please try again')]]);
+ });
+
+ it('removes award if already awarded', async () => {
+ const removeAwardEmojiMutationHandler = removeAwardEmojiMutationSuccessHandler;
+
+ createComponent({ removeAwardEmojiMutationHandler });
+
+ findAwardsList().vm.$emit('award', EMOJI_THUMBSDOWN);
+
+ await waitForPromises();
+
+ expect(removeAwardEmojiMutationHandler).toHaveBeenCalledWith({
+ awardableId: firstNote.id,
+ name: EMOJI_THUMBSDOWN,
+ });
+ });
+
+ it('restores award if remove fails', async () => {
+ createComponent({
+ removeAwardEmojiMutationHandler: jest.fn().mockRejectedValue('oh no'),
+ });
+ await waitForPromises();
+
+ findAwardsList().vm.$emit('award', EMOJI_THUMBSDOWN);
+
+ await waitForPromises();
+
+ expect(wrapper.emitted('error')).toEqual([[__('Failed to remove emoji. Please try again')]]);
+ });
+ });
+});
diff --git a/spec/frontend/work_items/components/notes/work_item_note_spec.js b/spec/frontend/work_items/components/notes/work_item_note_spec.js
index 8dbd2818fc5..09299f1733c 100644
--- a/spec/frontend/work_items/components/notes/work_item_note_spec.js
+++ b/spec/frontend/work_items/components/notes/work_item_note_spec.js
@@ -6,6 +6,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import { updateDraft, clearDraft } from '~/lib/utils/autosave';
import EditedAt from '~/issues/show/components/edited.vue';
import WorkItemNote from '~/work_items/components/notes/work_item_note.vue';
+import WorkItemNoteAwardsList from '~/work_items/components/notes/work_item_note_awards_list.vue';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import NoteBody from '~/work_items/components/notes/work_item_note_body.vue';
import NoteHeader from '~/notes/components/note_header.vue';
@@ -76,6 +77,7 @@ describe('Work Item Note', () => {
const errorHandler = jest.fn().mockRejectedValue('Oops');
+ const findAwardsList = () => wrapper.findComponent(WorkItemNoteAwardsList);
const findTimelineEntryItem = () => wrapper.findComponent(TimelineEntryItem);
const findNoteHeader = () => wrapper.findComponent(NoteHeader);
const findNoteBody = () => wrapper.findComponent(NoteBody);
@@ -92,10 +94,14 @@ describe('Work Item Note', () => {
updateWorkItemMutationHandler = updateWorkItemMutationSuccessHandler,
assignees = mockAssignees,
workItemByIidResponseHandler = workItemResponseHandler,
+ workItemsMvc2 = false,
} = {}) => {
wrapper = shallowMount(WorkItemNote, {
provide: {
fullPath: 'test-project-path',
+ glFeatures: {
+ workItemsMvc2,
+ },
},
propsData: {
workItemId,
@@ -404,5 +410,18 @@ describe('Work Item Note', () => {
});
});
});
+
+ it('does not show awards when feature flag disabled', () => {
+ createComponent();
+
+ expect(findAwardsList().exists()).toBe(false);
+ });
+
+ it('passes note props to awards list', () => {
+ createComponent({ note: mockWorkItemCommentNote, workItemsMvc2: true });
+
+ expect(findAwardsList().props('note')).toBe(mockWorkItemCommentNote);
+ expect(findAwardsList().props('workItemIid')).toBe('1');
+ });
});
});
diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js
index c9c73d29903..41f3adb6703 100644
--- a/spec/frontend/work_items/mock_data.js
+++ b/spec/frontend/work_items/mock_data.js
@@ -1982,6 +1982,9 @@ export const mockWorkItemNotesResponse = {
webUrl: 'http://127.0.0.1:3000/root',
__typename: 'UserCore',
},
+ awardEmoji: {
+ nodes: [],
+ },
__typename: 'Note',
},
],
@@ -2093,6 +2096,9 @@ export const mockWorkItemNotesByIidResponse = {
webUrl: 'http://127.0.0.1:3000/root',
__typename: 'UserCore',
},
+ awardEmoji: {
+ nodes: [],
+ },
__typename: 'Note',
},
],
@@ -2146,6 +2152,9 @@ export const mockWorkItemNotesByIidResponse = {
webUrl: 'http://127.0.0.1:3000/root',
__typename: 'UserCore',
},
+ awardEmoji: {
+ nodes: [],
+ },
__typename: 'Note',
},
],
@@ -2200,6 +2209,9 @@ export const mockWorkItemNotesByIidResponse = {
webUrl: 'http://127.0.0.1:3000/root',
__typename: 'UserCore',
},
+ awardEmoji: {
+ nodes: [],
+ },
__typename: 'Note',
},
],
@@ -2312,6 +2324,9 @@ export const mockMoreWorkItemNotesResponse = {
webUrl: 'http://127.0.0.1:3000/root',
__typename: 'UserCore',
},
+ awardEmoji: {
+ nodes: [],
+ },
__typename: 'Note',
},
],
@@ -2365,6 +2380,9 @@ export const mockMoreWorkItemNotesResponse = {
webUrl: 'http://127.0.0.1:3000/root',
__typename: 'UserCore',
},
+ awardEmoji: {
+ nodes: [],
+ },
__typename: 'Note',
},
],
@@ -2416,6 +2434,9 @@ export const mockMoreWorkItemNotesResponse = {
webUrl: 'http://127.0.0.1:3000/root',
__typename: 'UserCore',
},
+ awardEmoji: {
+ nodes: [],
+ },
__typename: 'Note',
},
],
@@ -2483,6 +2504,9 @@ export const createWorkItemNoteResponse = {
repositionNote: true,
__typename: 'NotePermissions',
},
+ awardEmoji: {
+ nodes: [],
+ },
__typename: 'Note',
},
],
@@ -2534,6 +2558,9 @@ export const mockWorkItemCommentNote = {
webUrl: 'http://127.0.0.1:3000/root',
__typename: 'UserCore',
},
+ awardEmoji: {
+ nodes: [mockAwardEmojiThumbsDown],
+ },
};
export const mockWorkItemCommentNoteByContributor = {
@@ -2633,6 +2660,9 @@ export const mockWorkItemNotesResponseWithComments = {
repositionNote: true,
__typename: 'NotePermissions',
},
+ awardEmoji: {
+ nodes: [mockAwardEmojiThumbsDown],
+ },
__typename: 'Note',
},
{
@@ -2673,6 +2703,9 @@ export const mockWorkItemNotesResponseWithComments = {
repositionNote: true,
__typename: 'NotePermissions',
},
+ awardEmoji: {
+ nodes: [],
+ },
__typename: 'Note',
},
],
@@ -2721,6 +2754,9 @@ export const mockWorkItemNotesResponseWithComments = {
webUrl: 'http://127.0.0.1:3000/root',
__typename: 'UserCore',
},
+ awardEmoji: {
+ nodes: [],
+ },
__typename: 'Note',
},
],
@@ -2797,6 +2833,9 @@ export const workItemNotesCreateSubscriptionResponse = {
webUrl: 'http://127.0.0.1:3000/root',
__typename: 'UserCore',
},
+ awardEmoji: {
+ nodes: [],
+ },
__typename: 'Note',
},
],
@@ -2824,6 +2863,9 @@ export const workItemNotesCreateSubscriptionResponse = {
webUrl: 'http://127.0.0.1:3000/root',
__typename: 'UserCore',
},
+ awardEmoji: {
+ nodes: [],
+ },
__typename: 'Note',
},
},
@@ -2869,6 +2911,9 @@ export const workItemNotesUpdateSubscriptionResponse = {
webUrl: 'http://127.0.0.1:3000/root',
__typename: 'UserCore',
},
+ awardEmoji: {
+ nodes: [],
+ },
__typename: 'Note',
},
},
@@ -3028,6 +3073,9 @@ export const workItemNotesWithSystemNotesWithChangedDescription = {
},
__typename: 'SystemNoteMetadata',
},
+ awardEmoji: {
+ nodes: [],
+ },
__typename: 'Note',
},
],
@@ -3091,6 +3139,9 @@ export const workItemNotesWithSystemNotesWithChangedDescription = {
},
__typename: 'SystemNoteMetadata',
},
+ awardEmoji: {
+ nodes: [],
+ },
__typename: 'Note',
},
],
@@ -3154,6 +3205,9 @@ export const workItemNotesWithSystemNotesWithChangedDescription = {
},
__typename: 'SystemNoteMetadata',
},
+ awardEmoji: {
+ nodes: [],
+ },
__typename: 'Note',
},
],
diff --git a/spec/frontend/work_items/notes/award_utils_spec.js b/spec/frontend/work_items/notes/award_utils_spec.js
new file mode 100644
index 00000000000..8ae32ce5f40
--- /dev/null
+++ b/spec/frontend/work_items/notes/award_utils_spec.js
@@ -0,0 +1,109 @@
+import { getMutation, optimisticAwardUpdate } from '~/work_items/notes/award_utils';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import mockApollo from 'helpers/mock_apollo_helper';
+import { __ } from '~/locale';
+import workItemNotesByIidQuery from '~/work_items/graphql/notes/work_item_notes_by_iid.query.graphql';
+import addAwardEmojiMutation from '~/work_items/graphql/notes/work_item_note_add_award_emoji.mutation.graphql';
+import removeAwardEmojiMutation from '~/work_items/graphql/notes/work_item_note_remove_award_emoji.mutation.graphql';
+import {
+ mockWorkItemNotesResponseWithComments,
+ mockAwardEmojiThumbsUp,
+ mockAwardEmojiThumbsDown,
+} from '../mock_data';
+
+function getWorkItem(data) {
+ return data.workspace.workItems.nodes[0];
+}
+function getFirstNote(workItem) {
+ return workItem.widgets.find((w) => w.type === 'NOTES').discussions.nodes[0].notes.nodes[0];
+}
+
+describe('Work item note award utils', () => {
+ const workItem = getWorkItem(mockWorkItemNotesResponseWithComments.data);
+ const firstNote = getFirstNote(workItem);
+ const fullPath = 'test-project-path';
+ const workItemIid = workItem.iid;
+ const currentUserId = getIdFromGraphQLId(mockAwardEmojiThumbsDown.user.id);
+
+ beforeEach(() => {
+ window.gon = { current_user_id: currentUserId };
+ });
+
+ describe('getMutation', () => {
+ it('returns remove mutation when user has already awarded award', () => {
+ const note = firstNote;
+ const { name } = mockAwardEmojiThumbsDown;
+
+ expect(getMutation({ note, name })).toEqual({
+ mutation: removeAwardEmojiMutation,
+ mutationName: 'awardEmojiRemove',
+ errorMessage: __('Failed to remove emoji. Please try again'),
+ });
+ });
+
+ it('returns remove mutation when user has not already awarded award', () => {
+ const note = {};
+ const { name } = mockAwardEmojiThumbsUp;
+
+ expect(getMutation({ note, name })).toEqual({
+ mutation: addAwardEmojiMutation,
+ mutationName: 'awardEmojiAdd',
+ errorMessage: __('Failed to add emoji. Please try again'),
+ });
+ });
+ });
+
+ describe('optimisticAwardUpdate', () => {
+ let apolloProvider;
+ beforeEach(() => {
+ apolloProvider = mockApollo();
+
+ apolloProvider.clients.defaultClient.writeQuery({
+ query: workItemNotesByIidQuery,
+ variables: { fullPath, iid: workItemIid },
+ ...mockWorkItemNotesResponseWithComments,
+ });
+ });
+
+ it('adds new emoji to cache', () => {
+ const note = firstNote;
+ const { name } = mockAwardEmojiThumbsUp;
+
+ const updateFn = optimisticAwardUpdate({ note, name, fullPath, workItemIid });
+
+ updateFn(apolloProvider.clients.defaultClient.cache);
+
+ const updatedResult = apolloProvider.clients.defaultClient.readQuery({
+ query: workItemNotesByIidQuery,
+ variables: { fullPath, iid: workItemIid },
+ });
+
+ const updatedWorkItem = getWorkItem(updatedResult);
+ const updatedNote = getFirstNote(updatedWorkItem);
+
+ expect(updatedNote.awardEmoji.nodes).toEqual([
+ mockAwardEmojiThumbsDown,
+ mockAwardEmojiThumbsUp,
+ ]);
+ });
+
+ it('removes existing emoji from cache', () => {
+ const note = firstNote;
+ const { name } = mockAwardEmojiThumbsDown;
+
+ const updateFn = optimisticAwardUpdate({ note, name, fullPath, workItemIid });
+
+ updateFn(apolloProvider.clients.defaultClient.cache);
+
+ const updatedResult = apolloProvider.clients.defaultClient.readQuery({
+ query: workItemNotesByIidQuery,
+ variables: { fullPath, iid: workItemIid },
+ });
+
+ const updatedWorkItem = getWorkItem(updatedResult);
+ const updatedNote = getFirstNote(updatedWorkItem);
+
+ expect(updatedNote.awardEmoji.nodes).toEqual([]);
+ });
+ });
+});
diff --git a/spec/models/organizations/organization_spec.rb b/spec/models/organizations/organization_spec.rb
index a3c6b9edf0d..f462fbefccc 100644
--- a/spec/models/organizations/organization_spec.rb
+++ b/spec/models/organizations/organization_spec.rb
@@ -9,6 +9,8 @@ RSpec.describe Organizations::Organization, type: :model, feature_category: :cel
describe 'associations' do
it { is_expected.to have_many :namespaces }
it { is_expected.to have_many :groups }
+ it { is_expected.to have_many(:users).through(:organization_users).inverse_of(:organizations) }
+ it { is_expected.to have_many(:organization_users).inverse_of(:organization) }
end
describe 'validations' do
diff --git a/spec/models/organizations/organization_user_spec.rb b/spec/models/organizations/organization_user_spec.rb
new file mode 100644
index 00000000000..392ffa1b5be
--- /dev/null
+++ b/spec/models/organizations/organization_user_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Organizations::OrganizationUser, type: :model, feature_category: :cell do
+ describe 'associations' do
+ it { is_expected.to belong_to(:organization).inverse_of(:organization_users).required }
+ it { is_expected.to belong_to(:user).inverse_of(:organization_users).required }
+ end
+end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 9709684f44b..1a7d86328e7 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -186,6 +186,15 @@ RSpec.describe User, feature_category: :user_profile do
it { is_expected.to have_many(:merge_request_assignment_events).class_name('ResourceEvents::MergeRequestAssignmentEvent') }
it do
+ is_expected.to have_many(:organization_users).class_name('Organizations::OrganizationUser').inverse_of(:user)
+ end
+
+ it do
+ is_expected.to have_many(:organizations)
+ .through(:organization_users).class_name('Organizations::Organization').inverse_of(:users)
+ end
+
+ it do
is_expected.to have_many(:alert_assignees).class_name('::AlertManagement::AlertAssignee').inverse_of(:assignee)
end
diff --git a/spec/support/formatters/json_formatter.rb b/spec/support/formatters/json_formatter.rb
index 10af5445b7a..e9d65af710a 100644
--- a/spec/support/formatters/json_formatter.rb
+++ b/spec/support/formatters/json_formatter.rb
@@ -74,7 +74,8 @@ module Support
product_group: example.metadata[:product_group],
feature_category: example.metadata[:feature_category],
ci_job_url: ENV['CI_JOB_URL'],
- retry_attempts: example.metadata[:retry_attempts]
+ retry_attempts: example.metadata[:retry_attempts],
+ level: example.metadata[:level]
}
end