summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-07-13 21:10:15 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-07-13 21:10:15 +0000
commita269fb8e7cca24b826dd3f53485641ffce93bbee (patch)
tree81a7c4f56feba26bb6244c8a3a76123533f68aa8
parent7e064974b92de60a3ef4642905e8af98a364a7a0 (diff)
downloadgitlab-ce-a269fb8e7cca24b826dd3f53485641ffce93bbee.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue26
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/constants.js8
-rw-r--r--app/controllers/projects/pipelines/tests_controller.rb17
-rw-r--r--app/graphql/types/project_type.rb4
-rw-r--r--app/helpers/projects_helper.rb3
-rw-r--r--app/models/ci/pipeline.rb6
-rw-r--r--app/models/merge_request.rb1
-rw-r--r--app/models/remote_mirror.rb5
-rw-r--r--app/services/projects/fork_service.rb5
-rw-r--r--app/services/protected_branches/api_service.rb4
-rw-r--r--app/services/protected_refs/access_level_params.rb (renamed from app/services/protected_branches/access_level_params.rb)4
-rw-r--r--app/views/devise/sessions/new.html.haml2
-rw-r--r--config/feature_flags/ops/remote_mirror_no_delay.yml7
-rw-r--r--db/migrate/20220524164122_limit_project_and_group_variables.rb8
-rw-r--r--db/post_migrate/20220705180843_validate_requirements_issue_id_not_null.rb15
-rw-r--r--db/schema_migrations/202205241641221
-rw-r--r--db/schema_migrations/202207051808431
-rw-r--r--db/structure.sql10
-rw-r--r--doc/api/graphql/reference/index.md4
-rw-r--r--doc/api/protected_tags.md1
-rw-r--r--doc/ci/variables/predefined_variables.md47
-rw-r--r--doc/development/feature_flags/index.md2
-rw-r--r--lib/api/helpers/protected_tags_helpers.rb15
-rw-r--r--lib/api/protected_tags.rb7
-rw-r--r--locale/gitlab.pot18
-rw-r--r--spec/controllers/projects/pipelines/tests_controller_spec.rb16
-rw-r--r--spec/features/projects/settings/visibility_settings_spec.rb6
-rw-r--r--spec/frontend/__helpers__/performance.js8
-rw-r--r--spec/frontend/design_management/components/image_spec.js5
-rw-r--r--spec/frontend/diffs/components/app_spec.js2
-rw-r--r--spec/frontend/environment.js6
-rw-r--r--spec/frontend/ide/components/ide_spec.js3
-rw-r--r--spec/frontend/ide/components/repo_editor_spec.js3
-rw-r--r--spec/frontend/ide/ide_router_spec.js3
-rw-r--r--spec/frontend/ide/stores/actions/file_spec.js3
-rw-r--r--spec/frontend/ide/stores/actions/merge_request_spec.js3
-rw-r--r--spec/frontend/ide/stores/actions/tree_spec.js3
-rw-r--r--spec/frontend/ide/stores/actions_spec.js3
-rw-r--r--spec/frontend/merge_request_tabs_spec.js11
-rw-r--r--spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js6
-rw-r--r--spec/frontend/pipelines/graph/graph_component_wrapper_spec.js5
-rw-r--r--spec/frontend/snippets/components/edit_spec.js3
-rw-r--r--spec/frontend/snippets/components/show_spec.js5
-rw-r--r--spec/frontend_integration/diffs/diffs_interopability_spec.js5
-rw-r--r--spec/frontend_integration/ide/ide_integration_spec.js4
-rw-r--r--spec/frontend_integration/ide/user_opens_file_spec.js2
-rw-r--r--spec/frontend_integration/ide/user_opens_ide_spec.js3
-rw-r--r--spec/frontend_integration/ide/user_opens_mr_spec.js3
-rw-r--r--spec/helpers/projects_helper_spec.rb4
-rw-r--r--spec/models/ci/pipeline_spec.rb16
-rw-r--r--spec/models/remote_mirror_spec.rb18
-rw-r--r--spec/requests/api/protected_tags_spec.rb7
-rw-r--r--spec/services/projects/fork_service_spec.rb3
-rw-r--r--spec/views/devise/sessions/new.html.haml_spec.rb38
54 files changed, 316 insertions, 102 deletions
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
index 81b0dbec0bd..fe51591c32d 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
@@ -182,6 +182,10 @@ export default {
required: false,
default: false,
},
+ membersPagePath: {
+ type: String,
+ required: true,
+ },
},
data() {
const defaults = {
@@ -521,12 +525,22 @@ export default {
/>
</div>
</div>
- <span v-if="!visibilityAllowed(visibilityLevel)" class="form-text text-muted">{{
- s__(
- 'ProjectSettings|Visibility options for this fork are limited by the current visibility of the source project.',
- )
- }}</span>
- <span class="form-text text-muted">{{ visibilityLevelDescription }}</span>
+ <span
+ v-if="!visibilityAllowed(visibilityLevel)"
+ class="gl-display-block gl-text-gray-500 gl-mt-2"
+ >{{
+ s__(
+ 'ProjectSettings|Visibility options for this fork are limited by the current visibility of the source project.',
+ )
+ }}</span
+ >
+ <span class="gl-display-block gl-text-gray-500 gl-mt-2">
+ <gl-sprintf :message="visibilityLevelDescription">
+ <template #membersPageLink="{ content }">
+ <gl-link class="gl-link" :href="membersPagePath">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </span>
<div v-if="showAdditonalSettings" class="gl-mt-4">
<strong class="gl-display-block">{{ s__('ProjectSettings|Additional options') }}</strong>
<label
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/constants.js b/app/assets/javascripts/pages/projects/shared/permissions/constants.js
index fb1acd5311c..cfca9d400e3 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/constants.js
+++ b/app/assets/javascripts/pages/projects/shared/permissions/constants.js
@@ -8,12 +8,10 @@ export const visibilityOptions = {
export const visibilityLevelDescriptions = {
[visibilityOptions.PRIVATE]: __(
- 'The project is accessible only by members of the project. Access must be granted explicitly to each user.',
- ),
- [visibilityOptions.INTERNAL]: __('The project can be accessed by any user who is logged in.'),
- [visibilityOptions.PUBLIC]: __(
- 'The project can be accessed by anyone, regardless of authentication.',
+ `Only accessible by %{membersPageLinkStart}project members%{membersPageLinkEnd}. Membership must be explicitly granted to each user.`,
),
+ [visibilityOptions.INTERNAL]: __('Accessible by any user who is logged in.'),
+ [visibilityOptions.PUBLIC]: __('Accessible by anyone, regardless of authentication.'),
};
export const featureAccessLevel = {
diff --git a/app/controllers/projects/pipelines/tests_controller.rb b/app/controllers/projects/pipelines/tests_controller.rb
index e5b2dd14f69..20bdb4d3af1 100644
--- a/app/controllers/projects/pipelines/tests_controller.rb
+++ b/app/controllers/projects/pipelines/tests_controller.rb
@@ -7,6 +7,7 @@ module Projects
before_action :authorize_read_build!
before_action :builds, only: [:show]
+ before_action :validate_test_reports!, only: [:show]
feature_category :code_testing
@@ -23,19 +24,21 @@ module Projects
def show
respond_to do |format|
format.json do
- if pipeline.has_expired_test_reports?
- render json: { errors: 'Test report artifacts have expired' }, status: :not_found
- else
- render json: TestSuiteSerializer
- .new(project: project, current_user: @current_user)
- .represent(test_suite, details: true)
- end
+ render json: TestSuiteSerializer
+ .new(project: project, current_user: @current_user)
+ .represent(test_suite, details: true)
end
end
end
private
+ def validate_test_reports!
+ unless pipeline.has_test_reports?
+ render json: { errors: 'Test report artifacts have expired' }, status: :not_found
+ end
+ end
+
def builds
@builds ||= pipeline.latest_builds.id_in(build_ids).presence || render_404
end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index c2e47e06361..7e3800c6a13 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -70,10 +70,10 @@ module Types
description: 'Indicates if shared runners are enabled for the project.'
field :service_desk_enabled, GraphQL::Types::Boolean, null: true,
- description: 'Indicates if the project has service desk enabled.'
+ description: 'Indicates if the project has Service Desk enabled.'
field :service_desk_address, GraphQL::Types::String, null: true,
- description: 'E-mail address of the service desk.'
+ description: 'E-mail address of the Service Desk.'
field :avatar_url, GraphQL::Types::String, null: true, calls_gitaly: true,
description: 'URL to avatar image file of the project.'
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 8a0820b2ba3..2ece3e87500 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -388,7 +388,8 @@ module ProjectsHelper
pagesAccessControlEnabled: Gitlab.config.pages.access_control,
pagesAccessControlForced: ::Gitlab::Pages.access_control_is_forced?,
pagesHelpPath: help_page_path('user/project/pages/introduction', anchor: 'gitlab-pages-access-control'),
- issuesHelpPath: help_page_path('user/project/issues/index')
+ issuesHelpPath: help_page_path('user/project/issues/index'),
+ membersPagePath: project_project_members_path(project)
}
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 2c5dc0c72b7..795def55306 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -1297,9 +1297,9 @@ module Ci
end
end
- def has_expired_test_reports?
- strong_memoize(:has_expired_test_reports) do
- has_reports?(::Ci::JobArtifact.test_reports.expired)
+ def has_test_reports?
+ strong_memoize(:has_test_reports) do
+ has_reports?(::Ci::JobArtifact.test_reports)
end
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 1a3464d05a2..b1fdfed5ae0 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -1567,6 +1567,7 @@ class MergeRequest < ApplicationRecord
variables.append(key: 'CI_MERGE_REQUEST_PROJECT_PATH', value: project.full_path)
variables.append(key: 'CI_MERGE_REQUEST_PROJECT_URL', value: project.web_url)
variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_NAME', value: target_branch.to_s)
+ variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_PROTECTED', value: ProtectedBranch.protected?(target_project, target_branch).to_s)
variables.append(key: 'CI_MERGE_REQUEST_TITLE', value: title)
variables.append(key: 'CI_MERGE_REQUEST_ASSIGNEES', value: assignee_username_list) if assignees.present?
variables.append(key: 'CI_MERGE_REQUEST_MILESTONE', value: milestone.title) if milestone
diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb
index 7f41f0907d5..f8d500e106b 100644
--- a/app/models/remote_mirror.rb
+++ b/app/models/remote_mirror.rb
@@ -128,7 +128,7 @@ class RemoteMirror < ApplicationRecord
def sync
return unless sync?
- if recently_scheduled?
+ if schedule_with_delay?
RepositoryUpdateRemoteMirrorWorker.perform_in(backoff_delay, self.id, Time.current)
else
RepositoryUpdateRemoteMirrorWorker.perform_async(self.id, Time.current)
@@ -261,7 +261,8 @@ class RemoteMirror < ApplicationRecord
super
end
- def recently_scheduled?
+ def schedule_with_delay?
+ return false if Feature.enabled?(:remote_mirror_no_delay, project, type: :ops)
return false unless self.last_update_started_at
self.last_update_started_at >= Time.current - backoff_delay
diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb
index 02ae30db1c3..70a04cd556a 100644
--- a/app/services/projects/fork_service.rb
+++ b/app/services/projects/fork_service.rb
@@ -65,7 +65,10 @@ module Projects
# exception.
relations_block: -> (project) { build_fork_network_member(project) },
skip_disk_validation: skip_disk_validation,
- external_authorization_classification_label: @project.external_authorization_classification_label
+ external_authorization_classification_label: @project.external_authorization_classification_label,
+ suggestion_commit_message: @project.suggestion_commit_message,
+ merge_commit_template: @project.merge_commit_template,
+ squash_commit_template: @project.squash_commit_template
}
if @project.avatar.present? && @project.avatar.image?
diff --git a/app/services/protected_branches/api_service.rb b/app/services/protected_branches/api_service.rb
index d0d0737fd66..f604a57bcd1 100644
--- a/app/services/protected_branches/api_service.rb
+++ b/app/services/protected_branches/api_service.rb
@@ -10,8 +10,8 @@ module ProtectedBranches
{
name: params[:name],
allow_force_push: allow_force_push?,
- push_access_levels_attributes: AccessLevelParams.new(:push, params).access_levels,
- merge_access_levels_attributes: AccessLevelParams.new(:merge, params).access_levels
+ push_access_levels_attributes: ::ProtectedRefs::AccessLevelParams.new(:push, params).access_levels,
+ merge_access_levels_attributes: ::ProtectedRefs::AccessLevelParams.new(:merge, params).access_levels
}
end
diff --git a/app/services/protected_branches/access_level_params.rb b/app/services/protected_refs/access_level_params.rb
index 6f7a289d9b4..59fc17868d1 100644
--- a/app/services/protected_branches/access_level_params.rb
+++ b/app/services/protected_refs/access_level_params.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-module ProtectedBranches
+module ProtectedRefs
class AccessLevelParams
attr_reader :type, :params
@@ -34,4 +34,4 @@ module ProtectedBranches
end
end
-ProtectedBranches::AccessLevelParams.prepend_mod_with('ProtectedBranches::AccessLevelParams')
+ProtectedRefs::AccessLevelParams.prepend_mod_with('ProtectedRefs::AccessLevelParams')
diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml
index c669f3efec6..9a09f6bee38 100644
--- a/app/views/devise/sessions/new.html.haml
+++ b/app/views/devise/sessions/new.html.haml
@@ -1,7 +1,9 @@
- page_title _("Sign in")
- content_for :page_specific_javascripts do
+ = render "layouts/google_tag_manager_head"
= render "layouts/one_trust"
= render "layouts/bizible"
+= render "layouts/google_tag_manager_body"
#signin-container
- if any_form_based_providers_enabled?
diff --git a/config/feature_flags/ops/remote_mirror_no_delay.yml b/config/feature_flags/ops/remote_mirror_no_delay.yml
new file mode 100644
index 00000000000..17937b35cf0
--- /dev/null
+++ b/config/feature_flags/ops/remote_mirror_no_delay.yml
@@ -0,0 +1,7 @@
+---
+name: remote_mirror_no_delay
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92093
+milestone: '15.2'
+type: ops
+group: group::scalability
+default_enabled: false
diff --git a/db/migrate/20220524164122_limit_project_and_group_variables.rb b/db/migrate/20220524164122_limit_project_and_group_variables.rb
new file mode 100644
index 00000000000..e8776e35730
--- /dev/null
+++ b/db/migrate/20220524164122_limit_project_and_group_variables.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+class LimitProjectAndGroupVariables < Gitlab::Database::Migration[2.0]
+ def change
+ add_column(:plan_limits, :project_ci_variables, :integer, default: 200, null: false)
+ add_column(:plan_limits, :group_ci_variables, :integer, default: 200, null: false)
+ end
+end
diff --git a/db/post_migrate/20220705180843_validate_requirements_issue_id_not_null.rb b/db/post_migrate/20220705180843_validate_requirements_issue_id_not_null.rb
new file mode 100644
index 00000000000..a0c8954b481
--- /dev/null
+++ b/db/post_migrate/20220705180843_validate_requirements_issue_id_not_null.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class ValidateRequirementsIssueIdNotNull < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ constraint_name = 'check_requirement_issue_not_null'
+
+ validate_not_null_constraint(:requirements, :issue_id, constraint_name: constraint_name)
+ end
+
+ def down
+ # No op
+ end
+end
diff --git a/db/schema_migrations/20220524164122 b/db/schema_migrations/20220524164122
new file mode 100644
index 00000000000..26e3b596910
--- /dev/null
+++ b/db/schema_migrations/20220524164122
@@ -0,0 +1 @@
+dc449f4ea28da3662fce663dcd5cdc9e37417e14b39e26897cc35a2bebfa22f0 \ No newline at end of file
diff --git a/db/schema_migrations/20220705180843 b/db/schema_migrations/20220705180843
new file mode 100644
index 00000000000..4e39726e61e
--- /dev/null
+++ b/db/schema_migrations/20220705180843
@@ -0,0 +1 @@
+755e06f8bd3a0a28820f6ec2ee52a39a7920eb9d8ae0315a8a179139c78645d9 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 14b1a1ba862..69a5285d4a1 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -18834,7 +18834,9 @@ CREATE TABLE plan_limits (
repository_size bigint DEFAULT 0 NOT NULL,
security_policy_scan_execution_schedules integer DEFAULT 0 NOT NULL,
web_hook_calls_mid integer DEFAULT 0 NOT NULL,
- web_hook_calls_low integer DEFAULT 0 NOT NULL
+ web_hook_calls_low integer DEFAULT 0 NOT NULL,
+ project_ci_variables integer DEFAULT 200 NOT NULL,
+ group_ci_variables integer DEFAULT 200 NOT NULL
);
CREATE SEQUENCE plan_limits_id_seq
@@ -20277,7 +20279,8 @@ CREATE TABLE requirements (
description text,
description_html text,
issue_id bigint,
- CONSTRAINT check_785ae25b9d CHECK ((char_length(description) <= 10000))
+ CONSTRAINT check_785ae25b9d CHECK ((char_length(description) <= 10000)),
+ CONSTRAINT check_requirement_issue_not_null CHECK ((issue_id IS NOT NULL))
);
CREATE SEQUENCE requirements_id_seq
@@ -24510,9 +24513,6 @@ ALTER TABLE sprints
ALTER TABLE projects
ADD CONSTRAINT check_fa75869cb1 CHECK ((project_namespace_id IS NOT NULL)) NOT VALID;
-ALTER TABLE requirements
- ADD CONSTRAINT check_requirement_issue_not_null CHECK ((issue_id IS NOT NULL)) NOT VALID;
-
ALTER TABLE ONLY ci_build_needs
ADD CONSTRAINT ci_build_needs_pkey PRIMARY KEY (id);
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 32c34b176c1..97f442d6063 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -15183,8 +15183,8 @@ Represents vulnerability finding of a security report on the pipeline.
| <a id="projectsecuritydashboardpath"></a>`securityDashboardPath` | [`String`](#string) | Path to project's security dashboard. |
| <a id="projectsecurityscanners"></a>`securityScanners` | [`SecurityScanners`](#securityscanners) | Information about security analyzers used in the project. |
| <a id="projectsentryerrors"></a>`sentryErrors` | [`SentryErrorCollection`](#sentryerrorcollection) | Paginated collection of Sentry errors on the project. |
-| <a id="projectservicedeskaddress"></a>`serviceDeskAddress` | [`String`](#string) | E-mail address of the service desk. |
-| <a id="projectservicedeskenabled"></a>`serviceDeskEnabled` | [`Boolean`](#boolean) | Indicates if the project has service desk enabled. |
+| <a id="projectservicedeskaddress"></a>`serviceDeskAddress` | [`String`](#string) | E-mail address of the Service Desk. |
+| <a id="projectservicedeskenabled"></a>`serviceDeskEnabled` | [`Boolean`](#boolean) | Indicates if the project has Service Desk enabled. |
| <a id="projectsharedrunnersenabled"></a>`sharedRunnersEnabled` | [`Boolean`](#boolean) | Indicates if shared runners are enabled for the project. |
| <a id="projectsnippetsenabled"></a>`snippetsEnabled` | [`Boolean`](#boolean) | Indicates if Snippets are enabled for the current user. |
| <a id="projectsquashcommittemplate"></a>`squashCommitTemplate` | [`String`](#string) | Template used to create squash commit message in merge requests. |
diff --git a/doc/api/protected_tags.md b/doc/api/protected_tags.md
index e2b27692373..b2ab3227a7e 100644
--- a/doc/api/protected_tags.md
+++ b/doc/api/protected_tags.md
@@ -100,6 +100,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitla
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
| `name` | string | yes | The name of the tag or wildcard |
| `create_access_level` | string | no | Access levels allowed to create (defaults: `40`, Maintainer role) |
+| `allowed_to_create` | array | no | Array of access levels allowed to create tags, with each described by a hash of the form `{user_id: integer}`, `{group_id: integer}`, or `{access_level: integer}` |
Example response:
diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md
index f8e83ac7123..6df614c1cda 100644
--- a/doc/ci/variables/predefined_variables.md
+++ b/doc/ci/variables/predefined_variables.md
@@ -139,29 +139,30 @@ These variables are available when:
- The pipelines [are merge request pipelines](../pipelines/merge_request_pipelines.md).
- The merge request is open.
-| Variable | GitLab | Runner | Description |
-|----------------------------------------|--------|--------|-------------|
-| `CI_MERGE_REQUEST_APPROVED` | 14.1 | all | Approval status of the merge request. `true` when [merge request approvals](../../user/project/merge_requests/approvals/index.md) is available and the merge request has been approved. |
-| `CI_MERGE_REQUEST_ASSIGNEES` | 11.9 | all | Comma-separated list of usernames of assignees for the merge request. |
-| `CI_MERGE_REQUEST_ID` | 11.6 | all | The instance-level ID of the merge request. This is a unique ID across all projects on GitLab. |
-| `CI_MERGE_REQUEST_IID` | 11.6 | all | The project-level IID (internal ID) of the merge request. This ID is unique for the current project. |
-| `CI_MERGE_REQUEST_LABELS` | 11.9 | all | Comma-separated label names of the merge request. |
-| `CI_MERGE_REQUEST_MILESTONE` | 11.9 | all | The milestone title of the merge request. |
-| `CI_MERGE_REQUEST_PROJECT_ID` | 11.6 | all | The ID of the project of the merge request. |
-| `CI_MERGE_REQUEST_PROJECT_PATH` | 11.6 | all | The path of the project of the merge request. For example `namespace/awesome-project`. |
-| `CI_MERGE_REQUEST_PROJECT_URL` | 11.6 | all | The URL of the project of the merge request. For example, `http://192.168.10.15:3000/namespace/awesome-project`. |
-| `CI_MERGE_REQUEST_REF_PATH` | 11.6 | all | The ref path of the merge request. For example, `refs/merge-requests/1/head`. |
-| `CI_MERGE_REQUEST_SOURCE_BRANCH_NAME` | 11.6 | all | The source branch name of the merge request. |
-| `CI_MERGE_REQUEST_SOURCE_BRANCH_SHA` | 11.9 | all | The HEAD SHA of the source branch of the merge request. The variable is empty in merge request pipelines. The SHA is present only in [merged results pipelines](../pipelines/merged_results_pipelines.md). |
-| `CI_MERGE_REQUEST_SOURCE_PROJECT_ID` | 11.6 | all | The ID of the source project of the merge request. |
-| `CI_MERGE_REQUEST_SOURCE_PROJECT_PATH` | 11.6 | all | The path of the source project of the merge request. |
-| `CI_MERGE_REQUEST_SOURCE_PROJECT_URL` | 11.6 | all | The URL of the source project of the merge request. |
-| `CI_MERGE_REQUEST_TARGET_BRANCH_NAME` | 11.6 | all | The target branch name of the merge request. |
-| `CI_MERGE_REQUEST_TARGET_BRANCH_SHA` | 11.9 | all | The HEAD SHA of the target branch of the merge request. The variable is empty in merge request pipelines. The SHA is present only in [merged results pipelines](../pipelines/merged_results_pipelines.md). |
-| `CI_MERGE_REQUEST_TITLE` | 11.9 | all | The title of the merge request. |
-| `CI_MERGE_REQUEST_EVENT_TYPE` | 12.3 | all | The event type of the merge request. Can be `detached`, `merged_result` or `merge_train`. |
-| `CI_MERGE_REQUEST_DIFF_ID` | 13.7 | all | The version of the merge request diff. |
-| `CI_MERGE_REQUEST_DIFF_BASE_SHA` | 13.7 | all | The base SHA of the merge request diff. |
+| Variable | GitLab | Runner | Description |
+|---------------------------------------------|--------|--------|-------------|
+| `CI_MERGE_REQUEST_APPROVED` | 14.1 | all | Approval status of the merge request. `true` when [merge request approvals](../../user/project/merge_requests/approvals/index.md) is available and the merge request has been approved. |
+| `CI_MERGE_REQUEST_ASSIGNEES` | 11.9 | all | Comma-separated list of usernames of assignees for the merge request. |
+| `CI_MERGE_REQUEST_ID` | 11.6 | all | The instance-level ID of the merge request. This is a unique ID across all projects on GitLab. |
+| `CI_MERGE_REQUEST_IID` | 11.6 | all | The project-level IID (internal ID) of the merge request. This ID is unique for the current project. |
+| `CI_MERGE_REQUEST_LABELS` | 11.9 | all | Comma-separated label names of the merge request. |
+| `CI_MERGE_REQUEST_MILESTONE` | 11.9 | all | The milestone title of the merge request. |
+| `CI_MERGE_REQUEST_PROJECT_ID` | 11.6 | all | The ID of the project of the merge request. |
+| `CI_MERGE_REQUEST_PROJECT_PATH` | 11.6 | all | The path of the project of the merge request. For example `namespace/awesome-project`. |
+| `CI_MERGE_REQUEST_PROJECT_URL` | 11.6 | all | The URL of the project of the merge request. For example, `http://192.168.10.15:3000/namespace/awesome-project`. |
+| `CI_MERGE_REQUEST_REF_PATH` | 11.6 | all | The ref path of the merge request. For example, `refs/merge-requests/1/head`. |
+| `CI_MERGE_REQUEST_SOURCE_BRANCH_NAME` | 11.6 | all | The source branch name of the merge request. |
+| `CI_MERGE_REQUEST_SOURCE_BRANCH_SHA` | 11.9 | all | The HEAD SHA of the source branch of the merge request. The variable is empty in merge request pipelines. The SHA is present only in [merged results pipelines](../pipelines/merged_results_pipelines.md). |
+| `CI_MERGE_REQUEST_SOURCE_PROJECT_ID` | 11.6 | all | The ID of the source project of the merge request. |
+| `CI_MERGE_REQUEST_SOURCE_PROJECT_PATH` | 11.6 | all | The path of the source project of the merge request. |
+| `CI_MERGE_REQUEST_SOURCE_PROJECT_URL` | 11.6 | all | The URL of the source project of the merge request. |
+| `CI_MERGE_REQUEST_TARGET_BRANCH_NAME` | 11.6 | all | The target branch name of the merge request. |
+| `CI_MERGE_REQUEST_TARGET_BRANCH_PROTECTED` | 15.2 | all | The protection status for the target branch of the merge request. |
+| `CI_MERGE_REQUEST_TARGET_BRANCH_SHA` | 11.9 | all | The HEAD SHA of the target branch of the merge request. The variable is empty in merge request pipelines. The SHA is present only in [merged results pipelines](../pipelines/merged_results_pipelines.md). |
+| `CI_MERGE_REQUEST_TITLE` | 11.9 | all | The title of the merge request. |
+| `CI_MERGE_REQUEST_EVENT_TYPE` | 12.3 | all | The event type of the merge request. Can be `detached`, `merged_result` or `merge_train`. |
+| `CI_MERGE_REQUEST_DIFF_ID` | 13.7 | all | The version of the merge request diff. |
+| `CI_MERGE_REQUEST_DIFF_BASE_SHA` | 13.7 | all | The base SHA of the merge request diff. |
## Predefined variables for external pull request pipelines
diff --git a/doc/development/feature_flags/index.md b/doc/development/feature_flags/index.md
index d21a46142a2..31ab6ca3c32 100644
--- a/doc/development/feature_flags/index.md
+++ b/doc/development/feature_flags/index.md
@@ -520,6 +520,8 @@ Feature.remove(:feature_flag_name)
```
- Any change behind a feature flag that is **enabled** by default **should** have a changelog entry.
+- The changelog for a feature flag should describe the feature and not the
+ flag, unless a default on feature flag is removed keeping the new code (`other` in the flowchart above).
## Feature flags in tests
diff --git a/lib/api/helpers/protected_tags_helpers.rb b/lib/api/helpers/protected_tags_helpers.rb
new file mode 100644
index 00000000000..cad4ec8d5bd
--- /dev/null
+++ b/lib/api/helpers/protected_tags_helpers.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module API
+ module Helpers
+ module ProtectedTagsHelpers
+ extend ActiveSupport::Concern
+ extend Grape::API::Helpers
+
+ params :optional_params_ee do
+ end
+ end
+ end
+end
+
+API::Helpers::ProtectedTagsHelpers.prepend_mod_with('API::Helpers::ProtectedTagsHelpers')
diff --git a/lib/api/protected_tags.rb b/lib/api/protected_tags.rb
index b9385df1f8d..4611ee58479 100644
--- a/lib/api/protected_tags.rb
+++ b/lib/api/protected_tags.rb
@@ -10,6 +10,8 @@ module API
feature_category :source_code_management
+ helpers Helpers::ProtectedTagsHelpers
+
params do
requires :id, type: String, desc: 'The ID of a project'
end
@@ -50,14 +52,15 @@ module API
end
params do
requires :name, type: String, desc: 'The name of the protected tag'
- optional :create_access_level, type: Integer, default: Gitlab::Access::MAINTAINER,
+ optional :create_access_level, type: Integer,
values: ProtectedTag::CreateAccessLevel.allowed_access_levels,
desc: 'Access levels allowed to create (defaults: `40`, maintainer access level)'
+ use :optional_params_ee
end
post ':id/protected_tags' do
protected_tags_params = {
name: params[:name],
- create_access_levels_attributes: [{ access_level: params[:create_access_level] }]
+ create_access_levels_attributes: ::ProtectedRefs::AccessLevelParams.new(:create, params).access_levels
}
protected_tag = ::ProtectedTags::CreateService.new(user_project,
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index a7e9a6a6c75..abba06393da 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1979,6 +1979,12 @@ msgstr ""
msgid "AccessibilityReport|The accessibility scanning found an error of the following type: %{code}"
msgstr ""
+msgid "Accessible by any user who is logged in."
+msgstr ""
+
+msgid "Accessible by anyone, regardless of authentication."
+msgstr ""
+
msgid "Account"
msgstr ""
@@ -27093,6 +27099,9 @@ msgstr ""
msgid "Only Task can be assigned as a child in hierarchy."
msgstr ""
+msgid "Only accessible by %{membersPageLinkStart}project members%{membersPageLinkEnd}. Membership must be explicitly granted to each user."
+msgstr ""
+
msgid "Only active projects show up in the search and on the dashboard."
msgstr ""
@@ -38859,21 +38868,12 @@ msgstr ""
msgid "The project can be accessed by any logged in user except external users."
msgstr ""
-msgid "The project can be accessed by any user who is logged in."
-msgstr ""
-
-msgid "The project can be accessed by anyone, regardless of authentication."
-msgstr ""
-
msgid "The project can be accessed without any authentication."
msgstr ""
msgid "The project has already been added to your dashboard."
msgstr ""
-msgid "The project is accessible only by members of the project. Access must be granted explicitly to each user."
-msgstr ""
-
msgid "The project is still being deleted. Please try again later."
msgstr ""
diff --git a/spec/controllers/projects/pipelines/tests_controller_spec.rb b/spec/controllers/projects/pipelines/tests_controller_spec.rb
index 2db54dbe671..deb96cc5bf9 100644
--- a/spec/controllers/projects/pipelines/tests_controller_spec.rb
+++ b/spec/controllers/projects/pipelines/tests_controller_spec.rb
@@ -45,6 +45,21 @@ RSpec.describe Projects::Pipelines::TestsController do
pipeline.job_artifacts.first.update!(expire_at: Date.yesterday)
end
+ it 'renders test suite', :aggregate_failures do
+ get_tests_show_json(build_ids)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['name']).to eq('test')
+ expect(json_response['total_count']).to eq(3)
+ expect(json_response['test_cases'].size).to eq(3)
+ end
+ end
+
+ context 'when artifacts do not exist' do
+ before do
+ pipeline.job_artifacts.each(&:destroy)
+ end
+
it 'renders not_found errors', :aggregate_failures do
get_tests_show_json(build_ids)
@@ -68,7 +83,6 @@ RSpec.describe Projects::Pipelines::TestsController do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['name']).to eq('test')
- expect(json_response['artifacts_expired']).to be_falsey
# Each test failure in this pipeline has a matching failure in the default branch
recent_failures = json_response['test_cases'].map { |tc| tc['recent_failures'] }
diff --git a/spec/features/projects/settings/visibility_settings_spec.rb b/spec/features/projects/settings/visibility_settings_spec.rb
index becb30c02b7..fc78b5b5769 100644
--- a/spec/features/projects/settings/visibility_settings_spec.rb
+++ b/spec/features/projects/settings/visibility_settings_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe 'Projects > Settings > Visibility settings', :js do
visibility_select_container = find('.project-visibility-setting')
expect(visibility_select_container.find('select').value).to eq project.visibility_level.to_s
- expect(visibility_select_container).to have_content 'The project can be accessed by anyone, regardless of authentication.'
+ expect(visibility_select_container).to have_content 'Accessible by anyone, regardless of authentication.'
end
it 'project visibility description updates on change' do
@@ -25,7 +25,7 @@ RSpec.describe 'Projects > Settings > Visibility settings', :js do
visibility_select.select('Private')
expect(visibility_select.value).to eq '0'
- expect(visibility_select_container).to have_content 'Access must be granted explicitly to each user.'
+ expect(visibility_select_container).to have_content 'Only accessible by project members. Membership must be explicitly granted to each user.'
end
context 'merge requests select' do
@@ -86,7 +86,7 @@ RSpec.describe 'Projects > Settings > Visibility settings', :js do
visibility_select_container = find('.project-visibility-setting')
expect(visibility_select_container).to have_selector 'select[name="project[visibility_level]"]:disabled'
- expect(visibility_select_container).to have_content 'The project can be accessed by anyone, regardless of authentication.'
+ expect(visibility_select_container).to have_content 'Accessible by anyone, regardless of authentication.'
end
context 'disable email notifications' do
diff --git a/spec/frontend/__helpers__/performance.js b/spec/frontend/__helpers__/performance.js
new file mode 100644
index 00000000000..3bdf163c22b
--- /dev/null
+++ b/spec/frontend/__helpers__/performance.js
@@ -0,0 +1,8 @@
+// FIXME(vslobodin): Remove this stub once we have migrated to Jest 28.
+// NOTE: Do not try to optimize these stubs as Jest 27 overwrites
+// the "global.performance" object in every suite where fake timers are enabled.
+export const stubPerformanceWebAPI = () => {
+ global.performance.getEntriesByName = () => [];
+ global.performance.mark = () => {};
+ global.performance.measure = () => {};
+};
diff --git a/spec/frontend/design_management/components/image_spec.js b/spec/frontend/design_management/components/image_spec.js
index e27b2bc9fa5..65ee0ae6238 100644
--- a/spec/frontend/design_management/components/image_spec.js
+++ b/spec/frontend/design_management/components/image_spec.js
@@ -1,6 +1,7 @@
import { GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
+import { stubPerformanceWebAPI } from 'helpers/performance';
import DesignImage from '~/design_management/components/image.vue';
describe('Design management large image component', () => {
@@ -15,6 +16,10 @@ describe('Design management large image component', () => {
wrapper.setData(data);
}
+ beforeEach(() => {
+ stubPerformanceWebAPI();
+ });
+
afterEach(() => {
wrapper.destroy();
});
diff --git a/spec/frontend/diffs/components/app_spec.js b/spec/frontend/diffs/components/app_spec.js
index 76e4a944d87..96f2ac1692c 100644
--- a/spec/frontend/diffs/components/app_spec.js
+++ b/spec/frontend/diffs/components/app_spec.js
@@ -18,6 +18,7 @@ import HiddenFilesWarning from '~/diffs/components/hidden_files_warning.vue';
import axios from '~/lib/utils/axios_utils';
import * as urlUtils from '~/lib/utils/url_utility';
+import { stubPerformanceWebAPI } from 'helpers/performance';
import createDiffsStore from '../create_diffs_store';
import diffsMockData from '../mock_data/merge_request_diffs';
@@ -79,6 +80,7 @@ describe('diffs/components/app', () => {
}
beforeEach(() => {
+ stubPerformanceWebAPI();
// setup globals (needed for component to mount :/)
window.mrTabs = {
resetViewContainer: jest.fn(),
diff --git a/spec/frontend/environment.js b/spec/frontend/environment.js
index 8465b57c660..dc1c1dfbe4a 100644
--- a/spec/frontend/environment.js
+++ b/spec/frontend/environment.js
@@ -67,12 +67,6 @@ class CustomEnvironment extends JSDOMEnvironment {
// Expose the jsdom (created in super class) to the global so that we can call reconfigure({ url: '' }) to properly set `window.location`
this.global.jsdom = this.dom;
- Object.assign(this.global.performance, {
- mark: () => null,
- measure: () => null,
- getEntriesByName: () => [],
- });
-
//
// Monaco-related environment variables
//
diff --git a/spec/frontend/ide/components/ide_spec.js b/spec/frontend/ide/components/ide_spec.js
index 37b42001a80..9172c69b10e 100644
--- a/spec/frontend/ide/components/ide_spec.js
+++ b/spec/frontend/ide/components/ide_spec.js
@@ -2,6 +2,7 @@ import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
import waitForPromises from 'helpers/wait_for_promises';
+import { stubPerformanceWebAPI } from 'helpers/performance';
import CannotPushCodeAlert from '~/ide/components/cannot_push_code_alert.vue';
import ErrorMessage from '~/ide/components/error_message.vue';
import Ide from '~/ide/components/ide.vue';
@@ -40,6 +41,8 @@ describe('WebIDE', () => {
const findAlert = () => wrapper.findComponent(CannotPushCodeAlert);
beforeEach(() => {
+ stubPerformanceWebAPI();
+
store = createStore();
});
diff --git a/spec/frontend/ide/components/repo_editor_spec.js b/spec/frontend/ide/components/repo_editor_spec.js
index 593fe6bf5a8..7a0bcda1b7a 100644
--- a/spec/frontend/ide/components/repo_editor_spec.js
+++ b/spec/frontend/ide/components/repo_editor_spec.js
@@ -6,6 +6,7 @@ import Vuex from 'vuex';
import { shallowMount } from '@vue/test-utils';
import '~/behaviors/markdown/render_gfm';
import waitForPromises from 'helpers/wait_for_promises';
+import { stubPerformanceWebAPI } from 'helpers/performance';
import { exampleConfigs, exampleFiles } from 'jest/ide/lib/editorconfig/mock_data';
import { EDITOR_CODE_INSTANCE_FN, EDITOR_DIFF_INSTANCE_FN } from '~/editor/constants';
import { EditorMarkdownExtension } from '~/editor/extensions/source_editor_markdown_ext';
@@ -130,6 +131,8 @@ describe('RepoEditor', () => {
const findPreviewTab = () => wrapper.find('[data-testid="preview-tab"]');
beforeEach(() => {
+ stubPerformanceWebAPI();
+
createInstanceSpy = jest.spyOn(SourceEditor.prototype, EDITOR_CODE_INSTANCE_FN);
createDiffInstanceSpy = jest.spyOn(SourceEditor.prototype, EDITOR_DIFF_INSTANCE_FN);
createModelSpy = jest.spyOn(monacoEditor, 'createModel');
diff --git a/spec/frontend/ide/ide_router_spec.js b/spec/frontend/ide/ide_router_spec.js
index cd10812f8ea..adbdba1b11e 100644
--- a/spec/frontend/ide/ide_router_spec.js
+++ b/spec/frontend/ide/ide_router_spec.js
@@ -1,4 +1,5 @@
import waitForPromises from 'helpers/wait_for_promises';
+import { stubPerformanceWebAPI } from 'helpers/performance';
import { createRouter } from '~/ide/ide_router';
import { createStore } from '~/ide/stores';
@@ -12,6 +13,8 @@ describe('IDE router', () => {
let router;
beforeEach(() => {
+ stubPerformanceWebAPI();
+
window.history.replaceState({}, '', '/');
store = createStore();
router = createRouter(store, DEFAULT_BRANCH);
diff --git a/spec/frontend/ide/stores/actions/file_spec.js b/spec/frontend/ide/stores/actions/file_spec.js
index 45d1beea3f8..6c1dee1e5ca 100644
--- a/spec/frontend/ide/stores/actions/file_spec.js
+++ b/spec/frontend/ide/stores/actions/file_spec.js
@@ -7,6 +7,7 @@ import { createStore } from '~/ide/stores';
import * as actions from '~/ide/stores/actions/file';
import * as types from '~/ide/stores/mutation_types';
import axios from '~/lib/utils/axios_utils';
+import { stubPerformanceWebAPI } from 'helpers/performance';
import { file, createTriggerRenameAction, createTriggerUpdatePayload } from '../../helpers';
const ORIGINAL_CONTENT = 'original content';
@@ -19,6 +20,8 @@ describe('IDE store file actions', () => {
let router;
beforeEach(() => {
+ stubPerformanceWebAPI();
+
mock = new MockAdapter(axios);
originalGon = window.gon;
window.gon = {
diff --git a/spec/frontend/ide/stores/actions/merge_request_spec.js b/spec/frontend/ide/stores/actions/merge_request_spec.js
index 5592e2664c4..abc3ba5b0a2 100644
--- a/spec/frontend/ide/stores/actions/merge_request_spec.js
+++ b/spec/frontend/ide/stores/actions/merge_request_spec.js
@@ -1,5 +1,6 @@
import MockAdapter from 'axios-mock-adapter';
import { range } from 'lodash';
+import { stubPerformanceWebAPI } from 'helpers/performance';
import { TEST_HOST } from 'helpers/test_constants';
import testAction from 'helpers/vuex_action_helper';
import createFlash from '~/flash';
@@ -35,6 +36,8 @@ describe('IDE store merge request actions', () => {
let mock;
beforeEach(() => {
+ stubPerformanceWebAPI();
+
store = createStore();
mock = new MockAdapter(axios);
diff --git a/spec/frontend/ide/stores/actions/tree_spec.js b/spec/frontend/ide/stores/actions/tree_spec.js
index fc44cbb21ae..d43393875eb 100644
--- a/spec/frontend/ide/stores/actions/tree_spec.js
+++ b/spec/frontend/ide/stores/actions/tree_spec.js
@@ -1,4 +1,5 @@
import MockAdapter from 'axios-mock-adapter';
+import { stubPerformanceWebAPI } from 'helpers/performance';
import { TEST_HOST } from 'helpers/test_constants';
import testAction from 'helpers/vuex_action_helper';
import { createRouter } from '~/ide/ide_router';
@@ -24,6 +25,8 @@ describe('Multi-file store tree actions', () => {
};
beforeEach(() => {
+ stubPerformanceWebAPI();
+
store = createStore();
router = createRouter(store);
jest.spyOn(router, 'push').mockImplementation();
diff --git a/spec/frontend/ide/stores/actions_spec.js b/spec/frontend/ide/stores/actions_spec.js
index 3889c4f11c3..f6d54491d77 100644
--- a/spec/frontend/ide/stores/actions_spec.js
+++ b/spec/frontend/ide/stores/actions_spec.js
@@ -1,4 +1,5 @@
import MockAdapter from 'axios-mock-adapter';
+import { stubPerformanceWebAPI } from 'helpers/performance';
import testAction from 'helpers/vuex_action_helper';
import eventHub from '~/ide/eventhub';
import { createRouter } from '~/ide/ide_router';
@@ -34,6 +35,8 @@ describe('Multi-file store actions', () => {
let router;
beforeEach(() => {
+ stubPerformanceWebAPI();
+
store = createStore();
router = createRouter(store);
diff --git a/spec/frontend/merge_request_tabs_spec.js b/spec/frontend/merge_request_tabs_spec.js
index f0f051cbc8b..96ac655381b 100644
--- a/spec/frontend/merge_request_tabs_spec.js
+++ b/spec/frontend/merge_request_tabs_spec.js
@@ -2,6 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
import $ from 'jquery';
import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import initMrPage from 'helpers/init_vue_mr_page_helper';
+import { stubPerformanceWebAPI } from 'helpers/performance';
import axios from '~/lib/utils/axios_utils';
import MergeRequestTabs from '~/merge_request_tabs';
import '~/lib/utils/common_utils';
@@ -24,6 +25,8 @@ describe('MergeRequestTabs', () => {
};
beforeEach(() => {
+ stubPerformanceWebAPI();
+
initMrPage();
testContext.class = new MergeRequestTabs({ stubLocation });
@@ -350,10 +353,6 @@ describe('MergeRequestTabs', () => {
describe('when switching tabs', () => {
const SCROLL_TOP = 100;
- beforeAll(() => {
- jest.useFakeTimers();
- });
-
beforeEach(() => {
jest.spyOn(window, 'scrollTo').mockImplementation(() => {});
testContext.class.mergeRequestTabs = document.createElement('div');
@@ -362,10 +361,6 @@ describe('MergeRequestTabs', () => {
testContext.class.scrollPositions = { newTab: SCROLL_TOP };
});
- afterAll(() => {
- jest.useRealTimers();
- });
-
it('scrolls to the stored position, if one is stored', () => {
testContext.class.tabShown('newTab');
diff --git a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js
index 46f83ac89e5..85660d09baa 100644
--- a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js
+++ b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js
@@ -51,6 +51,7 @@ const defaultProps = {
requestCveAvailable: true,
confirmationPhrase: 'my-fake-project',
showVisibilityConfirmModal: false,
+ membersPagePath: '/my-fake-project/-/project_members',
};
const FEATURE_ACCESS_LEVEL_ANONYMOUS = 30;
@@ -59,7 +60,7 @@ describe('Settings Panel', () => {
let wrapper;
const mountComponent = (
- { currentSettings = {}, glFeatures = {}, ...customProps } = {},
+ { currentSettings = {}, glFeatures = {}, stubs = {}, ...customProps } = {},
mountFn = shallowMount,
) => {
const propsData = {
@@ -76,6 +77,7 @@ describe('Settings Panel', () => {
...glFeatures,
},
},
+ stubs,
});
};
@@ -176,7 +178,7 @@ describe('Settings Panel', () => {
);
it('should set the visibility level description based upon the selected visibility level', () => {
- wrapper = mountComponent();
+ wrapper = mountComponent({ stubs: { GlSprintf } });
findProjectVisibilityLevelInput().setValue(visibilityOptions.INTERNAL);
diff --git a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
index 49d64c6eac0..34784a88ba9 100644
--- a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
+++ b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
@@ -5,6 +5,7 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
+import { stubPerformanceWebAPI } from 'helpers/performance';
import waitForPromises from 'helpers/wait_for_promises';
import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
import getUserCallouts from '~/graphql_shared/queries/get_user_callouts.query.graphql';
@@ -502,9 +503,7 @@ describe('Pipeline graph wrapper', () => {
describe('when no duration is obtained', () => {
beforeEach(async () => {
- jest.spyOn(window.performance, 'getEntriesByName').mockImplementation(() => {
- return [];
- });
+ stubPerformanceWebAPI();
createComponentWithApollo({
provide: {
diff --git a/spec/frontend/snippets/components/edit_spec.js b/spec/frontend/snippets/components/edit_spec.js
index 8a767765149..f49ceb2fede 100644
--- a/spec/frontend/snippets/components/edit_spec.js
+++ b/spec/frontend/snippets/components/edit_spec.js
@@ -5,6 +5,7 @@ import { merge } from 'lodash';
import VueApollo, { ApolloMutation } from 'vue-apollo';
import { useFakeDate } from 'helpers/fake_date';
import createMockApollo from 'helpers/mock_apollo_helper';
+import { stubPerformanceWebAPI } from 'helpers/performance';
import waitForPromises from 'helpers/wait_for_promises';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import GetSnippetQuery from 'shared_queries/snippet/snippet.query.graphql';
@@ -96,6 +97,8 @@ describe('Snippet Edit app', () => {
const originalRelativeUrlRoot = gon.relative_url_root;
beforeEach(() => {
+ stubPerformanceWebAPI();
+
getSpy = jest.fn().mockResolvedValue(createQueryResponse());
// See `mutateSpy` declaration comment for why we send a key
diff --git a/spec/frontend/snippets/components/show_spec.js b/spec/frontend/snippets/components/show_spec.js
index c73bf8f80a2..b29ed97099f 100644
--- a/spec/frontend/snippets/components/show_spec.js
+++ b/spec/frontend/snippets/components/show_spec.js
@@ -12,6 +12,7 @@ import {
SNIPPET_VISIBILITY_PUBLIC,
} from '~/snippets/constants';
import CloneDropdownButton from '~/vue_shared/components/clone_dropdown.vue';
+import { stubPerformanceWebAPI } from 'helpers/performance';
describe('Snippet view app', () => {
let wrapper;
@@ -45,6 +46,10 @@ describe('Snippet view app', () => {
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findEmbedDropdown = () => wrapper.findComponent(EmbedDropdown);
+ beforeEach(() => {
+ stubPerformanceWebAPI();
+ });
+
afterEach(() => {
wrapper.destroy();
});
diff --git a/spec/frontend_integration/diffs/diffs_interopability_spec.js b/spec/frontend_integration/diffs/diffs_interopability_spec.js
index 064e3d21180..8e9bc4f0a5f 100644
--- a/spec/frontend_integration/diffs/diffs_interopability_spec.js
+++ b/spec/frontend_integration/diffs/diffs_interopability_spec.js
@@ -1,6 +1,7 @@
import { waitFor } from '@testing-library/dom';
import setWindowLocation from 'helpers/set_window_location_helper';
import { TEST_HOST } from 'helpers/test_constants';
+import { stubPerformanceWebAPI } from 'helpers/performance';
import initDiffsApp from '~/diffs';
import { createStore } from '~/mr_notes/stores';
import {
@@ -74,6 +75,10 @@ const startDiffsApp = () => {
describe('diffs third party interoperability', () => {
let vm;
+ beforeEach(() => {
+ stubPerformanceWebAPI();
+ });
+
afterEach(() => {
vm.$destroy();
document.body.innerHTML = '';
diff --git a/spec/frontend_integration/ide/ide_integration_spec.js b/spec/frontend_integration/ide/ide_integration_spec.js
index a002ce91deb..da48c600764 100644
--- a/spec/frontend_integration/ide/ide_integration_spec.js
+++ b/spec/frontend_integration/ide/ide_integration_spec.js
@@ -3,8 +3,9 @@ import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import { setTestTimeout } from 'helpers/timeout';
import waitForPromises from 'helpers/wait_for_promises';
import { waitForText } from 'helpers/wait_for_text';
-import { createCommitId } from 'test_helpers/factories/commit_id';
import { useOverclockTimers } from 'test_helpers/utils/overclock_timers';
+import { createCommitId } from 'test_helpers/factories/commit_id';
+import { stubPerformanceWebAPI } from 'helpers/performance';
import * as ideHelper from './helpers/ide_helper';
import startWebIDE from './helpers/start';
@@ -15,6 +16,7 @@ describe('WebIDE', () => {
let container;
beforeEach(() => {
+ stubPerformanceWebAPI();
// For some reason these tests were timing out in CI.
// We will investigate in https://gitlab.com/gitlab-org/gitlab/-/issues/298714
setTestTimeout(20000);
diff --git a/spec/frontend_integration/ide/user_opens_file_spec.js b/spec/frontend_integration/ide/user_opens_file_spec.js
index c3131f6ad45..af6e2f3b44b 100644
--- a/spec/frontend_integration/ide/user_opens_file_spec.js
+++ b/spec/frontend_integration/ide/user_opens_file_spec.js
@@ -1,6 +1,7 @@
import { screen } from '@testing-library/dom';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import { useOverclockTimers } from 'test_helpers/utils/overclock_timers';
+import { stubPerformanceWebAPI } from 'helpers/performance';
import * as ideHelper from './helpers/ide_helper';
import startWebIDE from './helpers/start';
@@ -11,6 +12,7 @@ describe('IDE: User opens a file in the Web IDE', () => {
let container;
beforeEach(async () => {
+ stubPerformanceWebAPI();
setHTMLFixture('<div class="webide-container"></div>');
container = document.querySelector('.webide-container');
diff --git a/spec/frontend_integration/ide/user_opens_ide_spec.js b/spec/frontend_integration/ide/user_opens_ide_spec.js
index b2b85452451..552888f04a5 100644
--- a/spec/frontend_integration/ide/user_opens_ide_spec.js
+++ b/spec/frontend_integration/ide/user_opens_ide_spec.js
@@ -1,6 +1,7 @@
import { screen } from '@testing-library/dom';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import { useOverclockTimers } from 'test_helpers/utils/overclock_timers';
+import { stubPerformanceWebAPI } from 'helpers/performance';
import * as ideHelper from './helpers/ide_helper';
import startWebIDE from './helpers/start';
@@ -11,6 +12,8 @@ describe('IDE: User opens IDE', () => {
let container;
beforeEach(() => {
+ stubPerformanceWebAPI();
+
setHTMLFixture('<div class="webide-container"></div>');
container = document.querySelector('.webide-container');
});
diff --git a/spec/frontend_integration/ide/user_opens_mr_spec.js b/spec/frontend_integration/ide/user_opens_mr_spec.js
index 084aae9f297..af0276a5055 100644
--- a/spec/frontend_integration/ide/user_opens_mr_spec.js
+++ b/spec/frontend_integration/ide/user_opens_mr_spec.js
@@ -2,6 +2,7 @@ import { basename } from 'path';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import { getMergeRequests, getMergeRequestWithChanges } from 'test_helpers/fixtures';
import { useOverclockTimers } from 'test_helpers/utils/overclock_timers';
+import { stubPerformanceWebAPI } from 'helpers/performance';
import * as ideHelper from './helpers/ide_helper';
import startWebIDE from './helpers/start';
@@ -16,6 +17,8 @@ describe('IDE: User opens Merge Request', () => {
let changes;
beforeEach(async () => {
+ stubPerformanceWebAPI();
+
const [{ iid: mrId }] = getMergeRequests();
changes = getRelevantChanges();
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 42c97b61d19..b7cc8c217a4 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -969,6 +969,10 @@ RSpec.describe ProjectsHelper do
containerRegistryAccessLevel: project.project_feature.container_registry_access_level
)
end
+
+ it 'includes membersPagePath' do
+ expect(subject).to include(membersPagePath: project_project_members_path(project))
+ end
end
describe '#project_classes' do
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index e8973de5fd5..12053d56467 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -1041,6 +1041,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
'CI_MERGE_REQUEST_PROJECT_PATH' => merge_request.project.full_path,
'CI_MERGE_REQUEST_PROJECT_URL' => merge_request.project.web_url,
'CI_MERGE_REQUEST_TARGET_BRANCH_NAME' => merge_request.target_branch.to_s,
+ 'CI_MERGE_REQUEST_TARGET_BRANCH_PROTECTED' => ProtectedBranch.protected?(merge_request.target_project, merge_request.target_branch).to_s,
'CI_MERGE_REQUEST_TARGET_BRANCH_SHA' => '',
'CI_MERGE_REQUEST_SOURCE_PROJECT_ID' => merge_request.source_project.id.to_s,
'CI_MERGE_REQUEST_SOURCE_PROJECT_PATH' => merge_request.source_project.full_path,
@@ -1137,6 +1138,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
'CI_MERGE_REQUEST_PROJECT_PATH' => merge_request.project.full_path,
'CI_MERGE_REQUEST_PROJECT_URL' => merge_request.project.web_url,
'CI_MERGE_REQUEST_TARGET_BRANCH_NAME' => merge_request.target_branch.to_s,
+ 'CI_MERGE_REQUEST_TARGET_BRANCH_PROTECTED' => ProtectedBranch.protected?(merge_request.target_project, merge_request.target_branch).to_s,
'CI_MERGE_REQUEST_TARGET_BRANCH_SHA' => merge_request.target_branch_sha,
'CI_MERGE_REQUEST_SOURCE_PROJECT_ID' => merge_request.source_project.id.to_s,
'CI_MERGE_REQUEST_SOURCE_PROJECT_PATH' => merge_request.source_project.full_path,
@@ -4851,13 +4853,13 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
- describe '#has_expired_test_reports?' do
- subject { pipeline.has_expired_test_reports? }
+ describe '#has_test_reports?' do
+ subject { pipeline.has_test_reports? }
let(:pipeline) { create(:ci_pipeline, :success, :with_test_reports) }
context 'when artifacts are not expired' do
- it { is_expected.to be_falsey }
+ it { is_expected.to be_truthy }
end
context 'when artifacts are expired' do
@@ -4868,6 +4870,14 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
it { is_expected.to be_truthy }
end
+ context 'when artifacts are removed' do
+ before do
+ pipeline.job_artifacts.each(&:destroy)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+
context 'when the pipeline is still running' do
let(:pipeline) { create(:ci_pipeline, :running) }
diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb
index d2d7859e726..51351c9fdd1 100644
--- a/spec/models/remote_mirror_spec.rb
+++ b/spec/models/remote_mirror_spec.rb
@@ -5,6 +5,10 @@ require 'spec_helper'
RSpec.describe RemoteMirror, :mailer do
include GitHelpers
+ before do
+ stub_feature_flags(remote_mirror_no_delay: false)
+ end
+
describe 'URL validation' do
context 'with a valid URL' do
it 'is valid' do
@@ -343,6 +347,20 @@ RSpec.describe RemoteMirror, :mailer do
remote_mirror.sync
end
+
+ context 'when remote_mirror_no_delay is enabled' do
+ before do
+ stub_feature_flags(remote_mirror_no_delay: true)
+ end
+
+ it 'schedules a RepositoryUpdateRemoteMirrorWorker to run now' do
+ remote_mirror.last_update_started_at = Time.current - 30.seconds
+
+ expect(RepositoryUpdateRemoteMirrorWorker).to receive(:perform_async).with(remote_mirror.id, Time.current)
+
+ remote_mirror.sync
+ end
+ end
end
end
end
diff --git a/spec/requests/api/protected_tags_spec.rb b/spec/requests/api/protected_tags_spec.rb
index cc7261dafc9..84b7df86f31 100644
--- a/spec/requests/api/protected_tags_spec.rb
+++ b/spec/requests/api/protected_tags_spec.rb
@@ -3,9 +3,10 @@
require 'spec_helper'
RSpec.describe API::ProtectedTags do
- let(:user) { create(:user) }
- let!(:project) { create(:project, :repository) }
- let(:project2) { create(:project, path: 'project2', namespace: user.namespace) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:project2) { create(:project, path: 'project2', namespace: user.namespace) }
+
let(:protected_name) { 'feature' }
let(:tag_name) { protected_name }
let!(:protected_tag) do
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index b1b45e89c1b..48756cf774b 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -68,6 +68,9 @@ RSpec.describe Projects::ForkService do
it { expect(to_project.avatar.file).to be_exists }
it { expect(to_project.ci_config_path).to eq(@from_project.ci_config_path) }
it { expect(to_project.external_authorization_classification_label).to eq(@from_project.external_authorization_classification_label) }
+ it { expect(to_project.suggestion_commit_message).to eq(@from_project.suggestion_commit_message) }
+ it { expect(to_project.merge_commit_template).to eq(@from_project.merge_commit_template) }
+ it { expect(to_project.squash_commit_template).to eq(@from_project.squash_commit_template) }
# This test is here because we had a bug where the from-project lost its
# avatar after being forked.
diff --git a/spec/views/devise/sessions/new.html.haml_spec.rb b/spec/views/devise/sessions/new.html.haml_spec.rb
index e8232a2c067..b3cd1493149 100644
--- a/spec/views/devise/sessions/new.html.haml_spec.rb
+++ b/spec/views/devise/sessions/new.html.haml_spec.rb
@@ -63,6 +63,36 @@ RSpec.describe 'devise/sessions/new' do
end
end
+ describe 'Google Tag Manager' do
+ let!(:gtm_id) { 'GTM-WWKMTWS'}
+
+ subject { rendered }
+
+ before do
+ stub_devise
+ disable_captcha
+ stub_config(extra: { google_tag_manager_id: gtm_id, google_tag_manager_nonce_id: gtm_id })
+ end
+
+ describe 'when Google Tag Manager is enabled' do
+ before do
+ enable_gtm
+ render
+ end
+
+ it { is_expected.to match /www.googletagmanager.com/ }
+ end
+
+ describe 'when Google Tag Manager is disabled' do
+ before do
+ disable_gtm
+ render
+ end
+
+ it { is_expected.not_to match /www.googletagmanager.com/ }
+ end
+ end
+
def disable_other_signin_methods
allow(view).to receive(:password_authentication_enabled_for_web?).and_return(false)
allow(view).to receive(:omniauth_enabled?).and_return(false)
@@ -94,4 +124,12 @@ RSpec.describe 'devise/sessions/new' do
allow(view).to receive(:captcha_enabled?).and_return(false)
allow(view).to receive(:captcha_on_login_required?).and_return(false)
end
+
+ def disable_gtm
+ allow(view).to receive(:google_tag_manager_enabled?).and_return(false)
+ end
+
+ def enable_gtm
+ allow(view).to receive(:google_tag_manager_enabled?).and_return(true)
+ end
end