+
diff --git a/app/assets/javascripts/admin/topics/index.js b/app/assets/javascripts/admin/topics/index.js
index 09e9b20f220..d81690e8f4c 100644
--- a/app/assets/javascripts/admin/topics/index.js
+++ b/app/assets/javascripts/admin/topics/index.js
@@ -1,7 +1,20 @@
import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
+import showToast from '~/vue_shared/plugins/global_toast';
import RemoveAvatar from './components/remove_avatar.vue';
+import MergeTopics from './components/merge_topics.vue';
-export default () => {
+const toasts = document.querySelectorAll('.js-toast-message');
+toasts.forEach((toast) => showToast(toast.dataset.message));
+
+Vue.use(VueApollo);
+
+const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient(),
+});
+
+export const initRemoveAvatar = () => {
const el = document.querySelector('.js-remove-topic-avatar');
if (!el) {
@@ -21,3 +34,20 @@ export default () => {
},
});
};
+
+export const initMergeTopics = () => {
+ const el = document.querySelector('.js-merge-topics');
+
+ if (!el) return false;
+
+ const { path } = el.dataset;
+
+ return new Vue({
+ el,
+ apolloProvider,
+ provide: { path },
+ render(createElement) {
+ return createElement(MergeTopics);
+ },
+ });
+};
diff --git a/app/assets/javascripts/projects/settings/topics/queries/project_topics_search.query.graphql b/app/assets/javascripts/graphql_shared/queries/project_topics_search.query.graphql
similarity index 100%
rename from app/assets/javascripts/projects/settings/topics/queries/project_topics_search.query.graphql
rename to app/assets/javascripts/graphql_shared/queries/project_topics_search.query.graphql
diff --git a/app/assets/javascripts/pages/admin/topics/edit/index.js b/app/assets/javascripts/pages/admin/topics/edit/index.js
index f5e6d044865..b2cbd52fb27 100644
--- a/app/assets/javascripts/pages/admin/topics/edit/index.js
+++ b/app/assets/javascripts/pages/admin/topics/edit/index.js
@@ -2,7 +2,7 @@ import $ from 'jquery';
import GLForm from '~/gl_form';
import initFilePickers from '~/file_pickers';
import ZenMode from '~/zen_mode';
-import initRemoveAvatar from '~/admin/topics';
+import { initRemoveAvatar } from '~/admin/topics';
new GLForm($('.js-project-topic-form')); // eslint-disable-line no-new
initFilePickers();
diff --git a/app/assets/javascripts/pages/admin/topics/index.js b/app/assets/javascripts/pages/admin/topics/index.js
new file mode 100644
index 00000000000..ec0e11660d2
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/topics/index.js
@@ -0,0 +1,3 @@
+import { initMergeTopics } from '~/admin/topics';
+
+initMergeTopics();
diff --git a/app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue b/app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue
index 9c8de9bef2d..3d553e71f71 100644
--- a/app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue
+++ b/app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue
@@ -2,7 +2,7 @@
import { GlTokenSelector, GlAvatarLabeled } from '@gitlab/ui';
import { s__ } from '~/locale';
import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
-import searchProjectTopics from '../queries/project_topics_search.query.graphql';
+import searchProjectTopics from '~/graphql_shared/queries/project_topics_search.query.graphql';
export default {
components: {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue
index b93f36a562e..9b46dbc7b31 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue
@@ -85,6 +85,9 @@ export default {
};
},
computed: {
+ collapseButtonLabel() {
+ return sprintf(this.isCollapsed ? __('Show details') : __('Hide details'));
+ },
statusIcon() {
return this.error ? EXTENSION_ICONS.failed : this.statusIconName;
},
@@ -106,9 +109,6 @@ export default {
this.isLoading = false;
},
methods: {
- collapseButtonLabel() {
- return sprintf(this.isCollapsed ? __('Show details') : __('Hide details'));
- },
toggleCollapsed() {
this.isCollapsed = !this.isCollapsed;
},
diff --git a/app/controllers/admin/topics_controller.rb b/app/controllers/admin/topics_controller.rb
index 69bcfdf4791..c3b1c6793ad 100644
--- a/app/controllers/admin/topics_controller.rb
+++ b/app/controllers/admin/topics_controller.rb
@@ -56,9 +56,8 @@ class Admin::TopicsController < Admin::ApplicationController
end
message = _('Topic %{source_topic} was successfully merged into topic %{target_topic}.')
- redirect_to admin_topics_path,
- status: :found,
- notice: message % { source_topic: source_topic.name, target_topic: target_topic.name }
+ flash[:toast] = message % { source_topic: source_topic.name, target_topic: target_topic.name }
+ redirect_to admin_topics_path, status: :found
end
private
diff --git a/app/models/issue.rb b/app/models/issue.rb
index ea679fbbd39..30dc29fa2d8 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -716,7 +716,7 @@ class Issue < ApplicationRecord
end
def record_create_action
- Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_created_action(author: author)
+ Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_created_action(author: author, project: project)
end
# Returns `true` if this Issue is visible to everybody.
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 3c06e1aa983..f75acb98cd4 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -1318,7 +1318,6 @@ class MergeRequest < ApplicationRecord
# running `ReferenceExtractor` on each of them separately.
# This optimization does not apply to issues from external sources.
def cache_merge_request_closes_issues!(current_user = self.author)
- return unless project.issues_enabled?
return if closed? || merged?
transaction do
diff --git a/app/models/resource_state_event.rb b/app/models/resource_state_event.rb
index 689a9d8a8ae..5588897e792 100644
--- a/app/models/resource_state_event.rb
+++ b/app/models/resource_state_event.rb
@@ -32,9 +32,9 @@ class ResourceStateEvent < ResourceEvent
case state
when 'closed'
- issue_usage_counter.track_issue_closed_action(author: user)
+ issue_usage_counter.track_issue_closed_action(author: user, project: issue.project)
when 'reopened'
- issue_usage_counter.track_issue_reopened_action(author: user)
+ issue_usage_counter.track_issue_reopened_action(author: user, project: issue.project)
else
# no-op, nothing to do, not a state we're tracking
end
diff --git a/app/services/design_management/delete_designs_service.rb b/app/services/design_management/delete_designs_service.rb
index 9ed03a994c4..921c904d8de 100644
--- a/app/services/design_management/delete_designs_service.rb
+++ b/app/services/design_management/delete_designs_service.rb
@@ -16,7 +16,8 @@ module DesignManagement
version = delete_designs!
EventCreateService.new.destroy_designs(designs, current_user)
- Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_designs_removed_action(author: current_user)
+ Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_designs_removed_action(author: current_user,
+ project: project)
TodosDestroyer::DestroyedDesignsWorker.perform_async(designs.map(&:id))
success(version: version)
diff --git a/app/services/design_management/save_designs_service.rb b/app/services/design_management/save_designs_service.rb
index a1fce45434b..64537293e65 100644
--- a/app/services/design_management/save_designs_service.rb
+++ b/app/services/design_management/save_designs_service.rb
@@ -131,9 +131,11 @@ module DesignManagement
def track_usage_metrics(action)
if action == :update
- ::Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_designs_modified_action(author: current_user)
+ ::Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_designs_modified_action(author: current_user,
+ project: project)
else
- ::Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_designs_added_action(author: current_user)
+ ::Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_designs_added_action(author: current_user,
+ project: project)
end
::Gitlab::UsageDataCounters::DesignsCounter.count(action)
diff --git a/app/services/system_notes/issuables_service.rb b/app/services/system_notes/issuables_service.rb
index a6c14027ddf..45842760e13 100644
--- a/app/services/system_notes/issuables_service.rb
+++ b/app/services/system_notes/issuables_service.rb
@@ -303,7 +303,7 @@ module SystemNotes
cross_reference = noteable_ref.to_reference(project)
body = "moved #{direction} #{cross_reference}"
- issue_activity_counter.track_issue_moved_action(author: author) if noteable.is_a?(Issue)
+ issue_activity_counter.track_issue_moved_action(author: author, project: project) if noteable.is_a?(Issue)
create_note(NoteSummary.new(noteable, project, author, body, action: 'moved'))
end
diff --git a/app/views/admin/topics/index.html.haml b/app/views/admin/topics/index.html.haml
index 6485b8aa411..77823ed7058 100644
--- a/app/views/admin/topics/index.html.haml
+++ b/app/views/admin/topics/index.html.haml
@@ -1,16 +1,16 @@
- page_title _("Topics")
-= form_tag admin_topics_path, method: :get do |f|
- .gl-py-3.gl-display-flex.gl-flex-direction-column-reverse.gl-md-flex-direction-row.gl-border-b-solid.gl-border-gray-100.gl-border-b-1
- .gl-flex-grow-1.gl-mt-3.gl-md-mt-0
- .inline.gl-w-full.gl-md-w-auto
- - search = params.fetch(:search, nil)
- .search-field-holder
- = search_field_tag :search, search, class: "form-control gl-form-input search-text-input js-search-input", autofocus: true, spellcheck: false, placeholder: _('Search by name'), data: { qa_selector: 'topic_search_field' }
- = sprite_icon('search', css_class: 'search-icon')
- .nav-controls
- = link_to new_admin_topic_path, class: "gl-button btn btn-confirm gl-w-full gl-md-w-auto" do
- = _('New topic')
+.top-area
+ .nav-controls.gl-w-full.gl-mt-3.gl-mb-3
+ = form_tag admin_topics_path, method: :get do |f|
+ - search = params.fetch(:search, nil)
+ .search-field-holder
+ = search_field_tag :search, search, class: "form-control gl-form-input search-text-input js-search-input", autofocus: true, spellcheck: false, placeholder: _('Search by name'), data: { qa_selector: 'topic_search_field' }
+ = sprite_icon('search', css_class: 'search-icon')
+ .gl-flex-grow-1
+ .js-merge-topics{ data: { path: merge_admin_topics_path } }
+ = link_to new_admin_topic_path, class: "gl-button btn btn-confirm gl-w-full gl-md-w-auto" do
+ = _('New topic')
%ul.content-list
= render partial: 'topic', collection: @topics
diff --git a/config/feature_flags/ops/database_async_index_destruction.yml b/config/feature_flags/ops/database_async_index_destruction.yml
deleted file mode 100644
index a100b3a40f2..00000000000
--- a/config/feature_flags/ops/database_async_index_destruction.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: database_async_index_destruction
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92328
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367964
-milestone: '15.3'
-type: ops
-group: group::database
-default_enabled: false
diff --git a/doc/api/settings.md b/doc/api/settings.md
index d11269113a1..bb799d3d2c3 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -276,7 +276,7 @@ listed in the descriptions of the relevant settings.
| `deactivate_dormant_users` | boolean | no | Enable [automatic deactivation of dormant users](../user/admin_area/moderate_users.md#automatically-deactivate-dormant-users). |
| `deactivate_dormant_users_period` | integer | no | Length of time (in days) after which a user is considered dormant. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336747) in GitLab 15.3. |
| `default_artifacts_expire_in` | string | no | Set the default expiration time for each job's artifacts. |
-| `default_branch_name` | string | no | [Instance-level custom initial branch name](../user/project/repository/branches/default.md#instance-level-custom-initial-branch-name) ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/225258) in GitLab 13.2). |
+| `default_branch_name` | string | no | [Instance-level custom initial branch name](../user/project/repository/branches/default.md#instance-level-custom-initial-branch-name). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/225258) in GitLab 13.2. |
| `default_branch_protection` | integer | no | Determine if developers can push to the default branch. Can take: `0` _(not protected, both users with the Developer role or Maintainer role can push new commits and force push)_, `1` _(partially protected, users with the Developer role or Maintainer role can push new commits, but cannot force push)_ or `2` _(fully protected, users with the Developer or Maintainer role cannot push new commits, but users with the Developer or Maintainer role can; no one can force push)_ as a parameter. Default is `2`. |
| `default_ci_config_path` | string | no | Default CI/CD configuration file and path for new projects (`.gitlab-ci.yml` if not set). |
| `default_group_visibility` | string | no | What visibility level new groups receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. |
@@ -291,7 +291,7 @@ listed in the descriptions of the relevant settings.
| `diff_max_patch_bytes` | integer | no | Maximum [diff patch size](../user/admin_area/diff_limits.md), in bytes. |
| `diff_max_files` | integer | no | Maximum [files in a diff](../user/admin_area/diff_limits.md). |
| `diff_max_lines` | integer | no | Maximum [lines in a diff](../user/admin_area/diff_limits.md). |
-| `disable_feed_token` | boolean | no | Disable display of RSS/Atom and calendar feed tokens ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/231493) in GitLab 13.7) |
+| `disable_feed_token` | boolean | no | Disable display of RSS/Atom and calendar feed tokens. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/231493) in GitLab 13.7. |
| `disabled_oauth_sign_in_sources` | array of strings | no | Disabled OAuth sign-in sources. |
| `dns_rebinding_protection_enabled` | boolean | no | Enforce DNS rebinding attack protection. |
| `domain_denylist_enabled` | boolean | no | (**If enabled, requires:** `domain_denylist`) Allows blocking sign-ups from emails from specific domains. |
@@ -377,7 +377,7 @@ listed in the descriptions of the relevant settings.
| `max_artifacts_size` | integer | no | Maximum artifacts size in MB. |
| `max_attachment_size` | integer | no | Limit attachment size in MB. |
| `max_export_size` | integer | no | Maximum export size in MB. 0 for unlimited. Default = 0 (unlimited). |
-| `max_import_size` | integer | no | Maximum import size in MB. 0 for unlimited. Default = 0 (unlimited) [Modified](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50MB to 0 in GitLab 13.8. |
+| `max_import_size` | integer | no | Maximum import size in MB. 0 for unlimited. Default = 0 (unlimited). [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50 MB to 0 in GitLab 13.8. |
| `max_pages_size` | integer | no | Maximum size of pages repositories in MB. |
| `max_personal_access_token_lifetime` **(ULTIMATE SELF)** | integer | no | Maximum allowable lifetime for access tokens in days. |
| `max_ssh_key_lifetime` **(ULTIMATE SELF)** | integer | no | Maximum allowable lifetime for SSH keys in days. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1007) in GitLab 14.6. |
diff --git a/doc/development/database/adding_database_indexes.md b/doc/development/database/adding_database_indexes.md
index 8abd7c8298e..774703bd54f 100644
--- a/doc/development/database/adding_database_indexes.md
+++ b/doc/development/database/adding_database_indexes.md
@@ -328,8 +328,8 @@ asynchronously during weekend hours. Due to generally lower traffic and fewer de
index destruction can proceed at a lower level of risk.
1. [Schedule the index to be removed](#schedule-the-index-to-be-removed).
-1. [Verify the MR was deployed and the index exists in production](#verify-the-mr-was-deployed-and-the-index-exists-in-production).
-1. [Add a migration to create the index synchronously](#add-a-migration-to-create-the-index-synchronously).
+1. [Verify the MR was deployed and the index exists in production](#verify-the-mr-was-deployed-and-the-index-no-longer-exists-in-production).
+1. [Add a migration to destroy the index synchronously](#add-a-migration-to-destroy-the-index-synchronously).
### Schedule the index to be removed
@@ -357,21 +357,21 @@ to remove them.
You must test the database index changes locally before creating a merge request.
-### Verify the MR was deployed and the index exists in production
+### Verify the MR was deployed and the index no longer exists in production
You can verify if the MR was deployed to GitLab.com with
`/chatops run auto_deploy status `. To verify the existence of
the index, you can:
- Use a meta-command in `#database-lab`, for example: `\d `.
- - Make sure the index is not [`invalid`](https://www.postgresql.org/docs/12/sql-createindex.html#:~:text=The%20psql%20%5Cd%20command%20will%20report%20such%20an%20index%20as%20INVALID).
+- Make sure the index no longer exists
- Ask someone in `#database` to check if the index exists.
- If you have access, you can verify directly on production or in a
production clone.
### Add a migration to destroy the index synchronously
-After you verify the index exists in the production database, create a second
+After you verify the index no longer exists in the production database, create a second
merge request that removes the index synchronously. The schema changes must be
updated and committed to `structure.sql` in this second merge request.
The synchronous migration results in a no-op on GitLab.com, but you should still add the
@@ -379,7 +379,7 @@ migration as expected for other installations. For example, to
create the second migration for the previous asynchronous example:
**WARNING:**
-Verify that the index no longer exist in production before merging a second migration with `remove_concurrent_index_by_name`.
+Verify that the index no longer exists in production before merging a second migration with `remove_concurrent_index_by_name`.
If the second migration is deployed before the index has been destroyed,
the index is destroyed synchronously when the second migration executes.
@@ -395,7 +395,7 @@ def up
end
def down
- add_concurrent_index :ci_builds, :some_column, INDEX_NAME
+ add_concurrent_index :ci_builds, :some_column, name: INDEX_NAME
end
```
@@ -403,7 +403,7 @@ end
To test changes for removing an index, use the asynchronous index helpers on your local environment:
-1. Enable the feature flags by running `Feature.enable(:database_async_index_destruction)` and `Feature.enable(:database_reindexing)` in the Rails console.
+1. Enable the feature flags by running `Feature.enable(:database_reindexing)` in the Rails console.
1. Run `bundle exec rails db:migrate` which should create an entry in the `postgres_async_indexes` table.
1. Run `bundle exec rails gitlab:db:reindex` destroy the index asynchronously.
1. To verify the index, open the PostgreSQL console by using the [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/howto/postgresql.md)
diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md
index c689d61ad68..fd42a8dbbf0 100644
--- a/doc/user/admin_area/index.md
+++ b/doc/user/admin_area/index.md
@@ -288,6 +288,8 @@ To edit a topic, select **Edit** in that topic's row.
To remove a topic, select **Remove** in that topic's row.
+To remove a topic and move all assigned projects to another topic, select **Merge topics**.
+
To search for topics by name, enter your criteria in the search box. The topic search is case
insensitive and applies partial matching.
diff --git a/doc/user/project/issues/managing_issues.md b/doc/user/project/issues/managing_issues.md
index 40e5a6d6a92..adc680a3d98 100644
--- a/doc/user/project/issues/managing_issues.md
+++ b/doc/user/project/issues/managing_issues.md
@@ -458,7 +458,8 @@ The default issue closing pattern regex:
#### Disable automatic issue closing
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/19754) in GitLab 12.7.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/19754) in GitLab 12.7.
+> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/240922) in GitLab 15.4: The referenced issue's project setting is checked instead of the project of the commit or merge request.
You can disable the automatic issue closing feature on a per-project basis
in the [project's settings](../settings/index.md).
@@ -472,20 +473,15 @@ To disable automatic issue closing:
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Default branch**.
-1. Select **Auto-close referenced issues on default branch**.
+1. Clear the **Auto-close referenced issues on default branch** checkbox.
1. Select **Save changes**.
Referenced issues are still displayed, but are not closed automatically.
-The automatic issue closing is disabled by default in a project if the project has the issue tracker
-disabled. If you want to enable automatic issue closing, make sure to
-[enable GitLab Issues](../settings/index.md#configure-project-visibility-features-and-permissions).
-
Changing this setting applies only to new merge requests or commits. Already
closed issues remain as they are.
-If issue tracking is enabled, disabling automatic issue closing only applies to merge requests
-attempting to automatically close issues in the same project.
-Merge requests in other projects can still close another project's issues.
+Disabling automatic issue closing only applies to issues in the project where the setting was disabled.
+Merge requests and commits in this project can still close another project's issues.
#### Customize the issue closing pattern **(FREE SELF)**
diff --git a/lib/gitlab/closing_issue_extractor.rb b/lib/gitlab/closing_issue_extractor.rb
index 8e624215065..7104de2a3c3 100644
--- a/lib/gitlab/closing_issue_extractor.rb
+++ b/lib/gitlab/closing_issue_extractor.rb
@@ -17,7 +17,6 @@ module Gitlab
def closed_by_message(message)
return [] if message.nil?
- return [] unless @project.autoclose_referenced_issues
closing_statements = []
message.scan(ISSUE_CLOSING_REGEX) do
@@ -27,8 +26,9 @@ module Gitlab
@extractor.analyze(closing_statements.join(" "))
@extractor.issues.reject do |issue|
- # Don't extract issues from the project this project was forked from
- @extractor.project.forked_from?(issue.project)
+ @extractor.project.forked_from?(issue.project) ||
+ !issue.project.autoclose_referenced_issues ||
+ !issue.project.issues_enabled?
end
end
end
diff --git a/lib/gitlab/database/reindexing.rb b/lib/gitlab/database/reindexing.rb
index b96dffc99ac..aba45fcc57b 100644
--- a/lib/gitlab/database/reindexing.rb
+++ b/lib/gitlab/database/reindexing.rb
@@ -27,7 +27,7 @@ module Gitlab
# Hack: Before we do actual reindexing work, create async indexes
Gitlab::Database::AsyncIndexes.create_pending_indexes! if Feature.enabled?(:database_async_index_creation, type: :ops)
- Gitlab::Database::AsyncIndexes.drop_pending_indexes! if Feature.enabled?(:database_async_index_destruction, type: :ops)
+ Gitlab::Database::AsyncIndexes.drop_pending_indexes!
automatic_reindexing
end
diff --git a/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb b/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb
index 1ef0bcda7b2..fb45f871350 100644
--- a/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb
+++ b/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb
@@ -36,7 +36,8 @@ module Gitlab
ISSUE_COMMENT_REMOVED = 'g_project_management_issue_comment_removed'
class << self
- def track_issue_created_action(author:)
+ def track_issue_created_action(author:, project:)
+ track_snowplow_action(ISSUE_CREATED, author, project)
track_unique_action(ISSUE_CREATED, author)
end
@@ -65,11 +66,13 @@ module Gitlab
track_unique_action(ISSUE_MADE_VISIBLE, author)
end
- def track_issue_closed_action(author:)
+ def track_issue_closed_action(author:, project:)
+ track_snowplow_action(ISSUE_CLOSED, author, project)
track_unique_action(ISSUE_CLOSED, author)
end
- def track_issue_reopened_action(author:)
+ def track_issue_reopened_action(author:, project:)
+ track_snowplow_action(ISSUE_REOPENED, author, project)
track_unique_action(ISSUE_REOPENED, author)
end
@@ -88,7 +91,8 @@ module Gitlab
track_unique_action(ISSUE_CROSS_REFERENCED, author)
end
- def track_issue_moved_action(author:)
+ def track_issue_moved_action(author:, project:)
+ track_snowplow_action(ISSUE_MOVED, author, project)
track_unique_action(ISSUE_MOVED, author)
end
@@ -112,15 +116,18 @@ module Gitlab
track_unique_action(ISSUE_UNLOCKED, author)
end
- def track_issue_designs_added_action(author:)
+ def track_issue_designs_added_action(author:, project:)
+ track_snowplow_action(ISSUE_DESIGNS_ADDED, author, project)
track_unique_action(ISSUE_DESIGNS_ADDED, author)
end
- def track_issue_designs_modified_action(author:)
+ def track_issue_designs_modified_action(author:, project:)
+ track_snowplow_action(ISSUE_DESIGNS_MODIFIED, author, project)
track_unique_action(ISSUE_DESIGNS_MODIFIED, author)
end
- def track_issue_designs_removed_action(author:)
+ def track_issue_designs_removed_action(author:, project:)
+ track_snowplow_action(ISSUE_DESIGNS_REMOVED, author, project)
track_unique_action(ISSUE_DESIGNS_REMOVED, author)
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index a35e51ac42a..783a99f9308 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -24876,6 +24876,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -41075,6 +41099,15 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb
index 4614eced300..20705c1028f 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb
@@ -98,10 +98,6 @@ module QA
QA::Runtime::Logger.debug('Visiting the secondary Geo site')
QA::Flow::Login.while_signed_in(address: :geo_secondary) do
- EE::Page::Main::Banner.perform do |banner|
- expect(banner).to have_secondary_read_only_banner
- end
-
Page::Main::Menu.perform(&:go_to_projects)
Page::Dashboard::Projects.perform do |dashboard|
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
index ddfed73e2ca..63e0ee4a251 100644
--- a/spec/features/projects/settings/repository_settings_spec.rb
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -209,7 +209,9 @@ RSpec.describe 'Projects > Settings > Repository settings' do
end
it 'generates an SSH public key on submission', :js do
- fill_in 'url', with: 'ssh://user@localhost/project.git'
+ fill_in 'url', with: ssh_url
+ expect(page).to have_css(".js-mirror-url-hidden[value=\"#{ssh_url}\"]", visible: false)
+
select 'SSH public key', from: 'Authentication method'
select_direction
diff --git a/spec/frontend/admin/topics/components/topic_select_spec.js b/spec/frontend/admin/topics/components/topic_select_spec.js
new file mode 100644
index 00000000000..fae5ca1ef5f
--- /dev/null
+++ b/spec/frontend/admin/topics/components/topic_select_spec.js
@@ -0,0 +1,91 @@
+import { GlAvatarLabeled, GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import TopicSelect from '~/admin/topics/components/topic_select.vue';
+
+const mockTopics = [
+ { id: 1, name: 'topic1', title: 'Topic 1', avatarUrl: 'avatar.com/topic1.png' },
+ { id: 2, name: 'GitLab', title: 'GitLab', avatarUrl: 'avatar.com/GitLab.png' },
+];
+
+describe('TopicSelect', () => {
+ let wrapper;
+
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
+ const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
+
+ function createComponent(props = {}) {
+ wrapper = shallowMount(TopicSelect, {
+ propsData: props,
+ data() {
+ return {
+ topics: mockTopics,
+ search: '',
+ };
+ },
+ mocks: {
+ $apollo: {
+ queries: {
+ topics: { loading: false },
+ },
+ },
+ },
+ });
+ }
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('mounts', () => {
+ createComponent();
+
+ expect(wrapper.exists()).toBe(true);
+ });
+
+ it('`selectedTopic` prop defaults to `{}`', () => {
+ createComponent();
+
+ expect(wrapper.props('selectedTopic')).toEqual({});
+ });
+
+ it('`labelText` prop defaults to `null`', () => {
+ createComponent();
+
+ expect(wrapper.props('labelText')).toBe(null);
+ });
+
+ it('renders default text if no selected topic', () => {
+ createComponent();
+
+ expect(findDropdown().props('text')).toBe('Select a topic');
+ });
+
+ it('renders selected topic', () => {
+ createComponent({ selectedTopic: mockTopics[0] });
+
+ expect(findDropdown().props('text')).toBe('topic1');
+ });
+
+ it('renders label', () => {
+ createComponent({ labelText: 'my label' });
+
+ expect(wrapper.find('label').text()).toBe('my label');
+ });
+
+ it('renders dropdown items', () => {
+ createComponent();
+
+ const dropdownItems = findAllDropdownItems();
+
+ expect(dropdownItems.at(0).find(GlAvatarLabeled).props('label')).toBe('Topic 1');
+ expect(dropdownItems.at(1).find(GlAvatarLabeled).props('label')).toBe('GitLab');
+ });
+
+ it('emits `click` event when topic selected', () => {
+ createComponent();
+
+ findAllDropdownItems().at(0).vm.$emit('click');
+
+ expect(wrapper.emitted('click')).toEqual([[mockTopics[0]]]);
+ });
+});
diff --git a/spec/frontend/diffs/components/diff_gutter_avatars_spec.js b/spec/frontend/diffs/components/diff_gutter_avatars_spec.js
index c18f0b721da..e95d0656893 100644
--- a/spec/frontend/diffs/components/diff_gutter_avatars_spec.js
+++ b/spec/frontend/diffs/components/diff_gutter_avatars_spec.js
@@ -40,7 +40,7 @@ describe('DiffGutterAvatars', () => {
findCollapseButton().trigger('click');
await nextTick();
- expect(wrapper.emitted().toggleLineDiscussions).toBeTruthy();
+ expect(wrapper.emitted().toggleLineDiscussions).toBeDefined();
});
});
@@ -69,14 +69,14 @@ describe('DiffGutterAvatars', () => {
findUserAvatars().at(0).trigger('click');
await nextTick();
- expect(wrapper.emitted().toggleLineDiscussions).toBeTruthy();
+ expect(wrapper.emitted().toggleLineDiscussions).toBeDefined();
});
it('should emit toggleDiscussions event on more count text click', async () => {
findMoreCount().trigger('click');
await nextTick();
- expect(wrapper.emitted().toggleLineDiscussions).toBeTruthy();
+ expect(wrapper.emitted().toggleLineDiscussions).toBeDefined();
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js b/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js
index 2a760ef64f7..f6af7b31051 100644
--- a/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js
@@ -155,6 +155,21 @@ describe('MR Widget', () => {
});
describe('handle collapse toggle', () => {
+ it('displays the toggle button correctly', () => {
+ createComponent({
+ propsData: {
+ isCollapsible: true,
+ fetchCollapsedData: () => Promise.resolve(),
+ },
+ slots: {
+ content: 'More complex content',
+ },
+ });
+
+ expect(findToggleButton().attributes('title')).toBe('Show details');
+ expect(findToggleButton().attributes('aria-label')).toBe('Show details');
+ });
+
it('does not display the content slot until toggle is clicked', async () => {
createComponent({
propsData: {
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index 279486aa2a1..1422f83c629 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::ClosingIssueExtractor do
let_it_be_with_reload(:project) { create(:project) }
- let_it_be(:project2) { create(:project) }
+ let_it_be_with_reload(:project2) { create(:project) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:issue2) { create(:issue, project: project2) }
@@ -335,6 +335,17 @@ RSpec.describe Gitlab::ClosingIssueExtractor do
end
end
+ context 'when target project has autoclose issues disabled' do
+ before do
+ project2.update!(autoclose_referenced_issues: false)
+ end
+
+ it 'omits the issue reference' do
+ message = "Closes #{cross_reference}"
+ expect(subject.closed_by_message(message)).to be_empty
+ end
+ end
+
context "with an invalid URL" do
it do
message = "Closes https://google.com#{urls.project_issue_path(issue2.project, issue2)}"
@@ -443,14 +454,19 @@ RSpec.describe Gitlab::ClosingIssueExtractor do
end
context "with autoclose referenced issues disabled" do
- before do
+ before_all do
project.update!(autoclose_referenced_issues: false)
end
- it do
+ it 'excludes same project references' do
message = "Awesome commit (Closes #{reference})"
expect(subject.closed_by_message(message)).to eq([])
end
+
+ it 'includes issues from other projects with autoclose enabled' do
+ message = "Closes #{cross_reference}"
+ expect(subject.closed_by_message(message)).to eq([issue2])
+ end
end
end
diff --git a/spec/lib/gitlab/database/reindexing_spec.rb b/spec/lib/gitlab/database/reindexing_spec.rb
index 495e953f993..4c98185e780 100644
--- a/spec/lib/gitlab/database/reindexing_spec.rb
+++ b/spec/lib/gitlab/database/reindexing_spec.rb
@@ -46,25 +46,11 @@ RSpec.describe Gitlab::Database::Reindexing do
end
end
- context 'when async index destruction is enabled' do
- it 'executes async index destruction prior to any reindexing actions' do
- stub_feature_flags(database_async_index_destruction: true)
+ it 'executes async index destruction prior to any reindexing actions' do
+ expect(Gitlab::Database::AsyncIndexes).to receive(:drop_pending_indexes!).ordered.exactly(databases_count).times
+ expect(described_class).to receive(:automatic_reindexing).ordered.exactly(databases_count).times
- expect(Gitlab::Database::AsyncIndexes).to receive(:drop_pending_indexes!).ordered.exactly(databases_count).times
- expect(described_class).to receive(:automatic_reindexing).ordered.exactly(databases_count).times
-
- described_class.invoke
- end
- end
-
- context 'when async index destruction is disabled' do
- it 'does not execute async index destruction' do
- stub_feature_flags(database_async_index_destruction: false)
-
- expect(Gitlab::Database::AsyncIndexes).not_to receive(:drop_pending_indexes!)
-
- described_class.invoke
- end
+ described_class.invoke
end
context 'calls automatic reindexing' do
diff --git a/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb
index a8e5498444c..ef2efb82659 100644
--- a/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb
@@ -65,7 +65,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue created actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_CREATED }
def track_action(params)
@@ -75,7 +75,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue closed actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_CLOSED }
def track_action(params)
@@ -85,7 +85,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue reopened actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_REOPENED }
def track_action(params)
@@ -125,7 +125,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue moved actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_MOVED }
def track_action(params)
@@ -195,7 +195,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue designs added actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_DESIGNS_ADDED }
def track_action(params)
@@ -205,7 +205,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue designs modified actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_DESIGNS_MODIFIED }
def track_action(params)
@@ -215,7 +215,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue designs removed actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_DESIGNS_REMOVED }
def track_action(params)
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 7faa8ea053c..5b35fdd1eb9 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -283,6 +283,14 @@ RSpec.describe Issue do
create(:issue)
end
+
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:issue) { create(:issue) }
+ let(:project) { issue.project }
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_CREATED }
+ let(:user) { issue.author }
+ subject(:service_action) { issue }
+ end
end
context 'issue namespace' do
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 19026a4772d..19e9e2493a3 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -884,6 +884,16 @@ RSpec.describe MergeRequest, factory_default: :keep do
expect { subject.cache_merge_request_closes_issues!(subject.author) }
.not_to change(subject.merge_requests_closing_issues, :count)
end
+
+ it 'caches issues from another project with issues enabled' do
+ project = create(:project, :public, issues_enabled: true)
+ issue = create(:issue, project: project)
+ commit = double('commit1', safe_message: "Fixes #{issue.to_reference(full: true)}")
+ allow(subject).to receive(:commits).and_return([commit])
+
+ expect { subject.cache_merge_request_closes_issues!(subject.author) }
+ .to change(subject.merge_requests_closing_issues, :count).by(1)
+ end
end
end
diff --git a/spec/models/resource_state_event_spec.rb b/spec/models/resource_state_event_spec.rb
index 2b4898b750a..f84634bd220 100644
--- a/spec/models/resource_state_event_spec.rb
+++ b/spec/models/resource_state_event_spec.rb
@@ -42,16 +42,44 @@ RSpec.describe ResourceStateEvent, type: :model do
context 'callbacks' do
describe '#issue_usage_metrics' do
- it 'tracks closed issues' do
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_closed_action)
+ describe 'when an issue is closed' do
+ subject(:close_issue) do
+ create(described_class.name.underscore.to_sym, issue: issue,
+ state: described_class.states[:closed])
+ end
- create(described_class.name.underscore.to_sym, issue: issue, state: described_class.states[:closed])
+ it 'tracks closed issues' do
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_closed_action)
+
+ close_issue
+ end
+
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_CLOSED }
+ let(:project) { issue.project }
+ let(:user) { issue.author }
+ subject(:service_action) { close_issue }
+ end
end
- it 'tracks reopened issues' do
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_reopened_action)
+ describe 'when an issue is reopened' do
+ subject(:reopen_issue) do
+ create(described_class.name.underscore.to_sym, issue: issue,
+ state: described_class.states[:reopened])
+ end
- create(described_class.name.underscore.to_sym, issue: issue, state: described_class.states[:reopened])
+ it 'tracks reopened issues' do
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_reopened_action)
+
+ reopen_issue
+ end
+
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_REOPENED }
+ let(:project) { issue.project }
+ let(:user) { issue.author }
+ subject(:service_action) { reopen_issue }
+ end
end
it 'does not track merge requests' do
diff --git a/spec/models/work_item_spec.rb b/spec/models/work_item_spec.rb
index b2c07ebc44e..341f9a9c60f 100644
--- a/spec/models/work_item_spec.rb
+++ b/spec/models/work_item_spec.rb
@@ -59,6 +59,14 @@ RSpec.describe WorkItem do
create(:work_item)
end
+
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:work_item) { create(:work_item) }
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_CREATED }
+ let(:project) { work_item.project }
+ let(:user) { work_item.author }
+ subject(:service_action) { work_item }
+ end
end
context 'work item namespace' do
diff --git a/spec/services/design_management/delete_designs_service_spec.rb b/spec/services/design_management/delete_designs_service_spec.rb
index a0e049ea42a..48e53a92758 100644
--- a/spec/services/design_management/delete_designs_service_spec.rb
+++ b/spec/services/design_management/delete_designs_service_spec.rb
@@ -126,7 +126,8 @@ RSpec.describe DesignManagement::DeleteDesignsService do
end
it 'updates UsageData for removed designs' do
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_designs_removed_action).with(author: user)
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_designs_removed_action)
+ .with(author: user, project: project)
run_service
end
@@ -171,6 +172,11 @@ RSpec.describe DesignManagement::DeleteDesignsService do
run_service
end
+
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_DESIGNS_REMOVED }
+ subject(:service_action) { run_service }
+ end
end
context 'more than one design is passed' do
diff --git a/spec/services/design_management/save_designs_service_spec.rb b/spec/services/design_management/save_designs_service_spec.rb
index b76c91fbac9..c69df5f2eb9 100644
--- a/spec/services/design_management/save_designs_service_spec.rb
+++ b/spec/services/design_management/save_designs_service_spec.rb
@@ -106,7 +106,8 @@ RSpec.describe DesignManagement::SaveDesignsService do
it 'creates a commit, an event in the activity stream and updates the creation count', :aggregate_failures do
counter = Gitlab::UsageDataCounters::DesignsCounter
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_designs_added_action).with(author: user)
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_designs_added_action)
+ .with(author: user, project: project)
expect { run_service }
.to change { Event.count }.by(1)
@@ -119,6 +120,11 @@ RSpec.describe DesignManagement::SaveDesignsService do
)
end
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_DESIGNS_ADDED }
+ subject(:service_action) { run_service }
+ end
+
it 'can run the same command in parallel' do
parellism = 4
@@ -206,11 +212,17 @@ RSpec.describe DesignManagement::SaveDesignsService do
end
it 'updates UsageData for changed designs' do
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_designs_modified_action).with(author: user)
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_designs_modified_action)
+ .with(author: user, project: project)
run_service
end
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_DESIGNS_MODIFIED }
+ subject(:service_action) { run_service }
+ end
+
it 'records the correct events' do
counter = Gitlab::UsageDataCounters::DesignsCounter
expect { run_service }