Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
282e71d660
commit
8e359577a7
|
@ -9,6 +9,7 @@ module VerifiesWithEmail
|
|||
|
||||
included do
|
||||
prepend_before_action :verify_with_email, only: :create, unless: -> { two_factor_enabled? }
|
||||
skip_before_action :required_signup_info, only: :successful_verification
|
||||
end
|
||||
|
||||
def verify_with_email
|
||||
|
|
|
@ -32,6 +32,7 @@ class RegistrationsController < Devise::RegistrationsController
|
|||
|
||||
def create
|
||||
set_user_state
|
||||
token = set_custom_confirmation_token
|
||||
|
||||
super do |new_user|
|
||||
accept_pending_invitations if new_user.persisted?
|
||||
|
@ -39,6 +40,7 @@ class RegistrationsController < Devise::RegistrationsController
|
|||
persist_accepted_terms_if_required(new_user)
|
||||
set_role_required(new_user)
|
||||
track_experiment_event(new_user)
|
||||
send_custom_confirmation_instructions(new_user, token)
|
||||
|
||||
if pending_approval?
|
||||
NotificationService.new.new_instance_access_request(new_user)
|
||||
|
@ -118,8 +120,10 @@ class RegistrationsController < Devise::RegistrationsController
|
|||
def after_inactive_sign_up_path_for(resource)
|
||||
Gitlab::AppLogger.info(user_created_message)
|
||||
return new_user_session_path(anchor: 'login-pane') if resource.blocked_pending_approval?
|
||||
return dashboard_projects_path if Feature.enabled?(:soft_email_confirmation)
|
||||
return identity_verification_redirect_path if custom_confirmation_enabled?(resource)
|
||||
|
||||
Feature.enabled?(:soft_email_confirmation) ? dashboard_projects_path : users_almost_there_path(email: resource.email)
|
||||
users_almost_there_path(email: resource.email)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -236,6 +240,22 @@ class RegistrationsController < Devise::RegistrationsController
|
|||
# signing up and becoming users
|
||||
experiment(:logged_out_marketing_header, actor: new_user).track(:signed_up) if new_user.persisted?
|
||||
end
|
||||
|
||||
def identity_verification_redirect_path
|
||||
# overridden by EE module
|
||||
end
|
||||
|
||||
def custom_confirmation_enabled?(resource)
|
||||
# overridden by EE module
|
||||
end
|
||||
|
||||
def set_custom_confirmation_token
|
||||
# overridden by EE module
|
||||
end
|
||||
|
||||
def send_custom_confirmation_instructions(user, token)
|
||||
# overridden by EE module
|
||||
end
|
||||
end
|
||||
|
||||
RegistrationsController.prepend_mod_with('RegistrationsController')
|
||||
|
|
|
@ -18,3 +18,5 @@ module Types
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Types::BranchProtections::BaseAccessLevelType.prepend_mod
|
||||
|
|
|
@ -25,3 +25,5 @@ module Types
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Types::BranchRules::BranchProtectionType.prepend_mod_with('Types::BranchRules::BranchProtectionType')
|
||||
|
|
|
@ -13,3 +13,5 @@ module Emails
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Emails::IdentityVerification.prepend_mod
|
||||
|
|
|
@ -220,6 +220,7 @@ module Packages
|
|||
valid_until_field,
|
||||
rfc822_field('NotAutomatic', !@distribution.automatic, !@distribution.automatic),
|
||||
rfc822_field('ButAutomaticUpgrades', @distribution.automatic_upgrades, !@distribution.automatic && @distribution.automatic_upgrades),
|
||||
rfc822_field('Acquire-By-Hash', 'yes'),
|
||||
rfc822_field('Architectures', @distribution.architectures.map { |architecture| architecture.name }.sort.join(' ')),
|
||||
rfc822_field('Components', @distribution.components.map { |component| component.name }.sort.join(' ')),
|
||||
rfc822_field('Description', @distribution.description)
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
module Packages
|
||||
module Rpm
|
||||
module RepositoryMetadata
|
||||
class BaseBuilder
|
||||
def execute
|
||||
build_empty_structure
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_empty_structure
|
||||
Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
|
||||
xml.public_send(self.class::ROOT_TAG, self.class::ROOT_ATTRIBUTES) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end.to_xml
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
module Packages
|
||||
module Rpm
|
||||
module RepositoryMetadata
|
||||
class BuildFilelistXml < ::Packages::Rpm::RepositoryMetadata::BaseBuilder
|
||||
ROOT_TAG = 'filelists'
|
||||
ROOT_ATTRIBUTES = {
|
||||
xmlns: 'http://linux.duke.edu/metadata/filelists',
|
||||
packages: '0'
|
||||
}.freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
module Packages
|
||||
module Rpm
|
||||
module RepositoryMetadata
|
||||
class BuildOtherXml < ::Packages::Rpm::RepositoryMetadata::BaseBuilder
|
||||
ROOT_TAG = 'otherdata'
|
||||
ROOT_ATTRIBUTES = {
|
||||
xmlns: 'http://linux.duke.edu/metadata/other',
|
||||
packages: '0'
|
||||
}.freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
module Packages
|
||||
module Rpm
|
||||
module RepositoryMetadata
|
||||
class BuildPrimaryXml < ::Packages::Rpm::RepositoryMetadata::BaseBuilder
|
||||
ROOT_TAG = 'metadata'
|
||||
ROOT_ATTRIBUTES = {
|
||||
xmlns: 'http://linux.duke.edu/metadata/common',
|
||||
'xmlns:rpm': 'http://linux.duke.edu/metadata/rpm',
|
||||
packages: '0'
|
||||
}.freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,59 @@
|
|||
# frozen_string_literal: true
|
||||
module Packages
|
||||
module Rpm
|
||||
module RepositoryMetadata
|
||||
class BuildRepomdXml
|
||||
attr_reader :data
|
||||
|
||||
ROOT_ATTRIBUTES = {
|
||||
xmlns: 'http://linux.duke.edu/metadata/repo',
|
||||
'xmlns:rpm': 'http://linux.duke.edu/metadata/rpm'
|
||||
}.freeze
|
||||
|
||||
# Expected `data` structure
|
||||
#
|
||||
# data = {
|
||||
# filelists: {
|
||||
# checksum: { type: "sha256", value: "123" },
|
||||
# location: { href: "repodata/123-filelists.xml.gz" },
|
||||
# ...
|
||||
# },
|
||||
# ...
|
||||
# }
|
||||
def initialize(data)
|
||||
@data = data
|
||||
end
|
||||
|
||||
def execute
|
||||
build_repomd
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_repomd
|
||||
Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
|
||||
xml.repomd(ROOT_ATTRIBUTES) do
|
||||
xml.revision Time.now.to_i
|
||||
build_data_info(xml)
|
||||
end
|
||||
end.to_xml
|
||||
end
|
||||
|
||||
def build_data_info(xml)
|
||||
data.each do |filename, info|
|
||||
xml.data(type: filename) do
|
||||
build_file_info(info, xml)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def build_file_info(info, xml)
|
||||
info.each do |key, attributes|
|
||||
value = attributes.delete(:value)
|
||||
xml.public_send(key, value, attributes) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -27,10 +27,12 @@ module Users
|
|||
attr_reader :user
|
||||
|
||||
def verification_rate_limited?
|
||||
Gitlab::ApplicationRateLimiter.throttled?(:email_verification, scope: token)
|
||||
Gitlab::ApplicationRateLimiter.throttled?(:email_verification, scope: user[attr])
|
||||
end
|
||||
|
||||
def valid?
|
||||
return false unless token.present?
|
||||
|
||||
Devise.secure_compare(user[attr], digest)
|
||||
end
|
||||
|
||||
|
@ -59,11 +61,11 @@ module Users
|
|||
case reason
|
||||
when :rate_limited
|
||||
format(s_("IdentityVerification|You've reached the maximum amount of tries. "\
|
||||
'Wait %{interval} or resend a new code and try again.'), interval: email_verification_interval)
|
||||
'Wait %{interval} or send a new code and try again.'), interval: email_verification_interval)
|
||||
when :expired
|
||||
s_('IdentityVerification|The code has expired. Resend a new code and try again.')
|
||||
s_('IdentityVerification|The code has expired. Send a new code and try again.')
|
||||
when :invalid
|
||||
s_('IdentityVerification|The code is incorrect. Enter it again, or resend a new code.')
|
||||
s_('IdentityVerification|The code is incorrect. Enter it again, or send a new code.')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -8,4 +8,4 @@
|
|||
%p.gl-pt-2
|
||||
- redirect_url_start = '<a href="%{url}"">'.html_safe % { url: @redirect_url }
|
||||
- redirect_url_end = '</a>'.html_safe
|
||||
= html_escape(s_("IdentityVerification|Your account has been successfully verified. You'll be redirected to your account in just a moment or %{redirect_url_start}click here%{redirect_url_end} to refresh.")) % { redirect_url_start: redirect_url_start, redirect_url_end: redirect_url_end }
|
||||
= html_escape(s_("IdentityVerification|Your account has been successfully verified. You'll be redirected to your account in just a moment. You can also %{redirect_url_start}refresh the page%{redirect_url_end}.")) % { redirect_url_start: redirect_url_start, redirect_url_end: redirect_url_end }
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: identity_verification
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95722
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371389
|
||||
milestone: '15.4'
|
||||
type: development
|
||||
group: group::anti-abuse
|
||||
default_enabled: false
|
|
@ -10,6 +10,10 @@ value_type: number
|
|||
status: active
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: merge_request
|
||||
event: create
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
|
|
|
@ -10,6 +10,10 @@ value_type: number
|
|||
status: active
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: design_management_designs
|
||||
event: create
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
|
|
|
@ -10,6 +10,10 @@ value_type: number
|
|||
status: active
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: design_management_designs
|
||||
event: update
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
|
|
|
@ -10,6 +10,10 @@ value_type: number
|
|||
status: active
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: design_management_designs
|
||||
event: delete
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
---
|
||||
data_category: optional
|
||||
key_path: counts.diff_searches
|
||||
description: Total count of merge request diff searches
|
||||
product_section: dev
|
||||
|
@ -11,7 +12,10 @@ milestone: '14.2'
|
|||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66522
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
data_category: optional
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: diff
|
||||
event: searches
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
|
|
|
@ -11,6 +11,10 @@ value_type: number
|
|||
status: active
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: service_usage_data
|
||||
event: download_payload_click
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveUnusedAggregationColumns < Gitlab::Database::Migration[2.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
with_lock_retries do
|
||||
remove_column :analytics_cycle_analytics_aggregations, :last_full_run_processed_records
|
||||
remove_column :analytics_cycle_analytics_aggregations, :last_full_run_runtimes_in_seconds
|
||||
remove_column :analytics_cycle_analytics_aggregations, :last_full_run_issues_updated_at
|
||||
remove_column :analytics_cycle_analytics_aggregations, :last_full_run_mrs_updated_at
|
||||
remove_column :analytics_cycle_analytics_aggregations, :last_full_run_issues_id
|
||||
remove_column :analytics_cycle_analytics_aggregations, :last_full_run_merge_requests_id
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
add_column(:analytics_cycle_analytics_aggregations,
|
||||
:last_full_run_processed_records,
|
||||
:integer,
|
||||
array: true,
|
||||
default: [],
|
||||
null: false,
|
||||
if_not_exists: true)
|
||||
add_column(:analytics_cycle_analytics_aggregations,
|
||||
:last_full_run_runtimes_in_seconds,
|
||||
:integer,
|
||||
array: true,
|
||||
default: [],
|
||||
null: false,
|
||||
if_not_exists: true)
|
||||
add_column(:analytics_cycle_analytics_aggregations,
|
||||
:last_full_run_issues_updated_at,
|
||||
:datetime_with_timezone,
|
||||
if_not_exists: true)
|
||||
add_column(:analytics_cycle_analytics_aggregations,
|
||||
:last_full_run_mrs_updated_at,
|
||||
:datetime_with_timezone,
|
||||
if_not_exists: true)
|
||||
add_column(:analytics_cycle_analytics_aggregations,
|
||||
:last_full_run_issues_id,
|
||||
:integer,
|
||||
if_not_exists: true)
|
||||
add_column(:analytics_cycle_analytics_aggregations,
|
||||
:last_full_run_merge_requests_id,
|
||||
:integer,
|
||||
if_not_exists: true)
|
||||
end
|
||||
|
||||
add_check_constraint(:analytics_cycle_analytics_aggregations,
|
||||
'CARDINALITY(last_full_run_runtimes_in_seconds) <= 10',
|
||||
'chk_rails_7810292ec9')
|
||||
|
||||
add_check_constraint(:analytics_cycle_analytics_aggregations,
|
||||
'CARDINALITY(last_full_run_processed_records) <= 10',
|
||||
'chk_rails_8b9e89687c')
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
0bc8cd07786c950037731a0443e0d7da9c9692da39f13787b24769dbd122ba88
|
|
@ -10817,18 +10817,12 @@ CREATE TABLE analytics_cycle_analytics_aggregations (
|
|||
group_id bigint NOT NULL,
|
||||
incremental_runtimes_in_seconds integer[] DEFAULT '{}'::integer[] NOT NULL,
|
||||
incremental_processed_records integer[] DEFAULT '{}'::integer[] NOT NULL,
|
||||
last_full_run_runtimes_in_seconds integer[] DEFAULT '{}'::integer[] NOT NULL,
|
||||
last_full_run_processed_records integer[] DEFAULT '{}'::integer[] NOT NULL,
|
||||
last_incremental_issues_id integer,
|
||||
last_incremental_merge_requests_id integer,
|
||||
last_full_run_issues_id integer,
|
||||
last_full_run_merge_requests_id integer,
|
||||
last_incremental_run_at timestamp with time zone,
|
||||
last_incremental_issues_updated_at timestamp with time zone,
|
||||
last_incremental_merge_requests_updated_at timestamp with time zone,
|
||||
last_full_run_at timestamp with time zone,
|
||||
last_full_run_issues_updated_at timestamp with time zone,
|
||||
last_full_run_mrs_updated_at timestamp with time zone,
|
||||
last_consistency_check_updated_at timestamp with time zone,
|
||||
enabled boolean DEFAULT true NOT NULL,
|
||||
full_runtimes_in_seconds integer[] DEFAULT '{}'::integer[] NOT NULL,
|
||||
|
@ -10846,8 +10840,6 @@ CREATE TABLE analytics_cycle_analytics_aggregations (
|
|||
last_consistency_check_merge_requests_end_event_timestamp timestamp with time zone,
|
||||
last_consistency_check_merge_requests_issuable_id bigint,
|
||||
CONSTRAINT chk_rails_1ef688e577 CHECK ((cardinality(incremental_runtimes_in_seconds) <= 10)),
|
||||
CONSTRAINT chk_rails_7810292ec9 CHECK ((cardinality(last_full_run_processed_records) <= 10)),
|
||||
CONSTRAINT chk_rails_8b9e89687c CHECK ((cardinality(last_full_run_runtimes_in_seconds) <= 10)),
|
||||
CONSTRAINT chk_rails_e16bf3913a CHECK ((cardinality(incremental_processed_records) <= 10)),
|
||||
CONSTRAINT full_processed_records_size CHECK ((cardinality(full_processed_records) <= 10)),
|
||||
CONSTRAINT full_runtimes_in_seconds_size CHECK ((cardinality(full_runtimes_in_seconds) <= 10))
|
||||
|
|
|
@ -10132,6 +10132,7 @@ Branch protection details for a branch rule.
|
|||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="branchprotectionallowforcepush"></a>`allowForcePush` | [`Boolean!`](#boolean) | Toggle force push to the branch for users with write access. |
|
||||
| <a id="branchprotectioncodeownerapprovalrequired"></a>`codeOwnerApprovalRequired` | [`Boolean!`](#boolean) | Enforce code owner approvals before allowing a merge. |
|
||||
| <a id="branchprotectionmergeaccesslevels"></a>`mergeAccessLevels` | [`MergeAccessLevelConnection`](#mergeaccesslevelconnection) | Details about who can merge when this branch is the source branch. (see [Connections](#connections)) |
|
||||
| <a id="branchprotectionpushaccesslevels"></a>`pushAccessLevels` | [`PushAccessLevelConnection`](#pushaccesslevelconnection) | Details about who can push when this branch is the source branch. (see [Connections](#connections)) |
|
||||
|
||||
|
@ -13857,6 +13858,8 @@ Represents the merge access level of a branch protection.
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="mergeaccesslevelaccesslevel"></a>`accessLevel` | [`Int!`](#int) | GitLab::Access level. |
|
||||
| <a id="mergeaccesslevelaccessleveldescription"></a>`accessLevelDescription` | [`String!`](#string) | Human readable representation for this access level. |
|
||||
| <a id="mergeaccesslevelgroup"></a>`group` | [`Group`](#group) | Group associated with this access level. |
|
||||
| <a id="mergeaccessleveluser"></a>`user` | [`UserCore`](#usercore) | User associated with this access level. |
|
||||
|
||||
### `MergeRequest`
|
||||
|
||||
|
@ -17173,6 +17176,8 @@ Represents the push access level of a branch protection.
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="pushaccesslevelaccesslevel"></a>`accessLevel` | [`Int!`](#int) | GitLab::Access level. |
|
||||
| <a id="pushaccesslevelaccessleveldescription"></a>`accessLevelDescription` | [`String!`](#string) | Human readable representation for this access level. |
|
||||
| <a id="pushaccesslevelgroup"></a>`group` | [`Group`](#group) | Group associated with this access level. |
|
||||
| <a id="pushaccessleveluser"></a>`user` | [`UserCore`](#usercore) | User associated with this access level. |
|
||||
|
||||
### `PushRules`
|
||||
|
||||
|
|
|
@ -210,48 +210,27 @@ To reach the definition of done, the merge request must create no regressions an
|
|||
- Verified as working in production on GitLab.com.
|
||||
- Verified as working for self-managed instances.
|
||||
|
||||
If a regression occurs, we prefer you revert the change. We break the definition of done into two phases: [MR Merge](#mr-merge) and [Production use](#production-use).
|
||||
If a regression occurs, we prefer you revert the change.
|
||||
Your contribution is *incomplete* until you have made sure it meets all of these
|
||||
requirements.
|
||||
|
||||
### MR Merge
|
||||
### Functionality
|
||||
|
||||
1. Clear title and description explaining the relevancy of the contribution.
|
||||
1. Working and clean code that is commented where needed.
|
||||
1. The change is evaluated to [limit the impact of far-reaching work](https://about.gitlab.com/handbook/engineering/development/#reducing-the-impact-of-far-reaching-work).
|
||||
1. Testing:
|
||||
|
||||
- [Unit, integration, and system tests](../testing_guide/index.md) that all pass
|
||||
on the CI server.
|
||||
- Peer member testing is optional but recommended when the risk of a change is high.
|
||||
This includes when the changes are [far-reaching](https://about.gitlab.com/handbook/engineering/development/#reducing-the-impact-of-far-reaching-work)
|
||||
or are for [components critical for security](../code_review.md#security).
|
||||
- Description includes any steps or setup required to ensure reviewers can view the changes you've made (for example, include any information about feature flags).
|
||||
- Regressions and bugs are covered with tests that reduce the risk of the issue happening
|
||||
again.
|
||||
- For tests that use Capybara, read
|
||||
[how to write reliable, asynchronous integration tests](https://thoughtbot.com/blog/write-reliable-asynchronous-integration-tests-with-capybara).
|
||||
- [Black-box tests/end-to-end tests](../testing_guide/testing_levels.md#black-box-tests-at-the-system-level-aka-end-to-end-tests)
|
||||
added if required. Please contact [the quality team](https://about.gitlab.com/handbook/engineering/quality/#teams)
|
||||
with any questions.
|
||||
- The change is tested in a review app where possible and if appropriate.
|
||||
1. In case of UI changes:
|
||||
|
||||
- Use available components from the GitLab Design System,
|
||||
[Pajamas](https://design.gitlab.com/).
|
||||
- The MR must include *Before* and *After* screenshots if UI changes are made.
|
||||
- If the MR changes CSS classes, please include the list of affected pages, which
|
||||
can be found by running `grep css-class ./app -R`.
|
||||
1. [Performance guidelines](../merge_request_performance_guidelines.md) have been followed.
|
||||
1. [Secure coding guidelines](https://gitlab.com/gitlab-com/gl-security/security-guidelines) have been followed.
|
||||
1. [Application and rate limit guidelines](../merge_request_application_and_rate_limit_guidelines.md) have been followed.
|
||||
1. [Documented](../documentation/index.md) in the `/doc` directory.
|
||||
1. If your MR touches code that executes shell commands, reads or opens files, or
|
||||
handles paths to files on disk, make sure it adheres to the
|
||||
[shell command guidelines](../shell_commands.md)
|
||||
1. [Code changes should include observability instrumentation](../code_review.md#observability-instrumentation).
|
||||
1. If your code needs to handle file storage, see the [uploads documentation](../uploads/index.md).
|
||||
1. If your merge request adds one or more migrations:
|
||||
- Make sure to execute all migrations on a fresh database before the MR is reviewed.
|
||||
If the review leads to large changes in the MR, execute the migrations again
|
||||
after the review is complete.
|
||||
- Write tests for more complex migrations.
|
||||
1. If your merge request adds one or more migrations, make sure to execute all migrations on a fresh database
|
||||
before the MR is reviewed.
|
||||
If the review leads to large changes in the MR, execute the migrations again
|
||||
after the review is complete.
|
||||
1. If your merge request adds new validations to existing models, to make sure the
|
||||
data processing is backwards compatible:
|
||||
|
||||
|
@ -270,12 +249,37 @@ requirements.
|
|||
[self-managed instances](../../subscriptions/self_managed/index.md), so keep
|
||||
that in mind for any data implications with your merge request.
|
||||
|
||||
### Testing
|
||||
|
||||
1. [Unit, integration, and system tests](../testing_guide/index.md) that all pass
|
||||
on the CI server.
|
||||
1. Peer member testing is optional but recommended when the risk of a change is high.
|
||||
This includes when the changes are [far-reaching](https://about.gitlab.com/handbook/engineering/development/#reducing-the-impact-of-far-reaching-work)
|
||||
or are for [components critical for security](../code_review.md#security).
|
||||
1. Regressions and bugs are covered with tests that reduce the risk of the issue happening
|
||||
again.
|
||||
1. For tests that use Capybara, read
|
||||
[how to write reliable, asynchronous integration tests](https://thoughtbot.com/blog/write-reliable-asynchronous-integration-tests-with-capybara).
|
||||
1. [Black-box tests/end-to-end tests](../testing_guide/testing_levels.md#black-box-tests-at-the-system-level-aka-end-to-end-tests)
|
||||
added if required. Please contact [the quality team](https://about.gitlab.com/handbook/engineering/quality/#teams)
|
||||
with any questions.
|
||||
1. The change is tested in a review app where possible and if appropriate.
|
||||
1. Code affected by a feature flag is covered by [automated tests with the feature flag enabled and disabled](../feature_flags/index.md#feature-flags-in-tests), or both
|
||||
states are tested as part of peer member testing or as part of the rollout plan.
|
||||
1. [Performance guidelines](../merge_request_performance_guidelines.md) have been followed.
|
||||
1. [Secure coding guidelines](https://gitlab.com/gitlab-com/gl-security/security-guidelines) have been followed.
|
||||
1. [Application and rate limit guidelines](../merge_request_application_and_rate_limit_guidelines.md) have been followed.
|
||||
1. [Documented](../documentation/index.md) in the `/doc` directory.
|
||||
1. If your merge request adds one or more migrations, write tests for more complex migrations.
|
||||
|
||||
### UI changes
|
||||
|
||||
1. Use available components from the GitLab Design System,
|
||||
[Pajamas](https://design.gitlab.com/).
|
||||
1. The MR must include *Before* and *After* screenshots if UI changes are made.
|
||||
1. If the MR changes CSS classes, please include the list of affected pages, which
|
||||
can be found by running `grep css-class ./app -R`.
|
||||
|
||||
### Description of changes
|
||||
|
||||
1. Clear title and description explaining the relevancy of the contribution.
|
||||
1. Description includes any steps or setup required to ensure reviewers can view the changes you've made (for example, include any information about feature flags).
|
||||
1. [Changelog entry added](../changelog.md), if necessary.
|
||||
1. If your merge request introduces changes that require additional steps when
|
||||
installing GitLab from source, add them to `doc/install/installation.md` in
|
||||
|
@ -285,10 +289,13 @@ requirements.
|
|||
`doc/update/upgrading_from_source.md` in the same merge request. If these
|
||||
instructions are specific to a version, add them to the "Version specific
|
||||
upgrading instructions" section.
|
||||
1. Reviewed by relevant reviewers, and all concerns are addressed for Availability, Regressions, and Security. Documentation reviews should take place as soon as possible, but they should not block a merge request.
|
||||
|
||||
### Approval
|
||||
|
||||
1. The [MR acceptance checklist](../code_review.md#acceptance-checklist) has been checked as confirmed in the MR.
|
||||
1. Create an issue in the [infrastructure issue tracker](https://gitlab.com/gitlab-com/gl-infra/reliability/-/issues) to inform the Infrastructure department when your contribution is changing default settings or introduces a new setting, if relevant.
|
||||
1. An agreed-upon [rollout plan](https://about.gitlab.com/handbook/engineering/development/processes/rollout-plans/).
|
||||
1. Reviewed by relevant reviewers, and all concerns are addressed for Availability, Regressions, and Security. Documentation reviews should take place as soon as possible, but they should not block a merge request.
|
||||
1. Your merge request has at least 1 approval, but depending on your changes
|
||||
you might need additional approvals. Refer to the [Approval guidelines](../code_review.md#approval-guidelines).
|
||||
- You don't have to select any specific approvers, but you can if you really want
|
||||
|
@ -297,6 +304,8 @@ requirements.
|
|||
|
||||
### Production use
|
||||
|
||||
The following items are checked after the merge request has been merged:
|
||||
|
||||
1. Confirmed to be working in staging before implementing the change in production, where possible.
|
||||
1. Confirmed to be working in the production with no new [Sentry](https://about.gitlab.com/handbook/engineering/monitoring/#sentry) errors after the contribution is deployed.
|
||||
1. Confirmed that the [rollout plan](https://about.gitlab.com/handbook/engineering/development/processes/rollout-plans/) has been completed.
|
||||
|
|
|
@ -4,7 +4,7 @@ group: unassigned
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# `Gemfile` guidelines
|
||||
# Gemfile guidelines
|
||||
|
||||
When adding a new entry to `Gemfile` or upgrading an existing dependency pay
|
||||
attention to the following rules.
|
||||
|
|
|
@ -535,6 +535,7 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
|
|||
hook type. Global server hooks can no longer be a single hook file in the root of the custom hooks directory. For example, you must use `<custom_hooks_dir>/<hook_name>.d/*` rather
|
||||
than `<custom_hooks_dir>/<hook_name>`.
|
||||
- [Incorrect deletion of object storage files on Geo secondary sites]((https://gitlab.com/gitlab-org/gitlab/-/issues/371397)) can occur in certain situations. See [Geo: Incorrect object storage LFS file deletion on secondary site issue in GitLab 15.0.0 to 15.3.2](#geo-incorrect-object-storage-lfs-file-deletion-on-secondary-sites-in-gitlab-1500-to-1532).
|
||||
- The `FF_GITLAB_REGISTRY_HELPER_IMAGE` [feature flag](../administration/feature_flags.md#enable-or-disable-the-feature) is removed and helper images are always pulled from GitLab Registry.
|
||||
|
||||
### 14.10.0
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ module API
|
|||
def present_index_file!(file_type)
|
||||
relation = "::Packages::Debian::#{project_or_group.class.name}ComponentFile".constantize
|
||||
|
||||
component_file = relation
|
||||
relation = relation
|
||||
.preload_distribution
|
||||
.with_container(project_or_group)
|
||||
.with_codename_or_suite(params[:distribution])
|
||||
|
@ -55,9 +55,10 @@ module API
|
|||
.with_architecture_name(params[:architecture])
|
||||
.with_compression_type(nil)
|
||||
.order_created_asc
|
||||
.last!
|
||||
|
||||
present_carrierwave_file!(component_file.file)
|
||||
relation = relation.with_file_sha256(params[:file_sha256]) if params[:file_sha256]
|
||||
|
||||
present_carrierwave_file!(relation.last!.file)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -135,6 +136,17 @@ module API
|
|||
get 'Packages' do
|
||||
present_index_file!(:di_packages)
|
||||
end
|
||||
|
||||
# GET {projects|groups}/:id/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/by-hash/SHA256/:file_sha256
|
||||
# https://wiki.debian.org/DebianRepository/Format?action=show&redirect=RepositoryFormat#indices_acquisition_via_hashsums_.28by-hash.29
|
||||
desc 'The installer (udeb) binary files index by hash' do
|
||||
detail 'This feature was introduced in GitLab 15.4'
|
||||
end
|
||||
|
||||
route_setting :authentication, authenticate_non_public: true
|
||||
get 'by-hash/SHA256/:file_sha256' do
|
||||
present_index_file!(:di_packages)
|
||||
end
|
||||
end
|
||||
|
||||
namespace 'source', requirements: COMPONENT_ARCHITECTURE_REQUIREMENTS do
|
||||
|
@ -148,6 +160,17 @@ module API
|
|||
get 'Sources' do
|
||||
present_index_file!(:sources)
|
||||
end
|
||||
|
||||
# GET {projects|groups}/:id/packages/debian/dists/*distribution/:component/source/by-hash/SHA256/:file_sha256
|
||||
# https://wiki.debian.org/DebianRepository/Format?action=show&redirect=RepositoryFormat#indices_acquisition_via_hashsums_.28by-hash.29
|
||||
desc 'The source files index by hash' do
|
||||
detail 'This feature was introduced in GitLab 15.4'
|
||||
end
|
||||
|
||||
route_setting :authentication, authenticate_non_public: true
|
||||
get 'by-hash/SHA256/:file_sha256' do
|
||||
present_index_file!(:sources)
|
||||
end
|
||||
end
|
||||
|
||||
params do
|
||||
|
@ -165,6 +188,17 @@ module API
|
|||
get 'Packages' do
|
||||
present_index_file!(:packages)
|
||||
end
|
||||
|
||||
# GET {projects|groups}/:id/packages/debian/dists/*distribution/:component/binary-:architecture/by-hash/SHA256/:file_sha256
|
||||
# https://wiki.debian.org/DebianRepository/Format?action=show&redirect=RepositoryFormat#indices_acquisition_via_hashsums_.28by-hash.29
|
||||
desc 'The binary files index by hash' do
|
||||
detail 'This feature was introduced in GitLab 15.4'
|
||||
end
|
||||
|
||||
route_setting :authentication, authenticate_non_public: true
|
||||
get 'by-hash/SHA256/:file_sha256' do
|
||||
present_index_file!(:packages)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -45,6 +45,11 @@ module API
|
|||
desc 'Upload a RPM package'
|
||||
post do
|
||||
authorize_create_package!(authorized_user_project)
|
||||
|
||||
if authorized_user_project.actual_limits.exceeded?(:rpm_max_file_size, params[:file].size)
|
||||
bad_request!('File is too large')
|
||||
end
|
||||
|
||||
not_found!
|
||||
end
|
||||
|
||||
|
|
|
@ -10,16 +10,16 @@ module Gitlab
|
|||
CycleAnalyticsCounter,
|
||||
ProductivityAnalyticsCounter,
|
||||
SourceCodeCounter,
|
||||
MergeRequestCounter,
|
||||
DesignsCounter,
|
||||
KubernetesAgentCounter,
|
||||
DiffsCounter,
|
||||
ServiceUsageDataCounter,
|
||||
MergeRequestWidgetExtensionCounter
|
||||
].freeze
|
||||
|
||||
COUNTERS_MIGRATED_TO_INSTRUMENTATION_CLASSES = [
|
||||
PackageEventCounter,
|
||||
MergeRequestCounter,
|
||||
DesignsCounter,
|
||||
DiffsCounter,
|
||||
ServiceUsageDataCounter,
|
||||
WebIdeCounter
|
||||
].freeze
|
||||
|
||||
|
|
|
@ -19821,18 +19821,36 @@ msgstr ""
|
|||
msgid "Identities"
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|A new code has been sent."
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Before you create your first project, we need you to verify your identity with a valid payment method. You will not be charged during this step. If we ever need to charge you, we will let you know."
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Before you create your group, we need you to verify your identity with a valid payment method. You will not be charged during this step. If we ever need to charge you, we will let you know."
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Before you finish creating your account, we need to verify your identity. On the verification page, enter the following code."
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Before you sign in, we need to verify your identity. Enter the following code on the sign-in page."
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Confirm your email address"
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Create a project"
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Didn't receive a code?"
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Enter a code."
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Enter a valid code."
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
|
||||
msgstr ""
|
||||
|
||||
|
@ -19881,16 +19899,22 @@ msgstr ""
|
|||
msgid "IdentityVerification|Resend code"
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Send a new code"
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Send code"
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Something went wrong. Please try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Step 1: Verify phone number"
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
|
||||
msgid "IdentityVerification|The code has expired. Send a new code and try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|The code is incorrect. Enter it again, or resend a new code."
|
||||
msgid "IdentityVerification|The code is incorrect. Enter it again, or send a new code."
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Verification code"
|
||||
|
@ -19902,6 +19926,9 @@ msgstr ""
|
|||
msgid "IdentityVerification|Verify code"
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Verify email address"
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Verify payment method"
|
||||
msgstr ""
|
||||
|
||||
|
@ -19914,10 +19941,13 @@ msgstr ""
|
|||
msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
|
||||
msgid "IdentityVerification|You've reached the maximum amount of resends. Wait %{interval} and try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Your account has been successfully verified. You'll be redirected to your account in just a moment or %{redirect_url_start}click here%{redirect_url_end} to refresh."
|
||||
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or send a new code and try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Your account has been successfully verified. You'll be redirected to your account in just a moment. You can also %{redirect_url_start}refresh the page%{redirect_url_end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "IdentityVerification|Your verification code expires after %{expires_in_minutes} minutes."
|
||||
|
|
|
@ -129,6 +129,10 @@ RSpec.describe ConfirmationsController do
|
|||
|
||||
subject(:perform_request) { post(:create, params: { user: { email: user.email } }) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(identity_verification: false)
|
||||
end
|
||||
|
||||
context 'when reCAPTCHA is disabled' do
|
||||
before do
|
||||
stub_application_setting(recaptcha_enabled: false)
|
||||
|
|
|
@ -122,6 +122,7 @@ RSpec.describe RegistrationsController do
|
|||
context 'when `send_user_confirmation_email` is true' do
|
||||
before do
|
||||
stub_application_setting(send_user_confirmation_email: true)
|
||||
stub_feature_flags(identity_verification: false)
|
||||
end
|
||||
|
||||
it 'sends a confirmation email' do
|
||||
|
@ -134,6 +135,10 @@ RSpec.describe RegistrationsController do
|
|||
end
|
||||
|
||||
context 'email confirmation' do
|
||||
before do
|
||||
stub_feature_flags(identity_verification: false)
|
||||
end
|
||||
|
||||
context 'when send_user_confirmation_email is false' do
|
||||
it 'signs the user in' do
|
||||
stub_application_setting(send_user_confirmation_email: false)
|
||||
|
|
|
@ -38,6 +38,12 @@ FactoryBot.define do
|
|||
file_fixture { 'spec/fixtures/packages/debian/distribution/D-I-Packages' }
|
||||
end
|
||||
|
||||
trait(:older_sha256) do
|
||||
created_at { '2020-01-24T08:00:00Z' }
|
||||
file_sha256 { '157a1ad2b9102038560eea56771913b312ebf25093f5ef3b9842021c639c880d' }
|
||||
file_fixture { 'spec/fixtures/packages/debian/distribution/OtherSHA256' }
|
||||
end
|
||||
|
||||
trait(:object_storage) do
|
||||
file_store { Packages::PackageFileUploader::Store::REMOTE }
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@ FactoryBot.define do
|
|||
project
|
||||
|
||||
transient do
|
||||
ee { false }
|
||||
default_push_level { true }
|
||||
default_merge_level { true }
|
||||
default_access_level { true }
|
||||
|
@ -17,14 +18,11 @@ FactoryBot.define do
|
|||
ProtectedBranches::CacheService.new(protected_branch.project).refresh
|
||||
end
|
||||
|
||||
after(:build) do |protected_branch, evaluator|
|
||||
if evaluator.default_access_level && evaluator.default_push_level
|
||||
protected_branch.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
|
||||
end
|
||||
after(:build) do |obj, ctx|
|
||||
next if ctx.ee || !ctx.default_access_level
|
||||
|
||||
if evaluator.default_access_level && evaluator.default_merge_level
|
||||
protected_branch.merge_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
|
||||
end
|
||||
obj.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER) if ctx.default_push_level
|
||||
obj.merge_access_levels.new(access_level: Gitlab::Access::MAINTAINER) if ctx.default_merge_level
|
||||
end
|
||||
|
||||
trait :create_branch_on_repository do
|
||||
|
|
|
@ -34,7 +34,7 @@ RSpec.describe "Admin Runners" do
|
|||
|
||||
context "when there are runners" do
|
||||
context "with an instance runner" do
|
||||
let!(:instance_runner) { create(:ci_runner, :instance) }
|
||||
let_it_be(:instance_runner) { create(:ci_runner, :instance) }
|
||||
|
||||
before do
|
||||
visit admin_runners_path
|
||||
|
@ -86,10 +86,12 @@ RSpec.describe "Admin Runners" do
|
|||
end
|
||||
|
||||
describe 'search' do
|
||||
before do
|
||||
before_all do
|
||||
create(:ci_runner, :instance, description: 'runner-foo')
|
||||
create(:ci_runner, :instance, description: 'runner-bar')
|
||||
end
|
||||
|
||||
before do
|
||||
visit admin_runners_path
|
||||
end
|
||||
|
||||
|
@ -130,10 +132,12 @@ RSpec.describe "Admin Runners" do
|
|||
end
|
||||
|
||||
describe 'filter by paused' do
|
||||
before do
|
||||
before_all do
|
||||
create(:ci_runner, :instance, description: 'runner-active')
|
||||
create(:ci_runner, :instance, description: 'runner-paused', active: false)
|
||||
end
|
||||
|
||||
before do
|
||||
visit admin_runners_path
|
||||
end
|
||||
|
||||
|
@ -164,15 +168,17 @@ RSpec.describe "Admin Runners" do
|
|||
end
|
||||
|
||||
describe 'filter by status' do
|
||||
let!(:never_contacted) do
|
||||
let_it_be(:never_contacted) do
|
||||
create(:ci_runner, :instance, description: 'runner-never-contacted', contacted_at: nil)
|
||||
end
|
||||
|
||||
before do
|
||||
before_all do
|
||||
create(:ci_runner, :instance, description: 'runner-1', contacted_at: Time.zone.now)
|
||||
create(:ci_runner, :instance, description: 'runner-2', contacted_at: Time.zone.now)
|
||||
create(:ci_runner, :instance, description: 'runner-offline', contacted_at: 1.week.ago)
|
||||
end
|
||||
|
||||
before do
|
||||
visit admin_runners_path
|
||||
end
|
||||
|
||||
|
@ -238,7 +244,7 @@ RSpec.describe "Admin Runners" do
|
|||
end
|
||||
|
||||
describe 'filter by type' do
|
||||
before do
|
||||
before_all do
|
||||
create(:ci_runner, :project, description: 'runner-project', projects: [project])
|
||||
create(:ci_runner, :group, description: 'runner-group', groups: [group])
|
||||
end
|
||||
|
@ -345,7 +351,7 @@ RSpec.describe "Admin Runners" do
|
|||
end
|
||||
|
||||
describe 'filter by tag' do
|
||||
before do
|
||||
before_all do
|
||||
create(:ci_runner, :instance, description: 'runner-blue', tag_list: ['blue'])
|
||||
create(:ci_runner, :instance, description: 'runner-red', tag_list: ['red'])
|
||||
end
|
||||
|
@ -464,7 +470,7 @@ RSpec.describe "Admin Runners" do
|
|||
end
|
||||
|
||||
describe "Runner show page", :js do
|
||||
let(:runner) do
|
||||
let_it_be(:runner) do
|
||||
create(
|
||||
:ci_runner,
|
||||
description: 'runner-foo',
|
||||
|
@ -520,9 +526,9 @@ RSpec.describe "Admin Runners" do
|
|||
end
|
||||
|
||||
describe "Runner edit page" do
|
||||
let(:project_runner) { create(:ci_runner, :project) }
|
||||
let!(:project1) { create(:project) }
|
||||
let!(:project2) { create(:project) }
|
||||
let_it_be(:project1) { create(:project) }
|
||||
let_it_be(:project2) { create(:project) }
|
||||
let_it_be(:project_runner) { create(:ci_runner, :project) }
|
||||
|
||||
before do
|
||||
visit edit_admin_runner_path(project_runner)
|
||||
|
|
|
@ -246,6 +246,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do
|
|||
before do
|
||||
stub_feature_flags(soft_email_confirmation: false)
|
||||
allow(User).to receive(:allow_unconfirmed_access_for).and_return 0
|
||||
stub_feature_flags(identity_verification: false)
|
||||
end
|
||||
|
||||
it 'signs up and redirects to the group activity page' do
|
||||
|
|
|
@ -118,7 +118,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
|
|||
# Expect an error message
|
||||
expect_log_message('Failed Attempt', reason: 'rate_limited')
|
||||
expect(page).to have_content("You've reached the maximum amount of tries. "\
|
||||
'Wait 10 minutes or resend a new code and try again.')
|
||||
'Wait 10 minutes or send a new code and try again.')
|
||||
|
||||
# Wait for 10 minutes
|
||||
travel 10.minutes
|
||||
|
@ -138,7 +138,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
|
|||
|
||||
# Expect an error message
|
||||
expect_log_message('Failed Attempt', reason: 'invalid')
|
||||
expect(page).to have_content('The code is incorrect. Enter it again, or resend a new code.')
|
||||
expect(page).to have_content('The code is incorrect. Enter it again, or send a new code.')
|
||||
end
|
||||
|
||||
it 'verifies expired codes' do
|
||||
|
@ -155,7 +155,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
|
|||
|
||||
# Expect an error message
|
||||
expect_log_message('Failed Attempt', reason: 'expired')
|
||||
expect(page).to have_content('The code has expired. Resend a new code and try again.')
|
||||
expect(page).to have_content('The code has expired. Send a new code and try again.')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -255,7 +255,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
|
|||
perform_enqueued_jobs do
|
||||
# The user is prompted for a verification code
|
||||
gitlab_sign_in(user)
|
||||
expect(page).to have_content('Help us protect your account')
|
||||
expect(page).to have_content(s_('IdentityVerification|Help us protect your account'))
|
||||
code = expect_instructions_email_and_extract_code
|
||||
|
||||
# We toggle the feature flag off
|
||||
|
@ -266,12 +266,13 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
|
|||
new_code = expect_instructions_email_and_extract_code
|
||||
|
||||
verify_code(code)
|
||||
expect(page).to have_content('The code is incorrect. Enter it again, or resend a new code.')
|
||||
expect(page)
|
||||
.to have_content(s_('IdentityVerification|The code is incorrect. Enter it again, or send a new code.'))
|
||||
|
||||
travel Users::EmailVerification::ValidateTokenService::TOKEN_VALID_FOR_MINUTES.minutes + 1.second
|
||||
|
||||
verify_code(new_code)
|
||||
expect(page).to have_content('The code has expired. Resend a new code and try again.')
|
||||
expect(page).to have_content(s_('IdentityVerification|The code has expired. Send a new code and try again.'))
|
||||
|
||||
click_link 'Resend code'
|
||||
another_code = expect_instructions_email_and_extract_code
|
||||
|
@ -296,7 +297,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
|
|||
|
||||
it 'the unlock link still works' do
|
||||
# The user is locked and unlock instructions are sent
|
||||
expect(page).to have_content('Invalid login or password.')
|
||||
expect(page).to have_content(_('Invalid login or password.'))
|
||||
user.reload
|
||||
expect(user.locked_at).not_to be_nil
|
||||
expect(user.unlock_token).not_to be_nil
|
||||
|
@ -334,15 +335,15 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
|
|||
def expect_instructions_email_and_extract_code
|
||||
mail = find_email_for(user)
|
||||
expect(mail.to).to match_array([user.email])
|
||||
expect(mail.subject).to eq('Verify your identity')
|
||||
expect(mail.subject).to eq(s_('IdentityVerification|Verify your identity'))
|
||||
code = mail.body.parts.first.to_s[/\d{#{Users::EmailVerification::GenerateTokenService::TOKEN_LENGTH}}/o]
|
||||
reset_delivered_emails!
|
||||
code
|
||||
end
|
||||
|
||||
def verify_code(code)
|
||||
fill_in 'Verification code', with: code
|
||||
click_button 'Verify code'
|
||||
fill_in s_('IdentityVerification|Verification code'), with: code
|
||||
click_button s_('IdentityVerification|Verify code')
|
||||
end
|
||||
|
||||
def expect_log_message(event = nil, times = 1, reason: '', message: nil)
|
||||
|
|
|
@ -105,6 +105,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
|
|||
before do
|
||||
stub_application_setting(send_user_confirmation_email: true)
|
||||
allow(User).to receive(:allow_unconfirmed_access_for).and_return grace_period
|
||||
stub_feature_flags(identity_verification: false)
|
||||
end
|
||||
|
||||
context 'within the grace period' do
|
||||
|
@ -954,6 +955,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
|
|||
before do
|
||||
stub_application_setting(send_user_confirmation_email: true)
|
||||
stub_feature_flags(soft_email_confirmation: true)
|
||||
stub_feature_flags(identity_verification: false)
|
||||
allow(User).to receive(:allow_unconfirmed_access_for).and_return grace_period
|
||||
end
|
||||
|
||||
|
|
|
@ -203,6 +203,7 @@ RSpec.describe 'Signup' do
|
|||
context 'when soft email confirmation is not enabled' do
|
||||
before do
|
||||
stub_feature_flags(soft_email_confirmation: false)
|
||||
stub_feature_flags(identity_verification: false)
|
||||
end
|
||||
|
||||
it 'creates the user account and sends a confirmation email, and pre-fills email address after confirming' do
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Other SHA256
|
|
@ -16,17 +16,17 @@ describe('getAverageByMonth', () => {
|
|||
expect(getAverageByMonth(mockCountsData2)).toStrictEqual(countsMonthlyChartData2);
|
||||
});
|
||||
|
||||
it('it transforms a data point to the first of the month', () => {
|
||||
it('transforms a data point to the first of the month', () => {
|
||||
const item = mockCountsData1[0];
|
||||
const firstOfTheMonth = item.recordedAt.replace(/-[0-9]{2}$/, '-01');
|
||||
expect(getAverageByMonth([item])).toStrictEqual([[firstOfTheMonth, item.count]]);
|
||||
});
|
||||
|
||||
it('it uses sane defaults', () => {
|
||||
it('uses sane defaults', () => {
|
||||
expect(getAverageByMonth()).toStrictEqual([]);
|
||||
});
|
||||
|
||||
it('it errors when passing null', () => {
|
||||
it('errors when passing null', () => {
|
||||
expect(() => {
|
||||
getAverageByMonth(null);
|
||||
}).toThrow();
|
||||
|
|
|
@ -53,7 +53,7 @@ describe('Batch comments preview dropdown', () => {
|
|||
});
|
||||
|
||||
describe('clicking draft', () => {
|
||||
it('it toggles active file when viewDiffsFileByFile is true', async () => {
|
||||
it('toggles active file when viewDiffsFileByFile is true', async () => {
|
||||
factory({
|
||||
viewDiffsFileByFile: true,
|
||||
sortedDrafts: [{ id: 1, file_hash: 'hash' }],
|
||||
|
|
|
@ -118,7 +118,7 @@ describe('Batch comments draft preview item component', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('it renders thread resolved text', () => {
|
||||
it('renders thread resolved text', () => {
|
||||
expect(vm.$el.querySelector('.draft-note-resolution').textContent).toContain(
|
||||
'Thread will be resolved',
|
||||
);
|
||||
|
|
|
@ -24,7 +24,7 @@ describe('Batch comments review bar component', () => {
|
|||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('it adds review-bar-visible class to body when review bar is mounted', async () => {
|
||||
it('adds review-bar-visible class to body when review bar is mounted', async () => {
|
||||
expect(document.body.classList.contains(REVIEW_BAR_VISIBLE_CLASS_NAME)).toBe(false);
|
||||
|
||||
createComponent();
|
||||
|
@ -32,7 +32,7 @@ describe('Batch comments review bar component', () => {
|
|||
expect(document.body.classList.contains(REVIEW_BAR_VISIBLE_CLASS_NAME)).toBe(true);
|
||||
});
|
||||
|
||||
it('it removes review-bar-visible class to body when review bar is destroyed', async () => {
|
||||
it('removes review-bar-visible class to body when review bar is destroyed', async () => {
|
||||
createComponent();
|
||||
|
||||
wrapper.destroy();
|
||||
|
|
|
@ -92,7 +92,7 @@ describe('Batch comments submit dropdown', () => {
|
|||
canApprove | exists | existsText
|
||||
${true} | ${true} | ${'shows'}
|
||||
${false} | ${false} | ${'hides'}
|
||||
`('it $existsText approve checkbox if can_approve is $canApprove', ({ canApprove, exists }) => {
|
||||
`('$existsText approve checkbox if can_approve is $canApprove', ({ canApprove, exists }) => {
|
||||
factory({ canApprove });
|
||||
|
||||
expect(wrapper.findByTestId('approve_merge_request').exists()).toBe(exists);
|
||||
|
|
|
@ -90,7 +90,7 @@ describe('Issue boards new issue form', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('it uses the first issue ID as moveAfterId', async () => {
|
||||
it('uses the first issue ID as moveAfterId', async () => {
|
||||
findBoardNewItem().vm.$emit('form-submit', { title: 'Foo' });
|
||||
|
||||
await nextTick();
|
||||
|
|
|
@ -142,7 +142,7 @@ describe('ClustersMainViewComponent', () => {
|
|||
createWrapper({ certificateBasedClustersEnabled: false });
|
||||
});
|
||||
|
||||
it('it displays only the Agent tab', () => {
|
||||
it('displays only the Agent tab', () => {
|
||||
expect(findAllTabs()).toHaveLength(1);
|
||||
const agentTab = findGlTabAtIndex(0);
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ describe('getCurrentHoverElement', () => {
|
|||
value
|
||||
${'test'}
|
||||
${undefined}
|
||||
`('it returns cached current key', ({ value }) => {
|
||||
`('returns cached current key', ({ value }) => {
|
||||
if (value) {
|
||||
cachedData.set('current', value);
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ describe('addInteractionClass', () => {
|
|||
${1} | ${0} | ${0}
|
||||
${1} | ${0} | ${0}
|
||||
`(
|
||||
'it sets code navigation attributes for line $line and character $char',
|
||||
'sets code navigation attributes for line $line and character $char',
|
||||
({ line, char, index }) => {
|
||||
addInteractionClass({ path: 'index.js', d: { start_line: line, start_char: char } });
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ describe('ValueStreamMetrics', () => {
|
|||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('it should render an error message', () => {
|
||||
it('should render an error message', () => {
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: `There was an error while fetching value stream analytics ${fakeReqName} data.`,
|
||||
});
|
||||
|
|
|
@ -314,7 +314,7 @@ describe('deprecatedJQueryDropdown', () => {
|
|||
});
|
||||
|
||||
describe('with a trackSuggestionsClickedLabel', () => {
|
||||
it('it includes data-track attributes', () => {
|
||||
it('includes data-track attributes', () => {
|
||||
const dropdown = dropdownWithOptions({
|
||||
trackSuggestionClickedLabel: 'some_value_for_label',
|
||||
});
|
||||
|
@ -333,7 +333,7 @@ describe('deprecatedJQueryDropdown', () => {
|
|||
expect(link).toHaveAttr('data-track-property', 'suggestion-category');
|
||||
});
|
||||
|
||||
it('it defaults property to no_category when category not provided', () => {
|
||||
it('defaults property to no_category when category not provided', () => {
|
||||
const dropdown = dropdownWithOptions({
|
||||
trackSuggestionClickedLabel: 'some_value_for_label',
|
||||
});
|
||||
|
|
|
@ -682,7 +682,7 @@ describe('diffs/components/app', () => {
|
|||
${'123'} | ${2}
|
||||
${'312'} | ${1}
|
||||
`(
|
||||
'it calls navigateToDiffFileIndex with $index when $link is clicked',
|
||||
'calls navigateToDiffFileIndex with $index when $link is clicked',
|
||||
async ({ currentDiffFileId, targetFile }) => {
|
||||
createComponent({ fileByFileUserPreference: true }, ({ state }) => {
|
||||
state.diffs.diffFiles.push({ file_hash: '123' }, { file_hash: '312' });
|
||||
|
|
|
@ -106,7 +106,7 @@ describe('Diffs tree list component', () => {
|
|||
${'index.js'} | ${1}
|
||||
${'app/*.js'} | ${1}
|
||||
${'*.js, *.rb'} | ${2}
|
||||
`('it returns $itemSize item for $extension', async ({ extension, itemSize }) => {
|
||||
`('returns $itemSize item for $extension', async ({ extension, itemSize }) => {
|
||||
wrapper.find('[data-testid="diff-tree-search"]').setValue(extension);
|
||||
|
||||
await nextTick();
|
||||
|
|
|
@ -68,7 +68,7 @@ describe('Source Editor Toolbar', () => {
|
|||
});
|
||||
|
||||
describe('buttons update', () => {
|
||||
it('it properly updates buttons on Apollo cache update', async () => {
|
||||
it('properly updates buttons on Apollo cache update', async () => {
|
||||
const item = buildButton('first', {
|
||||
group: EDITOR_TOOLBAR_RIGHT_GROUP,
|
||||
});
|
||||
|
|
|
@ -382,7 +382,7 @@ describe('Source Editor Instance', () => {
|
|||
},
|
||||
);
|
||||
|
||||
it('it does not remove entry from the global registry to keep for potential future re-use', () => {
|
||||
it('does not remove entry from the global registry to keep for potential future re-use', () => {
|
||||
const extensionStore = new Map();
|
||||
seInstance = new SourceEditorInstance({}, extensionStore);
|
||||
const extensions = seInstance.use(fullExtensionsArray);
|
||||
|
|
|
@ -4,13 +4,13 @@ import { getFrequentlyUsedEmojis, addToFrequentlyUsed } from '~/emoji/components
|
|||
jest.mock('~/lib/utils/cookies');
|
||||
|
||||
describe('getFrequentlyUsedEmojis', () => {
|
||||
it('it returns null when no saved emojis set', () => {
|
||||
it('returns null when no saved emojis set', () => {
|
||||
jest.spyOn(Cookies, 'get').mockReturnValue(null);
|
||||
|
||||
expect(getFrequentlyUsedEmojis()).toBe(null);
|
||||
});
|
||||
|
||||
it('it returns frequently used emojis object', () => {
|
||||
it('returns frequently used emojis object', () => {
|
||||
jest.spyOn(Cookies, 'get').mockReturnValue('thumbsup,thumbsdown');
|
||||
|
||||
expect(getFrequentlyUsedEmojis()).toEqual({
|
||||
|
|
|
@ -363,7 +363,7 @@ describe('Environment table', () => {
|
|||
});
|
||||
|
||||
describe('sortedEnvironments', () => {
|
||||
it('it should sort children as well', () => {
|
||||
it('should sort children as well', () => {
|
||||
const mockItems = [
|
||||
{
|
||||
name: 'production',
|
||||
|
|
|
@ -65,7 +65,7 @@ describe('~/environments/components/new.vue', () => {
|
|||
input | value
|
||||
${() => name} | ${'test'}
|
||||
${() => url} | ${'https://example.org'}
|
||||
`('it changes the value of the input to $value', async ({ input, value }) => {
|
||||
`('changes the value of the input to $value', async ({ input, value }) => {
|
||||
await input().setValue(value);
|
||||
|
||||
expect(input().element.value).toBe(value);
|
||||
|
|
|
@ -164,19 +164,19 @@ describe('ErrorTrackingList', () => {
|
|||
expect(findSortDropdown().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('it searches by query', () => {
|
||||
it('searches by query', () => {
|
||||
findSearchBox().vm.$emit('input', 'search');
|
||||
findSearchBox().trigger('keyup.enter');
|
||||
expect(actions.searchByQuery.mock.calls[0][1]).toBe('search');
|
||||
});
|
||||
|
||||
it('it sorts by fields', () => {
|
||||
it('sorts by fields', () => {
|
||||
const findSortItem = () => findSortDropdown().find('.dropdown-item');
|
||||
findSortItem().trigger('click');
|
||||
expect(actions.sortByField).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('it filters by status', () => {
|
||||
it('filters by status', () => {
|
||||
const findStatusFilter = () => findStatusFilterDropdown().find('.dropdown-item');
|
||||
findStatusFilter().trigger('click');
|
||||
expect(actions.filterByStatus).toHaveBeenCalled();
|
||||
|
|
|
@ -557,11 +557,11 @@ describe('DropLab DropDown', () => {
|
|||
DropDown.prototype.show.call(testContext.dropdown);
|
||||
});
|
||||
|
||||
it('it should set .list display to block', () => {
|
||||
it('should set .list display to block', () => {
|
||||
expect(testContext.list.style.display).toBe('block');
|
||||
});
|
||||
|
||||
it('it should set .hidden to false', () => {
|
||||
it('should set .hidden to false', () => {
|
||||
expect(testContext.dropdown.hidden).toBe(false);
|
||||
});
|
||||
|
||||
|
@ -591,11 +591,11 @@ describe('DropLab DropDown', () => {
|
|||
DropDown.prototype.hide.call(testContext.dropdown);
|
||||
});
|
||||
|
||||
it('it should set .list display to none', () => {
|
||||
it('should set .list display to none', () => {
|
||||
expect(testContext.list.style.display).toBe('none');
|
||||
});
|
||||
|
||||
it('it should set .hidden to true', () => {
|
||||
it('should set .hidden to true', () => {
|
||||
expect(testContext.dropdown.hidden).toBe(true);
|
||||
});
|
||||
});
|
||||
|
@ -648,11 +648,11 @@ describe('DropLab DropDown', () => {
|
|||
DropDown.prototype.destroy.call(testContext.dropdown);
|
||||
});
|
||||
|
||||
it('it should call .hide', () => {
|
||||
it('should call .hide', () => {
|
||||
expect(testContext.dropdown.hide).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('it should call .removeEventListener', () => {
|
||||
it('should call .removeEventListener', () => {
|
||||
expect(testContext.list.removeEventListener).toHaveBeenCalledWith(
|
||||
'click',
|
||||
testContext.eventWrapper.clickEvent,
|
||||
|
|
|
@ -274,7 +274,7 @@ describe('GroupItemComponent', () => {
|
|||
${'itemscope'} | ${'itemscope'}
|
||||
${'itemtype'} | ${'https://schema.org/Organization'}
|
||||
${'itemprop'} | ${'subOrganization'}
|
||||
`('it does set correct $attr', ({ attr, value } = {}) => {
|
||||
`('does set correct $attr', ({ attr, value } = {}) => {
|
||||
expect(wrapper.attributes(attr)).toBe(value);
|
||||
});
|
||||
|
||||
|
@ -283,7 +283,7 @@ describe('GroupItemComponent', () => {
|
|||
${'img'} | ${'logo'}
|
||||
${'[data-testid="group-name"]'} | ${'name'}
|
||||
${'[data-testid="group-description"]'} | ${'description'}
|
||||
`('it does set correct $selector', ({ selector, propValue } = {}) => {
|
||||
`('does set correct $selector', ({ selector, propValue } = {}) => {
|
||||
expect(wrapper.find(selector).attributes('itemprop')).toBe(propValue);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -106,7 +106,7 @@ describe('CreateMergeRequestDropdown', () => {
|
|||
loading | hasClass
|
||||
${true} | ${false}
|
||||
${false} | ${true}
|
||||
`('it toggle loading spinner when loading is $loading', ({ loading, hasClass }) => {
|
||||
`('toggle loading spinner when loading is $loading', ({ loading, hasClass }) => {
|
||||
dropdown.setLoading(loading);
|
||||
|
||||
expect(document.querySelector('.js-spinner').classList.contains('gl-display-none')).toEqual(
|
||||
|
|
|
@ -6,7 +6,7 @@ describe('isNavigatingAway', () => {
|
|||
setNavigatingForTestsOnly(false);
|
||||
});
|
||||
|
||||
it.each([false, true])('it returns the navigation flag with value %s', (flag) => {
|
||||
it.each([false, true])('returns the navigation flag with value %s', (flag) => {
|
||||
setNavigatingForTestsOnly(flag);
|
||||
expect(isNavigatingAway()).toEqual(flag);
|
||||
});
|
||||
|
|
|
@ -63,7 +63,7 @@ describe('initPrefetchLinks', () => {
|
|||
expect(newLink.addEventListener).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('it is not fired when less then 100ms over link', () => {
|
||||
it('is not fired when less then 100ms over link', () => {
|
||||
const mouseOverEvent = new Event('mouseover');
|
||||
const mouseOutEvent = new Event('mouseout');
|
||||
|
||||
|
|
|
@ -333,7 +333,7 @@ describe('MergeRequestTabs', () => {
|
|||
${'show'} | ${false} | ${'shows'}
|
||||
${'diffs'} | ${true} | ${'hides'}
|
||||
${'commits'} | ${true} | ${'hides'}
|
||||
`('it $hidesText expand button on $tab tab', ({ tab, hides }) => {
|
||||
`('$hidesText expand button on $tab tab', ({ tab, hides }) => {
|
||||
window.gon = { features: { movedMrSidebar: true } };
|
||||
|
||||
const expandButton = document.createElement('div');
|
||||
|
|
|
@ -430,7 +430,7 @@ describe('Dashboard Panel', () => {
|
|||
expect(findTimeChart().props().projectPath).toBe(mockProjectPath);
|
||||
});
|
||||
|
||||
it('it renders a time series chart with no errors', () => {
|
||||
it('renders a time series chart with no errors', () => {
|
||||
expect(wrapper.findComponent(MonitorTimeSeriesChart).exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -407,7 +407,7 @@ describe('Dashboard', () => {
|
|||
await nextTick();
|
||||
});
|
||||
|
||||
it('it does not show loading icons in any group', async () => {
|
||||
it('does not show loading icons in any group', async () => {
|
||||
setupStoreWithData(store);
|
||||
|
||||
await nextTick();
|
||||
|
@ -614,7 +614,7 @@ describe('Dashboard', () => {
|
|||
const findFirstDraggableRemoveButton = () =>
|
||||
findDraggablePanels().at(0).find('.js-draggable-remove');
|
||||
|
||||
it('it enables draggables', async () => {
|
||||
it('enables draggables', async () => {
|
||||
findRearrangeButton().vm.$emit('click');
|
||||
await nextTick();
|
||||
|
||||
|
@ -656,7 +656,7 @@ describe('Dashboard', () => {
|
|||
expect(findDraggablePanels().length).toEqual(metricsDashboardPanelCount - 1);
|
||||
});
|
||||
|
||||
it('it disables draggables when clicked again', async () => {
|
||||
it('disables draggables when clicked again', async () => {
|
||||
findRearrangeButton().vm.$emit('click');
|
||||
await nextTick();
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ describe('ResolveWithIssueButton', () => {
|
|||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('it should have a link with the provided link property as href', () => {
|
||||
it('should have a link with the provided link property as href', () => {
|
||||
const button = wrapper.findComponent(GlButton);
|
||||
|
||||
expect(button.attributes().href).toBe(url);
|
||||
|
|
|
@ -71,7 +71,7 @@ describe('NugetInstallation', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('it has docs link', () => {
|
||||
it('has docs link', () => {
|
||||
expect(findSetupDocsLink().attributes()).toMatchObject({
|
||||
href: NUGET_HELP_PATH,
|
||||
target: '_blank',
|
||||
|
|
|
@ -24,13 +24,13 @@ describe('packages_filter', () => {
|
|||
wrapper = null;
|
||||
});
|
||||
|
||||
it('it binds all of his attrs to filtered search token', () => {
|
||||
it('binds all of his attrs to filtered search token', () => {
|
||||
mountComponent({ attrs: { foo: 'bar' } });
|
||||
|
||||
expect(findFilteredSearchToken().attributes('foo')).toBe('bar');
|
||||
});
|
||||
|
||||
it('it binds all of his events to filtered search token', () => {
|
||||
it('binds all of his events to filtered search token', () => {
|
||||
const clickListener = jest.fn();
|
||||
mountComponent({ listeners: { click: clickListener } });
|
||||
|
||||
|
|
|
@ -178,7 +178,7 @@ describe('PackagesApp', () => {
|
|||
${PACKAGE_TYPE_PYPI} | ${true}
|
||||
${PACKAGE_TYPE_NPM} | ${false}
|
||||
`(
|
||||
`It is $visible that the component is visible when the package is $packageType`,
|
||||
`is $visible that the component is visible when the package is $packageType`,
|
||||
async ({ packageType, visible }) => {
|
||||
createComponent({
|
||||
resolver: jest.fn().mockResolvedValue(
|
||||
|
|
|
@ -554,7 +554,7 @@ describe('Pipeline graph wrapper', () => {
|
|||
mock.restore();
|
||||
});
|
||||
|
||||
it('it calls reportPerformance with expected arguments', () => {
|
||||
it('calls reportPerformance with expected arguments', () => {
|
||||
expect(markAndMeasure).toHaveBeenCalled();
|
||||
expect(reportPerformance).toHaveBeenCalled();
|
||||
expect(reportPerformance).toHaveBeenCalledWith(metricsPath, metricsData);
|
||||
|
|
|
@ -59,7 +59,7 @@ describe('pipeline graph job item', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('it should render status and name', () => {
|
||||
it('should render status and name', () => {
|
||||
expect(wrapper.find('.ci-status-icon-success').exists()).toBe(true);
|
||||
expect(wrapper.find('a').exists()).toBe(false);
|
||||
|
||||
|
@ -72,7 +72,7 @@ describe('pipeline graph job item', () => {
|
|||
});
|
||||
|
||||
describe('action icon', () => {
|
||||
it('it should render the action icon', () => {
|
||||
it('should render the action icon', () => {
|
||||
createWrapper({ job: mockJob });
|
||||
|
||||
const actionComponent = findActionComponent();
|
||||
|
@ -82,7 +82,7 @@ describe('pipeline graph job item', () => {
|
|||
expect(actionComponent.attributes('disabled')).not.toBe('disabled');
|
||||
});
|
||||
|
||||
it('it should render disabled action icon when user cannot run the action', () => {
|
||||
it('should render disabled action icon when user cannot run the action', () => {
|
||||
createWrapper({ job: mockJobWithUnauthorizedAction });
|
||||
|
||||
const actionComponent = findActionComponent();
|
||||
|
|
|
@ -143,7 +143,7 @@ describe('AssigneeAvatarLink component', () => {
|
|||
issuableType | userId
|
||||
${'merge_request'} | ${undefined}
|
||||
${'issue'} | ${'1'}
|
||||
`('it sets data-user-id as $userId for $issuableType', ({ issuableType, userId }) => {
|
||||
`('sets data-user-id as $userId for $issuableType', ({ issuableType, userId }) => {
|
||||
createComponent({
|
||||
issuableType,
|
||||
});
|
||||
|
|
|
@ -845,7 +845,7 @@ describe('MrWidgetOptions', () => {
|
|||
${'closed'} | ${false} | ${'hides'}
|
||||
${'merged'} | ${true} | ${'shows'}
|
||||
${'open'} | ${true} | ${'shows'}
|
||||
`('it $showText merge error when state is $state', ({ state, show }) => {
|
||||
`('$showText merge error when state is $state', ({ state, show }) => {
|
||||
createComponent({ ...mockData, state, merge_error: 'Error!' });
|
||||
|
||||
expect(wrapper.find('[data-testid="merge_error"]').exists()).toBe(show);
|
||||
|
|
|
@ -49,7 +49,7 @@ describe('Pagination links component', () => {
|
|||
});
|
||||
|
||||
describe('rendering', () => {
|
||||
it('it renders the gl-paginated-list', () => {
|
||||
it('renders the gl-paginated-list', () => {
|
||||
expect(wrapper.find('ul.list-group').exists()).toBe(true);
|
||||
expect(wrapper.findAll('li.list-group-item').length).toBe(2);
|
||||
});
|
||||
|
|
|
@ -93,7 +93,7 @@ describe('ManageViaMr component', () => {
|
|||
createComponent({ apolloProvider, featureName, featureType, isFeatureConfigured: true });
|
||||
});
|
||||
|
||||
it('it does not render a button', () => {
|
||||
it('does not render a button', () => {
|
||||
expect(findButton().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
@ -104,7 +104,7 @@ describe('ManageViaMr component', () => {
|
|||
createComponent({ apolloProvider, featureName, featureType, isFeatureConfigured: false });
|
||||
});
|
||||
|
||||
it('it does render a button', () => {
|
||||
it('does render a button', () => {
|
||||
expect(findButton().exists()).toBe(true);
|
||||
});
|
||||
|
||||
|
|
|
@ -9,5 +9,5 @@ RSpec.describe GitlabSchema.types['MergeAccessLevel'] do
|
|||
|
||||
specify { is_expected.to require_graphql_authorizations(:read_protected_branch) }
|
||||
|
||||
specify { is_expected.to have_graphql_fields(fields) }
|
||||
specify { is_expected.to have_graphql_fields(fields).at_least }
|
||||
end
|
||||
|
|
|
@ -9,5 +9,5 @@ RSpec.describe GitlabSchema.types['BranchProtection'] do
|
|||
|
||||
specify { is_expected.to require_graphql_authorizations(:read_protected_branch) }
|
||||
|
||||
specify { is_expected.to have_graphql_fields(fields) }
|
||||
specify { is_expected.to have_graphql_fields(fields).at_least }
|
||||
end
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe Gitlab::Git::Commit, :seed_helper do
|
||||
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
|
||||
RSpec.describe Gitlab::Git::Commit do
|
||||
let(:repository) { create(:project, :repository).repository.raw }
|
||||
let(:commit) { described_class.find(repository, SeedRepo::Commit::ID) }
|
||||
|
||||
describe "Commit info from gitaly commit" do
|
||||
|
@ -121,14 +121,6 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
|
|||
it "returns nil for id containing NULL" do
|
||||
expect(described_class.find(repository, "HE\x00AD")).to be_nil
|
||||
end
|
||||
|
||||
context 'with broken repo' do
|
||||
let(:repository) { Gitlab::Git::Repository.new('default', TEST_BROKEN_REPO_PATH, '', 'group/project') }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(described_class.find(repository, SeedRepo::Commit::ID)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.find with Gitaly enabled' do
|
||||
|
@ -154,7 +146,7 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
|
|||
describe '#id' do
|
||||
subject { super().id }
|
||||
|
||||
it { is_expected.to eq(SeedRepo::LastCommit::ID) }
|
||||
it { is_expected.to eq(TestEnv::BRANCH_SHA['master']) }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -223,7 +215,7 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
|
|||
expect(subject.size).to eq(10)
|
||||
end
|
||||
|
||||
it { is_expected.to include(SeedRepo::EmptyCommit::ID) }
|
||||
it { is_expected.to include(TestEnv::BRANCH_SHA['master']) }
|
||||
end
|
||||
|
||||
context 'path is nil' do
|
||||
|
@ -242,28 +234,7 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
|
|||
expect(subject.size).to eq(10)
|
||||
end
|
||||
|
||||
it { is_expected.to include(SeedRepo::EmptyCommit::ID) }
|
||||
end
|
||||
|
||||
context 'ref is branch name' do
|
||||
subject do
|
||||
commits = described_class.where(
|
||||
repo: repository,
|
||||
ref: 'master',
|
||||
path: 'files',
|
||||
limit: 3,
|
||||
offset: 1
|
||||
)
|
||||
|
||||
commits.map { |c| c.id }
|
||||
end
|
||||
|
||||
it 'has 3 elements' do
|
||||
expect(subject.size).to eq(3)
|
||||
end
|
||||
|
||||
it { is_expected.to include("d14d6c0abdd253381df51a723d58691b2ee1ab08") }
|
||||
it { is_expected.not_to include("eb49186cfa5c4338011f5f590fac11bd66c5c631") }
|
||||
it { is_expected.to include(TestEnv::BRANCH_SHA['master']) }
|
||||
end
|
||||
|
||||
context 'ref is commit id' do
|
||||
|
@ -323,13 +294,12 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
|
|||
|
||||
context 'requesting a commit range' do
|
||||
let(:from) { 'v1.0.0' }
|
||||
let(:to) { 'v1.2.0' }
|
||||
let(:to) { 'v1.1.0' }
|
||||
|
||||
let(:commits_in_range) do
|
||||
%w[
|
||||
570e7b2abdd848b95f2f578043fc23bd6f6fd24d
|
||||
5937ac0a7beb003549fc5fd26fc247adbce4a52e
|
||||
eb49186cfa5c4338011f5f590fac11bd66c5c631
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -338,9 +308,9 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
|
|||
end
|
||||
|
||||
context 'limited' do
|
||||
let(:limit) { 2 }
|
||||
let(:limit) { 1 }
|
||||
|
||||
it { expect(commit_ids).to eq(commits_in_range.last(2)) }
|
||||
it { expect(commit_ids).to eq(commits_in_range.last(1)) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -383,16 +353,8 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
|
|||
commits.map(&:id)
|
||||
end
|
||||
|
||||
it 'has 34 elements' do
|
||||
expect(subject.size).to eq(34)
|
||||
end
|
||||
|
||||
it 'includes the expected commits' do
|
||||
expect(subject).to include(
|
||||
SeedRepo::Commit::ID,
|
||||
SeedRepo::Commit::PARENT_ID,
|
||||
SeedRepo::FirstCommit::ID
|
||||
)
|
||||
it 'has maximum elements' do
|
||||
expect(subject.size).to eq(50)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -408,13 +370,13 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
|
|||
commits.map(&:id)
|
||||
end
|
||||
|
||||
it 'has 24 elements' do
|
||||
expect(subject.size).to eq(24)
|
||||
it 'has 36 elements' do
|
||||
expect(subject.size).to eq(36)
|
||||
end
|
||||
|
||||
it 'includes the expected commits' do
|
||||
expect(subject).to include(SeedRepo::Commit::ID, SeedRepo::FirstCommit::ID)
|
||||
expect(subject).not_to include(SeedRepo::LastCommit::ID)
|
||||
expect(subject).not_to include(TestEnv::BRANCH_SHA['master'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -650,8 +612,8 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
|
|||
|
||||
subject { commit.ref_names(repository) }
|
||||
|
||||
it 'has 2 element' do
|
||||
expect(subject.size).to eq(2)
|
||||
it 'has 3 elements' do
|
||||
expect(subject.size).to eq(3)
|
||||
end
|
||||
|
||||
it { is_expected.to include("master") }
|
||||
|
@ -681,6 +643,8 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
|
|||
end
|
||||
|
||||
it 'gets messages in one batch', :request_store do
|
||||
repository # preload repository so that the project factory does not pollute request counts
|
||||
|
||||
expect { subject.map(&:itself) }.to change { Gitlab::GitalyClient.get_request_count }.by(1)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,11 +17,12 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
|||
end
|
||||
|
||||
describe '/api/v4/jobs' do
|
||||
let(:group) { create(:group, :nested) }
|
||||
let_it_be(:group) { create(:group, :nested) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
let(:project) { create(:project, namespace: group, shared_runners_enabled: false) }
|
||||
let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master') }
|
||||
let(:runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
let(:user) { create(:user) }
|
||||
let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master') }
|
||||
let(:job) do
|
||||
create(:ci_build, :pending, :queued, :artifacts, :extended_options,
|
||||
pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0)
|
||||
|
@ -354,6 +355,9 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
|||
end
|
||||
|
||||
context 'when GIT_DEPTH is not specified and there is no default git depth for the project' do
|
||||
let(:project) { create(:project, namespace: group, shared_runners_enabled: false) }
|
||||
let(:runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
|
||||
before do
|
||||
project.update!(ci_default_git_depth: nil)
|
||||
end
|
||||
|
@ -411,7 +415,8 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
|||
context 'when job is made for merge request' do
|
||||
let(:pipeline) { create(:ci_pipeline, source: :merge_request_event, project: project, ref: 'feature', merge_request: merge_request) }
|
||||
let!(:job) { create(:ci_build, :pending, :queued, pipeline: pipeline, name: 'spinach', ref: 'feature', stage: 'test', stage_idx: 0) }
|
||||
let(:merge_request) { create(:merge_request) }
|
||||
|
||||
let_it_be(:merge_request) { create(:merge_request) }
|
||||
|
||||
it 'sets branch as ref_type' do
|
||||
request_job
|
||||
|
@ -728,7 +733,9 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
|||
|
||||
describe 'timeout support' do
|
||||
context 'when project specifies job timeout' do
|
||||
let(:project) { create(:project, shared_runners_enabled: false, build_timeout: 1234) }
|
||||
let_it_be(:project) { create(:project, shared_runners_enabled: false, build_timeout: 1234) }
|
||||
|
||||
let(:runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
|
||||
it 'contains info about timeout taken from project' do
|
||||
request_job
|
||||
|
@ -926,8 +933,8 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
|||
end
|
||||
|
||||
context 'when the runner is of group type' do
|
||||
let(:group) { create(:group) }
|
||||
let(:runner) { create(:ci_runner, :group, groups: [group]) }
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:runner) { create(:ci_runner, :group, groups: [group]) }
|
||||
|
||||
it_behaves_like 'storing arguments in the application context for the API' do
|
||||
let(:expected_params) { { root_namespace: group.full_path_components.first, client_id: "runner/#{runner.id}" } }
|
||||
|
|
|
@ -36,18 +36,36 @@ RSpec.describe API::DebianGroupPackages do
|
|||
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete Packages file/
|
||||
end
|
||||
|
||||
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/by-hash/SHA256/:file_sha256' do
|
||||
let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/binary-#{architecture.name}/by-hash/SHA256/#{component_file_older_sha256.file_sha256}" }
|
||||
|
||||
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
|
||||
end
|
||||
|
||||
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/source/Sources' do
|
||||
let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/source/Sources" }
|
||||
|
||||
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete Sources file/
|
||||
end
|
||||
|
||||
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/source/by-hash/SHA256/:file_sha256' do
|
||||
let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/source/by-hash/SHA256/#{component_file_sources_older_sha256.file_sha256}" }
|
||||
|
||||
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
|
||||
end
|
||||
|
||||
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/Packages' do
|
||||
let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/Packages" }
|
||||
|
||||
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete D-I Packages file/
|
||||
end
|
||||
|
||||
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/by-hash/SHA256/:file_sha256' do
|
||||
let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/by-hash/SHA256/#{component_file_di_older_sha256.file_sha256}" }
|
||||
|
||||
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
|
||||
end
|
||||
|
||||
describe 'GET groups/:id/-/packages/debian/pool/:codename/:project_id/:letter/:package_name/:package_version/:file_name' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
|
|
|
@ -36,18 +36,36 @@ RSpec.describe API::DebianProjectPackages do
|
|||
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete Packages file/
|
||||
end
|
||||
|
||||
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/binary-:architecture/by-hash/SHA256/:file_sha256' do
|
||||
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/binary-#{architecture.name}/by-hash/SHA256/#{component_file_older_sha256.file_sha256}" }
|
||||
|
||||
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
|
||||
end
|
||||
|
||||
describe 'GET projects/:id/packages/debian/dists/*distribution/source/Sources' do
|
||||
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/source/Sources" }
|
||||
|
||||
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete Sources file/
|
||||
end
|
||||
|
||||
describe 'GET projects/:id/packages/debian/dists/*distribution/source/by-hash/SHA256/:file_sha256' do
|
||||
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/source/by-hash/SHA256/#{component_file_sources_older_sha256.file_sha256}" }
|
||||
|
||||
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
|
||||
end
|
||||
|
||||
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/Packages' do
|
||||
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/Packages" }
|
||||
|
||||
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete D-I Packages file/
|
||||
end
|
||||
|
||||
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/by-hash/SHA256/:file_sha256' do
|
||||
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/by-hash/SHA256/#{component_file_di_older_sha256.file_sha256}" }
|
||||
|
||||
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
|
||||
end
|
||||
|
||||
describe 'GET projects/:id/packages/debian/pool/:codename/:letter/:package_name/:package_version/:file_name' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
|
|
|
@ -3,6 +3,9 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe API::RpmProjectPackages do
|
||||
include HttpBasicAuthHelpers
|
||||
include WorkhorseHelpers
|
||||
|
||||
include_context 'workhorse headers'
|
||||
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
|
@ -141,8 +144,9 @@ RSpec.describe API::RpmProjectPackages do
|
|||
|
||||
describe 'POST /api/v4/projects/:project_id/packages/rpm' do
|
||||
let(:url) { "/projects/#{project.id}/packages/rpm" }
|
||||
let(:file_upload) { fixture_file_upload('spec/fixtures/packages/rpm/hello-0.0.1-1.fc29.x86_64.rpm') }
|
||||
|
||||
subject { post api(url), headers: headers }
|
||||
subject { post api(url), params: { file: file_upload }, headers: headers }
|
||||
|
||||
context 'with user token' do
|
||||
context 'with valid project' do
|
||||
|
@ -179,6 +183,41 @@ RSpec.describe API::RpmProjectPackages do
|
|||
it_behaves_like params[:shared_examples_name], params[:expected_status]
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user can upload file' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
let(:headers) { basic_auth_header(user.username, personal_access_token.token).merge(workhorse_headers) }
|
||||
|
||||
context 'when file size too large' do
|
||||
before do
|
||||
allow_next_instance_of(UploadedFile) do |uploaded_file|
|
||||
allow(uploaded_file).to receive(:size).and_return(project.actual_limits.rpm_max_file_size + 1)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns an error' do
|
||||
upload_file(params: { file: file_upload }, request_headers: headers)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(response.body).to match(/File is too large/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def upload_file(params: {}, request_headers: headers)
|
||||
url = "/projects/#{project.id}/packages/rpm"
|
||||
workhorse_finalize(
|
||||
api(url),
|
||||
method: :post,
|
||||
file_key: :file,
|
||||
params: params,
|
||||
headers: request_headers,
|
||||
send_rewritten_field: true
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'a deploy token for RPM requests'
|
||||
|
|
|
@ -18,7 +18,7 @@ RSpec.describe 'VerifiesWithEmail', :clean_gitlab_redis_sessions, :clean_gitlab_
|
|||
it 'sends an email' do
|
||||
mail = find_email_for(user)
|
||||
expect(mail.to).to match_array([user.email])
|
||||
expect(mail.subject).to eq('Verify your identity')
|
||||
expect(mail.subject).to eq(s_('IdentityVerification|Verify your identity'))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -50,7 +50,7 @@ RSpec.describe 'VerifiesWithEmail', :clean_gitlab_redis_sessions, :clean_gitlab_
|
|||
it 'adds a verification error message' do
|
||||
expect(response.body)
|
||||
.to include("You've reached the maximum amount of tries. "\
|
||||
'Wait 10 minutes or resend a new code and try again.')
|
||||
'Wait 10 minutes or send a new code and try again.')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -62,7 +62,8 @@ RSpec.describe 'VerifiesWithEmail', :clean_gitlab_redis_sessions, :clean_gitlab_
|
|||
it_behaves_like 'prompt for email verification'
|
||||
|
||||
it 'adds a verification error message' do
|
||||
expect(response.body).to include(('The code is incorrect. Enter it again, or resend a new code.'))
|
||||
expect(response.body)
|
||||
.to include((s_('IdentityVerification|The code is incorrect. Enter it again, or send a new code.')))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -75,7 +76,8 @@ RSpec.describe 'VerifiesWithEmail', :clean_gitlab_redis_sessions, :clean_gitlab_
|
|||
it_behaves_like 'prompt for email verification'
|
||||
|
||||
it 'adds a verification error message' do
|
||||
expect(response.body).to include(('The code has expired. Resend a new code and try again.'))
|
||||
expect(response.body)
|
||||
.to include((s_('IdentityVerification|The code has expired. Send a new code and try again.')))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -112,7 +114,8 @@ RSpec.describe 'VerifiesWithEmail', :clean_gitlab_redis_sessions, :clean_gitlab_
|
|||
|
||||
it 'redirects to the login form and shows an alert message' do
|
||||
expect(response).to redirect_to(new_user_session_path)
|
||||
expect(flash[:alert]).to eq('Maximum login attempts exceeded. Wait 10 minutes and try again.')
|
||||
expect(flash[:alert])
|
||||
.to eq(s_('IdentityVerification|Maximum login attempts exceeded. Wait 10 minutes and try again.'))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -217,6 +220,7 @@ RSpec.describe 'VerifiesWithEmail', :clean_gitlab_redis_sessions, :clean_gitlab_
|
|||
|
||||
describe 'successful_verification' do
|
||||
before do
|
||||
allow(user).to receive(:role_required?).and_return(true) # It skips the required signup info before_action
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Packages::Rpm::RepositoryMetadata::BaseBuilder do
|
||||
describe '#execute' do
|
||||
subject { described_class.new.execute }
|
||||
|
||||
before do
|
||||
stub_const("#{described_class}::ROOT_TAG", 'test')
|
||||
stub_const("#{described_class}::ROOT_ATTRIBUTES", { foo1: 'bar1', foo2: 'bar2' })
|
||||
end
|
||||
|
||||
it 'generate valid xml' do
|
||||
result = Nokogiri::XML::Document.parse(subject)
|
||||
|
||||
expect(result.children.count).to eq(1)
|
||||
expect(result.children.first.attributes.count).to eq(2)
|
||||
expect(result.children.first.attributes['foo1'].value).to eq('bar1')
|
||||
expect(result.children.first.attributes['foo2'].value).to eq('bar2')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Packages::Rpm::RepositoryMetadata::BuildFilelistXml do
|
||||
describe '#execute' do
|
||||
subject { described_class.new.execute }
|
||||
|
||||
context "when generate empty xml" do
|
||||
let(:expected_xml) do
|
||||
<<~XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<filelists xmlns="http://linux.duke.edu/metadata/filelists" packages="0"/>
|
||||
XML
|
||||
end
|
||||
|
||||
it 'generate expected xml' do
|
||||
expect(subject).to eq(expected_xml)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Packages::Rpm::RepositoryMetadata::BuildOtherXml do
|
||||
describe '#execute' do
|
||||
subject { described_class.new.execute }
|
||||
|
||||
context "when generate empty xml" do
|
||||
let(:expected_xml) do
|
||||
<<~XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<otherdata xmlns="http://linux.duke.edu/metadata/other" packages="0"/>
|
||||
XML
|
||||
end
|
||||
|
||||
it 'generate expected xml' do
|
||||
expect(subject).to eq(expected_xml)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Packages::Rpm::RepositoryMetadata::BuildPrimaryXml do
|
||||
describe '#execute' do
|
||||
subject { described_class.new.execute }
|
||||
|
||||
context "when generate empty xml" do
|
||||
let(:expected_xml) do
|
||||
<<~XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<metadata xmlns="http://linux.duke.edu/metadata/common" xmlns:rpm="http://linux.duke.edu/metadata/rpm" packages="0"/>
|
||||
XML
|
||||
end
|
||||
|
||||
it 'generate expected xml' do
|
||||
expect(subject).to eq(expected_xml)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,66 @@
|
|||
# frozen_string_literal: true
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Packages::Rpm::RepositoryMetadata::BuildRepomdXml do
|
||||
describe '#execute' do
|
||||
subject { described_class.new(data).execute }
|
||||
|
||||
let(:data) do
|
||||
{
|
||||
filelists: {
|
||||
checksum: { type: "sha256", value: "123" },
|
||||
'open-checksum': { type: "sha256", value: "123" },
|
||||
location: { href: "repodata/123-filelists.xml.gz" },
|
||||
timestamp: { value: 1644602784 },
|
||||
size: { value: 11111 },
|
||||
'open-size': { value: 11111 }
|
||||
},
|
||||
primary: {
|
||||
checksum: { type: "sha256", value: "234" },
|
||||
'open-checksum': { type: "sha256", value: "234" },
|
||||
location: { href: "repodata/234-primary.xml.gz" },
|
||||
timestamp: { value: 1644602784 },
|
||||
size: { value: 22222 },
|
||||
'open-size': { value: 22222 }
|
||||
},
|
||||
other: {
|
||||
checksum: { type: "sha256", value: "345" },
|
||||
'open-checksum': { type: "sha256", value: "345" },
|
||||
location: { href: "repodata/345-other.xml.gz" },
|
||||
timestamp: { value: 1644602784 },
|
||||
size: { value: 33333 },
|
||||
'open-size': { value: 33333 }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
let(:creation_timestamp) { 111111 }
|
||||
|
||||
before do
|
||||
allow(Time).to receive(:now).and_return(creation_timestamp)
|
||||
end
|
||||
|
||||
it 'generate valid xml' do
|
||||
# Have one root attribute
|
||||
result = Nokogiri::XML::Document.parse(subject)
|
||||
expect(result.children.count).to eq(1)
|
||||
|
||||
# Root attribute name is 'repomd'
|
||||
root = result.children.first
|
||||
expect(root.name).to eq('repomd')
|
||||
|
||||
# Have the same count of 'data' tags as count of keys in 'data'
|
||||
expect(result.css('data').count).to eq(data.count)
|
||||
end
|
||||
|
||||
it 'has all data info' do
|
||||
result = Nokogiri::XML::Document.parse(subject).remove_namespaces!
|
||||
|
||||
data.each do |tag_name, tag_attributes|
|
||||
tag_attributes.each_key do |key|
|
||||
expect(result.at("//repomd/data[@type=\"#{tag_name}\"]/#{key}")).not_to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -25,7 +25,8 @@ RSpec.describe Users::EmailVerification::ValidateTokenService, :clean_gitlab_red
|
|||
|
||||
context 'when rate limited' do
|
||||
before do
|
||||
allow(Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true)
|
||||
allow(Gitlab::ApplicationRateLimiter).to receive(:throttled?)
|
||||
.with(:email_verification, scope: encrypted_token).and_return(true)
|
||||
end
|
||||
|
||||
it 'returns a failure status' do
|
||||
|
@ -33,8 +34,8 @@ RSpec.describe Users::EmailVerification::ValidateTokenService, :clean_gitlab_red
|
|||
{
|
||||
status: :failure,
|
||||
reason: :rate_limited,
|
||||
message: "You've reached the maximum amount of tries. "\
|
||||
'Wait 10 minutes or resend a new code and try again.'
|
||||
message: format(s_("IdentityVerification|You've reached the maximum amount of tries. "\
|
||||
'Wait %{interval} or send a new code and try again.'), interval: '10 minutes')
|
||||
}
|
||||
)
|
||||
end
|
||||
|
@ -48,7 +49,7 @@ RSpec.describe Users::EmailVerification::ValidateTokenService, :clean_gitlab_red
|
|||
{
|
||||
status: :failure,
|
||||
reason: :expired,
|
||||
message: 'The code has expired. Resend a new code and try again.'
|
||||
message: s_('IdentityVerification|The code has expired. Send a new code and try again.')
|
||||
}
|
||||
)
|
||||
end
|
||||
|
@ -62,7 +63,22 @@ RSpec.describe Users::EmailVerification::ValidateTokenService, :clean_gitlab_red
|
|||
{
|
||||
status: :failure,
|
||||
reason: :invalid,
|
||||
message: 'The code is incorrect. Enter it again, or resend a new code.'
|
||||
message: s_('IdentityVerification|The code is incorrect. Enter it again, or send a new code.')
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when encrypted token was not set and a blank token is provided' do
|
||||
let(:encrypted_token) { nil }
|
||||
let(:token) { '' }
|
||||
|
||||
it 'returns a failure status' do
|
||||
expect(service.execute).to eq(
|
||||
{
|
||||
status: :failure,
|
||||
reason: :invalid,
|
||||
message: s_('IdentityVerification|The code is incorrect. Enter it again, or send a new code.')
|
||||
}
|
||||
)
|
||||
end
|
||||
|
|
|
@ -6,7 +6,6 @@ module UsageDataHelpers
|
|||
snippet_update
|
||||
snippet_comment
|
||||
merge_request_comment
|
||||
merge_request_create
|
||||
commit_comment
|
||||
wiki_pages_create
|
||||
wiki_pages_update
|
||||
|
@ -15,9 +14,6 @@ module UsageDataHelpers
|
|||
cycle_analytics_views
|
||||
productivity_analytics_views
|
||||
source_code_pushes
|
||||
design_management_designs_create
|
||||
design_management_designs_update
|
||||
design_management_designs_delete
|
||||
).freeze
|
||||
|
||||
COUNTS_KEYS = %i(
|
||||
|
@ -122,7 +118,6 @@ module UsageDataHelpers
|
|||
uploads
|
||||
web_hooks
|
||||
user_preferences_user_gitpod_enabled
|
||||
service_usage_data_download_payload_click
|
||||
).push(*SMAU_KEYS)
|
||||
|
||||
USAGE_DATA_KEYS = %i(
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.include SeedHelper, :seed_helper
|
||||
|
||||
config.before(:all, :seed_helper) do
|
||||
ensure_seeds
|
||||
end
|
||||
end
|
|
@ -18,8 +18,11 @@ RSpec.shared_context 'Debian repository shared context' do |container_type, can_
|
|||
let_it_be(:private_architecture_all, freeze: true) { create("debian_#{container_type}_architecture", distribution: private_distribution, name: 'all') }
|
||||
let_it_be(:private_architecture, freeze: true) { create("debian_#{container_type}_architecture", distribution: private_distribution, name: 'existing-arch') }
|
||||
let_it_be(:private_component_file) { create("debian_#{container_type}_component_file", component: private_component, architecture: private_architecture) }
|
||||
let_it_be(:private_component_sources) { create("debian_#{container_type}_component_file", :sources, component: private_component) }
|
||||
let_it_be(:private_component_file_sources) { create("debian_#{container_type}_component_file", :sources, component: private_component) }
|
||||
let_it_be(:private_component_file_di) { create("debian_#{container_type}_component_file", :di_packages, component: private_component, architecture: private_architecture) }
|
||||
let_it_be(:private_component_file_older_sha256) { create("debian_#{container_type}_component_file", :older_sha256, component: private_component, architecture: private_architecture) }
|
||||
let_it_be(:private_component_file_sources_older_sha256) { create("debian_#{container_type}_component_file", :sources, :older_sha256, component: private_component) }
|
||||
let_it_be(:private_component_file_di_older_sha256) { create("debian_#{container_type}_component_file", :di_packages, :older_sha256, component: private_component, architecture: private_architecture) }
|
||||
|
||||
let_it_be(:public_distribution, freeze: true) { create("debian_#{container_type}_distribution", :with_file, container: public_container, codename: 'existing-codename') }
|
||||
let_it_be(:public_distribution_key, freeze: true) { create("debian_#{container_type}_distribution_key", distribution: public_distribution) }
|
||||
|
@ -29,6 +32,9 @@ RSpec.shared_context 'Debian repository shared context' do |container_type, can_
|
|||
let_it_be(:public_component_file) { create("debian_#{container_type}_component_file", component: public_component, architecture: public_architecture) }
|
||||
let_it_be(:public_component_file_sources) { create("debian_#{container_type}_component_file", :sources, component: public_component) }
|
||||
let_it_be(:public_component_file_di) { create("debian_#{container_type}_component_file", :di_packages, component: public_component, architecture: public_architecture) }
|
||||
let_it_be(:public_component_file_older_sha256) { create("debian_#{container_type}_component_file", :older_sha256, component: public_component, architecture: public_architecture) }
|
||||
let_it_be(:public_component_file_sources_older_sha256) { create("debian_#{container_type}_component_file", :sources, :older_sha256, component: public_component) }
|
||||
let_it_be(:public_component_file_di_older_sha256) { create("debian_#{container_type}_component_file", :di_packages, :older_sha256, component: public_component, architecture: public_architecture) }
|
||||
|
||||
if container_type == :group
|
||||
let_it_be(:private_project) { create(:project, :private, group: private_container) }
|
||||
|
@ -52,6 +58,9 @@ RSpec.shared_context 'Debian repository shared context' do |container_type, can_
|
|||
let(:distribution) { { private: private_distribution, public: public_distribution }[visibility_level] }
|
||||
let(:architecture) { { private: private_architecture, public: public_architecture }[visibility_level] }
|
||||
let(:component) { { private: private_component, public: public_component }[visibility_level] }
|
||||
let(:component_file_older_sha256) { { private: private_component_file_older_sha256, public: public_component_file_older_sha256 }[visibility_level] }
|
||||
let(:component_file_sources_older_sha256) { { private: private_component_file_sources_older_sha256, public: public_component_file_sources_older_sha256 }[visibility_level] }
|
||||
let(:component_file_di_older_sha256) { { private: private_component_file_di_older_sha256, public: public_component_file_di_older_sha256 }[visibility_level] }
|
||||
let(:package) { { private: private_package, public: public_package }[visibility_level] }
|
||||
let(:letter) { package.name[0..2] == 'lib' ? package.name[0..3] : package.name[0] }
|
||||
|
||||
|
|
|
@ -190,6 +190,7 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
|
|||
Codename: unstable
|
||||
Date: Sat, 25 Jan 2020 15:17:18 +0000
|
||||
Valid-Until: Mon, 27 Jan 2020 15:17:18 +0000
|
||||
Acquire-By-Hash: yes
|
||||
Architectures: all amd64 arm64
|
||||
Components: contrib main
|
||||
MD5Sum:
|
||||
|
@ -249,6 +250,7 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
|
|||
Codename: unstable
|
||||
Date: Sat, 25 Jan 2020 15:17:18 +0000
|
||||
Valid-Until: Mon, 27 Jan 2020 15:17:18 +0000
|
||||
Acquire-By-Hash: yes
|
||||
MD5Sum:
|
||||
SHA256:
|
||||
EOF
|
||||
|
|
|
@ -30,15 +30,15 @@ module OmniAuth
|
|||
@raw_info ||= access_token.get(user_endpoint_url).parsed
|
||||
end
|
||||
|
||||
def callback_url
|
||||
options.redirect_url || (full_host + callback_path)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def user_endpoint_url
|
||||
options.client_options.site.match(API_SUFFIX_REGEX) ? 'user' : 'api/v4/user'
|
||||
end
|
||||
|
||||
def callback_url
|
||||
options.redirect_url || (full_host + script_name + callback_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -77,4 +77,26 @@ describe OmniAuth::Strategies::GitLab do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#callback_url' do
|
||||
let(:base_url) { 'https://example.com' }
|
||||
|
||||
context 'no script name present' do
|
||||
it 'has the correct default callback path' do
|
||||
allow(subject).to receive(:full_host) { base_url }
|
||||
allow(subject).to receive(:script_name) { '' }
|
||||
allow(subject).to receive(:query_string) { '' }
|
||||
expect(subject.callback_url).to eq(base_url + '/auth/gitlab/callback')
|
||||
end
|
||||
end
|
||||
|
||||
context 'script name' do
|
||||
it 'should set the callback path with script_name' do
|
||||
allow(subject).to receive(:full_host) { base_url }
|
||||
allow(subject).to receive(:script_name) { '/v1' }
|
||||
allow(subject).to receive(:query_string) { '' }
|
||||
expect(subject.callback_url).to eq(base_url + '/v1/auth/gitlab/callback')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue