Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-08-12 03:10:28 +00:00
parent faedfbb473
commit e8a7b1cd3f
22 changed files with 108 additions and 80 deletions

View File

@ -1 +1 @@
ac315ef254b265af5323387a1df1abb5d57daa95 3e9cc60933881ce8c33c379c72dad3907755aac2

View File

@ -11,7 +11,7 @@
padding-right: $gutter_collapsed_width; padding-right: $gutter_collapsed_width;
background: $white; background: $white;
border-top: 1px solid $border-color; border-top: 1px solid $border-color;
transition: padding $sidebar-transition-duration; transition: padding $gl-transition-duration-medium;
.page-with-icon-sidebar & { .page-with-icon-sidebar & {
padding-left: $contextual-sidebar-collapsed-width; padding-left: $contextual-sidebar-collapsed-width;

View File

@ -204,7 +204,7 @@
// //
.page-with-contextual-sidebar { .page-with-contextual-sidebar {
transition: padding-left $sidebar-transition-duration; transition: padding-left $gl-transition-duration-medium;
@include media-breakpoint-up(md) { @include media-breakpoint-up(md) {
padding-left: $contextual-sidebar-collapsed-width; padding-left: $contextual-sidebar-collapsed-width;
@ -233,7 +233,7 @@
@include gl-fixed; @include gl-fixed;
@include gl-bottom-0; @include gl-bottom-0;
@include gl-left-0; @include gl-left-0;
transition: width $sidebar-transition-duration, left $sidebar-transition-duration; transition: width $gl-transition-duration-medium, left $gl-transition-duration-medium;
z-index: 600; z-index: 600;
width: $contextual-sidebar-width; width: $contextual-sidebar-width;
top: $header-height; top: $header-height;

View File

@ -5,7 +5,7 @@
> a, > a,
> button { > button {
transition: padding $sidebar-transition-duration; transition: padding $gl-transition-duration-medium;
font-weight: $gl-font-weight-bold; font-weight: $gl-font-weight-bold;
display: flex; display: flex;
width: 100%; width: 100%;

View File

@ -478,7 +478,7 @@
} }
@mixin side-panel-toggle { @mixin side-panel-toggle {
transition: width $sidebar-transition-duration; transition: width $gl-transition-duration-medium;
height: $toggle-sidebar-height; height: $toggle-sidebar-height;
padding: 0 $gl-padding; padding: 0 $gl-padding;
background-color: $gray-light; background-color: $gray-light;

View File

@ -13,7 +13,7 @@
} }
.page-initialised .content-wrapper { .page-initialised .content-wrapper {
transition: padding $sidebar-transition-duration; transition: padding $gl-transition-duration-medium;
} }
.right-sidebar-collapsed { .right-sidebar-collapsed {
@ -109,7 +109,7 @@
@include maintain-sidebar-dimensions; @include maintain-sidebar-dimensions;
width: 0; width: 0;
padding: 0; padding: 0;
transition: width $sidebar-transition-duration; transition: width $gl-transition-duration-medium;
&.right-sidebar-expanded { &.right-sidebar-expanded {
@include maintain-sidebar-dimensions; @include maintain-sidebar-dimensions;

View File

@ -5,7 +5,6 @@ $grid-size: 8px;
$gutter-collapsed-width: 62px; $gutter-collapsed-width: 62px;
$gutter-width: 290px; $gutter-width: 290px;
$gutter-inner-width: 250px; $gutter-inner-width: 250px;
$sidebar-transition-duration: 0.3s;
$sidebar-breakpoint: 1024px; $sidebar-breakpoint: 1024px;
$default-transition-duration: 0.15s; $default-transition-duration: 0.15s;
$contextual-sidebar-width: 256px; $contextual-sidebar-width: 256px;

View File

@ -2,7 +2,7 @@
.fade-leave-active, .fade-leave-active,
.fade-in-enter-active, .fade-in-enter-active,
.fade-out-leave-active { .fade-out-leave-active {
transition: opacity $sidebar-transition-duration $general-hover-transition-curve; transition: opacity $gl-transition-duration-medium $general-hover-transition-curve;
} }
.fade-enter, .fade-enter,

View File

@ -35,7 +35,7 @@
.boards-app { .boards-app {
@include media-breakpoint-up(sm) { @include media-breakpoint-up(sm) {
transition: width $sidebar-transition-duration; transition: width $gl-transition-duration-medium;
width: 100%; width: 100%;
&.is-compact { &.is-compact {
@ -349,7 +349,7 @@
.right-sidebar.right-sidebar-expanded { .right-sidebar.right-sidebar-expanded {
&.boards-sidebar-slide-enter-active, &.boards-sidebar-slide-enter-active,
&.boards-sidebar-slide-leave-active { &.boards-sidebar-slide-leave-active {
transition: width $sidebar-transition-duration, padding $sidebar-transition-duration; transition: width $gl-transition-duration-medium, padding $gl-transition-duration-medium;
} }
&.boards-sidebar-slide-enter, &.boards-sidebar-slide-enter,

View File

@ -132,7 +132,7 @@
// stylelint-disable-next-line length-zero-no-unit // stylelint-disable-next-line length-zero-no-unit
bottom: var(--review-bar-height, 0px); bottom: var(--review-bar-height, 0px);
right: 0; right: 0;
transition: width $sidebar-transition-duration; transition: width $gl-transition-duration-medium;
background-color: $white; background-color: $white;
z-index: 200; z-index: 200;
overflow: hidden; overflow: hidden;

View File

@ -25,7 +25,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# the number of failed sign in attempts # the number of failed sign in attempts
def failure def failure
if params[:username].present? && AuthHelper.form_based_provider?(failed_strategy.name) if params[:username].present? && AuthHelper.form_based_provider?(failed_strategy.name)
user = User.by_login(params[:username]) user = User.find_by_login(params[:username])
user&.increment_failed_attempts! user&.increment_failed_attempts!
log_failed_login(params[:username], failed_strategy.name) log_failed_login(params[:username], failed_strategy.name)

View File

@ -215,11 +215,11 @@ class SessionsController < Devise::SessionsController
def find_user def find_user
strong_memoize(:find_user) do strong_memoize(:find_user) do
if session[:otp_user_id] && user_params[:login] if session[:otp_user_id] && user_params[:login]
User.by_id_and_login(session[:otp_user_id], user_params[:login]).first User.by_login(user_params[:login]).find_by_id(session[:otp_user_id])
elsif session[:otp_user_id] elsif session[:otp_user_id]
User.find(session[:otp_user_id]) User.find(session[:otp_user_id])
elsif user_params[:login] elsif user_params[:login]
User.by_login(user_params[:login]) User.find_by_login(user_params[:login])
end end
end end
end end

View File

@ -448,6 +448,11 @@ class User < ApplicationRecord
scope :without_projects, -> { joins('LEFT JOIN project_authorizations ON users.id = project_authorizations.user_id').where(project_authorizations: { user_id: nil }) } scope :without_projects, -> { joins('LEFT JOIN project_authorizations ON users.id = project_authorizations.user_id').where(project_authorizations: { user_id: nil }) }
scope :by_username, -> (usernames) { iwhere(username: Array(usernames).map(&:to_s)) } scope :by_username, -> (usernames) { iwhere(username: Array(usernames).map(&:to_s)) }
scope :by_name, -> (names) { iwhere(name: Array(names)) } scope :by_name, -> (names) { iwhere(name: Array(names)) }
scope :by_login, -> (login) do
return none if login.blank?
login.include?('@') ? iwhere(email: login) : iwhere(username: login)
end
scope :by_user_email, -> (emails) { iwhere(email: Array(emails)) } scope :by_user_email, -> (emails) { iwhere(email: Array(emails)) }
scope :by_emails, -> (emails) { joins(:emails).where(emails: { email: Array(emails).map(&:downcase) }) } scope :by_emails, -> (emails) { joins(:emails).where(emails: { email: Array(emails).map(&:downcase) }) }
scope :for_todos, -> (todos) { where(id: todos.select(:user_id).distinct) } scope :for_todos, -> (todos) { where(id: todos.select(:user_id).distinct) }
@ -482,7 +487,6 @@ class User < ApplicationRecord
scope :order_oldest_sign_in, -> { reorder(arel_table[:current_sign_in_at].asc.nulls_last) } scope :order_oldest_sign_in, -> { reorder(arel_table[:current_sign_in_at].asc.nulls_last) }
scope :order_recent_last_activity, -> { reorder(arel_table[:last_activity_on].desc.nulls_last, arel_table[:id].asc) } scope :order_recent_last_activity, -> { reorder(arel_table[:last_activity_on].desc.nulls_last, arel_table[:id].asc) }
scope :order_oldest_last_activity, -> { reorder(arel_table[:last_activity_on].asc.nulls_first, arel_table[:id].desc) } scope :order_oldest_last_activity, -> { reorder(arel_table[:last_activity_on].asc.nulls_first, arel_table[:id].desc) }
scope :by_id_and_login, ->(id, login) { where(id: id).where('username = LOWER(:login) OR email = LOWER(:login)', login: login) }
scope :dormant, -> { with_state(:active).human_or_service_user.where('last_activity_on <= ?', MINIMUM_INACTIVE_DAYS.day.ago.to_date) } scope :dormant, -> { with_state(:active).human_or_service_user.where('last_activity_on <= ?', MINIMUM_INACTIVE_DAYS.day.ago.to_date) }
scope :with_no_activity, -> { with_state(:active).human_or_service_user.where(last_activity_on: nil).where('created_at <= ?', MINIMUM_DAYS_CREATED.day.ago.to_date) } scope :with_no_activity, -> { with_state(:active).human_or_service_user.where(last_activity_on: nil).where('created_at <= ?', MINIMUM_DAYS_CREATED.day.ago.to_date) }
scope :by_provider_and_extern_uid, ->(provider, extern_uid) { joins(:identities).merge(Identity.with_extern_uid(provider, extern_uid)) } scope :by_provider_and_extern_uid, ->(provider, extern_uid) { joins(:identities).merge(Identity.with_extern_uid(provider, extern_uid)) }
@ -765,14 +769,8 @@ class User < ApplicationRecord
true true
end end
def by_login(login) def find_by_login(login)
return unless login by_login(login).take
if login.include?('@')
unscoped.iwhere(email: login).take
else
unscoped.iwhere(username: login).take
end
end end
def find_by_username(username) def find_by_username(username)

View File

@ -57,6 +57,8 @@ verification methods:
| Blobs | Pipeline artifacts _(object storage)_ | Geo with API/Managed (*2*) | _Not implemented_ | | Blobs | Pipeline artifacts _(object storage)_ | Geo with API/Managed (*2*) | _Not implemented_ |
| Blobs | Pages _(file system)_ | Geo with API | SHA256 checksum | | Blobs | Pages _(file system)_ | Geo with API | SHA256 checksum |
| Blobs | Pages _(object storage)_ | Geo with API/Managed (*2*) | _Not implemented_ | | Blobs | Pages _(object storage)_ | Geo with API/Managed (*2*) | _Not implemented_ |
| Blobs | CI Secure Files _(file system)_ | Geo with API | SHA256 checksum |
| Blobs | CI Secure Files _(object storage)_ | Geo with API/Managed (*2*) | _Not implemented_ |
- (*1*): Redis replication can be used as part of HA with Redis sentinel. It's not used between Geo sites. - (*1*): Redis replication can be used as part of HA with Redis sentinel. It's not used between Geo sites.
- (*2*): Object storage replication can be performed by Geo or by your object storage provider/appliance - (*2*): Object storage replication can be performed by Geo or by your object storage provider/appliance
@ -194,6 +196,7 @@ successfully, you must replicate their data using some other means.
|[Project snippets](../../../user/snippets.md) | **Yes** (10.2) | **Yes** (10.2) | N/A | N/A | | |[Project snippets](../../../user/snippets.md) | **Yes** (10.2) | **Yes** (10.2) | N/A | N/A | |
|[CI job artifacts](../../../ci/pipelines/job_artifacts.md) | **Yes** (10.4) | **Yes** (14.10) | [**Yes** (15.1)](https://gitlab.com/groups/gitlab-org/-/epics/5551) | [No](object_storage.md#verification-of-files-in-object-storage) | Verification is behind the feature flag `geo_job_artifact_replication`, enabled by default in 14.10. | |[CI job artifacts](../../../ci/pipelines/job_artifacts.md) | **Yes** (10.4) | **Yes** (14.10) | [**Yes** (15.1)](https://gitlab.com/groups/gitlab-org/-/epics/5551) | [No](object_storage.md#verification-of-files-in-object-storage) | Verification is behind the feature flag `geo_job_artifact_replication`, enabled by default in 14.10. |
|[CI Pipeline Artifacts](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/ci/pipeline_artifact.rb) | [**Yes** (13.11)](https://gitlab.com/gitlab-org/gitlab/-/issues/238464) | [**Yes** (13.11)](https://gitlab.com/gitlab-org/gitlab/-/issues/238464) | [**Yes** (15.1)](https://gitlab.com/groups/gitlab-org/-/epics/5551) | [No](object_storage.md#verification-of-files-in-object-storage) | Persists additional artifacts after a pipeline completes. | |[CI Pipeline Artifacts](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/ci/pipeline_artifact.rb) | [**Yes** (13.11)](https://gitlab.com/gitlab-org/gitlab/-/issues/238464) | [**Yes** (13.11)](https://gitlab.com/gitlab-org/gitlab/-/issues/238464) | [**Yes** (15.1)](https://gitlab.com/groups/gitlab-org/-/epics/5551) | [No](object_storage.md#verification-of-files-in-object-storage) | Persists additional artifacts after a pipeline completes. |
|[CI Secure Files](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/ci/secure_file.rb) | [**Yes** (15.3)](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91430) | [**Yes** (15.3)](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91430) | [**Yes** (15.3)](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91430) | [No](object_storage.md#verification-of-files-in-object-storage) | Verification is behind the feature flag `geo_ci_secure_file_replication`, enabled by default in 15.3. |
|[Container Registry](../../packages/container_registry.md) | **Yes** (12.3) | No | No | No | Disabled by default. See [instructions](container_registry.md) to enable. | |[Container Registry](../../packages/container_registry.md) | **Yes** (12.3) | No | No | No | Disabled by default. See [instructions](container_registry.md) to enable. |
|[Infrastructure Registry](../../../user/packages/infrastructure_registry/index.md) | **Yes** (14.0) | **Yes** (14.0) | [**Yes** (15.1)](https://gitlab.com/groups/gitlab-org/-/epics/5551) | [No](object_storage.md#verification-of-files-in-object-storage) | Behind feature flag `geo_package_file_replication`, enabled by default. | |[Infrastructure Registry](../../../user/packages/infrastructure_registry/index.md) | **Yes** (14.0) | **Yes** (14.0) | [**Yes** (15.1)](https://gitlab.com/groups/gitlab-org/-/epics/5551) | [No](object_storage.md#verification-of-files-in-object-storage) | Behind feature flag `geo_package_file_replication`, enabled by default. |
|[Project designs repository](../../../user/project/issues/design_management.md) | **Yes** (12.7) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/32467) | N/A | N/A | Designs also require replication of LFS objects and Uploads. | |[Project designs repository](../../../user/project/issues/design_management.md) | **Yes** (12.7) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/32467) | N/A | N/A | Designs also require replication of LFS objects and Uploads. |

View File

@ -11891,7 +11891,7 @@ Represents an external issue.
##### `GeoNode.ciSecureFileRegistries` ##### `GeoNode.ciSecureFileRegistries`
Find Ci Secure File registries on this Geo node Available only when feature flag `geo_ci_secure_file_replication` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. Find Ci Secure File registries on this Geo node.
Returns [`CiSecureFileRegistryConnection`](#cisecurefileregistryconnection). Returns [`CiSecureFileRegistryConnection`](#cisecurefileregistryconnection).

View File

@ -189,11 +189,14 @@ and are protected at the same time.
### Configure group-level memberships ### Configure group-level memberships
> - Operators are required to have Owner+ role from the original Maintainer+ role and this role change is introduced from GitLab 15.3 [with a flag](https://gitlab.com/gitlab-org/gitlab/-/issues/369873) named `group_level_protected_environment_settings_permission`. Disabled by default.
> - Original behavior where Operators are required to have Maintainer+ role can be achieved by enabling [flag](https://gitlab.com/gitlab-org/gitlab/-/issues/369875) named `override_group_level_protected_environment_settings_permission`. Disabled by default.
To maximize the effectiveness of group-level protected environments, To maximize the effectiveness of group-level protected environments,
[group-level memberships](../../user/group/index.md) must be correctly [group-level memberships](../../user/group/index.md) must be correctly
configured: configured:
- Operators should be given at least the Maintainer role - Operators should be given at least the Owner role
for the top-level group. They can maintain CI/CD configurations for for the top-level group. They can maintain CI/CD configurations for
the higher environments (such as production) in the group-level settings page, the higher environments (such as production) in the group-level settings page,
which includes group-level protected environments, which includes group-level protected environments,
@ -203,7 +206,7 @@ configured:
This ensures that only operators can configure the organization-wide This ensures that only operators can configure the organization-wide
deployment ruleset. deployment ruleset.
- Developers should be given no more than the Developer role - Developers should be given no more than the Developer role
for the top-level group, or explicitly given the Maintainer role for a child project for the top-level group, or explicitly given the Owner role for a child project
They do *not* have access to the CI/CD configurations in the They do *not* have access to the CI/CD configurations in the
top-level group, so operators can ensure that the critical configuration won't top-level group, so operators can ensure that the critical configuration won't
be accidentally changed by the developers. be accidentally changed by the developers.

View File

@ -62,6 +62,9 @@ Team members' domain expertise can be viewed on the [engineering projects](https
### Reviewer roulette ### Reviewer roulette
NOTE:
Reviewer roulette is an internal tool for use on GitLab.com, and not available for use on customer installations.
The [Danger bot](dangerbot.md) randomly picks a reviewer and a maintainer for The [Danger bot](dangerbot.md) randomly picks a reviewer and a maintainer for
each area of the codebase that your merge request seems to touch. It only makes each area of the codebase that your merge request seems to touch. It only makes
**recommendations** and you should override it if you think someone else is a better **recommendations** and you should override it if you think someone else is a better
@ -487,7 +490,7 @@ Before taking the decision to merge:
- If the MR contains both Quality and non-Quality-related changes, the MR should be merged by the relevant maintainer for user-facing changes (backend, frontend, or database) after the Quality related changes are approved by a Software Engineer in Test. - If the MR contains both Quality and non-Quality-related changes, the MR should be merged by the relevant maintainer for user-facing changes (backend, frontend, or database) after the Quality related changes are approved by a Software Engineer in Test.
If a merge request is fundamentally ready, but needs only trivial fixes (such as If a merge request is fundamentally ready, but needs only trivial fixes (such as
typos), consider demonstrating a [bias for action](https://about.gitlab.com/handbook/values/#bias-for-action) typos), consider demonstrating a [bias for action](https://about.gitlab.com/handbook/values/#bias-for-action)
by making those changes directly without going back to the author. You can do this by by making those changes directly without going back to the author. You can do this by
using the [suggest changes](../user/project/merge_requests/reviews/suggestions.md) feature to apply using the [suggest changes](../user/project/merge_requests/reviews/suggestions.md) feature to apply
your own suggestions to the merge request. Note that: your own suggestions to the merge request. Note that:

View File

@ -64,7 +64,7 @@ variables:
SAST_IMAGE_SUFFIX: '-fips' SAST_IMAGE_SUFFIX: '-fips'
include: include:
- template: Security/SAST-IaC.latest.gitlab-ci.yml - template: Jobs/SAST-IaC.gitlab-ci.yml
``` ```
### Making IaC analyzers available to all GitLab tiers ### Making IaC analyzers available to all GitLab tiers
@ -98,11 +98,11 @@ To configure IaC Scanning for a project you can:
### Configure IaC Scanning manually ### Configure IaC Scanning manually
To enable IaC Scanning you must [include](../../../ci/yaml/index.md#includetemplate) the To enable IaC Scanning you must [include](../../../ci/yaml/index.md#includetemplate) the
[`SAST-IaC.latest.gitlab-ci.yml template`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/SAST-IaC.latest.gitlab-ci.yml) provided as part of your GitLab installation. Here is an example of how to include it: [`SAST-IaC.gitlab-ci.yml template`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/SAST-IaC.gitlab-ci.yml) provided as part of your GitLab installation. Here is an example of how to include it:
```yaml ```yaml
include: include:
- template: Security/SAST-IaC.latest.gitlab-ci.yml - template: Jobs/SAST-IaC.gitlab-ci.yml
``` ```
The included template creates IaC scanning jobs in your CI/CD pipeline and scans The included template creates IaC scanning jobs in your CI/CD pipeline and scans

View File

@ -92,7 +92,7 @@ module Gitlab
return unless authenticate_using_internal_or_ldap_password? return unless authenticate_using_internal_or_ldap_password?
Gitlab::Auth::UniqueIpsLimiter.limit_user! do Gitlab::Auth::UniqueIpsLimiter.limit_user! do
user = User.by_login(login) user = User.find_by_login(login)
break if user && !user.can_log_in_with_non_expired_password? break if user && !user.can_log_in_with_non_expired_password?
@ -279,7 +279,7 @@ module Gitlab
if deploy_key_matches if deploy_key_matches
DeployKey.find(deploy_key_matches[1]) DeployKey.find(deploy_key_matches[1])
else else
User.by_login(login) User.find_by_login(login)
end end
return unless actor return unless actor

View File

@ -103,7 +103,7 @@ module Gitlab
return unless has_basic_credentials?(current_request) return unless has_basic_credentials?(current_request)
login, token = user_name_and_password(current_request) login, token = user_name_and_password(current_request)
user = User.by_login(login) user = User.find_by_login(login)
user if user && Gitlab::LfsToken.new(user).token_valid?(token) user if user && Gitlab::LfsToken.new(user).token_valid?(token)
end end

View File

@ -57,9 +57,9 @@ describe('Clusters', () => {
it('should show the creating container', () => { it('should show the creating container', () => {
cluster.updateContainer(null, 'creating'); cluster.updateContainer(null, 'creating');
expect(cluster.creatingContainer.classList.contains('hidden')).toBeFalsy(); expect(cluster.creatingContainer.classList.contains('hidden')).toBe(false);
expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); expect(cluster.successContainer.classList.contains('hidden')).toBe(true);
expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); expect(cluster.errorContainer.classList.contains('hidden')).toBe(true);
expect(window.location.reload).not.toHaveBeenCalled(); expect(window.location.reload).not.toHaveBeenCalled();
}); });
@ -67,9 +67,9 @@ describe('Clusters', () => {
cluster.updateContainer(null, 'creating'); cluster.updateContainer(null, 'creating');
cluster.updateContainer('creating', 'creating'); cluster.updateContainer('creating', 'creating');
expect(cluster.creatingContainer.classList.contains('hidden')).toBeFalsy(); expect(cluster.creatingContainer.classList.contains('hidden')).toBe(false);
expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); expect(cluster.successContainer.classList.contains('hidden')).toBe(true);
expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); expect(cluster.errorContainer.classList.contains('hidden')).toBe(true);
expect(window.location.reload).not.toHaveBeenCalled(); expect(window.location.reload).not.toHaveBeenCalled();
}); });
}); });
@ -80,9 +80,9 @@ describe('Clusters', () => {
cluster.updateContainer(null, 'creating'); cluster.updateContainer(null, 'creating');
cluster.updateContainer('creating', 'created'); cluster.updateContainer('creating', 'created');
expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); expect(cluster.creatingContainer.classList.contains('hidden')).toBe(true);
expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); expect(cluster.successContainer.classList.contains('hidden')).toBe(true);
expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); expect(cluster.errorContainer.classList.contains('hidden')).toBe(true);
expect(window.location.reload).toHaveBeenCalled(); expect(window.location.reload).toHaveBeenCalled();
expect(cluster.setClusterNewlyCreated).toHaveBeenCalledWith(true); expect(cluster.setClusterNewlyCreated).toHaveBeenCalledWith(true);
}); });
@ -94,9 +94,9 @@ describe('Clusters', () => {
cluster.updateContainer(null, 'created'); cluster.updateContainer(null, 'created');
cluster.updateContainer('created', 'created'); cluster.updateContainer('created', 'created');
expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); expect(cluster.creatingContainer.classList.contains('hidden')).toBe(true);
expect(cluster.successContainer.classList.contains('hidden')).toBeFalsy(); expect(cluster.successContainer.classList.contains('hidden')).toBe(false);
expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); expect(cluster.errorContainer.classList.contains('hidden')).toBe(true);
expect(window.location.reload).not.toHaveBeenCalled(); expect(window.location.reload).not.toHaveBeenCalled();
expect(cluster.setClusterNewlyCreated).toHaveBeenCalledWith(false); expect(cluster.setClusterNewlyCreated).toHaveBeenCalledWith(false);
}); });
@ -108,9 +108,9 @@ describe('Clusters', () => {
cluster.updateContainer(null, 'created'); cluster.updateContainer(null, 'created');
cluster.updateContainer('created', 'created'); cluster.updateContainer('created', 'created');
expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); expect(cluster.creatingContainer.classList.contains('hidden')).toBe(true);
expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); expect(cluster.successContainer.classList.contains('hidden')).toBe(true);
expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); expect(cluster.errorContainer.classList.contains('hidden')).toBe(true);
expect(window.location.reload).not.toHaveBeenCalled(); expect(window.location.reload).not.toHaveBeenCalled();
expect(cluster.setClusterNewlyCreated).not.toHaveBeenCalled(); expect(cluster.setClusterNewlyCreated).not.toHaveBeenCalled();
}); });
@ -120,11 +120,11 @@ describe('Clusters', () => {
it('should show the error container', () => { it('should show the error container', () => {
cluster.updateContainer(null, 'errored', 'this is an error'); cluster.updateContainer(null, 'errored', 'this is an error');
expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); expect(cluster.creatingContainer.classList.contains('hidden')).toBe(true);
expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); expect(cluster.successContainer.classList.contains('hidden')).toBe(true);
expect(cluster.errorContainer.classList.contains('hidden')).toBeFalsy(); expect(cluster.errorContainer.classList.contains('hidden')).toBe(false);
expect(cluster.errorReasonContainer.textContent).toContain('this is an error'); expect(cluster.errorReasonContainer.textContent).toContain('this is an error');
}); });
@ -132,11 +132,11 @@ describe('Clusters', () => {
it('should show `error` banner when previously `creating`', () => { it('should show `error` banner when previously `creating`', () => {
cluster.updateContainer('creating', 'errored'); cluster.updateContainer('creating', 'errored');
expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); expect(cluster.creatingContainer.classList.contains('hidden')).toBe(true);
expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); expect(cluster.successContainer.classList.contains('hidden')).toBe(true);
expect(cluster.errorContainer.classList.contains('hidden')).toBeFalsy(); expect(cluster.errorContainer.classList.contains('hidden')).toBe(false);
}); });
}); });

