Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-09-13 18:12:21 +00:00
parent 4597f7fe47
commit 05ade12880
161 changed files with 1123 additions and 296 deletions

View File

@ -67,7 +67,7 @@ variables:
RAILS_ENV: "test"
NODE_ENV: "test"
BUNDLE_WITHOUT: "production:development"
BUNDLE_INSTALL_FLAGS: "--jobs=$(nproc) --retry=3 --quiet"
BUNDLE_INSTALL_FLAGS: "--jobs=$(nproc) --retry=3"
BUNDLE_FROZEN: "true"
# we override the max_old_space_size to prevent OOM errors
NODE_OPTIONS: --max_old_space_size=3584

View File

@ -1801,6 +1801,10 @@
when: never
- <<: *if-default-branch-or-tag
changes: *code-backstage-qa-patterns
- <<: *if-dot-com-gitlab-org-merge-request
changes: [".gitlab/ci/setup.gitlab-ci.yml"]
when: manual
allow_failure: true
.setup:rules:dont-interrupt-me:
rules:

View File

@ -3,16 +3,20 @@
cache gems:
extends:
- .default-retry
- .rails-cache
- .ruby-cache
- .default-before_script
- .setup:rules:cache-gems
stage: test
needs: ["setup-test-env"]
stage: prepare
needs: []
variables:
BUNDLE_INSTALL_FLAGS: --with=production --with=development --with=test --jobs=2 --path=vendor --retry=3 --quiet
BUNDLE_WITHOUT: ""
BUNDLE_WITH: "production:development:test"
SETUP_DB: "false"
script:
- bundle package --all --all-platforms
- echo -e "\e[0Ksection_start:`date +%s`:bundle-package[collapsed=true]\r\e[0KPackaging gems"
- bundle config set cache_all true
- run_timed_command "bundle package --all-platforms"
- echo -e "\e[0Ksection_end:`date +%s`:bundle-package\r\e[0K"
artifacts:
paths:
- vendor/cache

View File

