Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-07-10 09:12:22 +00:00
parent fbdcfe539e
commit 704d7cf777
261 changed files with 859 additions and 823 deletions

View File

@ -0,0 +1,46 @@
---
Migration/PreventIndexCreation:
Details: grace period
Exclude:
- 'db/migrate/20250428232147_change_unique_contraints_ci_job_artifact_states.rb'
- 'db/post_migrate/20240213125219_schedule_index_approval_merge_request_rules_on_config_id_and_id_and_updated_at.rb'
- 'db/post_migrate/20240319005754_swap_columns_for_upstream_pipeline_id_between_ci_builds_and_ci_pipelines.rb'
- 'db/post_migrate/20240328123442_index_vulnerability_reads_for_vulnerability_export.rb'
- 'db/post_migrate/20240402105907_add_index_merge_requests_for_latest_diffs_with_state_merged.rb'
- 'db/post_migrate/20240402110451_add_index_on_merge_request_diffs_head_commit_sha.rb'
- 'db/post_migrate/20240403005214_add_concurrent_index_merge_requests_for_latest_diffs_with_state_merged.rb'
- 'db/post_migrate/20240403005435_add_concurrent_index_on_merge_request_diffs_head_commit_sha.rb'
- 'db/post_migrate/20240403020614_prepare_tmp_backfill_index_for_pipeline_ids_to_vulnerability_occurrences.rb'
- 'db/post_migrate/20240403104306_add_tmp_backfill_index_for_pipeline_ids_to_vulnerability_occurrences.rb'
- 'db/post_migrate/20240416005004_swap_columns_for_p_ci_builds_runner_id.rb'
- 'db/post_migrate/20240423235307_swap_columns_for_p_ci_builds_project_id.rb'
- 'db/post_migrate/20240424100929_create_indexes_for_merge_request_metrics_pipeline_id_convert_to_bigint.rb'
- 'db/post_migrate/20240430015514_swap_columns_for_p_ci_builds_user_id.rb'
- 'db/post_migrate/20240610106705_prepare_async_indexes_for_merge_requests_head_pipelines.rb'
- 'db/post_migrate/20240702081839_swap_head_pipeline_columns_for_merge_requests_head_pipelines.rb'
- 'db/post_migrate/20240714093324_add_index_on_job_artifact_state_verification_state.rb'
- 'db/post_migrate/20240819123915_add_index_vulnerability_occurrences_to_support_sbom_services.rb'
- 'db/post_migrate/20240821074453_index_ci_job_variables_on_project_id.rb'
- 'db/post_migrate/20240828082456_prepare_async_index_to_project_id_in_ci_build_needs.rb'
- 'db/post_migrate/20240923114736_prepare_index_for_has_vulnerability_resolution_on_vulnerability_reads.rb'
- 'db/post_migrate/20241007114424_add_index_for_has_vulnerability_resolution_on_vulnerability_reads.rb'
- 'db/post_migrate/20241014081028_add_index_ci_pipeline_messages_project_id.rb'
- 'db/post_migrate/20241016105456_schedule_index_approval_merge_request_rules_on_config_and_approval_policy_rule.rb'
- 'db/post_migrate/20241118121418_add_sync_index_to_sbom_occurrences_for_severity_aggregations.rb'
- 'db/post_migrate/20241204045404_prepare_async_index_for_p_ci_pipelines_trigger_id.rb'
- 'db/post_migrate/20241216064063_prepare_async_tmp_index_for_builds_trigger_request_id.rb'
- 'db/post_migrate/20241218062559_index_ci_build_needs_on_project_id.rb'
- 'db/post_migrate/20241230011511_sync_tmp_index_for_p_ci_builds_trigger_request_id.rb'
- 'db/post_migrate/20241230105009_sync_index_for_p_ci_pipelines_trigger_id.rb'
- 'db/post_migrate/20250113060958_sync_indexes_for_ci_pipeline_messages_project_id.rb'
- 'db/post_migrate/20250120081541_schedule_indexes_to_web_hook_logs_daily.rb'
- 'db/post_migrate/20250129150604_create_todos_coalesced_snoozed_until_created_at_index.rb'
- 'db/post_migrate/20250204075753_add_indexes_to_web_hook_logs_daily.rb'
- 'db/post_migrate/20250303230228_add_async_index_to_merge_request_diff_files.rb'
- 'db/post_migrate/20250319005645_prepare_index_for_p_ci_pipelines_trigger_id_and_id.rb'
- 'db/post_migrate/20250331050258_sync_index_for_p_ci_pipeline_trigger_id_and_id.rb'
- 'db/post_migrate/20250512110339_replace_tmp_idx_on_maven_packages_with_uniq_idx.rb'
- 'db/post_migrate/20250520214740_readd_index_users_for_auditors_for_gitlab_com.rb'
- 'db/post_migrate/20250529231801_async_add_index_ci_build_report_results_on_build_id_partition_id.rb'
- 'db/post_migrate/20250609181410_change_idx_ci_build_report_results_on_build_id_partition_id.rb'
- 'db/post_migrate/20250625234115_prepare_indexes_for_merge_request_diffs_bigint_conversion.rb'

View File

@ -1 +1 @@
a02ff0c8d8ae8c137651b6aa326bc2481c02b19b
5ea4031481151c6c9d055d7f7097ddeea2948594

View File

@ -326,13 +326,13 @@ module Clusters
duplicate_management_clusters = management_project.management_clusters
.where(environment_scope: environment_scope)
.where.not(id: id)
.id_not_in(id)
errors.add(:environment_scope, 'cannot add duplicated environment scope') if duplicate_management_clusters.any?
end
def unique_environment_scope
if clusterable.present? && clusterable.clusters.where(environment_scope: environment_scope).where.not(id: id).exists?
if clusterable.present? && clusterable.clusters.where(environment_scope: environment_scope).id_not_in(id).exists?
errors.add(:environment_scope, 'cannot add duplicated environment scope')
end
end

View File

@ -130,7 +130,7 @@ module Namespaces
def descendants
return super unless use_traversal_ids?
self_and_descendants.where.not(id: id)
self_and_descendants.id_not_in(id)
end
def self_and_hierarchy
@ -253,12 +253,12 @@ module Namespaces
].compact
roots = Gitlab::ObjectHierarchy
.new(Namespace.where(id: parent_ids))
.new(Namespace.id_in(parent_ids))
.base_and_ancestors
.reorder(nil)
.top_level
Namespace.lock('FOR NO KEY UPDATE').select(:id).where(id: roots).order(id: :asc).load
Namespace.lock('FOR NO KEY UPDATE').select(:id).id_in(roots).order(id: :asc).load
end
# Search this namespace's lineage. Bound inclusively by top node.

View File

@ -90,7 +90,7 @@ module Namespaces
if upto
upto_ancestor_ids = unscoped.where(id: upto).select(unnest_func(Arel.sql('traversal_ids')))
records = records.where.not(id: upto_ancestor_ids)
records = records.id_not_in(upto_ancestor_ids)
end
records

View File

@ -23,7 +23,7 @@ module Namespaces
if include_self
records
else
records.where.not(id: all.as_ids)
records.id_not_in(all.as_ids)
end
end
alias_method :recursive_self_and_ancestors, :self_and_ancestors

View File

@ -9,7 +9,7 @@ module Organizations
DEFAULT_ORGANIZATION_ID = 1
scope :without_default, -> { where.not(id: DEFAULT_ORGANIZATION_ID) }
scope :without_default, -> { id_not_in(DEFAULT_ORGANIZATION_ID) }
scope :with_namespace_path, ->(path) {
joins(namespaces: :route).where(route: { path: path.to_s })
}

View File

@ -101,7 +101,7 @@ module Organizations
def ensure_user_has_an_organization
return unless user
return unless user.organization_users.where.not(id: id).empty?
return unless user.organization_users.id_not_in(id).empty?
errors.add(:base, _('A user must associate with at least one organization'))
end

View File

@ -126,7 +126,7 @@ class PagesDeployment < ApplicationRecord
# Stop existing active deployment with same path when a deleted one is restored
def deactivate_deployments_with_same_path_prefix
project.pages_deployments.active.where.not(id: id).with_path_prefix(path_prefix).each(&:deactivate)
project.pages_deployments.active.id_not_in(id).with_path_prefix(path_prefix).each(&:deactivate)
end
end

View File

@ -96,7 +96,7 @@ class PoolRepository < ApplicationRecord
def unlink_repository(repository, disconnect: true)
repository.disconnect_alternates if disconnect
if member_projects.where.not(id: repository.project.id).exists?
if member_projects.id_not_in(repository.project.id).exists?
true
else
mark_obsolete

View File

@ -6,11 +6,11 @@ module Preloaders
class UserMaxAccessLevelInProjectsPreloader
def initialize(projects, user)
@projects = if projects.is_a?(Array)
Project.where(id: projects)
Project.id_in(projects)
else
# Push projects base query in to a sub-select to avoid
# table name clashes. Performs better than aliasing.
Project.where(id: projects.subquery(:id))
Project.id_in(projects.subquery(:id))
end
@user = user

View File

@ -241,7 +241,7 @@ class WikiPage
return unless container_id.present? && canonical_slug.present?
offending = self.class.with_canonical_slug(canonical_slug).where(container_key => container_id)
offending = offending.where.not(id: id) if persisted?
offending = offending.id_not_in(id) if persisted?
return unless offending.exists?

View File

@ -44,15 +44,13 @@ module Labels
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def label_ids_for_merge(group_label)
LabelsFinder
.new(current_user, title: group_label.title, group_id: project.group.id)
.execute(skip_authorization: true)
.where.not(id: group_label)
.id_not_in(group_label)
.select(:id, :project_id, :group_id, :type) # Can't use pluck() to avoid object-creation because of the batching
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def update_issuables(group_label, label_ids)

View File

@ -1,8 +1,10 @@
---
name: check_path_traversal_middleware
description: Enable middleware that detects and blocks path traversal attempts in HTTP requests
feature_issue_url: https://gitlab.com/groups/gitlab-org/-/epics/13437
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123477
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/415460
milestone: '16.5'
type: development
group: group::package registry
default_enabled: false
type: beta
default_enabled: true

View File

