Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-07-13 21:10:15 +00:00
parent 7e064974b9
commit a269fb8e7c
54 changed files with 316 additions and 102 deletions

View File

@ -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

View File

@ -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 = {

View File

@ -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

View File

@ -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.'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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?

View File

@ -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

View File

@ -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')

View File

@ -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?

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
dc449f4ea28da3662fce663dcd5cdc9e37417e14b39e26897cc35a2bebfa22f0

View File

@ -0,0 +1 @@
755e06f8bd3a0a28820f6ec2ee52a39a7920eb9d8ae0315a8a179139c78645d9

View File

@ -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);

View File

@ -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. |

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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')

View File

@ -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,

View File

@ -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 ""

View File

@ -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'] }

View File

@ -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

View File

@ -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 = () => {};
};

View File

@ -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();
});

View File

@ -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(),

View File

@ -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
//

View File

@ -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();
});

View File

@ -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');

View File

@ -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);

View File

@ -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 = {

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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');

View File

@ -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);

View File

@ -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: {

View File

@ -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

View File

@ -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();
});

View File

@ -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 = '';

View File

@ -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);

View File

@ -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');

View File

@ -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');
});

View File

@ -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();

View File

@ -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

View File

@ -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) }

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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