@ -69,14 +69,18 @@ export default {
</script>
<template>
<div>
<div v-if="!isLocalStorageAvailable" ref="localStorageNote" class="dropdown-info-note">
<div
v-if="!isLocalStorageAvailable"
data-testid="local-storage-note"
class="dropdown-info-note"
>
{{ __('This feature requires local storage to be enabled') }}
</div>
<ul v-else-if="hasItems">
<li
v-for="(item, index) in processedItems"
ref="dropdownItem"
:key="`processed-items-${index}`"
data-testid="dropdown-item"
>
<button
type="button"
@ -100,7 +104,7 @@ export default {
<li class="divider"></li>
<li>
<button
ref="clearButton"
data-testid="clear-button"
type="button"
class="filtered-search-history-clear-button"
@click="onRequestClearRecentSearches($event)"
@ -109,7 +113,7 @@ export default {
</button>
</li>
</ul>
<div v-else ref="dropdownNote" class="dropdown-info-note">
<div v-else data-testid="dropdown-note" class="dropdown-info-note">
{{ __("You don't have any recent searches") }}
</div>
</div>

View File

@ -58,7 +58,7 @@ export default {
<template>
<div class="frequent-items-list-container">
<ul ref="frequentItemsList" class="list-unstyled">
<ul data-testid="frequent-items-list" class="list-unstyled">
<li
v-if="isListEmpty"
:class="{ 'section-failure': isFetchFailed }"

View File

@ -79,16 +79,19 @@ export default {
:project-name="itemName"
aria-hidden="true"
/>
<div ref="frequentItemsItemMetadataContainer" class="frequent-items-item-metadata-container">
<div
data-testid="frequent-items-item-metadata-container"
class="frequent-items-item-metadata-container"
>
<div
ref="frequentItemsItemTitle"
v-safe-html="highlightedItemName"
data-testid="frequent-items-item-title"
:title="itemName"
class="frequent-items-item-title"
></div>
<div
v-if="namespace"
ref="frequentItemsItemNamespace"
data-testid="frequent-items-item-namespace"
:title="namespace"
class="frequent-items-item-namespace"
>

View File

@ -65,7 +65,7 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
add_pagination_headers(tokens)
end
::API::Entities::PersonalAccessTokenWithDetails.represent(tokens)
::PersonalAccessTokenSerializer.new.represent(tokens)
end
def add_pagination_headers(relation)

View File

@ -220,7 +220,13 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
def context_commits
# Get commits from repository
# or from cache if already merged
commits = ContextCommitsFinder.new(project, @merge_request, { search: params[:search], limit: params[:limit], offset: params[:offset] }).execute
commits = ContextCommitsFinder.new(project, @merge_request, {
search: params[:search],
author: params[:author],
committed_before: convert_date_to_epoch(params[:committed_before]),
committed_after: convert_date_to_epoch(params[:committed_after]),
limit: params[:limit]
}).execute
render json: CommitEntity.represent(commits, { type: :full, request: merge_request })
end
@ -553,6 +559,11 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
diffs_metadata_project_json_merge_request_path(project, merge_request, 'json', params)
end
def convert_date_to_epoch(date)
Date.strptime(date, "%Y-%m-%d")&.to_time&.to_i if date
rescue Date::Error, TypeError
end
end
Projects::MergeRequestsController.prepend_mod_with('Projects::MergeRequestsController')

View File

@ -5,8 +5,10 @@ class ContextCommitsFinder
@project = project
@merge_request = merge_request
@search = params[:search]
@author = params[:author]
@committed_before = params[:committed_before]
@committed_after = params[:committed_after]
@limit = (params[:limit] || 40).to_i
@offset = (params[:offset] || 0).to_i
end
def execute
@ -16,13 +18,13 @@ class ContextCommitsFinder
private
attr_reader :project, :merge_request, :search, :limit, :offset
attr_reader :project, :merge_request, :search, :author, :committed_before, :committed_after, :limit
def init_collection
if search.present?
search_commits
else
project.repository.commits(merge_request.target_branch, { limit: limit, offset: offset })
project.repository.commits(merge_request.target_branch, { limit: limit })
end
end
@ -41,7 +43,8 @@ class ContextCommitsFinder
commits = [commit_by_sha] if commit_by_sha
end
else
commits = project.repository.find_commits_by_message(search, merge_request.target_branch, nil, 20)
commits = project.repository.list_commits_by(search, merge_request.target_branch,
author: author, before: committed_before, after: committed_after, limit: limit)
end
commits

View File

@ -2,6 +2,8 @@
module Groups
class AcceptingGroupTransfersFinder < Base
include Gitlab::Utils::StrongMemoize
def initialize(current_user, group_to_be_transferred, params = {})
@current_user = current_user
@group_to_be_transferred = group_to_be_transferred
@ -11,7 +13,12 @@ module Groups
def execute
return Group.none unless can_transfer_group?
items = find_groups
items = if Feature.enabled?(:include_groups_from_group_shares_in_group_transfer_locations)
find_all_groups
else
find_groups
end
items = by_search(items)
sort(items)
@ -29,11 +36,30 @@ module Groups
).execute.without_order
end
def exclude_groups
exclude_groups = group_to_be_transferred.self_and_descendants.pluck_primary_key
exclude_groups << group_to_be_transferred.parent_id if group_to_be_transferred.parent_id
def find_all_groups
::Namespace.from_union(
[
find_groups,
groups_originating_from_group_shares_with_owner_access
]
)
end
exclude_groups
def groups_originating_from_group_shares_with_owner_access
GroupGroupLink
.with_owner_access
.groups_accessible_via(
current_user.owned_groups.select(:id)
).id_not_in(exclude_groups)
end
def exclude_groups
strong_memoize(:exclude_groups) do
exclude_groups = group_to_be_transferred.self_and_descendants.pluck_primary_key
exclude_groups << group_to_be_transferred.parent_id if group_to_be_transferred.parent_id
exclude_groups
end
end
def can_transfer_group?

View File

@ -976,6 +976,11 @@ module Ci
object_hierarchy.base_and_ancestors
end
# With multi-project and parent-child pipelines
def self_and_downstreams
object_hierarchy.base_and_descendants
end
# With multi-project and parent-child pipelines
def upstream_and_all_downstreams
object_hierarchy.all_objects
@ -1012,7 +1017,12 @@ module Ci
# Follow the upstream pipeline relationships, regardless of multi-project or
# parent-child, and return the top-level ancestor.
def upstream_root
object_hierarchy.base_and_ancestors(hierarchy_order: :desc).first
@upstream_root ||= object_hierarchy.base_and_ancestors(hierarchy_order: :desc).first
end
# Applies to all parent-child and multi-project pipelines
def complete_hierarchy_count
upstream_root.self_and_downstreams.count
end
def bridge_triggered?

View File

@ -53,7 +53,7 @@ module Ci
find_by(file_type: file_type)
end
def create_or_replace_for_pipeline!(pipeline:, file_type:, file:, size:)
def create_or_replace_for_pipeline!(pipeline:, file_type:, file:, size:, locked: :unknown)
transaction do
pipeline.pipeline_artifacts.find_by_file_type(file_type)&.destroy!
@ -63,7 +63,8 @@ module Ci
size: size,
file: file,
file_format: REPORT_TYPES[file_type],
expire_at: EXPIRATION_DATE.from_now
expire_at: EXPIRATION_DATE.from_now,
locked: locked
)
end
rescue ActiveRecord::ActiveRecordError => err

View File

@ -348,6 +348,12 @@ module Ci
end
end
def owner_project
return unless project_type?
runner_projects.order(:id).first.project
end
def belongs_to_one_project?
runner_projects.count == 1
end

View File

@ -319,11 +319,7 @@ class CommitStatus < Ci::ApplicationRecord
end
def stage_name
if Feature.enabled?(:ci_read_stage_records, project)
ci_stage&.name
else
stage
end
ci_stage&.name
end
private

View File

@ -19,6 +19,10 @@ class GroupGroupLink < ApplicationRecord
where(group_access: [Gitlab::Access::OWNER, Gitlab::Access::MAINTAINER])
end
scope :with_owner_access, -> do
where(group_access: [Gitlab::Access::OWNER])
end
scope :groups_accessible_via, -> (shared_with_group_ids) do
links = where(shared_with_group_id: shared_with_group_ids)
# a group share also gives you access to the descendants of the group being shared,

View File

@ -194,6 +194,18 @@ class Repository
CommitCollection.new(container, commits, ref)
end
def list_commits_by(query, ref, author: nil, before: nil, after: nil, limit: 1000)
return [] unless exists?
return [] unless has_visible_content?
return [] unless query.present? && ref.present?
commits = raw_repository.list_commits_by(
query, ref, author: author, before: before, after: after, limit: limit).map do |c|
commit(c)
end
CommitCollection.new(container, commits, ref)
end
def find_branch(name)
raw_repository.find_branch(name)
end

View File

@ -0,0 +1,8 @@
# frozen_string_literal: true
# rubocop: disable Gitlab/NamespacedClass
class AccessTokenEntityBase < API::Entities::PersonalAccessToken
expose :expired?, as: :expired
expose :expires_soon?, as: :expires_soon
end
# rubocop: enable Gitlab/NamespacedClass

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
# rubocop: disable Gitlab/NamespacedClass
class GroupAccessTokenEntity < API::Entities::PersonalAccessToken
class GroupAccessTokenEntity < AccessTokenEntityBase
include Gitlab::Routing
expose :revoke_path do |token, options|

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
# rubocop: disable Gitlab/NamespacedClass
class ImpersonationAccessTokenEntity < API::Entities::PersonalAccessToken
class ImpersonationAccessTokenEntity < AccessTokenEntityBase
include Gitlab::Routing
expose :revoke_path do |token, _options|

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
# rubocop: disable Gitlab/NamespacedClass
class PersonalAccessTokenEntity < API::Entities::PersonalAccessToken
class PersonalAccessTokenEntity < AccessTokenEntityBase
include Gitlab::Routing
expose :revoke_path do |token, options|

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
# rubocop: disable Gitlab/NamespacedClass
class ProjectAccessTokenEntity < API::Entities::PersonalAccessToken
class ProjectAccessTokenEntity < AccessTokenEntityBase
include Gitlab::Routing
expose :revoke_path do |token, options|

View File

@ -27,12 +27,18 @@ module Ci
end
def pipeline_artifact_params
{
attributes = {
pipeline: pipeline,
file_type: :code_coverage,
file: carrierwave_file,
size: carrierwave_file['tempfile'].size
}
if ::Feature.enabled?(:ci_update_unlocked_pipeline_artifacts, pipeline.project)
attributes[:locked] = pipeline.locked
end
attributes
end
def carrierwave_file

View File

@ -13,22 +13,32 @@ module Ci
return if pipeline.has_codequality_mr_diff_report?
return unless new_errors_introduced?
pipeline.pipeline_artifacts.create!(**artifact_attributes)
end
private
attr_reader :pipeline
def artifact_attributes
file = build_carrierwave_file!
pipeline.pipeline_artifacts.create!(
attributes = {
project_id: pipeline.project_id,
file_type: :code_quality_mr_diff,
file_format: Ci::PipelineArtifact::REPORT_TYPES.fetch(:code_quality_mr_diff),
size: file["tempfile"].size,
file: file,
expire_at: Ci::PipelineArtifact::EXPIRATION_DATE.from_now
)
}
if ::Feature.enabled?(:ci_update_unlocked_pipeline_artifacts, pipeline.project)
attributes[:locked] = pipeline.locked
end
attributes
end
private
attr_reader :pipeline
def merge_requests
strong_memoize(:merge_requests) do
pipeline.merge_requests_as_head_pipeline

View File

@ -0,0 +1,68 @@
# frozen_string_literal: true
module Ci
module Runners
class SetRunnerAssociatedProjectsService
# @param [Ci::Runner] runner: the project runner to assign/unassign projects from
# @param [User] current_user: the user performing the operation
# @param [Array<Integer>] project_ids: the IDs of the associated projects to assign the runner to
def initialize(runner:, current_user:, project_ids:)
@runner = runner
@current_user = current_user
@project_ids = project_ids
end
def execute
unless current_user&.can?(:assign_runner, runner)
return ServiceResponse.error(message: 'user not allowed to assign runner', http_status: :forbidden)
end
set_associated_projects
end
private
def set_associated_projects
new_project_ids = [runner.owner_project.id] + project_ids
response = ServiceResponse.success
runner.transaction do
# rubocop:disable CodeReuse/ActiveRecord
current_project_ids = runner.projects.ids
# rubocop:enable CodeReuse/ActiveRecord
unless associate_new_projects(new_project_ids, current_project_ids)
response = ServiceResponse.error(message: 'failed to assign projects to runner')
raise ActiveRecord::Rollback, response.errors
end
unless disassociate_old_projects(new_project_ids, current_project_ids)
response = ServiceResponse.error(message: 'failed to destroy runner project')
raise ActiveRecord::Rollback, response.errors
end
end
response
end
def associate_new_projects(new_project_ids, current_project_ids)
missing_projects = Project.id_in(new_project_ids - current_project_ids)
missing_projects.each do |project|
return false unless runner.assign_to(project, current_user)
end
true
end
def disassociate_old_projects(new_project_ids, current_project_ids)
Ci::RunnerProject
.destroy_by(project_id: current_project_ids - new_project_ids)
.all?(&:destroyed?)
end
attr_reader :runner, :current_user, :project_ids
end
end
end
Ci::Runners::SetRunnerAssociatedProjectsService.prepend_mod

View File

@ -7,9 +7,12 @@ module Ci
def execute(ci_ref, before_pipeline = nil)
results = {
unlocked_pipelines: 0,
unlocked_job_artifacts: 0
unlocked_job_artifacts: 0,
unlocked_pipeline_artifacts: 0
}
unlock_pipeline_artifacts_enabled = ::Feature.enabled?(:ci_update_unlocked_pipeline_artifacts, ci_ref.project)
if ::Feature.enabled?(:ci_update_unlocked_job_artifacts, ci_ref.project)
loop do
unlocked_pipelines = []
@ -18,6 +21,10 @@ module Ci
::Ci::Pipeline.transaction do
unlocked_pipelines = unlock_pipelines(ci_ref, before_pipeline)
unlocked_job_artifacts = unlock_job_artifacts(unlocked_pipelines)
if unlock_pipeline_artifacts_enabled
results[:unlocked_pipeline_artifacts] += unlock_pipeline_artifacts(unlocked_pipelines)
end
end
break if unlocked_pipelines.empty?
@ -100,6 +107,14 @@ module Ci
)
end
# rubocop:disable CodeReuse/ActiveRecord
def unlock_pipeline_artifacts(pipelines)
return 0 if pipelines.empty?
::Ci::PipelineArtifact.where(pipeline_id: pipelines.rows.flatten).update_all(locked: :unlocked)
end
# rubocop:enable CodeReuse/ActiveRecord
def unlock_pipelines(ci_ref, before_pipeline)
::Ci::Pipeline.connection.exec_query(unlock_pipelines_query(ci_ref, before_pipeline))
end

View File

@ -5,7 +5,6 @@ module Ci
def log_downstream_pipeline_creation(downstream_pipeline)
return unless downstream_pipeline&.persisted?
hierarchy_size = downstream_pipeline.upstream_and_all_downstreams.count
root_pipeline = downstream_pipeline.upstream_root
::Gitlab::AppLogger.info(
@ -14,7 +13,7 @@ module Ci
root_pipeline_id: root_pipeline.id,
downstream_pipeline_id: downstream_pipeline.id,
downstream_pipeline_relationship: downstream_pipeline.parent_pipeline? ? :parent_child : :multi_project,
hierarchy_size: hierarchy_size,
hierarchy_size: downstream_pipeline.complete_hierarchy_count,
root_pipeline_plan: root_pipeline.project.actual_plan_name,
root_pipeline_namespace_path: root_pipeline.project.namespace.full_path,
root_pipeline_project_path: root_pipeline.project.full_path

View File

@ -234,6 +234,7 @@ module MergeRequests
end
# Add comment about pushing new commits to merge requests and send nofitication emails
#
def notify_about_push(merge_request)
return unless @commits.present?

View File

@ -8,16 +8,15 @@
%h1.page-title.gl-font-size-h-display
= s_('TagsPage|New Tag')
%hr
= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "common-note-form tag-form js-quick-submit js-requires-input" do
.form-group.row
= label_tag :tag_name, nil, class: 'col-form-label col-sm-2'
.col-sm-10
.col-sm-12
= label_tag :tag_name, nil
= text_field_tag :tag_name, params[:tag_name], required: true, autofocus: true, class: 'form-control', data: { qa_selector: "tag_name_field" }
.form-group.row
= label_tag :ref, 'Create from', class: 'col-form-label col-sm-2'
.col-sm-10.create-from
.col-sm-12.create-from
= label_tag :ref, 'Create from'
.dropdown
= hidden_field_tag :ref, default_ref
= button_tag type: 'button', title: default_ref, class: 'dropdown-menu-toggle wide js-branch-select monospace', required: true, data: { toggle: 'dropdown', selected: default_ref, field_name: 'ref' } do
@ -27,15 +26,14 @@
.form-text.text-muted
= s_('TagsPage|Existing branch name, tag, or commit SHA')
.form-group.row
= label_tag :message, nil, class: 'col-form-label col-sm-2'
.col-sm-10
.col-sm-12
= label_tag :message, nil
= text_area_tag :message, @message, required: false, class: 'form-control', rows: 5, data: { qa_selector: "tag_message_field" }
.form-text.text-muted
= tag_description_help_text
%hr
.form-group.row
= label_tag :release_description, s_('TagsPage|Release notes'), class: 'col-form-label col-sm-2'
.col-sm-10
.col-sm-12
= label_tag :release_description, s_('TagsPage|Release notes'), class: 'gl-mb-0'
.form-text.mb-3
- link_start = '<a href="%{url}" rel="noopener noreferrer" target="_blank">'.html_safe
- releases_page_path = project_releases_path(@project)
@ -49,7 +47,7 @@
= render layout: 'shared/md_preview', locals: { url: preview_markdown_path(@project), referenced_users: true } do
= render 'shared/zen', attr: :release_description, classes: 'note-textarea', placeholder: s_('TagsPage|Write your release notes or drag files here…'), current_text: @release_description, qa_selector: 'release_notes_field'
= render 'shared/notes/hints'
.form-actions.gl-display-flex
.gl-display-flex
= render Pajamas::ButtonComponent.new(variant: :confirm, button_options: { class: 'gl-mr-3', data: { qa_selector: "create_tag_button" }, type: 'submit' }) do
= s_('TagsPage|Create tag')
= render Pajamas::ButtonComponent.new(href: project_tags_path(@project)) do

View File

@ -1,8 +1,8 @@
---
name: ci_read_stage_records
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94941
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371486
name: ci_update_unlocked_pipeline_artifacts
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97228
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372835
milestone: '15.4'
type: development
group: group::pipeline execution
group: group::pipeline insights
default_enabled: false

View File

@ -0,0 +1,8 @@
---
name: include_groups_from_group_shares_in_group_transfer_locations
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96347
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371961
milestone: '15.4'
type: development
group: group::workspace
default_enabled: false

View File

@ -0,0 +1,33 @@
# frozen_string_literal: true
class ScheduleDisableLegacyOpenSourceLicenseForProjectsLessThanOneMb < Gitlab::Database::Migration[2.0]
MIGRATION = 'DisableLegacyOpenSourceLicenseForProjectsLessThanOneMb'
INTERVAL = 2.minutes
BATCH_SIZE = 4_000
MAX_BATCH_SIZE = 50_000
SUB_BATCH_SIZE = 250
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_main
def up
return unless Gitlab.com?
queue_batched_background_migration(
MIGRATION,
:project_settings,
:project_id,
job_interval: INTERVAL,
batch_size: BATCH_SIZE,
max_batch_size: MAX_BATCH_SIZE,
sub_batch_size: SUB_BATCH_SIZE
)
end
def down
return unless Gitlab.com?
delete_batched_background_migration(MIGRATION, :project_settings, :project_id, [])
end
end

View File

@ -0,0 +1 @@
fc34cdbddc61ee9c23b790101f911d21892cf2ace34e3615b920817374c803f9

View File

@ -147,6 +147,18 @@ Do not use **and so on**. Instead, be more specific. For details, see
Use [**section**](#section) instead of **area**. The only exception is [the Admin Area](#admin-area).
## as
Do not use **as** to mean **because**.
Use:
- Because none of the endpoints return an ID...
Instead of:
- As none of the endpoints return an ID...
## associate
Do not use **associate** when describing adding issues to epics, or users to issues, merge requests,
@ -619,6 +631,10 @@ Instead of:
- Buy a license.
- Purchase a license.
## limitations
Do not use **limitations**. Use **known issues** instead.
## log in, log on
Do not use **log in** or **log on**. Use [sign in](#sign-in) instead. If the user interface has **Log in**, you can use it.

View File

@ -20,6 +20,32 @@ NOTE:
Support for tracking commits cherry-picked from the command line
is tracked [in this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/202215).
## Cherry-pick example
In this example of cherry-picking, a Git repository has two branches: `develop` and `main`.
```mermaid
gitGraph
commit id: "A"
branch develop
commit id:"B"
checkout main
commit id:"C"
checkout develop
commit id:"D"
checkout main
commit id:"E"
cherry-pick id:"B"
commit id:"G"
checkout develop
commit id:"H"
```
The example shows a cherry-pick of commit `B` from the `develop` branch, which is added
after commit `E` in the `main` branch.
Commit `G` is added after the cherry-pick.
## Cherry-pick all changes from a merge request
After a merge request is merged, you can cherry-pick all changes introduced

View File

@ -1,13 +0,0 @@
# frozen_string_literal: true
module API
module Entities
class PersonalAccessTokenWithDetails < Entities::PersonalAccessToken
expose :expired?, as: :expired
expose :expires_soon?, as: :expires_soon
expose :revoke_path do |token|
Gitlab::Routing.url_helpers.revoke_profile_personal_access_token_path(token)
end
end
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# Set `project_settings.legacy_open_source_license_available` to false for projects less than 1 MB
class DisableLegacyOpenSourceLicenseForProjectsLessThanOneMb < ::Gitlab::BackgroundMigration::BatchedMigrationJob
scope_to ->(relation) { relation.where(legacy_open_source_license_available: true) }
def perform
each_sub_batch(operation_name: :disable_legacy_open_source_license_for_projects_less_than_one_mb) do |sub_batch|
updates = { legacy_open_source_license_available: false, updated_at: Time.current }
sub_batch
.joins('INNER JOIN project_statistics ON project_statistics.project_id = project_settings.project_id')
.where('project_statistics.repository_size < ?', 1.megabyte)
.update_all(updates)
end
end
end
end
end

View File

@ -1056,6 +1056,24 @@ module Gitlab
end
end
def list_commits_by(query, ref, author: nil, before: nil, after: nil, limit: 1000)
params = {
author: author,
ignore_case: true,
commit_message_patterns: query,
before: before,
after: after,
reverse: false,
pagination_params: { limit: limit }
}
wrapped_gitaly_errors do
gitaly_commit_client
.list_commits([ref], params)
.map { |c| commit(c) }
end
end
def list_last_commits_for_tree(sha, path, offset: 0, limit: 25, literal_pathspec: false)
wrapped_gitaly_errors do
gitaly_commit_client.list_last_commits_for_tree(sha, path, offset: offset, limit: limit, literal_pathspec: literal_pathspec)

View File

@ -251,14 +251,23 @@ module Gitlab
consume_commits_response(response)
end
def list_commits(revisions, reverse: false, pagination_params: nil)
def list_commits(revisions, params = {})
request = Gitaly::ListCommitsRequest.new(
repository: @gitaly_repo,
revisions: Array.wrap(revisions),
reverse: reverse,
pagination_params: pagination_params
reverse: !!params[:reverse],
ignore_case: params[:ignore_case],
pagination_params: params[:pagination_params]
)
if params[:commit_message_patterns]
request.commit_message_patterns += Array.wrap(params[:commit_message_patterns])
end
request.author = encode_binary(params[:author]) if params[:author]
request.before = GitalyClient.timestamp(params[:before]) if params[:before]
request.after = GitalyClient.timestamp(params[:after]) if params[:after]
response = GitalyClient.call(@repository.storage, :commit_service, :list_commits, request, timeout: GitalyClient.medium_timeout)
consume_commits_response(response)
end

View File

@ -43942,9 +43942,6 @@ msgstr ""
msgid "Vulnerability|Information related to how the vulnerability was discovered and its impact on the system."
msgstr ""
msgid "Vulnerability|Learn more about this vulnerability and the best way to resolve it."
msgstr ""
msgid "Vulnerability|Links"
msgstr ""

View File

@ -38,6 +38,8 @@ function bundle_install_script() {
exit 1;
fi;
echo -e "section_start:`date +%s`:bundle-install[collapsed=true]\r\e[0KInstalling gems"
gem --version
bundle --version
gem install bundler --no-document --conservative --version 2.3.15
@ -48,7 +50,7 @@ function bundle_install_script() {
echo "${BUNDLE_WITHOUT}"
bundle config
run_timed_command "bundle install ${BUNDLE_INSTALL_FLAGS} ${extra_install_args} && bundle check"
run_timed_command "bundle install ${BUNDLE_INSTALL_FLAGS} ${extra_install_args}"
if [[ $(bundle info pg) ]]; then
# When we test multiple versions of PG in the same pipeline, we have a single `setup-test-env`
@ -56,6 +58,8 @@ function bundle_install_script() {
# Uncomment the following line if multiple versions of PG are tested in the same pipeline.
run_timed_command "bundle pristine pg"
fi
echo -e "section_end:`date +%s`:bundle-install\r\e[0K"
}
function setup_db_user_only() {

View File

@ -48,8 +48,8 @@ RSpec.describe Profiles::PersonalAccessTokensController do
end
it "only includes details of the active personal access token" do
active_personal_access_tokens_detail = ::API::Entities::PersonalAccessTokenWithDetails
.represent([active_personal_access_token])
active_personal_access_tokens_detail =
::PersonalAccessTokenSerializer.new.represent([active_personal_access_token])
expect(assigns(:active_personal_access_tokens).to_json).to eq(active_personal_access_tokens_detail.to_json)
end
@ -100,8 +100,8 @@ RSpec.describe Profiles::PersonalAccessTokensController do
get :index
first_token = assigns(:active_personal_access_tokens).first.as_json
expect(first_token[:name]).to eq("Token1")
expect(first_token[:expires_at]).to eq(expires_1_day_from_now.strftime("%Y-%m-%d"))
expect(first_token['name']).to eq("Token1")
expect(first_token['expires_at']).to eq(expires_1_day_from_now.strftime("%Y-%m-%d"))
end
it "orders tokens on id in case token has same expires_at" do
@ -110,12 +110,12 @@ RSpec.describe Profiles::PersonalAccessTokensController do
get :index
first_token = assigns(:active_personal_access_tokens).first.as_json
expect(first_token[:name]).to eq("Token3")
expect(first_token[:expires_at]).to eq(expires_1_day_from_now.strftime("%Y-%m-%d"))
expect(first_token['name']).to eq("Token3")
expect(first_token['expires_at']).to eq(expires_1_day_from_now.strftime("%Y-%m-%d"))
second_token = assigns(:active_personal_access_tokens).second.as_json
expect(second_token[:name]).to eq("Token1")
expect(second_token[:expires_at]).to eq(expires_1_day_from_now.strftime("%Y-%m-%d"))
expect(second_token['name']).to eq("Token1")
expect(second_token['expires_at']).to eq(expires_1_day_from_now.strftime("%Y-%m-%d"))
end
end

View File

@ -60,7 +60,7 @@ RSpec.describe 'Developer creates tag' do
it 'opens dropdown for ref', :js do
click_link 'New tag'
ref_row = find('.form-group:nth-of-type(2) .col-sm-10')
ref_row = find('.form-group:nth-of-type(2) .col-sm-12')
page.within ref_row do
ref_input = find('[name="ref"]', visible: false)
expect(ref_input.value).to eq 'master'

View File

@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe ContextCommitsFinder do
describe "#execute" do
let(:project) { create(:project, :repository) }
let(:merge_request) { create(:merge_request) }
let(:merge_request) { create(:merge_request, source_branch: 'feature', target_branch: 'master') }
let(:commit) { create(:commit, id: '6d394385cf567f80a8fd85055db1ab4c5295806f') }
it 'filters commits by valid sha/commit message' do
@ -24,5 +24,29 @@ RSpec.describe ContextCommitsFinder do
expect(commits).to be_empty
end
it 'returns commits based in author filter' do
params = { search: 'test text', author: 'Job van der Voort' }
commits = described_class.new(project, merge_request, params).execute
expect(commits.length).to eq(1)
expect(commits[0].id).to eq('b83d6e391c22777fca1ed3012fce84f633d7fed0')
end
it 'returns commits based in before filter' do
params = { search: 'test text', committed_before: 1474828200 }
commits = described_class.new(project, merge_request, params).execute
expect(commits.length).to eq(1)
expect(commits[0].id).to eq('498214de67004b1da3d820901307bed2a68a8ef6')
end
it 'returns commits based in after filter' do
params = { search: 'test text', committed_after: 1474828200 }
commits = described_class.new(project, merge_request, params).execute
expect(commits.length).to eq(1)
expect(commits[0].id).to eq('b83d6e391c22777fca1ed3012fce84f633d7fed0')
end
end
end

View File

@ -29,9 +29,27 @@ RSpec.describe Groups::AcceptingGroupTransfersFinder do
end
end
let_it_be(:shared_with_group_where_direct_owner_as_guest) { create(:group) }
let_it_be(:shared_with_group_where_direct_owner_as_owner) { create(:group) }
let_it_be(:subgroup_of_shared_with_group_where_direct_owner_as_owner) do
create(:group, parent: shared_with_group_where_direct_owner_as_owner)
end
let(:params) { {} }
describe '#execute' do
before_all do
create(:group_group_link, :owner,
shared_with_group: group_where_user_has_owner_access,
shared_group: shared_with_group_where_direct_owner_as_owner
)
create(:group_group_link, :guest,
shared_with_group: group_where_user_has_owner_access,
shared_group: shared_with_group_where_direct_owner_as_guest
)
end
let(:group_to_be_transferred) { parent_group }
subject(:result) do
@ -69,6 +87,10 @@ RSpec.describe Groups::AcceptingGroupTransfersFinder do
expect(result).not_to include(group_where_user_has_developer_access)
end
it 'excludes the groups arising from group shares where the user does not have OWNER access' do
expect(result).not_to include(shared_with_group_where_direct_owner_as_guest)
end
it 'includes ancestors, except immediate parent of the group to be transferred' do
expect(result).to include(great_grandparent_group)
end
@ -81,6 +103,13 @@ RSpec.describe Groups::AcceptingGroupTransfersFinder do
expect(result).to include(subgroup_of_group_where_user_has_owner_access)
end
it 'includes the groups where the user has OWNER access through group shares' do
expect(result).to include(
shared_with_group_where_direct_owner_as_owner,
subgroup_of_shared_with_group_where_direct_owner_as_owner
)
end
context 'on searching with a specific term' do
let(:params) { { search: 'great grandparent group' } }
@ -88,6 +117,19 @@ RSpec.describe Groups::AcceptingGroupTransfersFinder do
expect(result).to contain_exactly(great_grandparent_group)
end
end
context 'when the feature flag `include_groups_from_group_shares_in_group_transfer_locations` is turned off' do
before do
stub_feature_flags(include_groups_from_group_shares_in_group_transfer_locations: false)
end
it 'excludes the groups where the user has OWNER access through group shares' do
expect(result).not_to include(
shared_with_group_where_direct_owner_as_owner,
subgroup_of_shared_with_group_where_direct_owner_as_owner
)
end
end
end
end
end

View File

@ -48,9 +48,6 @@ testUtilsConfig.deprecationWarningHandler = (method, message) => {
const ALLOWED_DEPRECATED_METHODS = [
// https://gitlab.com/gitlab-org/gitlab/-/issues/295679
'finding components with `find` or `get`',
// https://gitlab.com/gitlab-org/gitlab/-/issues/295680
'finding components with `findAll`',
];
if (!ALLOWED_DEPRECATED_METHODS.includes(method)) {
global.console.error(message);

View File

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

View File

@ -67,7 +67,7 @@ describe('AlertIntegrationsList', () => {
});
it('renders an an edit and delete button for each integration', () => {
expect(findTableComponent().findAll(GlButton).length).toBe(4);
expect(findTableComponent().findAllComponents(GlButton).length).toBe(4);
});
it('renders an highlighted row when a current integration is selected to edit', () => {

View File

@ -83,7 +83,7 @@ describe('ProjectsDropdownFilter component', () => {
const findDropdownItems = () =>
findDropdown()
.findAll(GlDropdownItem)
.findAllComponents(GlDropdownItem)
.filter((w) => w.text() !== 'No matching results');
const findDropdownAtIndex = (index) => findDropdownItems().at(index);
@ -106,7 +106,7 @@ describe('ProjectsDropdownFilter component', () => {
};
// NOTE: Selected items are now visually separated from unselected items
const findSelectedDropdownItems = () => findHighlightedItems().findAll(GlDropdownItem);
const findSelectedDropdownItems = () => findHighlightedItems().findAllComponents(GlDropdownItem);
const findSelectedDropdownAtIndex = (index) => findSelectedDropdownItems().at(index);
const findSelectedButtonIdentIconAtIndex = (index) =>

View File

@ -27,7 +27,7 @@ describe('UsageTrendsApp', () => {
['Total projects & groups', 'Pipelines', 'Issues & merge requests'].forEach((usage) => {
it(`displays the ${usage} chart`, () => {
const chartTitles = wrapper
.findAll(UsageTrendsCountChart)
.findAllComponents(UsageTrendsCountChart)
.wrappers.map((chartComponent) => chartComponent.props('chartTitle'));
expect(chartTitles).toContain(usage);

View File

@ -35,7 +35,9 @@ describe('RecoveryCodes', () => {
const findRecoveryCodes = () => wrapper.findByTestId('recovery-codes');
const findCopyButton = () => wrapper.findComponent(ClipboardButton);
const findButtonByText = (text) =>
wrapper.findAll(GlButton).wrappers.find((buttonWrapper) => buttonWrapper.text() === text);
wrapper
.findAllComponents(GlButton)
.wrappers.find((buttonWrapper) => buttonWrapper.text() === text);
const findDownloadButton = () => findButtonByText('Download codes');
const findPrintButton = () => findButtonByText('Print codes');
const findProceedButton = () => findButtonByText('Proceed');

View File

@ -35,13 +35,13 @@ describe('Batch comments diff file drafts component', () => {
it('renders list of draft notes', () => {
factory();
expect(vm.findAll(DraftNote).length).toEqual(2);
expect(vm.findAllComponents(DraftNote).length).toEqual(2);
});
it('renders index of draft note', () => {
factory();
const elements = vm.findAll(DesignNotePin);
const elements = vm.findAllComponents(DesignNotePin);
expect(elements.length).toEqual(2);

View File

@ -28,7 +28,7 @@ describe('Batch comments publish dropdown component', () => {
it('renders list of drafts', () => {
createComponent();
expect(wrapper.findAll(GlDropdownItem).length).toBe(2);
expect(wrapper.findAllComponents(GlDropdownItem).length).toBe(2);
});
it('renders draft count in dropdown title', () => {

View File

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

View File

@ -143,7 +143,7 @@ describe('LockPopovers', () => {
});
it('mounts multiple popovers', () => {
const popovers = wrapper.findAll(GlPopover).wrappers;
const popovers = wrapper.findAllComponents(GlPopover).wrappers;
expectCorrectPopoverTarget(popoverMountEl1, popovers[0]);
expectCorrectPopoverTarget(popoverMountEl2, popovers[1]);

View File

@ -65,7 +65,7 @@ describe('Ci variable modal', () => {
const findAddorUpdateButton = () => wrapper.findByTestId('ciUpdateOrAddVariableBtn');
const deleteVariableButton = () =>
findModal()
.findAll(GlButton)
.findAllComponents(GlButton)
.wrappers.find((button) => button.props('variant') === 'danger');
const findProtectedVariableCheckbox = () =>
wrapper.findByTestId('ci-variable-protected-checkbox');

View File

@ -45,7 +45,7 @@ describe('Ci variable modal', () => {
const findAddorUpdateButton = () => findModal().find('[data-testid="ciUpdateOrAddVariableBtn"]');
const deleteVariableButton = () =>
findModal()
.findAll(GlButton)
.findAllComponents(GlButton)
.wrappers.find((button) => button.props('variant') === 'danger');
afterEach(() => {

View File

@ -65,7 +65,7 @@ describe('InstallAgentModal', () => {
const findAgentInstructions = () => findModal().findComponent(AgentToken);
const findButtonByVariant = (variant) =>
findModal()
.findAll(GlButton)
.findAllComponents(GlButton)
.wrappers.find((button) => button.props('variant') === variant);
const findActionButton = () => findButtonByVariant('confirm');
const findCancelButton = () => findButtonByVariant('default');

View File

@ -30,7 +30,7 @@ describe('Confidential merge request project dropdown component', () => {
},
]);
expect(vm.findAll(GlDropdownItem).length).toBe(2);
expect(vm.findAllComponents(GlDropdownItem).length).toBe(2);
});
it('shows lock icon', () => {

View File

@ -30,8 +30,8 @@ describe('Deploy freeze timezone dropdown', () => {
wrapper.setData({ searchTerm });
};
const findAllDropdownItems = () => wrapper.findAll(GlDropdownItem);
const findDropdownItemByIndex = (index) => wrapper.findAll(GlDropdownItem).at(index);
const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
const findDropdownItemByIndex = (index) => wrapper.findAllComponents(GlDropdownItem).at(index);
afterEach(() => {
wrapper.destroy();

View File

@ -274,7 +274,7 @@ describe('diffs/components/app', () => {
});
expect(wrapper.findComponent(NoChanges).exists()).toBe(false);
expect(wrapper.findAll(DiffFile).length).toBe(1);
expect(wrapper.findAllComponents(DiffFile).length).toBe(1);
});
});
@ -636,7 +636,7 @@ describe('diffs/components/app', () => {
await nextTick();
expect(wrapper.findAll(DiffFile).length).toBe(1);
expect(wrapper.findAllComponents(DiffFile).length).toBe(1);
});
describe('pagination', () => {

View File

@ -34,7 +34,7 @@ describe('CompareDropdownLayout', () => {
findListItems().wrappers.map((listItem) => ({
href: listItem.find('a').attributes('href'),
text: trimText(listItem.text()),
createdAt: listItem.findAll(TimeAgo).wrappers[0]?.props('time'),
createdAt: listItem.findAllComponents(TimeAgo).wrappers[0]?.props('time'),
isActive: listItem.classes().includes('is-active'),
}));

View File

@ -34,9 +34,9 @@ describe('DiffDiscussions', () => {
expect(wrapper.findComponent(NoteableDiscussion).exists()).toBe(true);
expect(wrapper.findComponent(DiscussionNotes).exists()).toBe(true);
expect(wrapper.findComponent(DiscussionNotes).findAll(TimelineEntryItem).length).toBe(
discussionsMockData.notes.length,
);
expect(
wrapper.findComponent(DiscussionNotes).findAllComponents(TimelineEntryItem).length,
).toBe(discussionsMockData.notes.length);
});
});

View File

@ -87,7 +87,7 @@ describe('diff_stats', () => {
describe('files changes', () => {
const findIcon = (name) =>
wrapper
.findAll(GlIcon)
.findAllComponents(GlIcon)
.filter((c) => c.attributes('name') === name)
.at(0).element.parentNode;

View File

@ -88,7 +88,7 @@ describe('DiffView', () => {
diffLines: [{ renderCommentRow: true, ...sides }],
inline: type === 'inline',
});
expect(wrapper.findAll(DiffCommentCell).length).toBe(total);
expect(wrapper.findAllComponents(DiffCommentCell).length).toBe(total);
expect(wrapper.find(container).findComponent(DiffCommentCell).exists()).toBe(true);
},
);

View File

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

View File

@ -76,7 +76,7 @@ describe('error tracking settings project dropdown', () => {
it('contains a number of dropdown items', () => {
expect(wrapper.findComponent(GlDropdownItem).exists()).toBe(true);
expect(wrapper.findAll(GlDropdownItem).length).toBe(2);
expect(wrapper.findAllComponents(GlDropdownItem).length).toBe(2);
});
});

View File

@ -1,4 +1,4 @@
import { shallowMount } from '@vue/test-utils';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import RecentSearchesDropdownContent from '~/filtered_search/components/recent_searches_dropdown_content.vue';
import eventHub from '~/filtered_search/event_hub';
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
@ -6,12 +6,12 @@ import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered
describe('Recent Searches Dropdown Content', () => {
let wrapper;
const findLocalStorageNote = () => wrapper.findComponent({ ref: 'localStorageNote' });
const findDropdownItems = () => wrapper.findAll({ ref: 'dropdownItem' });
const findDropdownNote = () => wrapper.findComponent({ ref: 'dropdownNote' });
const findLocalStorageNote = () => wrapper.findByTestId('local-storage-note');
const findDropdownItems = () => wrapper.findAllByTestId('dropdown-item');
const findDropdownNote = () => wrapper.findByTestId('dropdown-note');
const createComponent = (props) => {
wrapper = shallowMount(RecentSearchesDropdownContent, {
wrapper = shallowMountExtended(RecentSearchesDropdownContent, {
propsData: {
allowedKeys: IssuableFilteredSearchTokenKeys.getKeys(),
items: [],
@ -94,7 +94,7 @@ describe('Recent Searches Dropdown Content', () => {
});
it('emits requestClearRecentSearches on Clear resent searches button', () => {
wrapper.findComponent({ ref: 'clearButton' }).trigger('click');
wrapper.findByTestId('clear-button').trigger('click');
expect(onRequestClearRecentSearchesSpy).toHaveBeenCalled();
});

View File

@ -1,7 +1,7 @@
import { GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { trimText } from 'helpers/text_helper';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import frequentItemsListItemComponent from '~/frequent_items/components/frequent_items_list_item.vue';
@ -16,18 +16,18 @@ describe('FrequentItemsListItemComponent', () => {
let trackingSpy;
let store;
const findTitle = () => wrapper.findComponent({ ref: 'frequentItemsItemTitle' });
const findTitle = () => wrapper.findByTestId('frequent-items-item-title');
const findAvatar = () => wrapper.findComponent(ProjectAvatar);
const findAllTitles = () => wrapper.findAll({ ref: 'frequentItemsItemTitle' });
const findNamespace = () => wrapper.findComponent({ ref: 'frequentItemsItemNamespace' });
const findAllTitles = () => wrapper.findAllByTestId('frequent-items-item-title');
const findNamespace = () => wrapper.findByTestId('frequent-items-item-namespace');
const findAllButtons = () => wrapper.findAllComponents(GlButton);
const findAllNamespace = () => wrapper.findAll({ ref: 'frequentItemsItemNamespace' });
const findAllNamespace = () => wrapper.findAllByTestId('frequent-items-item-namespace');
const findAllAvatars = () => wrapper.findAllComponents(ProjectAvatar);
const findAllMetadataContainers = () =>
wrapper.findAll({ ref: 'frequentItemsItemMetadataContainer' });
wrapper.findAllByTestId('frequent-items-item-metadata-container');
const createComponent = (props = {}) => {
wrapper = shallowMount(frequentItemsListItemComponent, {
wrapper = shallowMountExtended(frequentItemsListItemComponent, {
store,
propsData: {
itemId: mockProject.id,

View File

@ -1,6 +1,6 @@
import { mount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import frequentItemsListComponent from '~/frequent_items/components/frequent_items_list.vue';
import frequentItemsListItemComponent from '~/frequent_items/components/frequent_items_list_item.vue';
import { createStore } from '~/frequent_items/store';
@ -12,7 +12,7 @@ describe('FrequentItemsListComponent', () => {
let wrapper;
const createComponent = (props = {}) => {
wrapper = mount(frequentItemsListComponent, {
wrapper = mountExtended(frequentItemsListComponent, {
store: createStore(),
propsData: {
namespace: 'projects',
@ -94,8 +94,8 @@ describe('FrequentItemsListComponent', () => {
await nextTick();
expect(wrapper.classes('frequent-items-list-container')).toBe(true);
expect(wrapper.findAll({ ref: 'frequentItemsList' })).toHaveLength(1);
expect(wrapper.findAll(frequentItemsListItemComponent)).toHaveLength(5);
expect(wrapper.findAllByTestId('frequent-items-list')).toHaveLength(1);
expect(wrapper.findAllComponents(frequentItemsListItemComponent)).toHaveLength(5);
});
it('should render component element with empty message', async () => {
@ -105,7 +105,7 @@ describe('FrequentItemsListComponent', () => {
await nextTick();
expect(wrapper.vm.$el.querySelectorAll('li.section-empty')).toHaveLength(1);
expect(wrapper.findAll(frequentItemsListItemComponent)).toHaveLength(0);
expect(wrapper.findAllComponents(frequentItemsListItemComponent)).toHaveLength(0);
});
});
});

View File

@ -30,7 +30,7 @@ describe('ImportProjectsTable', () => {
const findImportAllButton = () =>
wrapper
.findAll(GlButton)
.findAllComponents(GlButton)
.filter((w) => w.props().variant === 'confirm')
.at(0);
const findImportAllModal = () => wrapper.findComponent({ ref: 'importAllModal' });
@ -118,7 +118,7 @@ describe('ImportProjectsTable', () => {
.exists(),
).toBe(true);
expect(wrapper.findAll(ProviderRepoTableRow)).toHaveLength(repositories.length);
expect(wrapper.findAllComponents(ProviderRepoTableRow)).toHaveLength(repositories.length);
});
it.each`

View File

@ -44,12 +44,12 @@ describe('Incidents List', () => {
const findTableRows = () => wrapper.findAll('table tbody tr');
const findAlert = () => wrapper.findComponent(GlAlert);
const findLoader = () => wrapper.findComponent(GlLoadingIcon);
const findTimeAgo = () => wrapper.findAll(TimeAgoTooltip);
const findTimeAgo = () => wrapper.findAllComponents(TimeAgoTooltip);
const findAssignees = () => wrapper.findAll('[data-testid="incident-assignees"]');
const findCreateIncidentBtn = () => wrapper.find('[data-testid="createIncidentBtn"]');
const findClosedIcon = () => wrapper.findAll("[data-testid='incident-closed']");
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
const findSeverity = () => wrapper.findAll(SeverityToken);
const findSeverity = () => wrapper.findAllComponents(SeverityToken);
const findEscalationStatus = () => wrapper.findAll('[data-testid="incident-escalation-status"]');
const findIncidentLink = () => wrapper.findByTestId('incident-link');

View File

@ -23,7 +23,7 @@ describe('IncidentsSettingTabs', () => {
const findToggleButton = () => wrapper.findComponent({ ref: 'toggleBtn' });
const findSectionHeader = () => wrapper.findComponent({ ref: 'sectionHeader' });
const findIntegrationTabs = () => wrapper.findAll(GlTab);
const findIntegrationTabs = () => wrapper.findAllComponents(GlTab);
it('renders header text', () => {
expect(findSectionHeader().text()).toBe('Incidents');
});

View File

@ -24,7 +24,7 @@ describe('TriggerFields', () => {
});
const findTriggerLabel = () => wrapper.findByTestId('trigger-fields-group').find('label');
const findAllGlFormGroups = () => wrapper.find('#trigger-fields').findAll(GlFormGroup);
const findAllGlFormGroups = () => wrapper.find('#trigger-fields').findAllComponents(GlFormGroup);
const findAllGlFormCheckboxes = () => wrapper.findAllComponents(GlFormCheckbox);
const findAllGlFormInputs = () => wrapper.findAllComponents(GlFormInput);

View File

@ -27,7 +27,7 @@ describe('IssueAssigneesComponent', () => {
});
const findTooltipText = () => wrapper.find('.js-assignee-tooltip').text();
const findAvatars = () => wrapper.findAll(UserAvatarLink);
const findAvatars = () => wrapper.findAllComponents(UserAvatarLink);
const findOverflowCounter = () => wrapper.find('.avatar-counter');
it('returns default data props', () => {

View File

@ -20,7 +20,7 @@ describe('Issue title suggestions item component', () => {
}
const findLink = () => wrapper.findComponent(GlLink);
const findAuthorLink = () => wrapper.findAll(GlLink).at(1);
const findAuthorLink = () => wrapper.findAllComponents(GlLink).at(1);
const findIcon = () => wrapper.findComponent(GlIcon);
const findTooltip = () => wrapper.findComponent(GlTooltip);
const findUserAvatar = () => wrapper.findComponent(UserAvatarImage);

View File

@ -83,7 +83,7 @@ describe('Issue title suggestions component', () => {
wrapper.setData(data);
await nextTick();
expect(wrapper.findAll(TitleSuggestionsItem).length).toBe(2);
expect(wrapper.findAllComponents(TitleSuggestionsItem).length).toBe(2);
});
it('adds margin class to first item', async () => {

View File

@ -65,9 +65,9 @@ describe('RelatedMergeRequests', () => {
describe('template', () => {
it('should render related merge request items', () => {
expect(wrapper.find('[data-testid="count"]').text()).toBe('2');
expect(wrapper.findAll(RelatedIssuableItem)).toHaveLength(2);
expect(wrapper.findAllComponents(RelatedIssuableItem)).toHaveLength(2);
const props = wrapper.findAll(RelatedIssuableItem).at(1).props();
const props = wrapper.findAllComponents(RelatedIssuableItem).at(1).props();
const data = mockData[1];
expect(props.idKey).toEqual(data.id);

View File

@ -70,12 +70,12 @@ describe('HeaderActions component', () => {
const findDropdownBy = (dataTestId) => wrapper.find(`[data-testid="${dataTestId}"]`);
const findMobileDropdown = () => findDropdownBy('mobile-dropdown');
const findDesktopDropdown = () => findDropdownBy('desktop-dropdown');
const findMobileDropdownItems = () => findMobileDropdown().findAll(GlDropdownItem);
const findDesktopDropdownItems = () => findDesktopDropdown().findAll(GlDropdownItem);
const findMobileDropdownItems = () => findMobileDropdown().findAllComponents(GlDropdownItem);
const findDesktopDropdownItems = () => findDesktopDropdown().findAllComponents(GlDropdownItem);
const findModal = () => wrapper.findComponent(GlModal);
const findModalLinkAt = (index) => findModal().findAll(GlLink).at(index);
const findModalLinkAt = (index) => findModal().findAllComponents(GlLink).at(index);
const mountComponent = ({
props = {},

View File

@ -61,7 +61,7 @@ describe('Incident Tabs component', () => {
);
};
const findTabs = () => wrapper.findAll(GlTab);
const findTabs = () => wrapper.findAllComponents(GlTab);
const findSummaryTab = () => findTabs().at(0);
const findAlertDetailsTab = () => wrapper.find('[data-testid="alert-details-tab"]');
const findAlertDetailsComponent = () => wrapper.findComponent(AlertDetailsTable);

View File

@ -59,7 +59,7 @@ describe('IncidentTimelineEventList', () => {
};
const findTimelineEventGroups = () => wrapper.findAllByTestId('timeline-group');
const findItems = (base = wrapper) => base.findAll(IncidentTimelineEventItem);
const findItems = (base = wrapper) => base.findAllComponents(IncidentTimelineEventItem);
const findFirstTimelineEventGroup = () => findTimelineEventGroups().at(0);
const findSecondTimelineEventGroup = () => findTimelineEventGroups().at(1);
const findDates = () => wrapper.findAllByTestId('event-date');

View File

@ -9,7 +9,7 @@ const plainStatusUrl = 'https://status.com';
describe('PinnedLinks', () => {
let wrapper;
const findButtons = () => wrapper.findAll(GlButton);
const findButtons = () => wrapper.findAllComponents(GlButton);
const createComponent = (props) => {
wrapper = shallowMount(PinnedLinks, {

View File

@ -50,7 +50,7 @@ describe('GroupsList', () => {
const findGlAlert = () => wrapper.findComponent(GlAlert);
const findGlLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findAllItems = () => wrapper.findAll(GroupsListItem);
const findAllItems = () => wrapper.findAllComponents(GroupsListItem);
const findFirstItem = () => findAllItems().at(0);
const findSecondItem = () => findAllItems().at(1);
const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);

View File

@ -288,7 +288,7 @@ describe('JiraImportForm', () => {
});
it('updates the user list', () => {
expect(getUserDropdown().findAll(GlDropdownItem)).toHaveLength(1);
expect(getUserDropdown().findAllComponents(GlDropdownItem)).toHaveLength(1);
expect(getUserDropdown().findComponent(GlDropdownItem).text()).toContain(
'fchopin (Frederic Chopin)',
);

View File

@ -103,12 +103,12 @@ describe('Milestone combobox component', () => {
const findProjectMilestonesSection = () =>
wrapper.find('[data-testid="project-milestones-section"]');
const findProjectMilestonesDropdownItems = () =>
findProjectMilestonesSection().findAll(GlDropdownItem);
findProjectMilestonesSection().findAllComponents(GlDropdownItem);
const findFirstProjectMilestonesDropdownItem = () => findProjectMilestonesDropdownItems().at(0);
const findGroupMilestonesSection = () => wrapper.find('[data-testid="group-milestones-section"]');
const findGroupMilestonesDropdownItems = () =>
findGroupMilestonesSection().findAll(GlDropdownItem);
findGroupMilestonesSection().findAllComponents(GlDropdownItem);
const findFirstGroupMilestonesDropdownItem = () => findGroupMilestonesDropdownItems().at(0);
//

View File

@ -57,7 +57,7 @@ describe('CustomNotificationsModal', () => {
}
const findModalBodyDescription = () => wrapper.findComponent(GlSprintf);
const findAllCheckboxes = () => wrapper.findAll(GlFormCheckbox);
const findAllCheckboxes = () => wrapper.findAllComponents(GlFormCheckbox);
const findCheckboxAt = (index) => findAllCheckboxes().at(index);
beforeEach(() => {

View File

@ -42,7 +42,8 @@ describe('NotificationsDropdown', () => {
const findDropdown = () => wrapper.findComponent(GlDropdown);
const findByTestId = (testId) => wrapper.find(`[data-testid="${testId}"]`);
const findAllNotificationsDropdownItems = () => wrapper.findAll(NotificationsDropdownItem);
const findAllNotificationsDropdownItems = () =>
wrapper.findAllComponents(NotificationsDropdownItem);
const findDropdownItemAt = (index) =>
findAllNotificationsDropdownItems().at(index).findComponent(GlDropdownItem);
const findNotificationsModal = () => wrapper.findComponent(CustomNotificationsModal);

View File

@ -32,7 +32,7 @@ describe('tags list row', () => {
const findShortRevision = () => wrapper.find('[data-testid="digest"]');
const findClipboardButton = () => wrapper.findComponent(ClipboardButton);
const findTimeAgoTooltip = () => wrapper.findComponent(TimeAgoTooltip);
const findDetailsRows = () => wrapper.findAll(DetailsRow);
const findDetailsRows = () => wrapper.findAllComponents(DetailsRow);
const findPublishedDateDetail = () => wrapper.find('[data-testid="published-date-detail"]');
const findManifestDetail = () => wrapper.find('[data-testid="manifest-detail"]');
const findConfigurationDetail = () => wrapper.find('[data-testid="configuration-detail"]');

View File

@ -5,7 +5,7 @@ import { GlSkeletonLoader } from '../../stubs';
describe('TagsLoader component', () => {
let wrapper;
const findGlSkeletonLoaders = () => wrapper.findAll(GlSkeletonLoader);
const findGlSkeletonLoaders = () => wrapper.findAllComponents(GlSkeletonLoader);
const mountComponent = () => {
wrapper = shallowMount(component, {

View File

@ -8,7 +8,7 @@ import { imagesListResponse, pageInfo as defaultPageInfo } from '../../mock_data
describe('Image List', () => {
let wrapper;
const findRow = () => wrapper.findAll(ImageListRow);
const findRow = () => wrapper.findAllComponents(ImageListRow);
const findPagination = () => wrapper.findComponent(GlKeysetPagination);
const mountComponent = (props) => {

View File

@ -7,7 +7,7 @@ import { harborImagesList } from '../../mock_data';
describe('Harbor List', () => {
let wrapper;
const findHarborListRow = () => wrapper.findAll(HarborListRow);
const findHarborListRow = () => wrapper.findAllComponents(HarborListRow);
const mountComponent = (props) => {
wrapper = shallowMount(HarborList, {

View File

@ -7,7 +7,7 @@ describe('packages_filter', () => {
let wrapper;
const findFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken);
const findFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
const findFilteredSearchSuggestions = () => wrapper.findAllComponents(GlFilteredSearchSuggestion);
const mountComponent = ({ attrs, listeners } = {}) => {
wrapper = shallowMount(component, {

View File

@ -23,7 +23,7 @@ describe('cli_commands', () => {
let wrapper;
const findDropdownButton = () => wrapper.findComponent(GlDropdown);
const findCodeInstruction = () => wrapper.findAll(CodeInstruction);
const findCodeInstruction = () => wrapper.findAllComponents(CodeInstruction);
const mountComponent = () => {
wrapper = mount(QuickstartDropdown, {

View File

@ -14,7 +14,7 @@ describe('BitbucketServerStatusTable', () => {
const findReconfigureButton = () =>
wrapper
.findAll(GlButton)
.findAllComponents(GlButton)
.filter((w) => w.props().variant === 'info')
.at(0);

View File

@ -200,7 +200,7 @@ describe('ForkForm component', () => {
it('displays the correct description', () => {
createComponent();
const formRadios = wrapper.findAll(GlFormRadio);
const formRadios = wrapper.findAllComponents(GlFormRadio);
Object.keys(PROJECT_VISIBILITY_TYPE).forEach((visibilityType, index) => {
expect(formRadios.at(index).text()).toBe(PROJECT_VISIBILITY_TYPE[visibilityType]);
@ -210,7 +210,7 @@ describe('ForkForm component', () => {
it('displays all 3 visibility levels', () => {
createComponent();
expect(wrapper.findAll(GlFormRadio)).toHaveLength(3);
expect(wrapper.findAllComponents(GlFormRadio)).toHaveLength(3);
});
describe('when the namespace is changed', () => {

View File

@ -22,7 +22,7 @@ describe('Code Coverage', () => {
const findAlert = () => wrapper.findComponent(GlAlert);
const findAreaChart = () => wrapper.findComponent(GlAreaChart);
const findAllDropdownItems = () => wrapper.findAll(GlDropdownItem);
const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
const findFirstDropdownItem = () => findAllDropdownItems().at(0);
const findSecondDropdownItem = () => findAllDropdownItems().at(1);
const findDownloadButton = () => wrapper.find('[data-testid="download-button"]');

View File

@ -119,7 +119,7 @@ describe('Pipeline editor branch switcher', () => {
};
const findDropdown = () => wrapper.findComponent(GlDropdown);
const findDropdownItems = () => wrapper.findAll(GlDropdownItem);
const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
const findInfiniteScroll = () => wrapper.findComponent(GlInfiniteScroll);

View File

@ -31,7 +31,7 @@ describe('Pipeline editor file nav', () => {
const findTip = () => wrapper.findComponent(GlAlert);
const findCurrentConfigFilename = () => wrapper.findByTestId('current-config-filename');
const fileTreeItems = () => wrapper.findAll(PipelineEditorFileTreeItem);
const fileTreeItems = () => wrapper.findAllComponents(PipelineEditorFileTreeItem);
afterEach(() => {
localStorage.clear();

View File

@ -58,7 +58,7 @@ describe('~/pipeline_editor/components/ui/editor_tab.vue', () => {
const findSlotComponent = () => wrapper.findComponent(MockSourceEditor);
const findAlert = () => wrapper.findComponent(GlAlert);
const findBadges = () => wrapper.findAll(GlBadge);
const findBadges = () => wrapper.findAllComponents(GlBadge);
beforeEach(() => {
mockChildMounted = jest.fn();

View File

@ -20,7 +20,7 @@ describe('Pipeline New Form', () => {
let mock;
const findDropdown = () => wrapper.findComponent(GlDropdown);
const findRefsDropdownItems = () => wrapper.findAll(GlDropdownItem);
const findRefsDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
const createComponent = (props = {}, mountFn = shallowMount) => {

View File

@ -19,7 +19,7 @@ import {
describe('Pipeline DAG graph wrapper', () => {
let wrapper;
const getAlert = () => wrapper.findComponent(GlAlert);
const getAllAlerts = () => wrapper.findAll(GlAlert);
const getAllAlerts = () => wrapper.findAllComponents(GlAlert);
const getGraph = () => wrapper.findComponent(DagGraph);
const getNotes = () => wrapper.findComponent(DagAnnotations);
const getErrorText = (type) => wrapper.vm.$options.errorTexts[type];

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