@ -160,12 +160,11 @@ For more Kubernetes troubleshooting commands, see the [Kubernetes cheat sheet](h
## `production_json.log`
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/production_json.log` on Linux package installations.
- `/home/git/gitlab/log/production_json.log` on self-compiled installations.
On Helm Chart installations, the logs are available on the Webservice pods under the `subcomponent="production_json"` key.
- In the `/var/log/gitlab/gitlab-rails/production_json.log` file on Linux package installations.
- In the `/home/git/gitlab/log/production_json.log` file on self-compiled installations.
- On the Webservice pods under the `subcomponent="production_json"` key on Helm chart installations.
It contains a structured log for Rails controller requests received from
GitLab, thanks to [Lograge](https://github.com/roidrage/lograge/).
@ -322,10 +321,10 @@ If an error occurs, an
## `production.log`
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/production.log` on Linux package installations.
- `/home/git/gitlab/log/production.log` on self-compiled installations.
- In the `/var/log/gitlab/gitlab-rails/production.log` file on Linux package installations.
- In the `/home/git/gitlab/log/production.log` file on self-compiled installations.
It contains information about all performed requests. You can see the
URL and type of request, IP address, and what parts of code were
@ -358,12 +357,11 @@ The request was processed by `Projects::TreeController`.
## `api_json.log`
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/api_json.log` on Linux package installations.
- `/home/git/gitlab/log/api_json.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Webservice pods under the `subcomponent="api_json"` key.
- In the `/var/log/gitlab/gitlab-rails/api_json.log` file on Linux package installations.
- In the `/home/git/gitlab/log/api_json.log` file on self-compiled installations.
- On the Webservice pods under the `subcomponent="api_json"` key on Helm chart installations.
It helps you see requests made directly to the API. For example:
@ -421,10 +419,10 @@ process on Redis or external HTTP, not only the serialization process.
{{< /history >}}
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/application.log` on Linux package installations.
- `/home/git/gitlab/log/application.log` on self-compiled installations.
- In the `/var/log/gitlab/gitlab-rails/application.log` file on Linux package installations.
- In the `/home/git/gitlab/log/application.log` file on self-compiled installations.
It contains a less structured version of the logs in
[`application_json.log`](#application_jsonlog), like this example:
@ -439,12 +437,11 @@ October 07, 2014 11:25: Project "project133" was removed
## `application_json.log`
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/application_json.log` on Linux package installations.
- `/home/git/gitlab/log/application_json.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq and Webservice pods under the `subcomponent="application_json"` key.
- In the `/var/log/gitlab/gitlab-rails/application_json.log` file on Linux package installations.
- In the `/home/git/gitlab/log/application_json.log` file on self-compiled installations.
- On the Sidekiq and Webservice pods under the `subcomponent="application_json"` key on Helm chart installations.
It helps you discover events happening in your instance such as user creation
and project deletion. For example:
@ -466,12 +463,11 @@ and project deletion. For example:
## `integrations_json.log`
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/integrations_json.log` on Linux package installations.
- `/home/git/gitlab/log/integrations_json.log` on self-compiled installations.
On Helm Chart installations, the logs are available on the Sidekiq and Webservice pods under the `subcomponent="integrations_json"` key.
- In the `/var/log/gitlab/gitlab-rails/integrations_json.log` file on Linux package installations.
- In the `/home/git/gitlab/log/integrations_json.log` file on self-compiled installations.
- On the Sidekiq and Webservice pods under the `subcomponent="integrations_json"` key on Helm chart installations.
It contains information about [integration](../../user/project/integrations/_index.md)
activities, such as Jira, Asana, and irker services. It uses JSON format,
@ -507,23 +503,21 @@ like this example:
{{< /history >}}
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/kubernetes.log` on Linux package installations.
- `/home/git/gitlab/log/kubernetes.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq pods under the `subcomponent="kubernetes"` key.
- In the `/var/log/gitlab/gitlab-rails/kubernetes.log` file on Linux package installations.
- In the `/home/git/gitlab/log/kubernetes.log` file on self-compiled installations.
- On the Sidekiq pods under the `subcomponent="kubernetes"` key on Helm chart installations.
It logs information related to [certificate-based clusters](../../user/project/clusters/_index.md), such as connectivity errors. Each line contains JSON that can be ingested by services like Elasticsearch and Splunk.
## `git_json.log`
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/git_json.log` on Linux package installations.
- `/home/git/gitlab/log/git_json.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq pods under the `subcomponent="git_json"` key.
- In the `/var/log/gitlab/gitlab-rails/git_json.log` file on Linux package installations.
- In the `/home/git/gitlab/log/git_json.log` file on self-compiled installations.
- On the Sidekiq pods under the `subcomponent="git_json"` key on Helm chart installations.
GitLab has to interact with Git repositories, but in some rare cases
something can go wrong. If this happens, you need to know exactly what
@ -556,12 +550,11 @@ GitLab Premium tracks many more.
{{< /alert >}}
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/audit_json.log` on Linux package installations.
- `/home/git/gitlab/log/audit_json.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq and Webservice pods under the `subcomponent="audit_json"` key.
- In the `/var/log/gitlab/gitlab-rails/audit_json.log` file on Linux package installations.
- In the `/home/git/gitlab/log/audit_json.log` file on self-compiled installations.
- On the Sidekiq and Webservice pods under the `subcomponent="audit_json"` key on Helm chart installations.
Changes to group or project settings and memberships (`target_details`)
are logged to this file. For example:
@ -596,10 +589,10 @@ and as follows.
{{< /history >}}
This file is located at:
This log is located:
- `/var/log/gitlab/sidekiq/current` on Linux package installations.
- `/home/git/gitlab/log/sidekiq.log` on self-compiled installations.
- In the `/var/log/gitlab/sidekiq/current` file on Linux package installations.
- In the `/home/git/gitlab/log/sidekiq.log` file on self-compiled installations.
GitLab uses background jobs for processing tasks which can take a long
time. All information about processing these jobs are written to this
@ -662,12 +655,11 @@ For self-compiled installations, edit the `gitlab.yml` and set the Sidekiq
### `sidekiq_client.log`
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/sidekiq_client.log` on Linux package installations.
- `/home/git/gitlab/log/sidekiq_client.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Webservice pods under the `subcomponent="sidekiq_client"` key.
- In the `/var/log/gitlab/gitlab-rails/sidekiq_client.log` file on Linux package installations.
- In the `/home/git/gitlab/log/sidekiq_client.log` file on self-compiled installations.
- On the Webservice pods under the `subcomponent="sidekiq_client"` key on Helm chart installations.
This file contains logging information about jobs before Sidekiq starts
processing them, such as before being enqueued.
@ -757,58 +749,55 @@ failures received during processing of the responses from GitLab API.
### `puma_stdout.log`
This file is located at:
This log is located:
- `/var/log/gitlab/puma/puma_stdout.log` on Linux package installations.
- `/home/git/gitlab/log/puma_stdout.log` on self-compiled installations.
- In the `/var/log/gitlab/puma/puma_stdout.log` file on Linux package installations.
- In the `/home/git/gitlab/log/puma_stdout.log` file on self-compiled installations.
### `puma_stderr.log`
This file is located at:
This log is located:
- `/var/log/gitlab/puma/puma_stderr.log` on Linux package installations.
- `/home/git/gitlab/log/puma_stderr.log` on self-compiled installations.
- In the `/var/log/gitlab/puma/puma_stderr.log` file on Linux package installations.
- In the `/home/git/gitlab/log/puma_stderr.log` file on self-compiled installations.
## `repocheck.log`
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/repocheck.log` on Linux package installations.
- `/home/git/gitlab/log/repocheck.log` on self-compiled installations.
- In the `/var/log/gitlab/gitlab-rails/repocheck.log` file on Linux package installations.
- In the `/home/git/gitlab/log/repocheck.log` file on self-compiled installations.
It logs information whenever a [repository check is run](../repository_checks.md)
on a project.
## `importer.log`
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/importer.log` on Linux package installations.
- `/home/git/gitlab/log/importer.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq pods under the `subcomponent="importer"` key.
- In the `/var/log/gitlab/gitlab-rails/importer.log` file on Linux package installations.
- In the `/home/git/gitlab/log/importer.log` file on self-compiled installations.
- On the Sidekiq pods under the `subcomponent="importer"` key on Helm chart installations.
This file logs the progress of [project imports and migrations](../../user/project/import/_index.md).
## `exporter.log`
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/exporter.log` on Linux package installations.
- `/home/git/gitlab/log/exporter.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq and Webservice pods under the `subcomponent="exporter"` key.
- In the `/var/log/gitlab/gitlab-rails/exporter.log` file on Linux package installations.
- In the `/home/git/gitlab/log/exporter.log` file on self-compiled installations.
- On the Sidekiq and Webservice pods under the `subcomponent="exporter"` key on Helm chart installations.
It logs the progress of the export process.
## `features_json.log`
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/features_json.log` on Linux package installations.
- `/home/git/gitlab/log/features_json.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq and Webservice pods under the `subcomponent="features_json"` key.
- In the `/var/log/gitlab/gitlab-rails/features_json.log` file on Linux package installations.
- In the `/home/git/gitlab/log/features_json.log` file on self-compiled installations.
- On the Sidekiq and Webservice pods under the `subcomponent="features_json"` key on Helm chart installations.
The modification events from Feature flags in development of GitLab
are recorded in this file. For example:
@ -834,12 +823,11 @@ are recorded in this file. For example:
{{< /history >}}
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/ci_resource_groups_json.log` on Linux package installations.
- `/home/git/gitlab/log/ci_resource_group_json.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq and Webservice pods under the `subcomponent="ci_resource_groups_json"` key.
- In the `/var/log/gitlab/gitlab-rails/ci_resource_groups_json.log` file on Linux package installations.
- In the `/home/git/gitlab/log/ci_resource_group_json.log` file on self-compiled installations.
- On the Sidekiq and Webservice pods under the `subcomponent="ci_resource_groups_json"` key on Helm chart installations.
It contains information about [resource group](../../ci/resource_groups/_index.md) acquisition. For example:
@ -852,10 +840,10 @@ The examples show the `resource_group_id`, `processable_id`, `message`, and `suc
## `auth.log`
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/auth.log` on Linux package installations.
- `/home/git/gitlab/log/auth.log` on self-compiled installations.
- In the `/var/log/gitlab/gitlab-rails/auth.log` file on Linux package installations.
- In the `/home/git/gitlab/log/auth.log` file on self-compiled installations.
This log records:
@ -865,12 +853,11 @@ This log records:
## `auth_json.log`
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/auth_json.log` on Linux package installations.
- `/home/git/gitlab/log/auth_json.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq and Webservice pods under the `subcomponent="auth_json"` key.
- In the `/var/log/gitlab/gitlab-rails/auth_json.log` file on Linux package installations.
- In the `/home/git/gitlab/log/auth_json.log` file on self-compiled installations.
- On the Sidekiq and Webservice pods under the `subcomponent="auth_json"` key on Helm chart installations.
This file contains the JSON version of the logs in `auth.log`, for example:
@ -889,12 +876,11 @@ This file contains the JSON version of the logs in `auth.log`, for example:
## `graphql_json.log`
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/graphql_json.log` on Linux package installations.
- `/home/git/gitlab/log/graphql_json.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq and Webservice pods under the `subcomponent="graphql_json"` key.
- In the `/var/log/gitlab/gitlab-rails/graphql_json.log` file on Linux package installations.
- In the `/home/git/gitlab/log/graphql_json.log` file on self-compiled installations.
- On the Sidekiq and Webservice pods under the `subcomponent="graphql_json"` key on Helm chart installations.
GraphQL queries are recorded in the file. For example:
@ -910,31 +896,30 @@ GraphQL queries are recorded in the file. For example:
{{< /history >}}
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/clickhouse.log` on Linux package installations.
- `/home/git/gitlab/log/clickhouse.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq and Webservice pods under the `subcomponent="clickhouse"` key.
- In the `/var/log/gitlab/gitlab-rails/clickhouse.log` file on Linux package installations.
- In the `/home/git/gitlab/log/clickhouse.log` file on self-compiled installations.
- On the Sidekiq and Webservice pods under the `subcomponent="clickhouse"` key.
The `clickhouse.log` file logs information related to the
[ClickHouse database client](../../integration/clickhouse.md) in GitLab.
## `migrations.log`
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/migrations.log` on Linux package installations.
- `/home/git/gitlab/log/migrations.log` on self-compiled installations.
- In the `/var/log/gitlab/gitlab-rails/migrations.log` file on Linux package installations.
- In the `/home/git/gitlab/log/migrations.log` file on self-compiled installations.
This file logs the progress of [database migrations](../raketasks/maintenance.md#display-status-of-database-migrations).
## `mail_room_json.log` (default)
This file is located at:
This log is located:
- `/var/log/gitlab/mailroom/current` on Linux package installations.
- `/home/git/gitlab/log/mail_room_json.log` on self-compiled installations.
- In the `/var/log/gitlab/mailroom/current` file on Linux package installations.
- In the `/home/git/gitlab/log/mail_room_json.log` file on self-compiled installations.
This structured log file records internal activity in the `mail_room` gem.
Its name and path are configurable, so the name and path may not match this one
@ -948,12 +933,11 @@ documented previously.
{{< /history >}}
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/web_hooks.log` on Linux package installations.
- `/home/git/gitlab/log/web_hooks.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq pods under the `subcomponent="web_hooks"` key.
- In the `/var/log/gitlab/gitlab-rails/web_hooks.log` file on Linux package installations.
- In the `/home/git/gitlab/log/web_hooks.log` file on self-compiled installations.
- On the Sidekiq pods under the `subcomponent="web_hooks"` key on Helm chart installations.
The back-off, disablement, and re-enablement events for Webhook are recorded in this file. For example:
@ -1005,12 +989,11 @@ are generated in a location based on your installation method:
Contains details of GitLab [Database Load Balancing](../postgresql/database_load_balancing.md).
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/database_load_balancing.log` on Linux package installations.
- `/home/git/gitlab/log/database_load_balancing.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq and Webservice pods under the `subcomponent="database_load_balancing"` key.
- In the `/var/log/gitlab/gitlab-rails/database_load_balancing.log` file on Linux package installations.
- In the `/home/git/gitlab/log/database_load_balancing.log` file on self-compiled installations.
- On the Sidekiq and Webservice pods under the `subcomponent="database_load_balancing"` key on Helm chart installations.
## `zoekt.log`
@ -1028,12 +1011,12 @@ On Helm chart installations, the logs are available on the Sidekiq and Webservic
{{< /history >}}
This file logs information related to [exact code search](../../user/search/exact_code_search.md).
This file is located at:
- `/var/log/gitlab/gitlab-rails/zoekt.log` on Linux package installations.
- `/home/git/gitlab/log/zoekt.log` on self-compiled installations.
This log is located:
On Helm chart installations, the logs are available on the Sidekiq and Webservice pods under the `subcomponent="zoekt"` key.
- In the `/var/log/gitlab/gitlab-rails/zoekt.log` file on Linux package installations.
- In the `/home/git/gitlab/log/zoekt.log` file on self-compiled installations.
- On the Sidekiq and Webservice pods under the `subcomponent="zoekt"` key on Helm chart installations.
## `elasticsearch.log`
@ -1047,12 +1030,11 @@ On Helm chart installations, the logs are available on the Sidekiq and Webservic
This file logs information related to the Elasticsearch Integration, including
errors during indexing or searching Elasticsearch.
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/elasticsearch.log` on Linux package installations.
- `/home/git/gitlab/log/elasticsearch.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq and Webservice pods under the `subcomponent="elasticsearch"` key.
- In the `/var/log/gitlab/gitlab-rails/elasticsearch.log` file on Linux package installations.
- In the `/home/git/gitlab/log/elasticsearch.log` file on self-compiled installations.
- On the Sidekiq and Webservice pods under the `subcomponent="elasticsearch"` key on Helm chart installations.
Each line contains JSON that can be ingested by services like Elasticsearch and Splunk.
Line breaks have been added to the following example line for clarity:
@ -1076,12 +1058,12 @@ Line breaks have been added to the following example line for clarity:
This file logs the information about exceptions being tracked by
`Gitlab::ErrorTracking`, which provides a standard and consistent way of
processing rescued exceptions.
This file is located at:
- `/var/log/gitlab/gitlab-rails/exceptions_json.log` on Linux package installations.
- `/home/git/gitlab/log/exceptions_json.log` on self-compiled installations.
This log is located:
On Helm chart installations, the logs are available on the Sidekiq and Webservice pods under the `subcomponent="exceptions_json"` key.
- In the `/var/log/gitlab/gitlab-rails/exceptions_json.log` file on Linux package installations.
- In the `/home/git/gitlab/log/exceptions_json.log` file on self-compiled installations.
- On the Sidekiq and Webservice pods under the `subcomponent="exceptions_json"` key on Helm chart installations.
Each line contains JSON that can be ingested by Elasticsearch. For example:
@ -1104,12 +1086,11 @@ Each line contains JSON that can be ingested by Elasticsearch. For example:
## `service_measurement.log`
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/service_measurement.log` on Linux package installations.
- `/home/git/gitlab/log/service_measurement.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq and Webservice pods under the `subcomponent="service_measurement"` key.
- In the `/var/log/gitlab/gitlab-rails/service_measurement.log` file on Linux package installations.
- In the `/home/git/gitlab/log/service_measurement.log` file on self-compiled installations.
- On the Sidekiq and Webservice pods under the `subcomponent="service_measurement"` key on Helm chart installations.
It contains only a single structured log with measurements for each service execution.
It contains measurements such as the number of SQL calls, `execution_time`, `gc_stats`, and `memory usage`.
@ -1129,12 +1110,11 @@ For example:
{{< /details >}}
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/geo.log` on Linux package installations.
- `/home/git/gitlab/log/geo.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq and Webservice pods under the `subcomponent="geo"` key.
- In the `/var/log/gitlab/gitlab-rails/geo.log` file on Linux package installations.
- In the `/home/git/gitlab/log/geo.log` file on self-compiled installations.
- On the Sidekiq and Webservice pods under the `subcomponent="geo"` key on Helm chart installations.
This file contains information about when Geo attempts to sync repositories
and files. Each line in the file contains a separate JSON entry that can be
@ -1150,12 +1130,11 @@ This message shows that Geo detected that a repository update was needed for pro
## `update_mirror_service_json.log`
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/update_mirror_service_json.log` on Linux package installations.
- `/home/git/gitlab/log/update_mirror_service_json.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq pods under the `subcomponent="update_mirror_service_json"` key.
- In the `/var/log/gitlab/gitlab-rails/update_mirror_service_json.log` file on Linux package installations.
- In the `/home/git/gitlab/log/update_mirror_service_json.log` file on self-compiled installations.
- On the Sidekiq pods under the `subcomponent="update_mirror_service_json"` key on Helm chart installations.
This file contains information about LFS errors that occurred during project mirroring.
While we work to move other project mirroring errors into this log, the [general log](#productionlog)
@ -1216,10 +1195,9 @@ By default, the log does not contain LLM prompt input and response output to sup
The log file is located at:
- `/var/log/gitlab/gitlab-rails/llm.log` on Linux package installations.
- `/home/git/gitlab/log/llm.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Webservice pods under the `subcomponent="llm"` key.
- In the `/var/log/gitlab/gitlab-rails/llm.log` file on Linux package installations.
- In the `/home/git/gitlab/log/llm.log` file on self-compiled installations.
- On the Webservice pods under the `subcomponent="llm"` key on Helm chart installations.
## `epic_work_item_sync.log`
@ -1238,12 +1216,11 @@ On Helm chart installations, the logs are available on the Webservice pods under
The `epic_work_item_sync.log` file logs information related to syncing and migrating epics as work items.
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/epic_work_item_sync.log` on Linux package installations.
- `/home/git/gitlab/log/epic_work_item_sync.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq and Webservice pods under the `subcomponent="epic_work_item_sync"` key.
- In the `/var/log/gitlab/gitlab-rails/epic_work_item_sync.log` file on Linux package installations.
- In the `/home/git/gitlab/log/epic_work_item_sync.log` file on self-compiled installations.
- On the Sidekiq and Webservice pods under the `subcomponent="epic_work_item_sync"` key on Helm chart installations.
## `secret_push_protection.log`
@ -1262,12 +1239,11 @@ On Helm chart installations, the logs are available on the Sidekiq and Webservic
The `secret_push_protection.log` file logs information related to [Secret Push Protection](../../user/application_security/secret_detection/secret_push_protection/_index.md) feature.
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/secret_push_protection.log` on Linux package installations.
- `/home/git/gitlab/log/secret_push_protection.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Webservice pods under the `subcomponent="secret_push_protection"` key.
- In the `/var/log/gitlab/gitlab-rails/secret_push_protection.log` file on Linux package installations.
- In the `/home/git/gitlab/log/secret_push_protection.log` file on self-compiled installations.
- On the Webservice pods under the `subcomponent="secret_push_protection"` key on Helm chart installations.
## Registry logs
@ -1335,12 +1311,11 @@ The list of events can change in each version based on new features or changes t
{{< /alert >}}
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/product_usage_data.log` on Linux package installations.
- `/home/git/gitlab/log/product_usage_data.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Webservice pods under the `subcomponent="product_usage_data"` key.
- In the `/var/log/gitlab/gitlab-rails/product_usage_data.log` file on Linux package installations.
- In the `/home/git/gitlab/log/product_usage_data.log` file on self-compiled installations.
- On the Webservice pods under the `subcomponent="product_usage_data"` key on Helm chart installations.
It contains JSON-formatted logs of product usage events tracked through Snowplow. Each line in the file contains a separate JSON entry that can be ingested by services like Elasticsearch or Splunk. Line breaks were added to examples for legibility:
@ -1464,12 +1439,11 @@ This log is populated when a [GitLab backup is created](../backup_restore/_index
## Performance bar stats
This file is located at:
This log is located:
- `/var/log/gitlab/gitlab-rails/performance_bar_json.log` on Linux package installations.
- `/home/git/gitlab/log/performance_bar_json.log` on self-compiled installations.
On Helm chart installations, the logs are available on the Sidekiq pods under the `subcomponent="performance_bar_json"` key.
- In the `/var/log/gitlab/gitlab-rails/performance_bar_json.log` file on Linux package installations.
- In the `/home/git/gitlab/log/performance_bar_json.log` file on self-compiled installations.
- On the Sidekiq pods under the `subcomponent="performance_bar_json"` key on Helm chart installations.
Performance bar statistics (currently only duration of SQL queries) are recorded
in that file. For example:

View File

@ -1340,22 +1340,42 @@ efficient:
```ruby
class AddSecretToSomething < Gitlab::Database::Migration[2.1]
def change
add_column :something, :secret, :jsonb
add_column :something, :secret, :jsonb, null: true
end
end
```
When storing encrypted attributes in a JSONB column, it's good to add a length validation that
[follows the Active Record Encryption recommendations](https://guides.rubyonrails.org/active_record_encryption.html#important-about-storage-and-column-size).
For most encrypted attributes, a 510 max length should be enough.
When storing encrypted attributes in a JSONB column, you need to:
1. Add JSON schema validation
1. Add length validation following [Active Record Encryption recommendations](https://guides.rubyonrails.org/active_record_encryption.html#important-about-storage-and-column-size)
1. Allow `nil` values if the attribute is optional
### Model Configuration
```ruby
class Something < ApplicationRecord
encrypts :secret
validates :secret, length: { maximum: 510 }
validates :secret,
json_schema: { filename: 'something_secret' },
allow_nil: true,
length: { maximum: 510 }
end
```
### JSON Schema
Create a JSON schema file at `config/json_schemas/something_secret.json`:
```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Secret Configuration",
"type": "string",
"description": "Encrypted secret value"
}
```
## Testing
See the [Testing Rails migrations](testing_guide/testing_migrations_guide.md) style guide.

View File

@ -38,7 +38,6 @@ module API
use :pagination
end
# rubocop: disable CodeReuse/ActiveRecord
def find_groups(params, parent_id = nil)
find_params = params.slice(*allowable_find_params)
@ -52,11 +51,10 @@ module API
find_params.fetch(:all_available, current_user&.can_read_all_resources?)
groups = GroupsFinder.new(current_user, find_params).execute
groups = groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present?
groups = groups.id_not_in(params[:skip_groups]) if params[:skip_groups].present?
order_groups(groups).with_api_scopes
end
# rubocop: enable CodeReuse/ActiveRecord
def allowable_find_params
[:all_available,

View File

@ -410,20 +410,18 @@ module API
optional :username, type: String, desc: 'The username of the user'
use :optional_attributes
end
# rubocop: disable CodeReuse/ActiveRecord
put ":id", feature_category: :user_profile do
authenticated_as_admin!
user = User.find_by(id: params.delete(:id))
user = User.find_by_id(params.delete(:id))
not_found!('User') unless user
conflict!('Email has already been taken') if params[:email] &&
User.by_any_email(params[:email].downcase)
.where.not(id: user.id).exists?
User.by_any_email(params[:email].downcase).id_not_in(user.id).exists?
conflict!('Username has already been taken') if params[:username] &&
User.by_username(params[:username])
.where.not(id: user.id).exists?
User.by_username(params[:username]).id_not_in(user.id).exists?
user_params = declared_params(include_missing: false)
@ -451,7 +449,6 @@ module API
render_validation_error!(user)
end
end
# rubocop: enable CodeReuse/ActiveRecord
desc "Disable two factor authentication for a user. Available only for admins" do
detail 'This feature was added in GitLab 15.2'

View File

@ -128,7 +128,7 @@ module Backup
end
def skipped_path_relation
Project.where.not(id: Project.where_full_path_in(skip_paths).or(
Project.id_not_in(Project.where_full_path_in(skip_paths).or(
Project.where(namespace_id: Namespace.where_full_path_in(skip_paths).self_and_descendants)
))
end

View File

@ -25,11 +25,9 @@ module Gitlab
# Returns the set of descendants of a given relation, but excluding the given
# relation
# rubocop: disable CodeReuse/ActiveRecord
def descendants
base_and_descendants.where.not(id: descendants_base.select(:id))
base_and_descendants.id_not_in(descendants_base.select(:id))
end
# rubocop: enable CodeReuse/ActiveRecord
# Returns the maximum depth starting from the base
# A base object with no children has a maximum depth of `1`
@ -43,11 +41,9 @@ module Gitlab
# Passing an `upto` will stop the recursion once the specified parent_id is
# reached. So all ancestors *lower* than the specified ancestor will be
# included.
# rubocop: disable CodeReuse/ActiveRecord
def ancestors(upto: nil, hierarchy_order: nil)
base_and_ancestors(upto: upto, hierarchy_order: hierarchy_order).where.not(id: ancestors_base.select(:id))
base_and_ancestors(upto: upto, hierarchy_order: hierarchy_order).id_not_in(ancestors_base.select(:id))
end
# rubocop: enable CodeReuse/ActiveRecord
# Returns a relation that includes the ancestors_base set of objects
# and all their ancestors (recursively).

View File

@ -69,7 +69,7 @@ module Gitlab
label_id = label_ids.first
@updates[:remove_label_ids] =
quick_action_target.labels.on_project_boards(quick_action_target.project_id).where.not(id: label_id).pluck(:id) # rubocop: disable CodeReuse/ActiveRecord
quick_action_target.labels.on_project_boards(quick_action_target.project_id).id_not_in(label_id).pluck(:id) # rubocop: disable CodeReuse/ActiveRecord
@updates[:add_label_ids] = [label_id]
message = _("Moved issue to %{label} column in the board.") % { label: labels_to_reference(labels).first }

View File

@ -63,7 +63,7 @@ module RuboCop
PATTERN
def on_casgn(node)
@forbidden_tables_used = !!forbidden_constant_defined?(node)
@forbidden_tables_used ||= !!forbidden_constant_defined?(node)
end
def on_def(node)
@ -84,10 +84,12 @@ module RuboCop
end
def offense?(node)
add_index?(node) ||
add_concurrent_index?(node) ||
prepare_async_index?(node) ||
any_constant_used_with_forbidden_tables?(node)
return true if add_index?(node)
return true if add_concurrent_index?(node)
return true if prepare_async_index?(node)
return true if any_constant_used_with_forbidden_tables?(node)
false
end
def any_constant_used_with_forbidden_tables?(node)

View File

@ -36,7 +36,7 @@ export const createTestPiniaAction =
});
} else {
// eslint-disable-next-line jest/no-standalone-expect
expect(actionCalls.length).toBe(0);
expect(actionCalls).toHaveLength(0);
}
return result;

View File

@ -274,7 +274,7 @@ describe('Vue test utils helpers', () => {
resultWrapper.options === wrapper.options,
),
).toBe(true);
expect(result.length).toBe(3);
expect(result).toHaveLength(3);
});
});
@ -287,7 +287,7 @@ describe('Vue test utils helpers', () => {
const result = wrapper[findMethod](text, options);
expect(result).toBeInstanceOf(VTUWrapperArray);
expect(result.length).toBe(0);
expect(result).toHaveLength(0);
});
});
});
@ -311,7 +311,7 @@ describe('Vue test utils helpers', () => {
it('mounts component and provides extended queries', () => {
const wrapper = mountExtended(FakeComponent);
expect(wrapper.text()).toBe('Foo Bar');
expect(wrapper.findAllByTestId('fake-id').length).toBe(2);
expect(wrapper.findAllByTestId('fake-id')).toHaveLength(2);
});
});
@ -319,7 +319,7 @@ describe('Vue test utils helpers', () => {
it('shallow mounts component and provides extended queries', () => {
const wrapper = shallowMountExtended(FakeComponent);
expect(wrapper.text()).toBe('Foo');
expect(wrapper.findAllByTestId('fake-id').length).toBe(1);
expect(wrapper.findAllByTestId('fake-id')).toHaveLength(1);
});
});
});

View File

@ -55,7 +55,7 @@ describe('Achievements app', () => {
const achievements = wrapper.findAllComponents(CrudComponent);
expect(achievements.length).toBe(3);
expect(achievements).toHaveLength(3);
});
it('should render the correct achievement name and avatar (when present)', async () => {

View File

@ -42,6 +42,6 @@ describe('ReviewTabContainer', () => {
it('renders all passed commits as list', () => {
createWrapper({ commits: [commit] });
expect(wrapper.findAllComponents(CommitItem).length).toBe(1);
expect(wrapper.findAllComponents(CommitItem)).toHaveLength(1);
});
});

View File

@ -36,7 +36,7 @@ describe('AbuseReportsApp', () => {
createComponent();
expect(findEmptyState().exists()).toBe(false);
expect(findAbuseReportRows().length).toBe(mockAbuseReports.length);
expect(findAbuseReportRows()).toHaveLength(mockAbuseReports.length);
});
it('renders empty state when there are no reports', () => {

View File

@ -290,7 +290,7 @@ describe('AlertManagementTable', () => {
},
loading: false,
});
expect(findDateFields().length).toBe(1);
expect(findDateFields()).toHaveLength(1);
});
it('should not display time ago dates when values not provided', () => {

View File

@ -59,7 +59,7 @@ describe('AlertIntegrationsList', () => {
});
it('renders an an edit and delete button for each integration', () => {
expect(findTableComponent().findAllComponents(GlButton).length).toBe(4);
expect(findTableComponent().findAllComponents(GlButton)).toHaveLength(4);
});
describe('integration status', () => {

View File

@ -78,7 +78,7 @@ describe('Project PathNavigation', () => {
it('renders each stage', () => {
const result = findPathNavigationTitles();
expect(result.length).toBe(transformedProjectStagePathData.length);
expect(result).toHaveLength(transformedProjectStagePathData.length);
});
it('renders each stage with its median', () => {

View File

@ -25,7 +25,7 @@ describe('Value stream analytics utils', () => {
describe('transforms the data as expected', () => {
it('returns an array of stages', () => {
expect(Array.isArray(response)).toBe(true);
expect(response.length).toBe(stages.length);
expect(response).toHaveLength(stages.length);
});
it('selects the correct stage', () => {

View File

@ -218,8 +218,8 @@ describe('ProjectsDropdownFilter component', () => {
});
it('hides the unhighlighted items that do not match the string', () => {
expect(wrapper.find(`[name="Selected"]`).findAllComponents(GlListboxItem).length).toBe(1);
expect(wrapper.find(`[name="Unselected"]`).findAllComponents(GlListboxItem).length).toBe(0);
expect(wrapper.find(`[name="Selected"]`).findAllComponents(GlListboxItem)).toHaveLength(1);
expect(wrapper.find(`[name="Unselected"]`).findAllComponents(GlListboxItem)).toHaveLength(0);
});
});

View File

@ -315,7 +315,7 @@ describe('Api', () => {
return new Promise((resolve) => {
Api.groups(query, options, (response) => {
expect(response.length).toBe(1);
expect(response).toHaveLength(1);
expect(response[0].name).toBe('test');
resolve();
});
@ -336,7 +336,7 @@ describe('Api', () => {
]);
return Api.groupLabels(expectedGroup, options).then((res) => {
expect(res.length).toBe(1);
expect(res).toHaveLength(1);
expect(res[0].name).toBe('Foo Label');
});
});
@ -354,7 +354,7 @@ describe('Api', () => {
return new Promise((resolve) => {
Api.namespaces(query, {}, (response) => {
expect(response.length).toBe(1);
expect(response).toHaveLength(1);
expect(response[0].name).toBe('test');
resolve();
});
@ -376,7 +376,7 @@ describe('Api', () => {
return new Promise((resolve) => {
Api.projects(query, options, (response) => {
expect(response.length).toBe(1);
expect(response).toHaveLength(1);
expect(response[0].name).toBe('test');
resolve();
});
@ -395,7 +395,7 @@ describe('Api', () => {
return new Promise((resolve) => {
Api.projects(query, options, (response) => {
expect(response.length).toBe(1);
expect(response).toHaveLength(1);
expect(response[0].name).toBe('test');
resolve();
});
@ -428,7 +428,7 @@ describe('Api', () => {
]);
return Api.projectUsers('gitlab-org/gitlab-ce', query, options).then((response) => {
expect(response.length).toBe(1);
expect(response).toHaveLength(1);
expect(response[0].name).toBe('test');
});
});
@ -442,7 +442,7 @@ describe('Api', () => {
const mockData = [{ source_branch: 'foo' }, { source_branch: 'bar' }];
mock.onGet(expectedUrl).reply(HTTP_STATUS_OK, mockData);
return Api.projectMergeRequests(projectPath).then(({ data }) => {
expect(data.length).toEqual(2);
expect(data).toHaveLength(2);
expect(data[0].source_branch).toBe('foo');
expect(data[1].source_branch).toBe('bar');
});
@ -456,7 +456,7 @@ describe('Api', () => {
mock.onGet(expectedUrl, { params }).reply(HTTP_STATUS_OK, mockData);
return Api.projectMergeRequests(projectPath, params).then(({ data }) => {
expect(data.length).toEqual(1);
expect(data).toHaveLength(1);
expect(data[0].source_branch).toBe('bar');
});
});
@ -504,7 +504,7 @@ describe('Api', () => {
]);
return Api.projectMergeRequestVersions(projectPath, mergeRequestId).then(({ data }) => {
expect(data.length).toBe(1);
expect(data).toHaveLength(1);
expect(data[0].id).toBe(123);
});
});
@ -563,7 +563,7 @@ describe('Api', () => {
]);
return Api.projectMilestones(projectId, options).then(({ data }) => {
expect(data.length).toBe(1);
expect(data).toHaveLength(1);
expect(data[0].title).toBe('milestone1');
});
});
@ -675,7 +675,7 @@ describe('Api', () => {
]);
return Api.groupProjects(groupId, query, {}).then((response) => {
expect(response.data.length).toBe(1);
expect(response.data).toHaveLength(1);
expect(response.data[0].name).toBe('test');
});
});
@ -787,7 +787,7 @@ describe('Api', () => {
return new Promise((resolve) => {
Api.issueTemplates(namespace, project, templateType, (_, response) => {
expect(response.length).toBe(1);
expect(response).toHaveLength(1);
const { key, name, content } = response[0];
expect(key).toBe('Template1');
expect(name).toBe('Template 1');
@ -857,7 +857,7 @@ describe('Api', () => {
]);
return Api.users(query, options).then(({ data }) => {
expect(data.length).toBe(1);
expect(data).toHaveLength(1);
expect(data[0].name).toBe('test');
});
});
@ -918,7 +918,7 @@ describe('Api', () => {
return new Promise((resolve) => {
Api.userProjects(userId, query, options, (response) => {
expect(response.length).toBe(1);
expect(response).toHaveLength(1);
expect(response[0].name).toBe('test');
resolve();
});
@ -938,7 +938,7 @@ describe('Api', () => {
]);
return Api.commitPipelines(projectId, commitSha).then(({ data }) => {
expect(data.length).toBe(1);
expect(data).toHaveLength(1);
expect(data[0].name).toBe('test');
});
});
@ -1372,7 +1372,7 @@ describe('Api', () => {
]);
return Api.tags(projectId, query, options).then(({ data }) => {
expect(data.length).toBe(1);
expect(data).toHaveLength(1);
expect(data[0].name).toBe('test');
});
});

View File

@ -120,10 +120,10 @@ describe('AwardsHandler', () => {
const $emojiMenu = $('.emoji-menu');
expect($emojiMenu.length).toBe(1);
expect($emojiMenu).toHaveLength(1);
expect($emojiMenu.hasClass('is-visible')).toBe(true);
expect($emojiMenu.find('.js-emoji-menu-search').length).toBe(1);
expect($('.js-awards-block.current').length).toBe(1);
expect($emojiMenu.find('.js-emoji-menu-search')).toHaveLength(1);
expect($('.js-awards-block.current')).toHaveLength(1);
});
it('should also show emoji menu for the smiley icon in notes', async () => {
@ -131,7 +131,7 @@ describe('AwardsHandler', () => {
const $emojiMenu = $('.emoji-menu');
expect($emojiMenu.length).toBe(1);
expect($emojiMenu).toHaveLength(1);
});
it('should remove emoji menu when body is clicked', async () => {
@ -140,9 +140,9 @@ describe('AwardsHandler', () => {
const $emojiMenu = $('.emoji-menu');
$('body').click();
expect($emojiMenu.length).toBe(1);
expect($emojiMenu).toHaveLength(1);
expect($emojiMenu.hasClass('is-visible')).toBe(false);
expect($('.js-awards-block.current').length).toBe(0);
expect($('.js-awards-block.current')).toHaveLength(0);
});
it('should not remove emoji menu when search is clicked', async () => {
@ -151,9 +151,9 @@ describe('AwardsHandler', () => {
const $emojiMenu = $('.emoji-menu');
$('.emoji-search').click();
expect($emojiMenu.length).toBe(1);
expect($emojiMenu).toHaveLength(1);
expect($emojiMenu.hasClass('is-visible')).toBe(true);
expect($('.js-awards-block.current').length).toBe(1);
expect($('.js-awards-block.current')).toHaveLength(1);
});
});
@ -163,7 +163,7 @@ describe('AwardsHandler', () => {
awardsHandler.addAwardToEmojiBar($votesBlock, 'heart');
const $emojiButton = $votesBlock.find('[data-name=heart]');
expect($emojiButton.length).toBe(1);
expect($emojiButton).toHaveLength(1);
expect($emojiButton.next('.js-counter').text()).toBe('1');
expect($votesBlock.hasClass('hidden')).toBe(false);
});
@ -174,7 +174,7 @@ describe('AwardsHandler', () => {
awardsHandler.addAwardToEmojiBar($votesBlock, 'heart');
const $emojiButton = $votesBlock.find('[data-name=heart]');
expect($emojiButton.length).toBe(0);
expect($emojiButton).toHaveLength(0);
});
it('should decrement the emoji counter', () => {
@ -184,7 +184,7 @@ describe('AwardsHandler', () => {
$emojiButton.next('.js-counter').text(5);
awardsHandler.addAwardToEmojiBar($votesBlock, 'heart');
expect($emojiButton.length).toBe(1);
expect($emojiButton).toHaveLength(1);
expect($emojiButton.next('.js-counter').text()).toBe('4');
});
});
@ -222,10 +222,10 @@ describe('AwardsHandler', () => {
const $votesBlock = $('.js-awards-block').eq(0);
awardsHandler.addAward($votesBlock, awardUrl, 'fire');
expect($votesBlock.find('[data-name=fire]').length).toBe(1);
expect($votesBlock.find('[data-name=fire]')).toHaveLength(1);
awardsHandler.removeEmoji($votesBlock.find('[data-name=fire]').closest('button'));
expect($votesBlock.find('[data-name=fire]').length).toBe(0);
expect($votesBlock.find('[data-name=fire]')).toHaveLength(0);
});
});
@ -346,12 +346,12 @@ describe('AwardsHandler', () => {
const $block = $('.js-awards-block');
const $emoji = $menu.find(`.emoji-menu-list:not(.frequent-emojis) ${emojiSelector}`);
expect($emoji.length).toBe(1);
expect($block.find(emojiSelector).length).toBe(0);
expect($emoji).toHaveLength(1);
expect($block.find(emojiSelector)).toHaveLength(0);
$emoji.click();
expect($menu.hasClass('.is-visible')).toBe(false);
expect($block.find(emojiSelector).length).toBe(1);
expect($block.find(emojiSelector)).toHaveLength(1);
});
};
@ -369,7 +369,7 @@ describe('AwardsHandler', () => {
);
$emoji.click();
expect($block.find(emojiSelector).length).toBe(0);
expect($block.find(emojiSelector)).toHaveLength(0);
});
});

View File

@ -315,7 +315,7 @@ describe('Badges store actions', () => {
badgeInForm.linkUrl = "https://example.com?param=<script>alert('XSS')</script>";
await actions.renderBadge({ state, dispatch });
expect(axios.get.mock.calls.length).toBe(1);
expect(axios.get.mock.calls).toHaveLength(1);
const url = axios.get.mock.calls[0][0];
expect(url).toContain(

View File

@ -33,7 +33,7 @@ describe('Badges store mutations', () => {
store.commit(types.RECEIVE_DELETE_BADGE_ERROR, dummyBadge.id);
expect(store.state.badges.length).toBe(badgeCount);
expect(store.state.badges).toHaveLength(badgeCount);
expect(store.state.badges[0].isDeleting).toBe(false);
expect(store.state.badges[1].isDeleting).toBe(false);
expect(store.state.badges[2].isDeleting).toBe(true);
@ -177,7 +177,7 @@ describe('Badges store mutations', () => {
store.commit(types.RECEIVE_UPDATED_BADGE, newBadge);
expect(store.state.badges.length).toBe(badgeCount);
expect(store.state.badges).toHaveLength(badgeCount);
expect(store.state.badges[badgeIndex]).toStrictEqual(newBadge);
});
});
@ -216,7 +216,7 @@ describe('Badges store mutations', () => {
store.commit(types.REQUEST_DELETE_BADGE, dummyBadge.id);
expect(store.state.badges.length).toBe(badgeCount);
expect(store.state.badges).toHaveLength(badgeCount);
expect(store.state.badges[0].isDeleting).toBe(false);
expect(store.state.badges[1].isDeleting).toBe(true);
expect(store.state.badges[2].isDeleting).toBe(true);

View File

@ -40,7 +40,7 @@ describe('Batch comments diff file drafts component', () => {
it('renders list of draft notes', () => {
factory();
expect(wrapper.findAllComponents(DraftNote).length).toEqual(2);
expect(wrapper.findAllComponents(DraftNote)).toHaveLength(2);
});
it('renders index of draft note', () => {
@ -48,7 +48,7 @@ describe('Batch comments diff file drafts component', () => {
const elements = wrapper.findAllComponents(DesignNotePin);
expect(elements.length).toEqual(2);
expect(elements).toHaveLength(2);
expect(elements.at(0).props('label')).toEqual(1);

View File

@ -154,7 +154,7 @@ describe('BindInOut', () => {
});
it('should call .init for each element', () => {
expect(BindInOut.init.mock.calls.length).toEqual(3);
expect(BindInOut.init.mock.calls).toHaveLength(3);
});
it('should return an array of instances', () => {

View File

@ -114,7 +114,7 @@ describe('GlobalAlerts', () => {
wrapper.findComponent(GlAlert).vm.$emit('dismiss');
await nextTick();
expect(findAllAlerts().length).toBe(1);
expect(findAllAlerts()).toHaveLength(1);
expect(removeGlobalAlertById).toHaveBeenCalledWith(alert1.id);
});
});

View File

@ -29,7 +29,7 @@ describe('date_picker behavior', () => {
it('Instantiates Pickaday for every instance of a .datepicker class', () => {
initDatePickers();
expect(pikadayMock.mock.calls.length).toEqual(2);
expect(pikadayMock.mock.calls).toHaveLength(2);
expect(parseMock.mock.calls).toEqual([['2020-10-01'], ['']]);
});
});

View File

@ -42,7 +42,7 @@ describe('highlightCurrentUser', () => {
it('highlights current user', () => {
highlightCurrentUser(elements);
expect(elements.length).toBe(2);
expect(elements).toHaveLength(2);
expect(elements[0]).not.toHaveClass('current-user');
expect(elements[1]).toHaveClass('current-user');
});

View File

@ -44,7 +44,7 @@ describe('Blob Header Default Actions', () => {
});
it('exactly 3 buttons with predefined actions', () => {
expect(buttons.length).toBe(3);
expect(buttons).toHaveLength(3);
[BTN_COPY_CONTENTS_TITLE, BTN_RAW_TITLE, BTN_DOWNLOAD_TITLE].forEach((title, i) => {
expect(buttons.at(i).attributes('title')).toBe(title);
});

View File

@ -47,7 +47,7 @@ describe('Blob Header Viewer Switcher', () => {
});
it('renders exactly 2 buttons with predefined actions', () => {
expect(buttons.length).toBe(2);
expect(buttons).toHaveLength(2);
[SIMPLE_BLOB_VIEWER_TITLE, RICH_BLOB_VIEWER_TITLE].forEach((title, i) => {
expect(buttons.at(i).attributes('title')).toBe(title);
});

View File

@ -69,12 +69,12 @@ describe('Markdown table of contents component', () => {
const dropdown = findDropdown();
expect(dropdown.exists()).toBe(true);
expect(dropdown.props('items').length).toBe(4);
expect(dropdown.props('items')).toHaveLength(4);
// make sure that this only happens once
await setLoaded(true);
expect(dropdown.props('items').length).toBe(4);
expect(dropdown.props('items')).toHaveLength(4);
});
it('generates proper anchor links', async () => {

View File

@ -160,7 +160,7 @@ describe('LineHighlighter', () => {
});
expect(document.querySelector('#LC13').classList).toContain(testContext.css);
expect(document.querySelectorAll(`.${testContext.css}`).length).toBe(1);
expect(document.querySelectorAll(`.${testContext.css}`)).toHaveLength(1);
});
it('sets the hash', () => {
@ -180,7 +180,7 @@ describe('LineHighlighter', () => {
shiftKey: true,
});
expect(document.querySelectorAll(`.${testContext.css}`).length).toBe(6);
expect(document.querySelectorAll(`.${testContext.css}`)).toHaveLength(6);
for (let line = 15; line <= 20; line += 1) {
expect(document.querySelector(`#LC${line}`).classList).toContain(testContext.css);
}
@ -192,7 +192,7 @@ describe('LineHighlighter', () => {
shiftKey: true,
});
expect(document.querySelectorAll(`.${testContext.css}`).length).toBe(6);
expect(document.querySelectorAll(`.${testContext.css}`)).toHaveLength(6);
for (let line = 5; line <= 10; line += 1) {
expect(document.querySelector(`#LC${line}`).classList).toContain(testContext.css);
}
@ -214,7 +214,7 @@ describe('LineHighlighter', () => {
shiftKey: true,
});
expect(document.querySelectorAll(`.${testContext.css}`).length).toBe(6);
expect(document.querySelectorAll(`.${testContext.css}`)).toHaveLength(6);
for (let line = 5; line <= 10; line += 1) {
expect(document.querySelector(`#LC${line}`).classList).toContain(testContext.css);
}
@ -225,7 +225,7 @@ describe('LineHighlighter', () => {
shiftKey: true,
});
expect(document.querySelectorAll(`.${testContext.css}`).length).toBe(6);
expect(document.querySelectorAll(`.${testContext.css}`)).toHaveLength(6);
for (let line = 10; line <= 15; line += 1) {
expect(document.querySelector(`#LC${line}`).classList).toContain(testContext.css);
}

View File

@ -349,7 +349,7 @@ describe('Board card component', () => {
});
it('renders all three assignees', () => {
expect(wrapper.findAll('.board-card-assignee .gl-avatar').length).toEqual(3);
expect(wrapper.findAll('.board-card-assignee .gl-avatar')).toHaveLength(3);
});
describe('more than three assignees', () => {
@ -377,7 +377,7 @@ describe('Board card component', () => {
});
it('renders two assignees', () => {
expect(wrapper.findAll('.board-card-assignee .gl-avatar').length).toEqual(2);
expect(wrapper.findAll('.board-card-assignee .gl-avatar')).toHaveLength(2);
});
it('renders 99+ avatar counter', async () => {
@ -412,7 +412,7 @@ describe('Board card component', () => {
});
it('does not render list label but renders all other labels', () => {
expect(wrapper.findAllComponents(GlLabel).length).toBe(1);
expect(wrapper.findAllComponents(GlLabel)).toHaveLength(1);
const label = wrapper.findComponent(GlLabel);
expect(label.props('title')).toEqual(label1.title);
expect(label.props('description')).toEqual(label1.description);
@ -424,7 +424,7 @@ describe('Board card component', () => {
await nextTick();
expect(wrapper.findAllComponents(GlLabel).length).toBe(1);
expect(wrapper.findAllComponents(GlLabel)).toHaveLength(1);
expect(wrapper.text()).not.toContain('closed');
});
});
@ -453,7 +453,7 @@ describe('Board card component', () => {
});
it('emits setFilters event', () => {
expect(wrapper.emitted('setFilters').length).toBe(1);
expect(wrapper.emitted('setFilters')).toHaveLength(1);
});
});

View File

@ -80,7 +80,7 @@ describe('Board list component', () => {
});
it('renders issues', () => {
expect(wrapper.findAllComponents(BoardCard).length).toBe(1);
expect(wrapper.findAllComponents(BoardCard)).toHaveLength(1);
});
it('sets data attribute with issue id', () => {

View File

@ -299,7 +299,7 @@ describe('BoardForm', () => {
await waitForPromises();
expect(global.window.location.href).not.toContain('?group_by=epic');
expect(wrapper.emitted('updateBoard').length).toBe(1);
expect(wrapper.emitted('updateBoard')).toHaveLength(1);
expect(wrapper.emitted('updateBoard')).toEqual([
[
{

View File

@ -126,7 +126,7 @@ describe('boards sidebar remove issue', () => {
await nextTick();
expect(wrapper.emitted().open.length).toBe(1);
expect(wrapper.emitted().open).toHaveLength(1);
});
it('does not emits events when collapsing with false `emitEvent`', async () => {

View File

@ -73,7 +73,7 @@ describe('ProjectSelect component', () => {
await waitForPromises();
findGlCollapsibleListBox().vm.$emit('shown');
await nextTick();
expect(findGlCollapsibleListBox().props('items').length).toEqual(mockProjects.length - 1);
expect(findGlCollapsibleListBox().props('items')).toHaveLength(mockProjects.length - 1);
});
});

View File

@ -17,7 +17,7 @@ describe('Branch divergence graph component', () => {
maxCommits: 100,
});
expect(vm.findAllComponents(GraphBar).length).toBe(2);
expect(vm.findAllComponents(GraphBar)).toHaveLength(2);
expect(vm.element).toMatchSnapshot();
});
@ -41,7 +41,7 @@ describe('Branch divergence graph component', () => {
maxCommits: 100,
});
expect(vm.findAllComponents(GraphBar).length).toBe(1);
expect(vm.findAllComponents(GraphBar)).toHaveLength(1);
expect(vm.element).toMatchSnapshot();
});

View File

@ -33,7 +33,7 @@ describe('initRecaptchaScript', () => {
it('is memoized', () => {
expect(initRecaptchaScript()).toBe(result);
expect(document.head.querySelectorAll('script').length).toBe(1);
expect(document.head.querySelectorAll('script')).toHaveLength(1);
});
describe('when onload is triggered', () => {

View File

@ -275,19 +275,19 @@ describe('JobArtifactsTable component', () => {
describe('row expansion', () => {
it('toggles the visibility of the row details', async () => {
expect(findDetailsRows().length).toBe(0);
expect(findDetailsRows()).toHaveLength(0);
expect(findCountIcon().props('isOn')).toBe(false);
findCount().trigger('click');
await nextTick();
expect(findDetailsRows().length).toBe(1);
expect(findDetailsRows()).toHaveLength(1);
expect(findCountIcon().props('isOn')).toBe(true);
findCount().trigger('click');
await nextTick();
expect(findDetailsRows().length).toBe(0);
expect(findDetailsRows()).toHaveLength(0);
expect(findCountIcon().props('isOn')).toBe(false);
});

View File

@ -107,7 +107,7 @@ describe('CiResourcesListItem', () => {
createComponent();
const markdown = findMarkdown();
expect(markdown.props().markdown.length).toBe(260);
expect(markdown.props().markdown).toHaveLength(260);
});
it('hides the resource description on mobile devices', () => {

View File

@ -260,7 +260,7 @@ describe('Ci variable table', () => {
});
it('hides alert', () => {
expect(findLimitReachedAlerts().length).toBe(0);
expect(findLimitReachedAlerts()).toHaveLength(0);
});
});
@ -268,7 +268,7 @@ describe('Ci variable table', () => {
it('hides alert when limit has not been reached', () => {
createComponent({ provide });
expect(findLimitReachedAlerts().length).toBe(0);
expect(findLimitReachedAlerts()).toHaveLength(0);
});
it('shows alert when limit has been reached', () => {
@ -282,7 +282,7 @@ describe('Ci variable table', () => {
props: { maxVariableLimit: mockMaxVariableLimit },
});
expect(findLimitReachedAlerts().length).toBe(2);
expect(findLimitReachedAlerts()).toHaveLength(2);
expect(findLimitReachedAlerts().at(0).props('dismissible')).toBe(false);
expect(findLimitReachedAlerts().at(0).text()).toContain(exceedsVariableLimitText);

View File

@ -21,6 +21,6 @@ describe('InputsTableSkeletonLoader', () => {
});
it('renders 8 rects (2 rows x 4 columns)', () => {
expect(findSkeletonRects().length).toBe(8);
expect(findSkeletonRects()).toHaveLength(8);
});
});

View File

@ -109,7 +109,7 @@ describe('Job Log Header Line', () => {
wrapper.trigger('click');
await nextTick();
expect(wrapper.emitted().toggleLine.length).toBe(1);
expect(wrapper.emitted().toggleLine).toHaveLength(1);
});
});

View File

@ -80,7 +80,7 @@ describe('stage column component', () => {
});
it('should render the provided groups', () => {
expect(findAllStageColumnGroups().length).toBe(mockGroups.length);
expect(findAllStageColumnGroups()).toHaveLength(mockGroups.length);
});
it('should emit updateMeasurements event on mount', () => {
@ -138,9 +138,9 @@ describe('stage column component', () => {
});
it('shows failed jobs grouped', () => {
expect(findAllStageColumnFailedGroups().length).toBe(1);
expect(findAllStageColumnFailedGroups()).toHaveLength(1);
expect(findAllStageColumnFailedTitle().text()).toEqual('Failed jobs');
expect(findAllStageColumnGroups().length).toBe(1);
expect(findAllStageColumnGroups()).toHaveLength(1);
});
});

View File

@ -127,7 +127,7 @@ describe('Test reports suite table', () => {
});
it('renders one page of test cases', () => {
expect(allCaseRows().length).toBe(perPage);
expect(allCaseRows()).toHaveLength(perPage);
});
it('renders a pagination component', () => {

View File

@ -49,7 +49,7 @@ describe('Test reports summary table', () => {
it('renders the correct number of rows', () => {
expect(noSuitesToShow().exists()).toBe(false);
expect(allSuitesRows().length).toBe(testReports.test_suites.length);
expect(allSuitesRows()).toHaveLength(testReports.test_suites.length);
});
describe('when there is a suite error', () => {

View File

@ -193,7 +193,7 @@ describe('PipelineStageDropdown', () => {
const { name } = jobs.data.ciPipelineStage.jobs.nodes[5];
wrapper.findComponent(GlSearchBoxByType).vm.$emit('input', name);
await nextTick();
expect(findJobDropdownItems().length).toBe(1);
expect(findJobDropdownItems()).toHaveLength(1);
});
});

View File

@ -49,14 +49,14 @@ describe('Variable values listbox', () => {
await nextTick();
expect(findListboxItems().length).toBe(1);
expect(findListboxItems()).toHaveLength(1);
expect(findListboxItems().at(0).text()).toContain(searchString);
search('');
await nextTick();
expect(findListboxItems().length).toBe(3);
expect(findListboxItems()).toHaveLength(3);
});
it('filters options with fuzzy filtering', async () => {
@ -66,7 +66,7 @@ describe('Variable values listbox', () => {
await nextTick();
expect(findListboxItems().length).toBe(1);
expect(findListboxItems()).toHaveLength(1);
expect(findListboxItems().at(0).text()).toBe('production');
});
});

View File

@ -35,7 +35,7 @@ describe('CI Templates', () => {
});
it('renders all suggested templates', () => {
expect(findTemplateNames().length).toBe(3);
expect(findTemplateNames()).toHaveLength(3);
expect(wrapper.text()).toContain('Android', 'Bash', 'C++');
});

View File

@ -97,7 +97,7 @@ describe('RunnerProjects', () => {
});
it('Shows projects', () => {
expect(findRunnerAssignedItems().length).toBe(mockProjects.length);
expect(findRunnerAssignedItems()).toHaveLength(mockProjects.length);
});
it('Shows a project', () => {
@ -218,7 +218,7 @@ describe('RunnerProjects', () => {
createComponent();
expect(wrapper.findByText(I18N_NO_PROJECTS_FOUND).exists()).toBe(false);
expect(findRunnerAssignedItems().length).toBe(0);
expect(findRunnerAssignedItems()).toHaveLength(0);
expect(findGlSearchBoxByType().props('isLoading')).toBe(true);
});

View File

@ -38,7 +38,7 @@ describe('RunnerTags', () => {
it('Displays all tags', () => {
expect(wrapper.text()).toMatchInterpolatedText('tag1 tag2 tag3');
expect(findTags().length).toBe(3);
expect(findTags()).toHaveLength(3);
expect(findTagAt(0).props('tag')).toBe('tag1');
expect(findTagAt(1).props('tag')).toBe('tag2');
@ -56,7 +56,7 @@ describe('RunnerTags', () => {
it('Displays limited tags', () => {
expect(wrapper.text()).toMatchInterpolatedText('tag1 +2 more');
expect(findTags().length).toBe(1);
expect(findTags()).toHaveLength(1);
expect(findTagAt(0).props('tag')).toBe('tag1');
expect(findButton().text()).toBe('+2 more');
@ -68,7 +68,7 @@ describe('RunnerTags', () => {
expect(wrapper.text()).toMatchInterpolatedText('tag1 tag2 tag3');
expect(findTags().length).toBe(3);
expect(findTags()).toHaveLength(3);
expect(findTagAt(0).props('tag')).toBe('tag1');
expect(findTagAt(1).props('tag')).toBe('tag2');
expect(findTagAt(2).props('tag')).toBe('tag3');

View File

@ -90,7 +90,7 @@ describe('IntegrationStatus', () => {
({ tokens, integrationStatuses }) => {
createWrapper(tokens);
expect(findAgentIntegrationStatusRows().length).toBe(integrationStatuses.length);
expect(findAgentIntegrationStatusRows()).toHaveLength(integrationStatuses.length);
integrationStatuses.forEach((integrationStatus, index) => {
expect(findAgentIntegrationStatusRows().at(index).props()).toMatchObject({

View File

@ -89,7 +89,7 @@ describe('ClustersViewAllComponent', () => {
});
it('should render 2 cards', () => {
expect(findCards().length).toBe(2);
expect(findCards()).toHaveLength(2);
});
});

View File

@ -101,7 +101,7 @@ describe('Code navigation popover component', () => {
});
expect(wrapper.find('[data-testid="references-tab"]').exists()).toBe(true);
expect(wrapper.findAll('[data-testid="reference-link"]').length).toBe(2);
expect(wrapper.findAll('[data-testid="reference-link"]')).toHaveLength(2);
});
describe('code output', () => {

View File

@ -116,7 +116,7 @@ describe('Code navigation actions', () => {
actions.showBlobInteractionZones({ state }, 'index.js');
expect(addInteractionClass).toHaveBeenCalled();
expect(addInteractionClass.mock.calls.length).toBe(2);
expect(addInteractionClass.mock.calls).toHaveLength(2);
expect(addInteractionClass.mock.calls[0]).toEqual([
{ path: 'index.js', d: 'test', wrapTextNodes },
]);

View File

@ -88,14 +88,14 @@ describe('addInteractionClass', () => {
it('does not wrap text nodes by default', () => {
addInteractionClass(params);
const spans = findAllSpans();
expect(spans.length).toBe(0);
expect(spans).toHaveLength(0);
});
it('wraps text nodes if wrapTextNodes is true', () => {
addInteractionClass({ ...params, wrapTextNodes: true });
const spans = findAllSpans();
expect(spans.length).toBe(3);
expect(spans).toHaveLength(3);
expect(spans[0].textContent).toBe(' ');
expect(spans[1].textContent).toBe('Text');
expect(spans[2].textContent).toBe(' ');

View File

@ -39,7 +39,7 @@ describe('Comment templates index page component', () => {
await waitForPromises();
expect(wrapper.findAllComponents(ListItem).length).toBe(0);
expect(wrapper.findAllComponents(ListItem)).toHaveLength(0);
});
it('renders list of comment templates', async () => {
@ -49,7 +49,7 @@ describe('Comment templates index page component', () => {
await waitForPromises();
expect(wrapper.findAllComponents(ListItem).length).toBe(2);
expect(wrapper.findAllComponents(ListItem)).toHaveLength(2);
expect(wrapper.findAllComponents(ListItem).at(0).props('template')).toEqual(
expect.objectContaining(savedReplies[0]),
);

View File

@ -70,7 +70,7 @@ describe('Commits List', () => {
// The last commit header should be removed
// since the previous one has the same data-day value.
expect(commitsList.processCommits(data).find('li.commit-header').length).toBe(0);
expect(commitsList.processCommits(data).find('li.commit-header')).toHaveLength(0);
});
});

View File

@ -45,7 +45,7 @@ describe('content_editor/components/toolbar_text_style_dropdown', () => {
TEXT_STYLE_DROPDOWN_ITEMS.forEach((textStyle, index) => {
expect(findListbox().props('items').at(index).text).toContain(textStyle.label);
});
expect(findListbox().props('items').length).toBe(TEXT_STYLE_DROPDOWN_ITEMS.length);
expect(findListbox().props('items')).toHaveLength(TEXT_STYLE_DROPDOWN_ITEMS.length);
});
describe('when there is an active item', () => {

View File

@ -126,7 +126,7 @@ describe('Contributors', () => {
});
it('renders the individual charts', () => {
expect(findIndividualCharts().length).toBe(1);
expect(findIndividualCharts()).toHaveLength(1);
expect(findIndividualCharts().at(0).props()).toMatchObject({
contributor: {
name: 'John',

View File

@ -70,7 +70,7 @@ describe('CreateItemDropdown', () => {
const $itemEls = $wrapperEl.find('.js-dropdown-content a');
expect($itemEls.length).toEqual(DROPDOWN_ITEM_DATA.length);
expect($itemEls).toHaveLength(DROPDOWN_ITEM_DATA.length);
DROPDOWN_ITEM_DATA.forEach((dataItem, i) => {
expect($($itemEls[i]).text()).toEqual(dataItem.text);
@ -116,7 +116,7 @@ describe('CreateItemDropdown', () => {
const $itemEls = $wrapperEl.find('.js-dropdown-content a');
expect($itemEls.length).toEqual(1 + DROPDOWN_ITEM_DATA.length);
expect($itemEls).toHaveLength(1 + DROPDOWN_ITEM_DATA.length);
expect($($itemEls.get(DROPDOWN_ITEM_DATA.length)).text()).toEqual(NEW_ITEM_TEXT);
});
@ -125,7 +125,7 @@ describe('CreateItemDropdown', () => {
const $itemEls = $wrapperEl.find('.js-dropdown-content a');
expect($itemEls.length).toEqual(DROPDOWN_ITEM_DATA.length);
expect($itemEls).toHaveLength(DROPDOWN_ITEM_DATA.length);
});
});
@ -152,13 +152,13 @@ describe('CreateItemDropdown', () => {
const $itemElsAfterFilter = $wrapperEl.find('.js-dropdown-content a');
expect($itemElsAfterFilter.length).toEqual(1);
expect($itemElsAfterFilter).toHaveLength(1);
createItemDropdown.clearDropdown();
const $itemElsAfterClear = $wrapperEl.find('.js-dropdown-content a');
expect($itemElsAfterClear.length).toEqual(0);
expect($itemElsAfterClear).toHaveLength(0);
expect(filterInput.val()).toEqual('');
});
});
@ -188,7 +188,7 @@ describe('CreateItemDropdown', () => {
const $itemEls = $wrapperEl.find('.js-dropdown-content a');
expect($itemEls.length).toEqual(1 + DROPDOWN_ITEM_DATA.length);
expect($itemEls).toHaveLength(1 + DROPDOWN_ITEM_DATA.length);
expect($($itemEls[DROPDOWN_ITEM_DATA.length]).text()).toEqual('new-item-text');
expect($wrapperEl.find('.dropdown-toggle-text').text()).toEqual('new-item-title');
});

View File

@ -68,14 +68,14 @@ describe('Deploy freeze table', () => {
it('displays data', () => {
const tableRows = findDeployFreezeTable().findAll('tbody tr');
expect(tableRows.length).toBe(freezePeriodsFixture.length);
expect(tableRows).toHaveLength(freezePeriodsFixture.length);
expect(findEmptyFreezePeriods().exists()).toBe(false);
expect(findEditDeployFreezeButton().exists()).toBe(true);
});
it('displays correct count', () => {
const tableRows = findDeployFreezeTable().findAll('tbody tr');
expect(tableRows.length).toBe(freezePeriodsFixture.length);
expect(tableRows).toHaveLength(freezePeriodsFixture.length);
expect(findCount().text()).toBe('3');
});

View File

@ -102,7 +102,7 @@ describe('Deploy keys app component', () => {
},
});
await mountComponent();
expect(findKeyPanels().length).toBe(3);
expect(findKeyPanels()).toHaveLength(3);
});
describe.each`

View File

@ -142,7 +142,7 @@ describe('Deploy keys key', () => {
await createComponent({ deployKey });
const labels = wrapper.findAll('.deploy-project-label');
expect(labels.length).toBe(2);
expect(labels).toHaveLength(2);
expect(labels.at(1).text()).toContain('others');
expect(labels.at(1).attributes('title')).toContain('Expand');
});
@ -167,7 +167,7 @@ describe('Deploy keys key', () => {
const labels = wrapper.findAll('.deploy-project-label');
expect(labels.length).toBe(2);
expect(labels).toHaveLength(2);
expect(labels.at(1).text()).toContain(deployKey.deployKeysProjects[1].project.fullName);
});
});

View File

@ -123,10 +123,10 @@ describe('deprecatedJQueryDropdown', () => {
});
it('should select a following item on DOWN keypress', () => {
expect($(FOCUSED_ITEM_SELECTOR, test.$dropdownMenuElement).length).toBe(0);
expect($(FOCUSED_ITEM_SELECTOR, test.$dropdownMenuElement)).toHaveLength(0);
const randomIndex = Math.floor(Math.random() * (test.projectsData.length - 1)) + 0;
navigateWithKeys('down', randomIndex, () => {
expect($(FOCUSED_ITEM_SELECTOR, test.$dropdownMenuElement).length).toBe(1);
expect($(FOCUSED_ITEM_SELECTOR, test.$dropdownMenuElement)).toHaveLength(1);
expect($(`${ITEM_SELECTOR}:eq(${randomIndex}) a`, test.$dropdownMenuElement)).toHaveClass(
'is-focused',
);
@ -134,12 +134,12 @@ describe('deprecatedJQueryDropdown', () => {
});
it('should select a previous item on UP keypress', () => {
expect($(FOCUSED_ITEM_SELECTOR, test.$dropdownMenuElement).length).toBe(0);
expect($(FOCUSED_ITEM_SELECTOR, test.$dropdownMenuElement)).toHaveLength(0);
navigateWithKeys('down', test.projectsData.length - 1, () => {
expect($(FOCUSED_ITEM_SELECTOR, test.$dropdownMenuElement).length).toBe(1);
expect($(FOCUSED_ITEM_SELECTOR, test.$dropdownMenuElement)).toHaveLength(1);
const randomIndex = Math.floor(Math.random() * (test.projectsData.length - 2)) + 0;
navigateWithKeys('up', randomIndex, () => {
expect($(FOCUSED_ITEM_SELECTOR, test.$dropdownMenuElement).length).toBe(1);
expect($(FOCUSED_ITEM_SELECTOR, test.$dropdownMenuElement)).toHaveLength(1);
expect(
$(
`${ITEM_SELECTOR}:eq(${test.projectsData.length - 2 - randomIndex}) a`,
@ -296,7 +296,7 @@ describe('deprecatedJQueryDropdown', () => {
const li = dropdown.renderItem(sep);
expect(li).toHaveClass('separator');
expect(li.childNodes.length).toEqual(0);
expect(li.childNodes).toHaveLength(0);
});
it('should return an empty .divider li when when appropriate', () => {
@ -305,7 +305,7 @@ describe('deprecatedJQueryDropdown', () => {
const li = dropdown.renderItem(div);
expect(li).toHaveClass('divider');
expect(li.childNodes.length).toEqual(0);
expect(li.childNodes).toHaveLength(0);
});
it('should return a .dropdown-header li with the correct content when when appropriate', () => {
@ -315,7 +315,7 @@ describe('deprecatedJQueryDropdown', () => {
const li = dropdown.renderItem(header);
expect(li).toHaveClass('dropdown-header');
expect(li.childNodes.length).toEqual(1);
expect(li.childNodes).toHaveLength(1);
expect(li.textContent).toEqual(text);
});

View File

@ -226,7 +226,7 @@ describe('Design management index page', () => {
createComponent({ allVersions: [mockVersion] });
expect(findDesignsWrapper().exists()).toBe(true);
expect(findDesigns().length).toBe(3);
expect(findDesigns()).toHaveLength(3);
expect(findDesignToolbarWrapper().exists()).toBe(true);
expect(findDesignUploadButton().exists()).toBe(true);
});

View File

@ -688,7 +688,7 @@ describe('diffs/components/app', () => {
await nextTick();
expect(wrapper.findAllComponents(DiffFile).length).toBe(1);
expect(wrapper.findAllComponents(DiffFile)).toHaveLength(1);
});
describe('rechecking the url hash for scrolling', () => {

View File

@ -50,8 +50,8 @@ describe('DiffDiscussions', () => {
expect(findNoteableDiscussion().exists()).toBe(true);
expect(wrapper.findComponent(DiscussionNotes).exists()).toBe(true);
expect(
wrapper.findComponent(DiscussionNotes).findAllComponents(TimelineEntryItem).length,
).toBe(discussionsMockData.notes.length);
wrapper.findComponent(DiscussionNotes).findAllComponents(TimelineEntryItem),
).toHaveLength(discussionsMockData.notes.length);
});
});

View File

@ -301,7 +301,7 @@ describe('DiffFile', () => {
expect(el.id).toEqual(file_hash);
expect(el.classList.contains('diff-file')).toEqual(true);
expect(el.querySelectorAll('.diff-content.hidden').length).toEqual(0);
expect(el.querySelectorAll('.diff-content.hidden')).toHaveLength(0);
expect(el.querySelector('.js-file-title')).toBeDefined();
expect(wrapper.findComponent(DiffFileHeaderComponent).exists()).toBe(true);
expect(el.querySelector('.js-syntax-highlight')).toBeDefined();
@ -340,7 +340,7 @@ describe('DiffFile', () => {
createComponent();
makeFileAutomaticallyCollapsed();
expect(findDiffContentArea(wrapper).element.children.length).toBe(1);
expect(findDiffContentArea(wrapper).element.children).toHaveLength(1);
expect(wrapper.classes('has-body')).toBe(true);
});
@ -348,7 +348,7 @@ describe('DiffFile', () => {
createComponent();
makeFileManuallyCollapsed();
expect(findDiffContentArea(wrapper).element.children.length).toBe(1);
expect(findDiffContentArea(wrapper).element.children).toHaveLength(1);
expect(wrapper.classes('has-body')).toBe(true);
});
});
@ -384,7 +384,7 @@ describe('DiffFile', () => {
createComponent();
await nextTick();
expect(findDiffContentArea(wrapper).element.children.length).toBe(0);
expect(findDiffContentArea(wrapper).element.children).toHaveLength(0);
});
it('should not have the class `has-body` to present the header differently', () => {

View File

@ -61,7 +61,7 @@ describe('DiffGutterAvatars', () => {
});
it('renders correct amount of user avatars', () => {
expect(findUserAvatars().length).toBe(3);
expect(findUserAvatars()).toHaveLength(3);
});
// Avoid images in file contents copy: https://gitlab.com/gitlab-org/gitlab/-/issues/337139

View File

@ -34,7 +34,7 @@ describe('diff_stats', () => {
it('is not rendered if diffsCount is empty', () => {
createComponent();
expect(findDiffStatsGroup().length).toBe(2);
expect(findDiffStatsGroup()).toHaveLength(2);
});
it('is not rendered if diffsCount is not a number', () => {
@ -42,7 +42,7 @@ describe('diff_stats', () => {
diffsCount: null,
});
expect(findDiffStatsGroup().length).toBe(2);
expect(findDiffStatsGroup()).toHaveLength(2);
});
});

View File

@ -78,7 +78,7 @@ describe('DiffView', () => {
inline: type === 'inline',
},
});
expect(wrapper.findAllComponents(DiffCommentCell).length).toBe(total);
expect(wrapper.findAllComponents(DiffCommentCell)).toHaveLength(total);
expect(wrapper.find(container).findComponent(DiffCommentCell).exists()).toBe(true);
},
);

View File

@ -288,15 +288,14 @@ describe('Diffs Module Getters', () => {
{},
{},
{ discussions: [discussionMock] },
)(diffFileMock).length,
).toEqual(1);
)(diffFileMock),
).toHaveLength(1);
});
it('returns an empty array when no discussions are found in the given diff', () => {
expect(
getters.getDiffFileDiscussions(localState, {}, {}, { discussions: [] })(diffFileMock)
.length,
).toEqual(0);
getters.getDiffFileDiscussions(localState, {}, {}, { discussions: [] })(diffFileMock),
).toHaveLength(0);
});
});

View File

@ -58,7 +58,7 @@ describe('DiffsStoreMutations', () => {
mutations[types.SET_DIFF_FILES](state, ['file', 'another file']);
expect(state.diffFiles.length).toEqual(2);
expect(state.diffFiles).toHaveLength(2);
});
it('should not set anything except diffFiles in state', () => {
@ -309,7 +309,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toEqual(1);
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions).toHaveLength(1);
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].id).toEqual(1);
});
@ -356,7 +356,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
expect(state.diffFiles[0].discussions.length).toEqual(1);
expect(state.diffFiles[0].discussions).toHaveLength(1);
expect(state.diffFiles[0].discussions[0].id).toEqual(1);
});
@ -406,7 +406,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toEqual(1);
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions).toHaveLength(1);
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].id).toEqual(1);
mutations[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, {
@ -414,7 +414,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toEqual(1);
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions).toHaveLength(1);
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].id).toEqual(1);
});
@ -464,7 +464,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toEqual(1);
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions).toHaveLength(1);
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].id).toEqual(1);
mutations[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, {
@ -476,7 +476,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].notes.length).toBe(1);
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].notes).toHaveLength(1);
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].resolved).toBe(true);
});
@ -542,7 +542,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toBe(1);
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions).toHaveLength(1);
});
it('should add legacy discussions to the given line', () => {
@ -590,7 +590,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toEqual(1);
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions).toHaveLength(1);
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].id).toEqual(1);
});
@ -672,7 +672,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode: null,
});
expect(state.diffFiles[0].discussions.length).toEqual(1);
expect(state.diffFiles[0].discussions).toHaveLength(1);
});
describe('expanded state', () => {
@ -928,7 +928,7 @@ describe('DiffsStoreMutations', () => {
lineCode: 'ABC_1',
});
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toEqual(0);
expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions).toHaveLength(0);
});
});
@ -1332,7 +1332,7 @@ describe('DiffsStoreMutations', () => {
mutations[types.ADD_DRAFT_TO_FILE](state, { filePath: 'path', draft: 'test' });
expect(state.diffFiles[0].drafts.length).toEqual(1);
expect(state.diffFiles[0].drafts).toHaveLength(1);
expect(state.diffFiles[0].drafts[0]).toEqual('test');
});
});

View File

@ -440,7 +440,7 @@ describe('DiffsStoreUtils', () => {
it('sets the collapsed attribute on files', () => {
const checkLine = preparedDiff.diff_files[0][INLINE_DIFF_LINES_KEY][0];
expect(checkLine.discussions.length).toBe(0);
expect(checkLine.discussions).toHaveLength(0);
expect(checkLine).not.toHaveAttr('text');
const firstChar = checkLine.rich_text.charAt(0);
@ -453,11 +453,11 @@ describe('DiffsStoreUtils', () => {
it('guarantees an empty array for both diff styles', () => {
expect(splitInlineDiff.diff_files[0][INLINE_DIFF_LINES_KEY].length).toBeGreaterThan(0);
expect(splitParallelDiff.diff_files[0][INLINE_DIFF_LINES_KEY].length).toEqual(0);
expect(splitParallelDiff.diff_files[0][INLINE_DIFF_LINES_KEY]).toHaveLength(0);
});
it('merges existing diff files with newly loaded diff files to ensure split diffs are eventually completed', () => {
expect(completedDiff.diff_files.length).toEqual(1);
expect(completedDiff.diff_files).toHaveLength(1);
expect(completedDiff.diff_files[0][INLINE_DIFF_LINES_KEY].length).toBeGreaterThan(0);
});
@ -544,7 +544,7 @@ describe('DiffsStoreUtils', () => {
});
it('guarantees an empty array of lines for both diff styles', () => {
expect(preparedDiffFiles[0][INLINE_DIFF_LINES_KEY].length).toEqual(0);
expect(preparedDiffFiles[0][INLINE_DIFF_LINES_KEY]).toHaveLength(0);
});
it('leaves files in the existing state', () => {
@ -553,7 +553,7 @@ describe('DiffsStoreUtils', () => {
const priorFiles = [fileMock];
const updatedFilesList = utils.prepareDiffData({ diff: metaData, priorFiles, meta: true });
expect(updatedFilesList.length).toEqual(2);
expect(updatedFilesList).toHaveLength(2);
expect(updatedFilesList[0]).toEqual(fileMock);
});

View File

@ -305,12 +305,12 @@ describe('Diffs Module Getters', () => {
discussionMock.diff_file.file_hash = diffFileMock.file_hash;
useNotes().discussions = [discussionMock];
expect(store.getDiffFileDiscussions(diffFileMock).length).toEqual(1);
expect(store.getDiffFileDiscussions(diffFileMock)).toHaveLength(1);
});
it('returns an empty array when no discussions are found in the given diff', () => {
useNotes().discussions = [];
expect(store.getDiffFileDiscussions(diffFileMock).length).toEqual(0);
expect(store.getDiffFileDiscussions(diffFileMock)).toHaveLength(0);
});
});

View File

@ -56,7 +56,7 @@ describe('DiffsStoreMutations', () => {
it('should set diffFiles in state', () => {
store[types.SET_DIFF_FILES](['file', 'another file']);
expect(store.diffFiles.length).toEqual(2);
expect(store.diffFiles).toHaveLength(2);
});
});
@ -297,7 +297,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toEqual(1);
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions).toHaveLength(1);
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].id).toEqual(1);
});
@ -344,7 +344,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
expect(store.diffFiles[0].discussions.length).toEqual(1);
expect(store.diffFiles[0].discussions).toHaveLength(1);
expect(store.diffFiles[0].discussions[0].id).toEqual(1);
});
@ -394,7 +394,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toEqual(1);
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions).toHaveLength(1);
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].id).toEqual(1);
store[types.SET_LINE_DISCUSSIONS_FOR_FILE]({
@ -402,7 +402,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toEqual(1);
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions).toHaveLength(1);
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].id).toEqual(1);
});
@ -452,7 +452,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toEqual(1);
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions).toHaveLength(1);
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].id).toEqual(1);
store[types.SET_LINE_DISCUSSIONS_FOR_FILE]({
@ -464,7 +464,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].notes.length).toBe(1);
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].notes).toHaveLength(1);
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].resolved).toBe(true);
});
@ -530,7 +530,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toBe(1);
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions).toHaveLength(1);
});
it('should add legacy discussions to the given line', () => {
@ -578,7 +578,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toEqual(1);
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions).toHaveLength(1);
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].id).toEqual(1);
});
@ -660,7 +660,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode: null,
});
expect(store.diffFiles[0].discussions.length).toEqual(1);
expect(store.diffFiles[0].discussions).toHaveLength(1);
});
describe('expanded state', () => {
@ -916,7 +916,7 @@ describe('DiffsStoreMutations', () => {
lineCode: 'ABC_1',
});
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toEqual(0);
expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions).toHaveLength(0);
});
});
@ -1312,7 +1312,7 @@ describe('DiffsStoreMutations', () => {
store[types.ADD_DRAFT_TO_FILE]({ filePath: 'path', draft: 'test' });
expect(store.diffFiles[0].drafts.length).toEqual(1);
expect(store.diffFiles[0].drafts).toHaveLength(1);
expect(store.diffFiles[0].drafts[0]).toEqual('test');
});
});

View File

@ -26,9 +26,9 @@ describe('Source Editor utils', () => {
});
it('removes all child nodes from an element', () => {
expect(el.children.length).toBe(1);
expect(el.children).toHaveLength(1);
utils.clearDomElement(el);
expect(el.children.length).toBe(0);
expect(el.children).toHaveLength(0);
});
});

View File

@ -24,7 +24,7 @@ describe('Emoji category component', () => {
});
it('renders emoji groups', () => {
expect(wrapper.findAllComponents(EmojiGroup).length).toBe(2);
expect(wrapper.findAllComponents(EmojiGroup)).toHaveLength(2);
});
it('renders group', async () => {

View File

@ -37,7 +37,7 @@ describe('Emoji group component', () => {
});
expect(wrapper.findAllByTestId('emoji-button').exists()).toBe(true);
expect(wrapper.findAllByTestId('emoji-button').length).toBe(2);
expect(wrapper.findAllByTestId('emoji-button')).toHaveLength(2);
});
it('emits emoji-click', () => {

View File

@ -128,7 +128,7 @@ describe('retrieval of emojis.json', () => {
});
const assertCorrectLocalStorage = () => {
expect(localStorage.length).toBe(1);
expect(localStorage).toHaveLength(1);
expect(localStorage.getItem(CACHE_KEY)).toBe(
JSON.stringify({ data: mockEmojiData, EMOJI_VERSION }),
);
@ -151,7 +151,7 @@ describe('retrieval of emojis.json', () => {
await initEmojiMap();
assertEmojiBeingLoadedCorrectly();
expect(mock.history.get.length).toBe(1);
expect(mock.history.get).toHaveLength(1);
assertCorrectLocalStorage();
});
});
@ -165,7 +165,7 @@ describe('retrieval of emojis.json', () => {
it('should not call the API and not mutate the localStorage', () => {
assertEmojiBeingLoadedCorrectly();
expect(mock.history.get.length).toBe(0);
expect(mock.history.get).toHaveLength(0);
expect(localStorage.setItem).not.toHaveBeenCalled();
assertCorrectLocalStorage();
});
@ -183,7 +183,7 @@ describe('retrieval of emojis.json', () => {
it('should call the API and store results in localStorage', () => {
assertEmojiBeingLoadedCorrectly();
expect(mock.history.get.length).toBe(1);
expect(mock.history.get).toHaveLength(1);
assertCorrectLocalStorage();
});
});
@ -197,7 +197,7 @@ describe('retrieval of emojis.json', () => {
it('should call the API and store results in localStorage', () => {
assertEmojiBeingLoadedCorrectly();
expect(mock.history.get.length).toBe(1);
expect(mock.history.get).toHaveLength(1);
assertCorrectLocalStorage();
});
});
@ -211,7 +211,7 @@ describe('retrieval of emojis.json', () => {
it('should call the API and store results in localStorage', () => {
assertEmojiBeingLoadedCorrectly();
expect(mock.history.get.length).toBe(1);
expect(mock.history.get).toHaveLength(1);
assertCorrectLocalStorage();
});
});
@ -230,8 +230,8 @@ describe('retrieval of emojis.json', () => {
it('should call API but not store the results', () => {
assertEmojiBeingLoadedCorrectly();
expect(mock.history.get.length).toBe(1);
expect(localStorage.length).toBe(0);
expect(mock.history.get).toHaveLength(1);
expect(localStorage).toHaveLength(0);
expect(localStorage.setItem).toHaveBeenCalledTimes(1);
expect(localStorage.setItem).toHaveBeenCalledWith(
CACHE_KEY,
@ -275,12 +275,12 @@ describe('retrieval of emojis.json', () => {
// Load emoji the old way to pre-populate the cache
let res = await prevImplementation();
expect(res).toEqual(mockEmojiData);
expect(mock.history.get.length).toBe(1);
expect(mock.history.get).toHaveLength(1);
localStorage.setItem.mockClear();
// Load emoji the new way
await initEmojiMap();
expect(mock.history.get.length).toBe(2);
expect(mock.history.get).toHaveLength(2);
assertEmojiBeingLoadedCorrectly();
assertCorrectLocalStorage();
localStorage.setItem.mockClear();
@ -288,7 +288,7 @@ describe('retrieval of emojis.json', () => {
// Load emoji the old way to pre-populate the cache
res = await prevImplementation();
expect(res).toEqual(mockEmojiData);
expect(mock.history.get.length).toBe(3);
expect(mock.history.get).toHaveLength(3);
expect(localStorage.setItem.mock.calls).toEqual([
[CACHE_VERSION_KEY, EMOJI_VERSION],
[CACHE_KEY, JSON.stringify(mockEmojiData)],
@ -297,7 +297,7 @@ describe('retrieval of emojis.json', () => {
// Load emoji the old way should work again (and be taken from the cache)
res = await prevImplementation();
expect(res).toEqual(mockEmojiData);
expect(mock.history.get.length).toBe(3);
expect(mock.history.get).toHaveLength(3);
});
});
});

View File

@ -44,7 +44,7 @@ describe('Unicode Support Map', () => {
});
it('should not call .getItem or .setItem', () => {
expect(window.localStorage.getItem.mock.calls.length).toBe(1);
expect(window.localStorage.getItem.mock.calls).toHaveLength(1);
expect(window.localStorage.setItem).not.toHaveBeenCalled();
});
});

View File

@ -33,7 +33,7 @@ describe('Enable Review Apps Modal', () => {
});
it('displays instructions', () => {
expect(findInstructions().length).toBe(7);
expect(findInstructions()).toHaveLength(7);
expect(findInstructionAt(0).text()).toContain(i18n.instructions.step1);
});

View File

@ -46,7 +46,7 @@ describe('EnvironmentActions Component', () => {
it('should render a dropdown button with 2 icons', () => {
createComponent();
expect(wrapper.findComponent(GlDisclosureDropdown).findAllComponents(GlIcon).length).toBe(2);
expect(wrapper.findComponent(GlDisclosureDropdown).findAllComponents(GlIcon)).toHaveLength(2);
});
it('should render a dropdown button with aria-label description', () => {

View File

@ -80,7 +80,7 @@ describe('EnvironmentsFolderAppComponent', () => {
it('should show skeletons while loading', () => {
createWrapper();
expect(findSkeletonLoaders().length).toBe(3);
expect(findSkeletonLoaders()).toHaveLength(3);
});
describe('when environments are loaded', () => {
@ -91,12 +91,12 @@ describe('EnvironmentsFolderAppComponent', () => {
it('should list environments in folder', () => {
const items = findEnvironmentItems();
expect(items.length).toBe(resolvedFolder.environments.length);
expect(items).toHaveLength(resolvedFolder.environments.length);
});
it('should render active and stopped tabs', () => {
const tabs = findTabs();
expect(tabs.length).toBe(2);
expect(tabs).toHaveLength(2);
});
[

View File

@ -212,8 +212,8 @@ describe('ErrorDetails', () => {
});
it('should not convert interpolated text to html entities', () => {
expect(findReportedText().findAll('script').length).toEqual(0);
expect(findReportedText().findAll('strong').length).toEqual(1);
expect(findReportedText().findAll('script')).toHaveLength(0);
expect(findReportedText().findAll('strong')).toHaveLength(1);
});
it('should render text instead of converting to html entities', () => {
@ -225,13 +225,13 @@ describe('ErrorDetails', () => {
it('should show language and error level badges', async () => {
const detailedError = { tags: { level: 'error', logger: 'ruby' } };
await createComponent({ detailedError });
expect(wrapper.findAllComponents(GlBadge).length).toBe(2);
expect(wrapper.findAllComponents(GlBadge)).toHaveLength(2);
});
it('should NOT show the badge if the tag is not present', async () => {
const detailedError = { tags: { level: 'error', logger: null } };
await createComponent({ detailedError });
expect(wrapper.findAllComponents(GlBadge).length).toBe(1);
expect(wrapper.findAllComponents(GlBadge)).toHaveLength(1);
});
it.each(Object.keys(severityLevel))(

Some files were not shown because too many files have changed in this diff Show More