diff --git a/.rubocop_todo/layout/argument_alignment.yml b/.rubocop_todo/layout/argument_alignment.yml
index ca7dba5c8db..5a145708439 100644
--- a/.rubocop_todo/layout/argument_alignment.yml
+++ b/.rubocop_todo/layout/argument_alignment.yml
@@ -1058,29 +1058,6 @@ Layout/ArgumentAlignment:
- 'ee/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb'
- 'ee/spec/features/projects/milestones/milestone_spec.rb'
- 'ee/spec/features/projects/security/user_views_security_configuration_spec.rb'
- - 'ee/spec/features/projects/settings/ee/repository_mirrors_settings_spec.rb'
- - 'ee/spec/features/projects/settings/merge_requests/user_manages_approval_settings_spec.rb'
- - 'ee/spec/features/projects/settings/merge_requests/user_manages_merge_pipelines_spec.rb'
- - 'ee/spec/features/projects/settings/merge_requests/user_manages_merge_requests_template_spec.rb'
- - 'ee/spec/features/projects/settings/merge_requests/user_manages_merge_trains_spec.rb'
- - 'ee/spec/features/projects/settings/user_manages_approval_settings_spec.rb'
- - 'ee/spec/features/projects/settings/user_manages_merge_requests_template_spec.rb'
- - 'ee/spec/features/registrations/email_confirmation_spec.rb'
- - 'ee/spec/features/registrations/saas/standard_flow_company_creating_project_spec.rb'
- - 'ee/spec/features/registrations/saas/standard_flow_company_joining_project_spec.rb'
- - 'ee/spec/features/registrations/saas/standard_flow_just_me_creating_project_spec.rb'
- - 'ee/spec/features/registrations/saas/standard_flow_just_me_importing_project_spec.rb'
- - 'ee/spec/features/registrations/saas/standard_flow_just_me_joining_project_spec.rb'
- - 'ee/spec/features/registrations/saas/subscription_flow_paid_plan_spec.rb'
- - 'ee/spec/features/registrations/saas/trial_flow_company_creating_project_spec.rb'
- - 'ee/spec/features/registrations/saas/trial_flow_company_importing_project_spec.rb'
- - 'ee/spec/features/registrations/saas/trial_flow_just_me_creating_project_spec.rb'
- - 'ee/spec/features/registrations/saas/trial_flow_just_me_importing_project_spec.rb'
- - 'ee/spec/features/registrations/sign_up_with_trial_from_external_site_without_confirmation_spec.rb'
- - 'ee/spec/features/search/elastic/global_search_spec.rb'
- - 'ee/spec/features/search/elastic/group_search_spec.rb'
- - 'ee/spec/features/security/project/discover_spec.rb'
- - 'ee/spec/features/users/identity_verification_spec.rb'
- 'ee/spec/frontend/fixtures/dora/metrics.rb'
- 'ee/spec/frontend/fixtures/oncall_schedule.rb'
- 'ee/spec/graphql/ee/mutations/boards/lists/create_spec.rb'
@@ -1570,22 +1547,6 @@ Layout/ArgumentAlignment:
- 'spec/components/previews/pajamas/alert_component_preview.rb'
- 'spec/components/previews/pajamas/banner_component_preview.rb'
- 'spec/components/previews/pajamas/button_component_preview.rb'
- - 'spec/features/admin/admin_mode/login_spec.rb'
- - 'spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb'
- - 'spec/features/atom/issues_spec.rb'
- - 'spec/features/atom/merge_requests_spec.rb'
- - 'spec/features/atom/users_spec.rb'
- - 'spec/features/boards/issue_ordering_spec.rb'
- - 'spec/features/boards/multi_select_spec.rb'
- - 'spec/features/boards/sidebar_assignee_spec.rb'
- - 'spec/features/calendar_spec.rb'
- - 'spec/features/clusters/cluster_health_dashboard_spec.rb'
- - 'spec/features/commits_spec.rb'
- - 'spec/features/dashboard/activity_spec.rb'
- - 'spec/features/dashboard/datetime_on_tooltips_spec.rb'
- - 'spec/features/dashboard/merge_requests_spec.rb'
- - 'spec/features/dashboard/todos/todos_sorting_spec.rb'
- - 'spec/features/dashboard/todos/todos_spec.rb'
- 'spec/features/error_tracking/user_filters_errors_by_status_spec.rb'
- 'spec/features/error_tracking/user_searches_sentry_errors_spec.rb'
- 'spec/features/error_tracking/user_sees_error_details_spec.rb'
diff --git a/app/assets/javascripts/branches/components/delete_merged_branches.vue b/app/assets/javascripts/branches/components/delete_merged_branches.vue
index 117c15be907..50fe610d335 100644
--- a/app/assets/javascripts/branches/components/delete_merged_branches.vue
+++ b/app/assets/javascripts/branches/components/delete_merged_branches.vue
@@ -69,7 +69,7 @@ export default {
this.openModal();
},
extraAttrs: {
- 'data-qa-selector': 'delete_merged_branches_button',
+ 'data-testid': 'delete-merged-branches-button',
class: 'gl-text-red-500!',
},
},
@@ -102,12 +102,11 @@ export default {
category="tertiary"
no-caret
placement="right"
- data-qa-selector="delete_merged_branches_dropdown_button"
class="gl-display-none gl-md-display-block!"
:items="dropdownItems"
/>
{{ $options.i18n.deleteButtonText }}
-
-
- {{ $options.i18n.deleteFile }}
-
-
+
+
+ {{ $options.i18n.deleteFile }}
+
+
+
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index d839f2ffc51..ae8d230f356 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -11,9 +11,9 @@
= branch.name
= clipboard_button(text: branch.name, title: _("Copy branch name"))
- if is_default_branch
- = gl_badge_tag s_('DefaultBranchLabel|default'), { variant: :neutral, size: :sm }, { class: 'gl-ml-2', data: { qa_selector: 'badge_content' } }
+ = gl_badge_tag s_('DefaultBranchLabel|default'), { variant: :neutral, size: :sm }, { class: 'gl-ml-2' }
- if protected_branch?(@project, branch)
- = gl_badge_tag s_('Branches|protected'), { variant: :muted, size: :sm }, { class: 'gl-ml-2', data: { qa_selector: 'badge_content' } }
+ = gl_badge_tag s_('Branches|protected'), { variant: :muted, size: :sm }, { class: 'gl-ml-2' }
= render_if_exists 'projects/branches/diverged_from_upstream', branch: branch
@@ -39,7 +39,7 @@
.issuable-reference.gl-display-flex.gl-justify-content-end.gl-overflow-hidden
= gl_badge_tag issuable_reference(related_merge_request),
{ icon: mr_status[:icon], variant: mr_status[:variant], size: :md, href: merge_request_path(related_merge_request) },
- { class: 'gl-display-block gl-text-truncate', title: mr_status[:title], data: { toggle: 'tooltip', container: 'body', qa_selector: 'badge_content' } }
+ { class: 'gl-display-block gl-text-truncate', title: mr_status[:title], data: { toggle: 'tooltip', container: 'body' } }
- elsif mr_status.nil? && create_mr_button?(from: branch.name, source_project: @project)
= render Pajamas::ButtonComponent.new(icon: 'merge-request', href: create_mr_path(from: branch.name, source_project: @project), button_options: { class: 'has-tooltip', title: _('New merge request') }) do
diff --git a/db/post_migrate/20230619072744_schedule_index_events_on_project_id_and_id_desc_on_merged_action_for_removal.rb b/db/post_migrate/20230619072744_schedule_index_events_on_project_id_and_id_desc_on_merged_action_for_removal.rb
new file mode 100644
index 00000000000..b73ddfffad6
--- /dev/null
+++ b/db/post_migrate/20230619072744_schedule_index_events_on_project_id_and_id_desc_on_merged_action_for_removal.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class ScheduleIndexEventsOnProjectIdAndIdDescOnMergedActionForRemoval < Gitlab::Database::Migration[2.1]
+ INDEX_NAME = 'index_events_on_project_id_and_id_desc_on_merged_action'
+
+ # TODO: Index to be destroyed synchronously in https://gitlab.com/gitlab-org/gitlab/-/issues/415091
+
+ def up
+ prepare_async_index_removal :events, [:project_id, :id], order: { id: :desc },
+ where: "action = 7", name: INDEX_NAME
+ end
+
+ def down
+ unprepare_async_index :events, [:project_id, :id], order: { id: :desc },
+ where: "action = 7", name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20230619134106_prepare_index_for_vulnerability_reads_on_common_project_filters.rb b/db/post_migrate/20230619134106_prepare_index_for_vulnerability_reads_on_common_project_filters.rb
new file mode 100644
index 00000000000..60335f572d5
--- /dev/null
+++ b/db/post_migrate/20230619134106_prepare_index_for_vulnerability_reads_on_common_project_filters.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class PrepareIndexForVulnerabilityReadsOnCommonProjectFilters < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_project_vulnerability_reads_common_finder_query_desc'
+
+ def up
+ prepare_async_index :vulnerability_reads,
+ [:project_id, :state, :report_type, :severity, :vulnerability_id],
+ order: { severity: :desc, vulnerability_id: :desc },
+ name: INDEX_NAME
+ end
+
+ def down
+ unprepare_async_index_by_name :vulnerability_reads, INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20230619072744 b/db/schema_migrations/20230619072744
new file mode 100644
index 00000000000..ba71e5cd392
--- /dev/null
+++ b/db/schema_migrations/20230619072744
@@ -0,0 +1 @@
+6bf4fa6d2e43f1b589204f3b58323f32d9db2344882507e14bc487913cbe6f8e
\ No newline at end of file
diff --git a/db/schema_migrations/20230619134106 b/db/schema_migrations/20230619134106
new file mode 100644
index 00000000000..a23db7839fa
--- /dev/null
+++ b/db/schema_migrations/20230619134106
@@ -0,0 +1 @@
+248e7dabf83e225c5f5ee0de87e0842e8c3ec13f6098720830ce3b817a4d36a8
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index a43c8fa1238..045e024f94a 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -33596,7 +33596,7 @@ CREATE UNIQUE INDEX unique_index_for_project_pages_unique_domain ON project_sett
CREATE UNIQUE INDEX unique_index_on_system_note_metadata_id ON resource_link_events USING btree (system_note_metadata_id);
-CREATE UNIQUE INDEX unique_instance_audit_event_destination_namespace_id_and_name ON audit_events_instance_external_audit_event_destinations USING btree (name);
+CREATE UNIQUE INDEX unique_instance_audit_event_destination_name ON audit_events_instance_external_audit_event_destinations USING btree (name);
CREATE UNIQUE INDEX unique_merge_request_diff_llm_summaries_on_mr_diff_id ON merge_request_diff_llm_summaries USING btree (merge_request_diff_id);
diff --git a/doc/administration/integration/plantuml.md b/doc/administration/integration/plantuml.md
index 9c5e07eedaa..20c51aa398c 100644
--- a/doc/administration/integration/plantuml.md
+++ b/doc/administration/integration/plantuml.md
@@ -183,8 +183,8 @@ see the [Tomcat Documentation](https://tomcat.apache.org/tomcat-10.1-doc/index.h
1. Install and configure Tomcat 10:
```shell
- cd /tmp & wget https://dlcdn.apache.org/tomcat/tomcat-10/v10.1.9/bin/apache-tomcat-10.1.9.tar.gz
- sudo tar xzvf apache-tomcat-10*tar.gz -C /opt/tomcat --strip-components=1
+ wget https://dlcdn.apache.org/tomcat/tomcat-10/v10.1.9/bin/apache-tomcat-10.1.9.tar.gz -P /tmp
+ sudo tar xzvf /tmp/apache-tomcat-10*tar.gz -C /opt/tomcat --strip-components=1
sudo chown -R tomcat:tomcat /opt/tomcat/
sudo chmod -R u+x /opt/tomcat/bin
```
@@ -270,7 +270,8 @@ see the [Tomcat Documentation](https://tomcat.apache.org/tomcat-10.1-doc/index.h
1. Install PlantUML and copy the `.war` file:
```shell
- cd / & git clone https://github.com/plantuml/plantuml-server.git
+ cd /
+ git clone https://github.com/plantuml/plantuml-server.git
cd plantuml-server
mvn package
cp /plantuml-server/target/plantuml.war /opt/tomcat/webapps/plantuml.war
diff --git a/doc/api/settings.md b/doc/api/settings.md
index ab78b9b7c74..b318c00845d 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -115,9 +115,20 @@ Example response:
```
Users on [GitLab Premium or Ultimate](https://about.gitlab.com/pricing/) may also see
-the `group_owners_can_manage_default_branch_protection`, `file_template_project_id`, `delayed_project_deletion`,
-`delayed_group_deletion`, `default_project_deletion_protection`, `deletion_adjourned_period`, `disable_personal_access_tokens`, `geo_node_allowed_ips`,
-or the `security_policy_global_group_approvers_enabled` parameters.
+these parameters:
+
+- `group_owners_can_manage_default_branch_protection`
+- `file_template_project_id`
+- `geo_node_allowed_ips`
+- `geo_status_timeout`
+- `delayed_project_deletion`
+- `delayed_group_deletion`
+- `default_project_deletion_protection`
+- `deletion_adjourned_period`
+- `disable_personal_access_tokens`
+- `security_policy_global_group_approvers_enabled`
+- `delete_unconfirmed_users`
+- `unconfirmed_users_delete_after_days`
From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled,
the `delayed_project_deletion` and `delayed_group_deletion` attributes will not be exposed. These attributes will be removed in GitLab 16.0.
@@ -257,6 +268,8 @@ these parameters:
- `deletion_adjourned_period`
- `disable_personal_access_tokens`
- `security_policy_global_group_approvers_enabled`
+- `delete_unconfirmed_users`
+- `unconfirmed_users_delete_after_days`
From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled,
the `delayed_project_deletion` and `delayed_group_deletion` attributes will not be exposed. These attributes will be removed in GitLab 16.0.
@@ -328,6 +341,7 @@ listed in the descriptions of the relevant settings.
| `delayed_project_deletion` **(PREMIUM SELF)** | boolean | no | Enable delayed project deletion by default in new groups. Default is `false`. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), can only be enabled when `delayed_group_deletion` is true. From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled, this attribute has been removed. This attribute will be completely removed in GitLab 16.0. |
| `delayed_group_deletion` **(PREMIUM SELF)** | boolean | no | Enable delayed group deletion. Default is `true`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352959) in GitLab 15.0. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), disables and locks the group-level setting for delayed protect deletion when set to `false`. From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled, this attribute has been removed. This attribute will be completely removed in GitLab 16.0. |
| `default_project_deletion_protection` **(PREMIUM SELF)** | boolean | no | Enable default project deletion protection so only administrators can delete projects. Default is `false`. |
+| `delete_unconfirmed_users` **(PREMIUM SELF)** | boolean | no | Specifies whether users who have not confirmed their email should be deleted. Default is `false`. When set to `true`, unconfirmed users are deleted after `unconfirmed_users_delete_after_days` days. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352514) in GitLab 16.1. |
| `deletion_adjourned_period` **(PREMIUM SELF)** | integer | no | The number of days to wait before deleting a project or group that is marked for deletion. Value must be between `1` and `90`. Defaults to `7`. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), a hook on `deletion_adjourned_period` sets the period to `1` on every update, and sets both `delayed_project_deletion` and `delayed_group_deletion` to `false` if the period is `0`. |
| `diagramsnet_enabled` | boolean | no | (If enabled, requires `diagramsnet_url`) Enable [Diagrams.net integration](../administration/integration/diagrams_net.md). Default is `true`. |
| `diagramsnet_url` | string | required by: `diagramsnet_enabled` | The Diagrams.net instance URL for integration. |
@@ -534,6 +548,7 @@ listed in the descriptions of the relevant settings.
| `throttle_unauthenticated_web_requests_per_period` | integer | required by:
`throttle_unauthenticated_web_enabled` | Max requests per period per IP. |
| `time_tracking_limit_to_hours` | boolean | no | Limit display of time tracking units to hours. Default is `false`. |
| `two_factor_grace_period` | integer | required by: `require_two_factor_authentication` | Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication. |
+| `unconfirmed_users_delete_after_days` **(PREMIUM SELF)** | integer | no | Specifies how many days after sign-up to delete users who have not confirmed their email. Only applicable if `delete_unconfirmed_users` is set to `true`. Must be `1` or greater. Default is `7`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352514) in GitLab 16.1. |
| `unique_ips_limit_enabled` | boolean | no | (**If enabled, requires:** `unique_ips_limit_per_user` and `unique_ips_limit_time_window`) Limit sign in from multiple IPs. |
| `unique_ips_limit_per_user` | integer | required by: `unique_ips_limit_enabled` | Maximum number of IPs per user. |
| `unique_ips_limit_time_window` | integer | required by: `unique_ips_limit_enabled` | How many seconds an IP is counted towards the limit. |
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index db1ea0a3801..3d6fa87d809 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -872,20 +872,21 @@ If you want to reduce risk slightly, consider putting the migrations into a
second merge request after the application changes are merged. This approach
provides an opportunity to roll back.
-Removing the foreign key on the `projects` table:
+Removing the foreign key on the `projects` table using a non-transactional migration:
```ruby
# first migration file
+class RemovingForeignKeyMigrationClass < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
-def up
- with_lock_retries do
- remove_foreign_key :my_table, :projects
+ def up
+ with_lock_retries do
+ remove_foreign_key :my_table, :projects
+ end
end
-end
-def down
- with_lock_retries do
- add_foreign_key :my_table, :projects
+ def down
+ add_concurrent_foreign_key :my_table, :projects, column: COLUMN_NAME
end
end
```
@@ -894,17 +895,19 @@ Dropping the table:
```ruby
# second migration file
+class DroppingTableMigrationClass < Gitlab::Database::Migration[2.1]
+ def up
+ drop_table :my_table
+ end
-def up
- drop_table :my_table
-end
-
-def down
- # create_table ...
+ def down
+ # create_table with the same schema but without the removed foreign key ...
+ end
end
```
-After a table has been dropped, it should be added to the database dictionary, following the steps in the [database dictionary guide](database/database_dictionary.md#dropping-tables).
+After a table has been dropped, it should be added to the database dictionary, following the
+steps in the [database dictionary guide](database/database_dictionary.md#dropping-tables).
## Dropping a sequence
diff --git a/doc/development/software_design.md b/doc/development/software_design.md
index d4edbaa72be..33a6dfcb4a7 100644
--- a/doc/development/software_design.md
+++ b/doc/development/software_design.md
@@ -139,3 +139,165 @@ end
If classes that are defined into a namespace have a lot in common with classes in other namespaces,
chances are that these two namespaces are part of the same bounded context.
+
+## Taming Omniscient classes
+
+We must consider not adding new data and behavior to [omniscient classes](https://en.wikipedia.org/wiki/God_object) (also known as god objects).
+We consider `Project`, `User`, `MergeRequest`, `Ci::Pipeline` and any classes above 1000 LOC to be omniscient.
+
+Such classes are overloaded with responsibilities. New data and behavior can most of the time be added
+as a separate and dedicated class.
+
+Guidelines:
+
+- If you mostly need a reference to the object ID (for example `Project#id`) you could add a new model
+ that uses the foreign key or a thin wrapper around the object to add special behavior.
+- If you find out that by adding a method to the omniscient class you also end up adding a couple of other methods
+ (private or public) it's a sign that these methods should be encapsulated in a dedicated class.
+- It's temping to add a method to `Project` because that's the starting point of data and associations.
+ Try to define behavior in the bounded context where it belongs, not where the data (or some of it) is.
+ This helps creating facets of the omniscient object that are much more relevant in the bounded context than
+ having generic and overloaded objects which bring more coupling and complexity.
+
+### Example: Define a thin domain object around a generic model
+
+Instead of adding multiple methods to `User` because it has an association to `abuse_trust_scores`,
+try inverting the dependency.
+
+```ruby
+##
+# BAD: Behavior added to User object.
+class User
+ def spam_score
+ abuse_trust_scores.spamcheck.average(:score) || 0.0
+ end
+
+ def spammer?
+ # Warning sign: we use a constant that belongs to a specific bounded context!
+ spam_score > Abuse::TrustScore::SPAMCHECK_HAM_THRESHOLD
+ end
+
+ def telesign_score
+ abuse_trust_scores.telesign.recent_first.first&.score || 0.0
+ end
+
+ def arkose_global_score
+ abuse_trust_scores.arkose_global_score.recent_first.first&.score || 0.0
+ end
+
+ def arkose_custom_score
+ abuse_trust_scores.arkose_custom_score.recent_first.first&.score || 0.0
+ end
+end
+
+# Usage:
+user = User.find(1)
+user.spam_score
+user.telesign_score
+user.arkose_global_score
+```
+
+```ruby
+##
+# GOOD: Define a thin class that represents a user trust score
+class Abuse::UserTrustScore
+ def initialize(user)
+ @user = user
+ end
+
+ def spam
+ scores.spamcheck.average(:score) || 0.0
+ end
+
+ def spammer?
+ spam > Abuse::TrustScore::SPAMCHECK_HAM_THRESHOLD
+ end
+
+ def telesign
+ scores.telesign.recent_first.first&.score || 0.0
+ end
+
+ def arkose_global
+ scores.arkose_global_score.recent_first.first&.score || 0.0
+ end
+
+ def arkose_custom
+ scores.arkose_custom_score.recent_first.first&.score || 0.0
+ end
+
+ private
+
+ def scores
+ Abuse::TrustScore.for_user(@user)
+ end
+end
+
+# Usage:
+user = User.find(1)
+user_score = Abuse::UserTrustScore.new(user)
+user_score.spam
+user_score.spammer?
+user_score.telesign
+user_score.arkose_global
+```
+
+See a real example [merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117853#note_1423070054).
+
+### Example: Use Dependency Inversion to extract a domain concept
+
+```ruby
+##
+# BAD: methods related to integrations defined in Project.
+class Project
+ has_many :integrations
+
+ def find_or_initialize_integrations
+ # ...
+ end
+
+ def find_or_initialize_integration(name)
+ # ...
+ end
+
+ def disabled_integrations
+ # ...
+ end
+
+ def ci_integrations
+ # ...
+ end
+
+ # many more methods...
+end
+```
+
+```ruby
+##
+# GOOD: All logic related to Integrations is enclosed inside the `Integrations::`
+# bounded context.
+module Integrations
+ class ProjectIntegrations
+ def initialize(project)
+ @project = project
+ end
+
+ def all_integrations
+ @project.integrations # can still leverage caching of AR associations
+ end
+
+ def find_or_initialize(name)
+ # ...
+ end
+
+ def all_disabled
+ all_integrations.disabled
+ end
+
+ def all_ci
+ all_integrations.ci_integration
+ end
+ end
+end
+```
+
+Real example of [similar refactoring](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92985).
diff --git a/doc/topics/offline/quick_start_guide.md b/doc/topics/offline/quick_start_guide.md
index e51e015dddf..cc450fed56a 100644
--- a/doc/topics/offline/quick_start_guide.md
+++ b/doc/topics/offline/quick_start_guide.md
@@ -262,7 +262,7 @@ PKG_METADATA_MANIFEST_OUTPUT_FILE="/tmp/license_db_export_manifest.json"
PKG_METADATA_DOWNLOADS_OUTPUT_FILE="/tmp/license_db_object_links.tsv"
# Download the contents of the bucket
-curl --silent --show-error --request GET "https://storage.googleapis.com/storage/v1/b/prod-export-license-bucket-1a6c642fc4de57d4/o" > "$PKG_METADATA_MANIFEST_OUTPUT_FILE"
+curl --silent --show-error --request GET "https://storage.googleapis.com/storage/v1/b/prod-export-license-bucket-1a6c642fc4de57d4/o?maxResults=7500" > "$PKG_METADATA_MANIFEST_OUTPUT_FILE"
# Parse the links and names for the bucket objects and output them into a tsv file
jq -r '.items[] | [.name, .mediaLink] | @tsv' "$PKG_METADATA_MANIFEST_OUTPUT_FILE" > "$PKG_METADATA_DOWNLOADS_OUTPUT_FILE"
diff --git a/doc/update/index.md b/doc/update/index.md
index 0380f1a69ef..a7c9bdb0933 100644
--- a/doc/update/index.md
+++ b/doc/update/index.md
@@ -186,6 +186,15 @@ If you don't want any downtime, read how to [upgrade with zero downtime](zero_do
For a dynamic view of examples of supported upgrade paths, try the [Upgrade Path tool](https://gitlab-com.gitlab.io/support/toolbox/upgrade-path/) maintained by the [GitLab Support team](https://about.gitlab.com/handbook/support/#about-the-support-team). To share feedback and help improve the tool, create an issue or MR in the [upgrade-path project](https://gitlab.com/gitlab-com/support/toolbox/upgrade-path).
+Required upgrade stops are versions of GitLab that you must upgrade to before upgrading to later versions. Required upgrade stops allow required background
+migrations to finish.
+
+During GitLab 16.x, we are scheduling two or three required upgrade stops. We will give at least two milestones of notice when we
+schedule a required upgrade stop.
+
+The first planned required upgrade stop is scheduled for GitLab 16.3. If nothing is introduced requiring an upgrade stop, GitLab 16.3 will be treated as a
+regular upgrade.
+
Find where your version sits in the upgrade path below, and upgrade GitLab
accordingly, while also consulting the
[version-specific upgrade instructions](#version-specific-upgrading-instructions):
diff --git a/doc/user/admin_area/moderate_users.md b/doc/user/admin_area/moderate_users.md
index ee6d360ac1d..c357e16c967 100644
--- a/doc/user/admin_area/moderate_users.md
+++ b/doc/user/admin_area/moderate_users.md
@@ -200,6 +200,33 @@ When this feature is enabled, GitLab runs a job once a day to deactivate the dor
A maximum of 100,000 users can be deactivated per day.
+### Automatically delete unconfirmed users **(PREMIUM SELF)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352514) in GitLab 16.1 [with a flag](../../administration/feature_flags.md) named `delete_unconfirmed_users_setting`. Disabled by default.
+
+Prerequisites:
+
+- You must be an administrator.
+
+You can enable automatic deletion of users who both:
+
+- Never confirmed their email address.
+- Signed up for GitLab more than a specified number of days in the past.
+
+You can configure these settings using either the [Settings API](../../api/settings.md) or in a Rails console:
+
+```ruby
+ Gitlab::CurrentSettings.update(delete_unconfirmed_users: true)
+ Gitlab::CurrentSettings.update(unconfirmed_users_delete_after_days: 365)
+```
+
+When the `delete_unconfirmed_users` setting is enabled, GitLab runs a job once an hour to delete the unconfirmed users.
+The job only deletes users who signed up more than `unconfirmed_users_delete_after_days` days in the past.
+
+This job only runs when the `email_confirmation_setting` is set to `soft` or `hard`.
+
+A maximum of 240,000 users can be deleted per day.
+
### Activate a user
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22257) in GitLab 12.4.
diff --git a/doc/user/group/compliance_frameworks.md b/doc/user/group/compliance_frameworks.md
index 55d096a654e..7d81fc11bd0 100644
--- a/doc/user/group/compliance_frameworks.md
+++ b/doc/user/group/compliance_frameworks.md
@@ -11,9 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
You can create a compliance framework that is a label to identify that your project has certain compliance
requirements or needs additional oversight. The label can optionally enforce
-[compliance pipeline configuration](#compliance-pipelines) to the projects on which it is
-applied. Refer to our
-applied. For more information, see [Add a compliance framework to a project](../project/settings/index.md#add-a-compliance-framework-to-a-project).
+[compliance pipeline configuration](#compliance-pipelines) to the projects on which it is applied.
Compliance frameworks are created on top-level groups. Group owners can create, edit, and delete compliance frameworks:
@@ -25,6 +23,33 @@ Compliance frameworks are created on top-level groups. Group owners can create,
Subgroups and projects have access to all compliance frameworks created on their top-level group. However, compliance frameworks cannot be created, edited,
or deleted at the subgroup or project level. Project owners can choose a framework to apply to their projects.
+## Add a compliance framework to a project
+
+Prerequisite:
+
+- The group to which the project belongs must have a compliance framework.
+
+To assign a compliance framework to a project:
+
+1. On the left sidebar, at the top, select **Search GitLab** (**{search}**) to find your project.
+1. Select **Settings** > **General**.
+1. Expand **Compliance frameworks**.
+1. Select a compliance framework.
+1. Select **Save changes**.
+
+NOTE:
+Frameworks cannot be added to projects in personal namespaces.
+
+### GraphQL API
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/333249) in GitLab 14.2.
+
+You can use the [GraphQL API](../../api/graphql/reference/index.md#mutationprojectsetcomplianceframework) to add a
+compliance framework to a project.
+
+If you create compliance frameworks on subgroups with GraphQL, the framework is created on the root ancestor if the user
+has the correct permissions. The GitLab UI presents a read-only view to discourage this behavior.
+
## Default compliance frameworks
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/375036) in GitLab 15.6.
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index 8deb05c45ef..1e086c03888 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -45,22 +45,9 @@ If you're an instance administrator, you can administer all project topics from
## Add a compliance framework to a project **(PREMIUM)**
-[Compliance frameworks](../../group/compliance_frameworks.md) can be assigned to projects within group that has a
-compliance framework using either:
-
-- The GitLab UI:
- 1. On the left sidebar, at the top, select **Search GitLab** (**{search}**) to find your project.
- 1. Select **Settings** > **General**.
- 1. Expand **Compliance frameworks**.
- 1. Select a compliance framework.
- 1. Select **Save changes**.
-- In [GitLab 14.2](https://gitlab.com/gitlab-org/gitlab/-/issues/333249) and later, using the
- [GraphQL API](../../../api/graphql/reference/index.md#mutationprojectsetcomplianceframework). If you create
- compliance frameworks on subgroups with GraphQL, the framework is created on the root ancestor if the user has the
- correct permissions. The GitLab UI presents a read-only view to discourage this behavior.
-
-NOTE:
-Frameworks can not be added to projects in personal namespaces.
+You can
+[add compliance frameworks to projects](../../group/compliance_frameworks.md#add-a-compliance-framework-to-a-project)
+in a group that has a compliance framework.
## Configure project visibility, features, and permissions
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index bb71da89e74..69b992e432b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -6540,6 +6540,9 @@ msgstr ""
msgid "AuditStreams|An error occurred when updating external audit event stream destination. Please try it again."
msgstr ""
+msgid "AuditStreams|Are you sure about deleting this destination?"
+msgstr ""
+
msgid "AuditStreams|Cancel editing"
msgstr ""
@@ -6549,6 +6552,12 @@ msgstr ""
msgid "AuditStreams|Delete %{link}"
msgstr ""
+msgid "AuditStreams|Delete destination"
+msgstr ""
+
+msgid "AuditStreams|Deleting the streaming destination %{destination} will stop audit events being streamed"
+msgstr ""
+
msgid "AuditStreams|Destination URL"
msgstr ""
diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb
index 35c5262e767..0e8a1c68bc5 100644
--- a/qa/qa/git/repository.rb
+++ b/qa/qa/git/repository.rb
@@ -144,7 +144,7 @@ module QA
end
def init_repository
- run_git("git init")
+ run_git("git init --initial-branch=#{default_branch}")
end
def pull(repository = nil, branch = nil)
diff --git a/qa/qa/page/project/branches/show.rb b/qa/qa/page/project/branches/show.rb
index af328f876f7..bbe0f91abf6 100644
--- a/qa/qa/page/project/branches/show.rb
+++ b/qa/qa/page/project/branches/show.rb
@@ -14,7 +14,6 @@ module QA
end
view 'app/views/projects/branches/_branch.html.haml' do
- element :badge_content
element :branch_container
element :branch_link
end
@@ -23,13 +22,6 @@ module QA
element :all_branches_container
end
- view 'app/assets/javascripts/branches/components/delete_merged_branches.vue' do
- element :delete_merged_branches_dropdown_button
- element :delete_merged_branches_button
- element :delete_merged_branches_input
- element :delete_merged_branches_confirmation_button
- end
-
def delete_branch(branch_name)
within_element(:branch_container, name: branch_name) do
click_element(:delete_branch_button)
@@ -47,20 +39,6 @@ module QA
end
end
end
-
- def has_branch_with_badge?(branch_name, badge)
- within_element(:branch_container, name: branch_name) do
- has_element?(:badge_content, text: badge)
- end
- end
-
- def delete_merged_branches(branches_length)
- click_element(:delete_merged_branches_dropdown_button)
- click_element(:delete_merged_branches_button)
- fill_element(:delete_merged_branches_input, branches_length)
- click_element(:delete_merged_branches_confirmation_button)
- finished_loading?
- end
end
end
end
diff --git a/qa/qa/resource/repository/branch.rb b/qa/qa/resource/repository/branch.rb
new file mode 100644
index 00000000000..9e6132b8387
--- /dev/null
+++ b/qa/qa/resource/repository/branch.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ module Repository
+ class Branch < Base
+ attr_accessor :name, :ref
+
+ attribute :project do
+ Project.fabricate_via_api! do |resource|
+ resource.name = 'branch-project'
+ resource.initialize_with_readme = true
+ end
+ end
+
+ def initialize
+ @name = 'test'
+ @ref = Runtime::Env.default_branch
+ end
+
+ def fabricate!
+ raise NotImplementedError
+ end
+
+ def fabricate_via_api!
+ resource_web_url(api_get)
+ rescue ResourceNotFoundError
+ super
+ end
+
+ def api_get_path
+ "/projects/#{project.id}/repository/branches/#{name}"
+ end
+
+ def api_delete_path
+ api_get_path
+ end
+
+ def api_post_path
+ "/projects/#{project.id}/repository/branches"
+ end
+
+ def api_post_body
+ {
+ branch: name,
+ ref: ref
+ }
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/3_create/repository/add_list_delete_branches_spec.rb b/qa/qa/specs/features/api/3_create/repository/add_list_delete_branches_spec.rb
new file mode 100644
index 00000000000..f69a8a4dfa5
--- /dev/null
+++ b/qa/qa/specs/features/api/3_create/repository/add_list_delete_branches_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Create' do
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-qa-test'
+ project.description = 'project for qa test'
+ end
+ end
+
+ describe 'Create, Retrieve and Delete branches via API', :requires_admin, product_group: :source_code do
+ created_branch = 'create-branch'
+ deleted_branch = 'delete-branch'
+ filename = 'file.txt'
+ default_branch_commit_message = "Add #{filename}"
+
+ before do
+ Git::Repository.perform do |repository|
+ repository.uri = project.repository_http_location.uri
+ repository.use_default_credentials
+ repository.try_add_credentials_to_netrc
+
+ repository.act do
+ init_repository
+ configure_identity('GitLab QA', 'root@gitlab.com')
+
+ commit_file(filename, 'Test file content', default_branch_commit_message)
+ push_changes
+
+ checkout(deleted_branch, new_branch: true)
+ push_changes(deleted_branch)
+ end
+ end
+ project.wait_for_push default_branch_commit_message
+ end
+
+ it(
+ 'creates, retrieves and deletes branches',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347740'
+ ) do
+ # Create branch
+ Resource::Repository::Branch.fabricate_via_api! do |branch|
+ branch.name = created_branch
+ branch.project = project
+ end
+
+ # Retrieve branch
+ delete_branch = Resource::Repository::Branch.fabricate_via_api! do |branch|
+ branch.name = deleted_branch
+ branch.project = project
+ end
+
+ # Delete branch
+ delete_branch.remove_via_api!
+
+ # Clone repository, verify branches and commits
+ Git::Repository.perform do |repository|
+ repository.uri = project.repository_http_location.uri
+ repository.use_default_credentials
+ repository.try_add_credentials_to_netrc
+
+ repository.clone
+
+ branches = repository.remote_branches
+ expect(branches).to include(created_branch)
+ expect(branches).not_to include(deleted_branch)
+
+ expect(repository.commits.first).to include(default_branch_commit_message)
+
+ repository.checkout(created_branch)
+ expect(repository.commits.first).to include(default_branch_commit_message)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb
deleted file mode 100644
index 631b4ae099d..00000000000
--- a/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb
+++ /dev/null
@@ -1,99 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- RSpec.describe 'Create' do
- describe 'Create, list, and delete branches via web', :requires_admin, product_group: :source_code do
- master_branch = nil
- second_branch = 'second-branch'
- third_branch = 'third-branch'
- file_1_master = 'file.txt'
- file_2_master = 'other-file.txt'
- file_second_branch = 'file-2.txt'
- file_third_branch = 'file-3.txt'
- first_commit_message_of_master_branch = "Add #{file_1_master}"
- second_commit_message_of_master_branch = "Add #{file_2_master}"
- commit_message_of_second_branch = "Add #{file_second_branch}"
- commit_message_of_third_branch = "Add #{file_third_branch}"
-
- before do
- Flow::Login.sign_in
-
- project = Resource::Project.fabricate_via_api! do |proj|
- proj.name = 'project-qa-test'
- proj.description = 'project for qa test'
- proj.initialize_with_readme = true
- end
-
- master_branch = project.default_branch
-
- Git::Repository.perform do |repository|
- repository.uri = project.repository_http_location.uri
- repository.use_default_credentials
- repository.try_add_credentials_to_netrc
- repository.default_branch = master_branch
-
- repository.act do
- clone
- configure_identity('GitLab QA', 'root@gitlab.com')
- commit_file(file_1_master, 'Test file content', first_commit_message_of_master_branch)
- push_changes
- checkout(second_branch, new_branch: true)
- commit_file(file_second_branch, 'File 2 content', commit_message_of_second_branch)
- push_changes(second_branch)
- checkout(master_branch)
- # This second commit on master is needed for the master branch to be ahead
- # of the second branch, and when the second branch is merged to master it will
- # show the 'merged' badge on it.
- # Refer to the below issue note:
- # https://gitlab.com/gitlab-org/gitlab-foss/issues/55524#note_126100848
- commit_file(file_2_master, 'Other test file content', second_commit_message_of_master_branch)
- push_changes
- merge(second_branch)
- push_changes
- checkout(third_branch, new_branch: true)
- commit_file(file_third_branch, 'File 3 content', commit_message_of_third_branch)
- push_changes(third_branch)
- end
- end
- project.wait_for_push commit_message_of_third_branch
- project.visit!
- end
-
- it(
- 'lists branches correctly after CRUD operations',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347740',
- quarantine: {
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/414026',
- type: :stale
- }
- ) do
- Page::Project::Menu.perform(&:go_to_repository_branches)
-
- expect(page).to have_content(master_branch)
- expect(page).to have_content(second_branch)
- expect(page).to have_content(third_branch)
- expect(page).to have_content("Merge branch 'second-branch'")
- expect(page).to have_content(commit_message_of_second_branch)
- expect(page).to have_content(commit_message_of_third_branch)
-
- Page::Project::Branches::Show.perform do |branches_page|
- expect(branches_page).to have_branch_with_badge(second_branch, 'merged')
-
- branches_page.delete_branch(third_branch)
-
- expect(branches_page).to have_no_branch(third_branch)
-
- branches_page.delete_merged_branches('delete')
-
- expect(branches_page).to have_content(
- 'Merged branches are being deleted. This can take some time depending on the number of branches. Please refresh the page to see changes.'
- )
-
- branches_page.refresh
-
- expect(branches_page).to have_no_branch(second_branch, reload: true)
- end
- end
- end
- end
-end
diff --git a/spec/features/admin/admin_mode/login_spec.rb b/spec/features/admin/admin_mode/login_spec.rb
index c0c8b12342a..72c7083f459 100644
--- a/spec/features/admin/admin_mode/login_spec.rb
+++ b/spec/features/admin/admin_mode/login_spec.rb
@@ -139,8 +139,10 @@ RSpec.describe 'Admin Mode Login', feature_category: :system_access do
context 'when authn_context is worth two factors' do
let(:mock_saml_response) do
File.read('spec/fixtures/authentication/saml_response.xml')
- .gsub('urn:oasis:names:tc:SAML:2.0:ac:classes:Password',
- 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS')
+ .gsub(
+ 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password',
+ 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS'
+ )
end
it 'signs user in without prompting for second factor' do
diff --git a/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb b/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb
index d0ca5d76cc7..881ccec017b 100644
--- a/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb
+++ b/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe 'User activates the instance-level Mattermost Slash Command integration', :js,
-feature_category: :integrations do
+ feature_category: :integrations do
include_context 'instance integration activation'
before do
diff --git a/spec/features/atom/issues_spec.rb b/spec/features/atom/issues_spec.rb
index 89db70c6680..bd5903efe10 100644
--- a/spec/features/atom/issues_spec.rb
+++ b/spec/features/atom/issues_spec.rb
@@ -49,8 +49,11 @@ RSpec.describe 'Issues Feed', feature_category: :devops_reports do
before do
personal_access_token = create(:personal_access_token, user: user)
- visit project_issues_path(project, :atom,
- private_token: personal_access_token.token)
+ visit project_issues_path(
+ project,
+ :atom,
+ private_token: personal_access_token.token
+ )
end
it_behaves_like 'an authenticated issuable atom feed'
@@ -59,8 +62,11 @@ RSpec.describe 'Issues Feed', feature_category: :devops_reports do
context 'when authenticated via feed token' do
before do
- visit project_issues_path(project, :atom,
- feed_token: user.feed_token)
+ visit project_issues_path(
+ project,
+ :atom,
+ feed_token: user.feed_token
+ )
end
it_behaves_like 'an authenticated issuable atom feed'
diff --git a/spec/features/atom/merge_requests_spec.rb b/spec/features/atom/merge_requests_spec.rb
index b9e1c7042b2..0238380da90 100644
--- a/spec/features/atom/merge_requests_spec.rb
+++ b/spec/features/atom/merge_requests_spec.rb
@@ -46,8 +46,11 @@ RSpec.describe 'Merge Requests Feed', feature_category: :devops_reports do
before do
personal_access_token = create(:personal_access_token, user: user)
- visit project_merge_requests_path(project, :atom,
- private_token: personal_access_token.token)
+ visit project_merge_requests_path(
+ project,
+ :atom,
+ private_token: personal_access_token.token
+ )
end
it_behaves_like 'an authenticated issuable atom feed'
@@ -56,8 +59,11 @@ RSpec.describe 'Merge Requests Feed', feature_category: :devops_reports do
context 'when authenticated via feed token' do
before do
- visit project_merge_requests_path(project, :atom,
- feed_token: user.feed_token)
+ visit project_merge_requests_path(
+ project,
+ :atom,
+ feed_token: user.feed_token
+ )
end
it_behaves_like 'an authenticated issuable atom feed'
diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb
index b743f900ae7..f801f93686c 100644
--- a/spec/features/atom/users_spec.rb
+++ b/spec/features/atom/users_spec.rb
@@ -25,27 +25,33 @@ RSpec.describe "User Feed", feature_category: :devops_reports do
context 'feed content' do
let(:project) { create(:project, :repository) }
let(:issue) do
- create(:issue,
- project: project,
- author: user,
- description: "Houston, we have a bug!\n\n***\n\nI guess.")
+ create(
+ :issue,
+ project: project,
+ author: user,
+ description: "Houston, we have a bug!\n\n***\n\nI guess."
+ )
end
let(:note) do
- create(:note,
- noteable: issue,
- author: user,
- note: 'Bug confirmed :+1:',
- project: project)
+ create(
+ :note,
+ noteable: issue,
+ author: user,
+ note: 'Bug confirmed :+1:',
+ project: project
+ )
end
let(:merge_request) do
- create(:merge_request,
- title: 'Fix bug',
- author: user,
- source_project: project,
- target_project: project,
- description: "Here is the fix: ")
+ create(
+ :merge_request,
+ title: 'Fix bug',
+ author: user,
+ source_project: project,
+ target_project: project,
+ description: "Here is the fix: "
+ )
end
let(:push_event) { create(:push_event, project: project, author: user) }
diff --git a/spec/features/boards/issue_ordering_spec.rb b/spec/features/boards/issue_ordering_spec.rb
index b6196fa6a1d..35e387c9d8a 100644
--- a/spec/features/boards/issue_ordering_spec.rb
+++ b/spec/features/boards/issue_ordering_spec.rb
@@ -220,12 +220,14 @@ RSpec.describe 'Issue Boards', :js, feature_category: :team_planning do
end
def drag(selector: '.board-list', list_from_index: 1, from_index: 0, to_index: 0, list_to_index: 1, duration: 1000)
- drag_to(selector: selector,
- scrollable: '#board-app',
- list_from_index: list_from_index,
- from_index: from_index,
- to_index: to_index,
- list_to_index: list_to_index,
- duration: duration)
+ drag_to(
+ selector: selector,
+ scrollable: '#board-app',
+ list_from_index: list_from_index,
+ from_index: from_index,
+ to_index: to_index,
+ list_to_index: list_to_index,
+ duration: duration
+ )
end
end
diff --git a/spec/features/boards/multi_select_spec.rb b/spec/features/boards/multi_select_spec.rb
index 7afe34be3d8..03b1643d7c4 100644
--- a/spec/features/boards/multi_select_spec.rb
+++ b/spec/features/boards/multi_select_spec.rb
@@ -11,13 +11,15 @@ RSpec.describe 'Multi Select Issue', :js, feature_category: :team_planning do
let(:user) { create(:user) }
def drag(selector: '.board-list', list_from_index: 1, from_index: 0, to_index: 0, list_to_index: 1, duration: 1000)
- drag_to(selector: selector,
- scrollable: '#board-app',
- list_from_index: list_from_index,
- from_index: from_index,
- to_index: to_index,
- list_to_index: list_to_index,
- duration: duration)
+ drag_to(
+ selector: selector,
+ scrollable: '#board-app',
+ list_from_index: list_from_index,
+ from_index: from_index,
+ to_index: to_index,
+ list_to_index: list_to_index,
+ duration: duration
+ )
end
def wait_for_board_cards(board_number, expected_cards)
diff --git a/spec/features/boards/sidebar_assignee_spec.rb b/spec/features/boards/sidebar_assignee_spec.rb
index a912ea28ddc..899ab5863e1 100644
--- a/spec/features/boards/sidebar_assignee_spec.rb
+++ b/spec/features/boards/sidebar_assignee_spec.rb
@@ -2,8 +2,9 @@
require 'spec_helper'
-RSpec.describe 'Project issue boards sidebar assignee', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/332078',
- feature_category: :team_planning do
+RSpec.describe 'Project issue boards sidebar assignee', :js,
+ quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/332078',
+ feature_category: :team_planning do
include BoardHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index fd09a7f7343..6d07c68dd13 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -15,13 +15,15 @@ RSpec.describe 'Commits', feature_category: :source_code_management do
let(:creator) { create(:user, developer_projects: [project]) }
let!(:pipeline) do
- create(:ci_pipeline,
- project: project,
- user: creator,
- ref: project.default_branch,
- sha: project.commit.sha,
- status: :success,
- created_at: 5.months.ago)
+ create(
+ :ci_pipeline,
+ project: project,
+ user: creator,
+ ref: project.default_branch,
+ sha: project.commit.sha,
+ status: :success,
+ created_at: 5.months.ago
+ )
end
context 'commit status is Generic Commit Status' do
@@ -61,11 +63,13 @@ RSpec.describe 'Commits', feature_category: :source_code_management do
describe 'Project commits' do
let!(:pipeline_from_other_branch) do
- create(:ci_pipeline,
- project: project,
- ref: 'fix',
- sha: project.commit.sha,
- status: :failed)
+ create(
+ :ci_pipeline,
+ project: project,
+ ref: 'fix',
+ sha: project.commit.sha,
+ status: :failed
+ )
end
before do
diff --git a/spec/features/dashboard/activity_spec.rb b/spec/features/dashboard/activity_spec.rb
index 2345e4be722..60621f57bde 100644
--- a/spec/features/dashboard/activity_spec.rb
+++ b/spec/features/dashboard/activity_spec.rb
@@ -59,12 +59,14 @@ RSpec.describe 'Dashboard > Activity', feature_category: :user_profile do
let!(:push_event) do
event = create(:push_event, project: project, author: user)
- create(:push_event_payload,
- event: event,
- action: :created,
- commit_to: '0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e',
- ref: 'new_design',
- commit_count: 1)
+ create(
+ :push_event_payload,
+ event: event,
+ action: :created,
+ commit_to: '0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e',
+ ref: 'new_design',
+ commit_count: 1
+ )
event
end
diff --git a/spec/features/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb
index c6e78c8b57c..e84a3c8cc66 100644
--- a/spec/features/dashboard/datetime_on_tooltips_spec.rb
+++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb
@@ -18,8 +18,13 @@ RSpec.describe 'Tooltips on .timeago dates', :js, feature_category: :user_profil
context 'on the activity tab' do
before do
- Event.create!(project: project, author_id: user.id, action: :joined,
- updated_at: created_date, created_at: created_date)
+ Event.create!(
+ project: project,
+ author_id: user.id,
+ action: :joined,
+ updated_at: created_date,
+ created_at: created_date
+ )
sign_in user
visit user_activity_path(user)
diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb
index d53f5affe64..624f3530f81 100644
--- a/spec/features/dashboard/merge_requests_spec.rb
+++ b/spec/features/dashboard/merge_requests_spec.rb
@@ -79,39 +79,52 @@ RSpec.describe 'Dashboard Merge Requests', feature_category: :code_review_workfl
end
let!(:assigned_merge_request_from_fork) do
- create(:merge_request,
- source_branch: 'markdown', assignees: [current_user],
- target_project: public_project, source_project: forked_project,
- author: author_user)
+ create(
+ :merge_request,
+ source_branch: 'markdown',
+ assignees: [current_user],
+ target_project: public_project,
+ source_project: forked_project,
+ author: author_user
+ )
end
let!(:authored_merge_request) do
- create(:merge_request,
- source_branch: 'markdown',
- source_project: project,
- author: current_user)
+ create(
+ :merge_request,
+ source_branch: 'markdown',
+ source_project: project,
+ author: current_user
+ )
end
let!(:authored_merge_request_from_fork) do
- create(:merge_request,
- source_branch: 'feature_conflict',
- author: current_user,
- target_project: public_project, source_project: forked_project)
+ create(
+ :merge_request,
+ source_branch: 'feature_conflict',
+ author: current_user,
+ target_project: public_project,
+ source_project: forked_project
+ )
end
let!(:labeled_merge_request) do
- create(:labeled_merge_request,
- source_branch: 'labeled',
- labels: [label],
- author: current_user,
- source_project: project)
+ create(
+ :labeled_merge_request,
+ source_branch: 'labeled',
+ labels: [label],
+ author: current_user,
+ source_project: project
+ )
end
let!(:other_merge_request) do
- create(:merge_request,
- source_branch: 'fix',
- source_project: project,
- author: author_user)
+ create(
+ :merge_request,
+ source_branch: 'fix',
+ source_project: project,
+ author: author_user
+ )
end
before do
diff --git a/spec/features/dashboard/todos/todos_sorting_spec.rb b/spec/features/dashboard/todos/todos_sorting_spec.rb
index e449f71878b..e1460e345fc 100644
--- a/spec/features/dashboard/todos/todos_sorting_spec.rb
+++ b/spec/features/dashboard/todos/todos_sorting_spec.rb
@@ -27,8 +27,9 @@ RSpec.describe 'Dashboard > User sorts todos', feature_category: :team_planning
create(:todo, user: user, project: project, target: issue_2, created_at: 4.hours.ago, updated_at: 4.hours.ago)
create(:todo, user: user, project: project, target: issue_3, created_at: 3.hours.ago, updated_at: 2.minutes.ago)
create(:todo, user: user, project: project, target: issue_1, created_at: 2.hours.ago, updated_at: 2.hours.ago)
- create(:todo, user: user, project: project, target: merge_request_1, created_at: 1.hour.ago,
- updated_at: 1.hour.ago)
+ create(
+ :todo, user: user, project: project, target: merge_request_1, created_at: 1.hour.ago, updated_at: 1.hour.ago
+ )
merge_request_1.labels << label_1
issue_3.labels << label_1
diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb
index d0003b69415..9d59126df8d 100644
--- a/spec/features/dashboard/todos/todos_spec.rb
+++ b/spec/features/dashboard/todos/todos_spec.rb
@@ -443,12 +443,15 @@ RSpec.describe 'Dashboard Todos', feature_category: :team_planning do
let_it_be(:target) { create(:design, issue: issue, project: project) }
let_it_be(:note) { create(:note, project: project, note: 'I am note, hear me roar') }
let_it_be(:todo) do
- create(:todo, :mentioned,
- user: user,
- project: project,
- target: target,
- author: author,
- note: note)
+ create(
+ :todo,
+ :mentioned,
+ user: user,
+ project: project,
+ target: target,
+ author: author,
+ note: note
+ )
end
before do
@@ -467,10 +470,12 @@ RSpec.describe 'Dashboard Todos', feature_category: :team_planning do
context 'User requested access' do
shared_examples 'has todo present with access request content' do
specify do
- create(:todo, :member_access_requested,
- user: user,
- target: target,
- author: author
+ create(
+ :todo,
+ :member_access_requested,
+ user: user,
+ target: target,
+ author: author
)
target.add_owner(user)
diff --git a/spec/features/projects/blobs/blame_spec.rb b/spec/features/projects/blobs/blame_spec.rb
index 798cd401dac..dfda200cded 100644
--- a/spec/features/projects/blobs/blame_spec.rb
+++ b/spec/features/projects/blobs/blame_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'File blame', :js, feature_category: :groups_and_projects do
+RSpec.describe 'File blame', :js, feature_category: :source_code_management do
include TreeHelper
let_it_be(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb
index 2490b1fde8e..9f1f7d04339 100644
--- a/spec/features/projects/environments/environments_spec.rb
+++ b/spec/features/projects/environments/environments_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Environments page', :js, feature_category: :groups_and_projects do
+RSpec.describe 'Environments page', :js, feature_category: :continuous_delivery do
include Spec::Support::Helpers::ModalHelpers
let(:project) { create(:project) }
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index cbaad4e069b..a5ff4be5e07 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Pipelines', :js, feature_category: :groups_and_projects do
+RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do
include ListboxHelpers
include ProjectForksHelper
include Spec::Support::Helpers::ModalHelpers
diff --git a/spec/frontend/branches/components/__snapshots__/delete_merged_branches_spec.js.snap b/spec/frontend/branches/components/__snapshots__/delete_merged_branches_spec.js.snap
index 9db6a523dec..4da56a865d5 100644
--- a/spec/frontend/branches/components/__snapshots__/delete_merged_branches_spec.js.snap
+++ b/spec/frontend/branches/components/__snapshots__/delete_merged_branches_spec.js.snap
@@ -5,7 +5,6 @@ exports[`Delete merged branches component Delete merged branches confirmation mo
{
};
const findDeleteButton = () =>
- wrapper.findComponent('[data-qa-selector="delete_merged_branches_button"]');
+ wrapper.findComponent('[data-testid="delete-merged-branches-button"]');
const findModal = () => wrapper.findComponent(GlModal);
const findConfirmationButton = () =>
wrapper.findByTestId('delete-merged-branches-confirmation-button');
diff --git a/spec/frontend/issues/show/components/delete_issue_modal_spec.js b/spec/frontend/issues/show/components/delete_issue_modal_spec.js
index b8adeb24005..f122180a403 100644
--- a/spec/frontend/issues/show/components/delete_issue_modal_spec.js
+++ b/spec/frontend/issues/show/components/delete_issue_modal_spec.js
@@ -37,11 +37,14 @@ describe('DeleteIssueModal component', () => {
});
describe('when "primary" event is emitted', () => {
- let formSubmitSpy;
+ const submitMock = jest.fn();
+ // Mock the form submit method
+ Object.defineProperty(HTMLFormElement.prototype, 'submit', {
+ value: submitMock,
+ });
beforeEach(() => {
wrapper = mountComponent();
- formSubmitSpy = jest.spyOn(wrapper.vm.$refs.form, 'submit');
findModal().vm.$emit('primary');
});
@@ -50,7 +53,7 @@ describe('DeleteIssueModal component', () => {
});
it('submits the form', () => {
- expect(formSubmitSpy).toHaveBeenCalled();
+ expect(submitMock).toHaveBeenCalledTimes(1);
});
});
});
diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/package_files_spec.js b/spec/frontend/packages_and_registries/package_registry/components/details/package_files_spec.js
index 21d1c870880..2c712feac86 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/details/package_files_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/details/package_files_spec.js
@@ -1,7 +1,6 @@
import {
GlAlert,
- GlDropdown,
- GlButton,
+ GlDisclosureDropdown,
GlFormCheckbox,
GlLoadingIcon,
GlModal,
@@ -64,9 +63,10 @@ describe('Package Files', () => {
const findFirstRowDownloadLink = () => findFirstRow().findByTestId('download-link');
const findFirstRowFileIcon = () => findFirstRow().findComponent(FileIcon);
const findFirstRowCreatedAt = () => findFirstRow().findComponent(TimeAgoTooltip);
- const findFirstActionMenu = () => extendedWrapper(findFirstRow().findComponent(GlDropdown));
+ const findFirstActionMenu = () =>
+ extendedWrapper(findFirstRow().findComponent(GlDisclosureDropdown));
const findActionMenuDelete = () => findFirstActionMenu().findByTestId('delete-file');
- const findFirstToggleDetailsButton = () => findFirstRow().findComponent(GlButton);
+ const findFirstToggleDetailsButton = () => findFirstRow().findByTestId('toggle-details-button');
const findFirstRowShaComponent = (id) => wrapper.findByTestId(id);
const findCheckAllCheckbox = () => wrapper.findByTestId('package-files-checkbox-all');
const findAllRowCheckboxes = () => wrapper.findAllByTestId('package-files-checkbox');
@@ -262,7 +262,7 @@ describe('Package Files', () => {
expect(findFirstActionMenu().exists()).toBe(true);
expect(findFirstActionMenu().props('icon')).toBe('ellipsis_v');
expect(findFirstActionMenu().props('textSrOnly')).toBe(true);
- expect(findFirstActionMenu().props('text')).toMatchInterpolatedText('More actions');
+ expect(findFirstActionMenu().props('toggleText')).toMatchInterpolatedText('More actions');
});
describe('menu items', () => {
@@ -272,7 +272,7 @@ describe('Package Files', () => {
});
it('shows delete file confirmation modal', async () => {
- await findActionMenuDelete().trigger('click');
+ await findActionMenuDelete().vm.$emit('action');
expect(showMock).toHaveBeenCalledTimes(1);