View File

@ -1083,20 +1083,6 @@ RSpec.describe User do
end end
end end
describe '.by_id_and_login' do
let_it_be(:user) { create(:user) }
it 'finds a user regardless of case' do
expect(described_class.by_id_and_login(user.id, user.username.upcase))
.to contain_exactly(user)
end
it 'finds a user when login is an email address regardless of case' do
expect(described_class.by_id_and_login(user.id, user.email.upcase))
.to contain_exactly(user)
end
end
describe '.for_todos' do describe '.for_todos' do
let_it_be(:user1) { create(:user) } let_it_be(:user1) { create(:user) }
let_it_be(:user2) { create(:user) } let_it_be(:user2) { create(:user) }
@ -3003,17 +2989,53 @@ RSpec.describe User do
end end
end end
describe '.by_login' do shared_examples "find user by login" do
let(:username) { 'John' } let_it_be(:user) { create(:user) }
let!(:user) { create(:user, username: username) } let_it_be(:invalid_login) { "#{user.username}-NOT-EXISTS" }
it 'gets the correct user' do context 'when login is nil or empty' do
expect(described_class.by_login(user.email.upcase)).to eq user it 'returns nil' do
expect(described_class.by_login(user.email)).to eq user expect(login_method(nil)).to be_nil
expect(described_class.by_login(username.downcase)).to eq user expect(login_method('')).to be_nil
expect(described_class.by_login(username)).to eq user end
expect(described_class.by_login(nil)).to be_nil end
expect(described_class.by_login('')).to be_nil
context 'when login is invalid' do
it 'returns nil' do
expect(login_method(invalid_login)).to be_nil
end
end
context 'when login is username' do
it 'returns user' do
expect(login_method(user.username)).to eq(user)
expect(login_method(user.username.downcase)).to eq(user)
expect(login_method(user.username.upcase)).to eq(user)
end
end
context 'when login is email' do
it 'returns user' do
expect(login_method(user.email)).to eq(user)
expect(login_method(user.email.downcase)).to eq(user)
expect(login_method(user.email.upcase)).to eq(user)
end
end
end
describe '.by_login' do
it_behaves_like "find user by login" do
def login_method(login)
described_class.by_login(login).take
end
end
end
describe '.find_by_login' do
it_behaves_like "find user by login" do
def login_method(login)
described_class.find_by_login(login)
end
end end
end end