diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-13 12:12:50 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-13 12:12:50 +0000 |
commit | 37a739daec0d7021b2af6ad03c60d37ac3461d88 (patch) | |
tree | e9621e1d5b8c59a2f2768deb3f153e1e5fb01437 | |
parent | bfd344aeac677543c2a2f623fd103d4cf0f4e247 (diff) | |
download | gitlab-ce-37a739daec0d7021b2af6ad03c60d37ac3461d88.tar.gz |
Add latest changes from gitlab-org/gitlab@master
120 files changed, 917 insertions, 631 deletions
diff --git a/.gitlab/ci/package-and-test/main.gitlab-ci.yml b/.gitlab/ci/package-and-test/main.gitlab-ci.yml index cdf3f270487..76b16145129 100644 --- a/.gitlab/ci/package-and-test/main.gitlab-ci.yml +++ b/.gitlab/ci/package-and-test/main.gitlab-ci.yml @@ -261,6 +261,66 @@ ee:decomposition-multiple-db-parallel: - !reference [.rules:test:qa-parallel, rules] - if: $QA_SUITES =~ /Test::Instance::All/ +ee:object-storage: + extends: .qa + variables: + QA_SCENARIO: Test::Instance::Image + QA_RSPEC_TAGS: --tag object_storage + GITLAB_QA_OPTS: --omnibus-config object_storage + rules: + - !reference [.rules:test:qa-non-parallel, rules] + - if: $QA_SUITES =~ /Test::Instance::ObjectStorage/ +ee:object-storage-parallel: + extends: ee:object-storage + parallel: 2 + rules: + - !reference [.rules:test:qa-parallel, rules] + - if: $QA_SUITES =~ /Test::Instance::ObjectStorage/ + +ee:object-storage-aws: + extends: ee:object-storage + variables: + AWS_S3_ACCESS_KEY: $QA_AWS_S3_ACCESS_KEY + AWS_S3_BUCKET_NAME: $QA_AWS_S3_BUCKET_NAME + AWS_S3_KEY_ID: $QA_AWS_S3_KEY_ID + AWS_S3_REGION: $QA_AWS_S3_REGION + GITLAB_QA_OPTS: --omnibus-config object_storage_aws +ee:object-storage-aws-parallel: + extends: ee:object-storage-aws + parallel: 2 + rules: + - !reference [ee:object-storage-parallel, rules] + +ee:object-storage-gcs: + extends: ee:object-storage + variables: + GCS_BUCKET_NAME: $QA_GCS_BUCKET_NAME + GOOGLE_PROJECT: $QA_GOOGLE_PROJECT + GOOGLE_JSON_KEY: $QA_GOOGLE_JSON_KEY + GOOGLE_CLIENT_EMAIL: $QA_GOOGLE_CLIENT_EMAIL + GITLAB_QA_OPTS: --omnibus-config object_storage_gcs +ee:object-storage-gcs-parallel: + extends: ee:object-storage-gcs + parallel: 2 + rules: + - !reference [ee:object-storage-parallel, rules] + +ee:packages: + extends: .qa + variables: + QA_SCENARIO: Test::Instance::Image + QA_RSPEC_TAGS: --tag packages + GITLAB_QA_OPTS: --omnibus-config packages + rules: + - !reference [.rules:test:qa-non-parallel, rules] + - if: $QA_SUITES =~ /Test::Instance::Packages/ +ee:packages-parallel: + extends: ee:packages + parallel: 2 + rules: + - !reference [.rules:test:qa-parallel, rules] + - if: $QA_SUITES =~ /Test::Instance::Packages/ + # ------------------------------------------ # Non parallel jobs # ------------------------------------------ @@ -370,9 +430,6 @@ ee:registry: ee:registry-with-cdn: extends: .qa - before_script: - - unset GITLAB_QA_ADMIN_ACCESS_TOKEN - - !reference [.gitlab-qa-install, before_script] variables: QA_SCENARIO: Test::Integration::RegistryWithCDN GCS_CDN_BUCKET_NAME: $QA_GCS_CDN_BUCKET_NAME @@ -380,6 +437,9 @@ ee:registry-with-cdn: GOOGLE_CDN_JSON_KEY: $QA_GOOGLE_CDN_JSON_KEY GOOGLE_CDN_SIGNURL_KEY: $QA_GOOGLE_CDN_SIGNURL_KEY GOOGLE_CDN_SIGNURL_KEY_NAME: $QA_GOOGLE_CDN_SIGNURL_KEY_NAME + before_script: + - unset GITLAB_QA_ADMIN_ACCESS_TOKEN + - !reference [.qa, before_script] rules: - !reference [.rules:test:qa, rules] - if: $QA_SUITES =~ /Test::Integration::RegistryWithCDN/ @@ -434,55 +494,17 @@ ee:metrics: - !reference [.rules:test:qa, rules] - if: $QA_SUITES =~ /Test::Instance::Metrics/ -ee:packages: - extends: .qa - variables: - QA_SCENARIO: Test::Instance::Image - QA_RSPEC_TAGS: --tag packages - GITLAB_QA_OPTS: --omnibus-config packages - rules: - - !reference [.rules:test:qa, rules] - - if: $QA_SUITES =~ /Test::Instance::Packages/ - ee:elasticsearch: extends: .qa variables: QA_SCENARIO: "Test::Integration::Elasticsearch" - script: + before_script: - unset ELASTIC_URL # unset url which is globally defined in .gitlab-ci.yml - - !reference [.qa, script] + - !reference [.qa, before_script] rules: - !reference [.rules:test:qa, rules] - if: $QA_SUITES =~ /Test::Integration::Elasticsearch/ -ee:object-storage: - extends: .qa - variables: - QA_SCENARIO: Test::Instance::Image - QA_RSPEC_TAGS: --tag object_storage - GITLAB_QA_OPTS: --omnibus-config object_storage - rules: - - !reference [.rules:test:qa, rules] - - if: $QA_SUITES =~ /Test::Instance::ObjectStorage/ - -ee:object-storage-aws: - extends: ee:object-storage - variables: - AWS_S3_ACCESS_KEY: $QA_AWS_S3_ACCESS_KEY - AWS_S3_BUCKET_NAME: $QA_AWS_S3_BUCKET_NAME - AWS_S3_KEY_ID: $QA_AWS_S3_KEY_ID - AWS_S3_REGION: $QA_AWS_S3_REGION - GITLAB_QA_OPTS: --omnibus-config object_storage_aws - -ee:object-storage-gcs: - extends: ee:object-storage - variables: - GCS_BUCKET_NAME: $QA_GCS_BUCKET_NAME - GOOGLE_PROJECT: $QA_GOOGLE_PROJECT - GOOGLE_JSON_KEY: $QA_GOOGLE_JSON_KEY - GOOGLE_CLIENT_EMAIL: $QA_GOOGLE_CLIENT_EMAIL - GITLAB_QA_OPTS: --omnibus-config object_storage_gcs - ee:registry-object-storage-tls: extends: ee:object-storage-aws variables: diff --git a/.rubocop_todo/layout/first_array_element_indentation.yml b/.rubocop_todo/layout/first_array_element_indentation.yml index 0b435dbd34a..177ec291b64 100644 --- a/.rubocop_todo/layout/first_array_element_indentation.yml +++ b/.rubocop_todo/layout/first_array_element_indentation.yml @@ -4,26 +4,6 @@ Layout/FirstArrayElementIndentation: Exclude: - 'ee/lib/ee/api/helpers/award_emoji.rb' - 'ee/spec/graphql/mutations/incident_management/escalation_policy/create_spec.rb' - - 'ee/spec/graphql/resolvers/dora_metrics_resolver_spec.rb' - - 'ee/spec/graphql/resolvers/security_orchestration/scan_execution_policy_resolver_spec.rb' - - 'ee/spec/graphql/resolvers/timebox_report_resolver_spec.rb' - - 'ee/spec/graphql/types/ci/pipeline_type_spec.rb' - - 'ee/spec/graphql/types/dast_scanner_profile_type_spec.rb' - - 'ee/spec/graphql/types/dast_site_profile_type_spec.rb' - - 'ee/spec/helpers/paid_feature_callout_helper_spec.rb' - - 'ee/spec/helpers/trial_status_widget_helper_spec.rb' - - 'ee/spec/lib/ee/gitlab/auth/ldap/access_levels_spec.rb' - - 'ee/spec/lib/ee/gitlab/auth/ldap/sync/group_spec.rb' - - 'ee/spec/lib/ee/gitlab/usage_data_spec.rb' - - 'ee/spec/lib/gitlab/checks/diff_check_spec.rb' - - 'ee/spec/lib/gitlab/ci/config/security_orchestration_policies/processor_spec.rb' - - 'ee/spec/lib/gitlab/ci/templates/Jobs/browser_performance_testing_gitlab_ci_yaml_spec.rb' - - 'ee/spec/lib/gitlab/ci/templates/Jobs/dast_default_branch_gitlab_ci_yaml_spec.rb' - - 'ee/spec/lib/gitlab/ci/templates/Jobs/load_performance_testing_gitlab_ci_yaml_spec.rb' - - 'ee/spec/lib/gitlab/ci/yaml_processor_spec.rb' - - 'ee/spec/lib/gitlab/graphql/aggregations/epics/epic_node_spec.rb' - - 'ee/spec/lib/gitlab/graphql/aggregations/epics/lazy_links_aggregate_spec.rb' - - 'ee/spec/lib/gitlab/graphql/aggregations/issues/lazy_links_aggregate_spec.rb' - 'ee/spec/lib/gitlab/graphql/loaders/bulk_epic_aggregate_loader_spec.rb' - 'ee/spec/lib/gitlab/usage/metrics/instrumentations/count_ci_builds_metric_spec.rb' - 'ee/spec/lib/gitlab/usage/metrics/instrumentations/count_users_creating_ci_builds_metric_spec.rb' diff --git a/.rubocop_todo/rspec/expect_change.yml b/.rubocop_todo/rspec/expect_change.yml index 7b96991fbd5..f8962515874 100644 --- a/.rubocop_todo/rspec/expect_change.yml +++ b/.rubocop_todo/rspec/expect_change.yml @@ -428,6 +428,7 @@ RSpec/ExpectChange: - 'spec/models/project_auto_devops_spec.rb' - 'spec/models/project_import_state_spec.rb' - 'spec/models/project_spec.rb' + - 'spec/models/project_statistics_spec.rb' - 'spec/models/projects/build_artifacts_size_refresh_spec.rb' - 'spec/models/projects/ci_feature_usage_spec.rb' - 'spec/models/release_spec.rb' diff --git a/.rubocop_todo/rspec/predicate_matcher.yml b/.rubocop_todo/rspec/predicate_matcher.yml index 7b31ca3622c..f3cfe93191d 100644 --- a/.rubocop_todo/rspec/predicate_matcher.yml +++ b/.rubocop_todo/rspec/predicate_matcher.yml @@ -340,6 +340,7 @@ RSpec/PredicateMatcher: - 'spec/models/concerns/awardable_spec.rb' - 'spec/models/concerns/chronic_duration_attribute_spec.rb' - 'spec/models/concerns/ci/has_deployment_name_spec.rb' + - 'spec/models/concerns/counter_attribute_spec.rb' - 'spec/models/concerns/featurable_spec.rb' - 'spec/models/concerns/ignorable_columns_spec.rb' - 'spec/models/concerns/integrations/has_data_fields_spec.rb' diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb index 5bfda526fb0..2a20c67a23d 100644 --- a/app/controllers/projects/blame_controller.rb +++ b/app/controllers/projects/blame_controller.rb @@ -23,11 +23,10 @@ class Projects::BlameController < Projects::ApplicationController environment_params[:find_latest] = true @environment = ::Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last - blame_service = Projects::BlameService.new(@blob, @commit, params.permit(:page)) + blame_service = Projects::BlameService.new(@blob, @commit, params.permit(:page, :no_pagination)) @blame = Gitlab::View::Presenter::Factory.new(blame_service.blame, project: @project, path: @path, page: blame_service.page).fabricate! - - render locals: { blame_pagination: blame_service.pagination } + @blame_pagination = blame_service.pagination end end diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb index f04412d7603..a68c2ffa06d 100644 --- a/app/controllers/projects/merge_requests/diffs_controller.rb +++ b/app/controllers/projects/merge_requests/diffs_controller.rb @@ -64,20 +64,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic return unless stale?(etag: [cache_context + diff_options_hash.fetch(:paths, []), diffs]) - if diff_options_hash[:paths].blank? - if Feature.enabled?(:remove_caching_diff_batches, @merge_request.project) - render json: PaginatedDiffSerializer.new(current_user: current_user).represent(diffs, options) - else - render_cached( - diffs, - with: PaginatedDiffSerializer.new(current_user: current_user), - cache_context: -> (_) { [Digest::SHA256.hexdigest(cache_context.to_s)] }, - **options - ) - end - else - render json: PaginatedDiffSerializer.new(current_user: current_user).represent(diffs, options) - end + render json: PaginatedDiffSerializer.new(current_user: current_user).represent(diffs, options) end # rubocop: enable Metrics/AbcSize diff --git a/app/controllers/projects/merge_requests/drafts_controller.rb b/app/controllers/projects/merge_requests/drafts_controller.rb index c75c3099051..74bb3ad1a63 100644 --- a/app/controllers/projects/merge_requests/drafts_controller.rb +++ b/app/controllers/projects/merge_requests/drafts_controller.rb @@ -57,10 +57,12 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli end if Gitlab::Utils.to_boolean(approve_params[:approve]) - success = ::MergeRequests::ApprovalService.new(project: @project, current_user: current_user, params: approve_params).execute(merge_request) + unless merge_request.approved_by?(current_user) + success = ::MergeRequests::ApprovalService.new(project: @project, current_user: current_user, params: approve_params).execute(merge_request) - unless success - return render json: { message: _('An error occurred while approving, please try again.') }, status: :internal_server_error + unless success + return render json: { message: _('An error occurred while approving, please try again.') }, status: :internal_server_error + end end merge_request_activity_counter.track_submit_review_approve(user: current_user) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 2c84da4862a..6c09e15f56f 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -92,32 +92,6 @@ module BlobHelper end end - def replace_blob_link(project = @project, ref = @ref, path = @path, blob:) - modify_file_button( - project, - ref, - path, - blob: blob, - label: _("Replace"), - action: "replace", - btn_class: "default", - modal_type: "upload" - ) - end - - def delete_blob_link(project = @project, ref = @ref, path = @path, blob:) - modify_file_button( - project, - ref, - path, - blob: blob, - label: _("Delete"), - action: "delete", - btn_class: "default", - modal_type: "remove" - ) - end - def can_modify_blob?(blob, project = @project, ref = @ref) !blob.stored_externally? && can_edit_tree?(project, ref) end diff --git a/app/models/concerns/counter_attribute.rb b/app/models/concerns/counter_attribute.rb index 65cf3246d11..08c098384b5 100644 --- a/app/models/concerns/counter_attribute.rb +++ b/app/models/concerns/counter_attribute.rb @@ -65,6 +65,10 @@ module CounterAttribute def counter_attribute_after_flush(&callback) after_flush_callbacks << callback end + + def counter_attribute_enabled?(attribute) + counter_attributes.include?(attribute) + end end # This method must only be called by FlushCounterIncrementsWorker @@ -157,7 +161,7 @@ module CounterAttribute end def counter_attribute_enabled?(attribute) - self.class.counter_attributes.include?(attribute) + self.class.counter_attribute_enabled?(attribute) end private diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb index a0af1b47d01..a91e0291438 100644 --- a/app/models/project_statistics.rb +++ b/app/models/project_statistics.rb @@ -11,9 +11,10 @@ class ProjectStatistics < ApplicationRecord default_value_for :snippets_size, 0 counter_attribute :build_artifacts_size - counter_attribute :storage_size counter_attribute_after_flush do |project_statistic| + project_statistic.refresh_storage_size! + Namespaces::ScheduleAggregationWorker.perform_async(project_statistic.namespace_id) end @@ -21,7 +22,6 @@ class ProjectStatistics < ApplicationRecord COLUMNS_TO_REFRESH = [:repository_size, :wiki_size, :lfs_objects_size, :commit_count, :snippets_size, :uploads_size, :container_registry_size].freeze INCREMENTABLE_COLUMNS = { - build_artifacts_size: %i[storage_size], packages_size: %i[storage_size], pipeline_artifacts_size: %i[storage_size], snippets_size: %i[storage_size] @@ -109,21 +109,25 @@ class ProjectStatistics < ApplicationRecord self.storage_size = storage_size end - # Since this incremental update method does not call update_storage_size above, - # we have to update the storage_size here as additional column. - # Additional columns are updated depending on key => [columns], which allows - # to update statistics which are and also those which aren't included in storage_size - # or any other additional summary column in the future. + def refresh_storage_size! + update_storage_size + save! + end + + # Since this incremental update method does not call update_storage_size above through before_save, + # we have to update the storage_size separately. + # + # For counter attributes, storage_size will be refreshed after the counter is flushed, + # through counter_attribute_after_flush + # + # For non-counter attributes, storage_size is updated depending on key => [columns] in INCREMENTABLE_COLUMNS def self.increment_statistic(project, key, amount) - raise ArgumentError, "Cannot increment attribute: #{key}" unless INCREMENTABLE_COLUMNS.key?(key) + raise ArgumentError, "Cannot increment attribute: #{key}" unless incrementable_attribute?(key) return if amount == 0 project.statistics.try do |project_statistics| - if project_statistics.counter_attribute_enabled?(key) - statistics_to_increment = [key] + INCREMENTABLE_COLUMNS[key].to_a - statistics_to_increment.each do |statistic| - project_statistics.delayed_increment_counter(statistic, amount) - end + if counter_attribute_enabled?(key) + project_statistics.delayed_increment_counter(key, amount) else legacy_increment_statistic(project, key, amount) end @@ -149,6 +153,10 @@ class ProjectStatistics < ApplicationRecord update_all(updates.join(', ')) end + def self.incrementable_attribute?(key) + INCREMENTABLE_COLUMNS.key?(key) || counter_attribute_enabled?(key) + end + private def schedule_namespace_aggregation_worker diff --git a/app/policies/packages/policies/project_policy.rb b/app/policies/packages/policies/project_policy.rb index 8e05165c00d..c754d24349a 100644 --- a/app/policies/packages/policies/project_policy.rb +++ b/app/policies/packages/policies/project_policy.rb @@ -7,9 +7,24 @@ module Packages overrides(:read_package) - rule { anonymous & ~project.public_project }.prevent_all + condition(:package_registry_access_level_feature_flag_enabled, scope: :subject) do + ::Feature.enabled?(:package_registry_access_level, @subject) + end + + condition(:packages_enabled_for_everyone, scope: :subject) do + @subject.package_registry_access_level == ProjectFeature::PUBLIC + end + + # This rule can be removed if the `package_registry_access_level` feature flag is removed. + # Reason: If the feature flag is globally enabled, this rule will never be executed. + rule { anonymous & ~project.public_project & ~package_registry_access_level_feature_flag_enabled }.prevent_all - rule { ~project.public_project & ~project.internal_access & ~project.project_allowed_for_job_token }.prevent_all + # This rule can be removed if the `package_registry_access_level` feature flag is removed. + # Reason: If the feature flag is globally enabled, this rule will never be executed. + rule do + ~project.public_project & ~project.internal_access & + ~project.project_allowed_for_job_token & ~package_registry_access_level_feature_flag_enabled + end.prevent_all rule { project.packages_disabled }.policy do prevent(:read_package) @@ -30,6 +45,10 @@ module Packages rule { project.write_package_registry_deploy_token }.policy do enable :read_package end + + rule { package_registry_access_level_feature_flag_enabled & packages_enabled_for_everyone }.policy do + enable :read_package + end end end end diff --git a/app/services/projects/blame_service.rb b/app/services/projects/blame_service.rb index b324ea27360..57b913b04e6 100644 --- a/app/services/projects/blame_service.rb +++ b/app/services/projects/blame_service.rb @@ -10,6 +10,7 @@ module Projects @blob = blob @commit = commit @page = extract_page(params) + @pagination_enabled = pagination_state(params) end attr_reader :page @@ -19,7 +20,7 @@ module Projects end def pagination - return unless pagination_enabled? + return unless pagination_enabled Kaminari.paginate_array([], total_count: blob_lines_count, limit: per_page) .tap { |pagination| pagination.max_paginates_per(per_page) } @@ -28,10 +29,10 @@ module Projects private - attr_reader :blob, :commit + attr_reader :blob, :commit, :pagination_enabled def blame_range - return unless pagination_enabled? + return unless pagination_enabled first_line = (page - 1) * per_page + 1 last_line = (first_line + per_page).to_i - 1 @@ -51,6 +52,12 @@ module Projects PER_PAGE end + def pagination_state(params) + return false if Gitlab::Utils.to_boolean(params[:no_pagination], default: false) + + Feature.enabled?(:blame_page_pagination, commit.project) + end + def overlimit?(page) page * per_page >= blob_lines_count + per_page end @@ -58,9 +65,5 @@ module Projects def blob_lines_count @blob_lines_count ||= blob.data.lines.count end - - def pagination_enabled? - Feature.enabled?(:blame_page_pagination, commit.project) - end end end diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 9f52dd89ff2..c72f9b4b602 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -158,14 +158,25 @@ module Projects priority: UserProjectAccessChangedService::LOW_PRIORITY ) else - @project.add_owner(@project.namespace.owner, current_user: current_user) + owner_user = @project.namespace.owner + owner_member = @project.add_owner(owner_user, current_user: current_user) + + # There is a possibility that the sidekiq job to refresh the authorizations of the owner_user in this project + # isn't picked up (or finished) by the time the user is redirected to the newly created project's page. + # If that happens, the user will hit a 404. To avoid that scenario, we manually create a `project_authorizations` record for the user here. + if owner_member.persisted? + owner_user.project_authorizations.safe_find_or_create_by( + project: @project, + access_level: ProjectMember::OWNER + ) + end # During the process of adding a project owner, a check on permissions is made on the user which caches # the max member access for that user on this project. # Since that is `0` before the member is created - and we are still inside the request # cycle when we need to do other operations that might check those permissions (e.g. write a commit) # we need to purge that cache so that the updated permissions is fetched instead of using the outdated cached value of 0 # from before member creation - @project.team.purge_member_access_cache_for_user_id(@project.namespace.owner.id) + @project.team.purge_member_access_cache_for_user_id(owner_user.id) end end diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml index b44c773adff..d4efca668eb 100644 --- a/app/views/projects/blame/show.html.haml +++ b/app/views/projects/blame/show.html.haml @@ -1,9 +1,9 @@ - page_title _("Blame"), @blob.path, @ref -#blob-content-holder.tree-holder +#blob-content-holder.tree-holder{ data: { testid: 'blob-content-holder' } } = render "projects/blob/breadcrumb", blob: @blob, blame: true - .file-holder + .file-holder.gl-overflow-hidden = render "projects/blob/header", blob: @blob, blame: true .file-blame-legend @@ -20,7 +20,7 @@ %span.legend-box.legend-box-9 %span.right-label Older - .table-responsive.file-content.blame.code{ class: user_color_scheme, data: { qa_selector: 'blame_file_content' } } + .table-responsive.file-content.blame.code{ class: "#{user_color_scheme} gl-rounded-0!", data: { qa_selector: 'blame_file_content' } } %table - current_line = @blame.first_line - @blame.groups.each do |blame_group| @@ -59,5 +59,11 @@ - current_line += line_count - - if blame_pagination - = paginate(blame_pagination, theme: "gitlab") + - if @blame_pagination + .gl-display-flex.gl-justify-content-center.gl-flex-direction-column.gl-align-items-center.gl-p-3.gl-bg-gray-50.gl-border-t-solid.gl-border-t-1.gl-border-gray-100 + = _('For faster browsing, not all history is shown.') + = render Pajamas::ButtonComponent.new(href: namespace_project_blame_path(namespace_id: @project.namespace, project_id: @project, id: @id, no_pagination: true), size: :small, button_options: { class: 'gl-mt-3' }) do |c| + = _('View entire blame') + + - if @blame_pagination + = paginate(@blame_pagination, theme: "gitlab") diff --git a/app/views/projects/blob/_header.html.haml b/app/views/projects/blob/_header.html.haml index e141a006415..9c07713b9f8 100644 --- a/app/views/projects/blob/_header.html.haml +++ b/app/views/projects/blob/_header.html.haml @@ -6,12 +6,6 @@ .file-actions.gl-display-flex.gl-align-items-center.gl-flex-wrap.gl-md-justify-content-end< = render 'projects/blob/viewer_switcher', blob: blob unless blame = render 'shared/web_ide_button', blob: blob - - unless blame - .btn-group{ role: "group", class: ("gl-ml-3" if current_user) }> - = render_if_exists 'projects/blob/header_file_locks_link' - - if current_user - = replace_blob_link(@project, @ref, @path, blob: blob) - = delete_blob_link(@project, @ref, @path, blob: blob) .btn-group.gl-ml-3{ role: "group" } = copy_blob_source_button(blob) unless blame = open_raw_blob_button(blob) diff --git a/app/workers/flush_counter_increments_worker.rb b/app/workers/flush_counter_increments_worker.rb index e21a7ee35e7..8c7e17587d0 100644 --- a/app/workers/flush_counter_increments_worker.rb +++ b/app/workers/flush_counter_increments_worker.rb @@ -11,6 +11,7 @@ class FlushCounterIncrementsWorker data_consistency :always sidekiq_options retry: 3 + loggable_arguments 0, 2 # The increments in `ProjectStatistics` are owned by several teams depending # on the counter diff --git a/config/feature_flags/development/remove_caching_diff_batches.yml b/config/feature_flags/development/draft_quick_action_non_toggle.yml index 9e84bc289d0..4d28b61f3bf 100644 --- a/config/feature_flags/development/remove_caching_diff_batches.yml +++ b/config/feature_flags/development/draft_quick_action_non_toggle.yml @@ -1,7 +1,7 @@ --- -name: remove_caching_diff_batches -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95562 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/370933 +name: draft_quick_action_non_toggle +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92654 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/368610 milestone: '15.4' type: development group: group::code review diff --git a/config/helpers/evaluate_module_from_source.js b/config/helpers/evaluate_module_from_source.js new file mode 100644 index 00000000000..b55bbb82b63 --- /dev/null +++ b/config/helpers/evaluate_module_from_source.js @@ -0,0 +1,37 @@ +const vm = require('vm'); + +/** + * This function uses Node's `vm` modules to evaluate the `module.exports` of a given source string + * + * Example: + * + * ```javascript + * const { exports: moduleExports } = evaluateModuleFromSource("const foo = 7;\n module.exports.bar = 10 + foo;"); + * + * assert(moduleExports.bar === 17); + * ``` + * + * @param {String} source to be evaluated using Node's `vm` modules + * @param {{ require: Function }} options used in the context during evaluation of the Node module + * @returns {{ exports: any }} exports added to the script's `module.exports` context + */ +const evaluateModuleFromSource = (source, { require } = {}) => { + const context = { + module: { + exports: {}, + }, + require, + }; + + try { + const script = new vm.Script(source); + script.runInNewContext(context); + } catch (e) { + console.error(e); + throw e; + } + + return context.module; +}; + +module.exports = { evaluateModuleFromSource }; diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 83cae631a88..623940f671a 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -712,9 +712,6 @@ Gitlab.ee do Settings.cron_jobs['ldap_sync_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['ldap_sync_worker']['cron'] ||= '30 1 * * *' Settings.cron_jobs['ldap_sync_worker']['job_class'] = 'LdapSyncWorker' - Settings.cron_jobs['free_user_cap_data_remediation'] ||= Settingslogic.new({}) - Settings.cron_jobs['free_user_cap_data_remediation']['cron'] ||= '17 6,10,14,18 * * *' - Settings.cron_jobs['free_user_cap_data_remediation']['job_class'] = 'Namespaces::FreeUserCap::RemediationWorker' Settings.cron_jobs['update_max_seats_used_for_gitlab_com_subscriptions_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['update_max_seats_used_for_gitlab_com_subscriptions_worker']['cron'] ||= '0 12 * * *' Settings.cron_jobs['update_max_seats_used_for_gitlab_com_subscriptions_worker']['job_class'] = 'UpdateMaxSeatsUsedForGitlabComSubscriptionsWorker' diff --git a/config/plugins/graphql_known_operations_plugin.js b/config/plugins/graphql_known_operations_plugin.js index 164b34c1dd1..c340849e084 100644 --- a/config/plugins/graphql_known_operations_plugin.js +++ b/config/plugins/graphql_known_operations_plugin.js @@ -1,9 +1,10 @@ /* eslint-disable no-underscore-dangle */ const yaml = require('js-yaml'); +const { evaluateModuleFromSource } = require('../helpers/evaluate_module_from_source'); + const PLUGIN_NAME = 'GraphqlKnownOperationsPlugin'; const GRAPHQL_PATH_REGEX = /(query|mutation)\.graphql$/; -const OPERATION_NAME_SOURCE_REGEX = /^\s*module\.exports.*oneQuery.*"(\w+)"/gm; /** * Returns whether a given webpack module is a "graphql" module @@ -26,9 +27,19 @@ const getOperationNames = (module) => { return []; } - const matches = originalSource.source().toString().matchAll(OPERATION_NAME_SOURCE_REGEX); + const { exports: moduleExports } = evaluateModuleFromSource(originalSource.source().toString(), { + // what: stub require(...) when evaluating the graphql module + // why: require(...) is used to fetch fragments. We only need operation metadata, so it's fine to stub these out. + require: () => ({ definitions: [] }), + }); + + const names = moduleExports.definitions + .filter((x) => ['query', 'mutation'].includes(x.operation)) + .map((x) => x.name?.value) + // why: It's possible for operations to not have a name. That violates our eslint rule, but either way, let's ignore those here. + .filter(Boolean); - return Array.from(matches).map((match) => match[1]); + return names; }; const createFileContents = (knownOperations) => { @@ -60,7 +71,7 @@ const onSucceedModule = ({ module, knownOperations }) => { return; } - getOperationNames(module).forEach((x) => knownOperations.add(x)); + getOperationNames(module).forEach((name) => knownOperations.add(name)); }; const onCompilerEmit = ({ compilation, knownOperations, filename }) => { diff --git a/db/post_migrate/20220908125146_remove_free_user_cap_remediation_worker.rb b/db/post_migrate/20220908125146_remove_free_user_cap_remediation_worker.rb new file mode 100644 index 00000000000..e95ea9c58b4 --- /dev/null +++ b/db/post_migrate/20220908125146_remove_free_user_cap_remediation_worker.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class RemoveFreeUserCapRemediationWorker < Gitlab::Database::Migration[2.0] + def up + Sidekiq::Cron::Job.find('free_user_cap_data_remediation')&.destroy + end + + def down + # no-op + end +end diff --git a/db/post_migrate/20220912085047_add_index_to_todos_pending_query.rb b/db/post_migrate/20220912085047_add_index_to_todos_pending_query.rb new file mode 100644 index 00000000000..7d721421463 --- /dev/null +++ b/db/post_migrate/20220912085047_add_index_to_todos_pending_query.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class AddIndexToTodosPendingQuery < Gitlab::Database::Migration[2.0] + disable_ddl_transaction! + + INDEX_NAME = 'index_on_todos_user_project_target_and_state' + COLUMNS = %i[user_id project_id target_type target_id id].freeze + + def up + add_concurrent_index :todos, COLUMNS, name: INDEX_NAME, where: "state = 'pending'" + end + + def down + remove_concurrent_index_by_name :todos, INDEX_NAME + end +end diff --git a/db/schema_migrations/20220908125146 b/db/schema_migrations/20220908125146 new file mode 100644 index 00000000000..2b8475f0d70 --- /dev/null +++ b/db/schema_migrations/20220908125146 @@ -0,0 +1 @@ +d5d264f90203ba371edcf0688d1227aa69cbf0018033d141257e4c88072ee7d7
\ No newline at end of file diff --git a/db/schema_migrations/20220912085047 b/db/schema_migrations/20220912085047 new file mode 100644 index 00000000000..7279f94eb21 --- /dev/null +++ b/db/schema_migrations/20220912085047 @@ -0,0 +1 @@ +30d9f3352daa48f529486030e30667a1339b04e96b207be815505477ab498adb
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index c591f79b119..12f3de04cb3 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -29531,6 +29531,8 @@ CREATE INDEX index_on_projects_path ON projects USING btree (path); CREATE INDEX index_on_routes_lower_path ON routes USING btree (lower((path)::text)); +CREATE INDEX index_on_todos_user_project_target_and_state ON todos USING btree (user_id, project_id, target_type, target_id, id) WHERE ((state)::text = 'pending'::text); + CREATE INDEX index_on_users_lower_email ON users USING btree (lower((email)::text)); CREATE INDEX index_on_users_lower_username ON users USING btree (lower((username)::text)); diff --git a/doc/api/members.md b/doc/api/members.md index 3ffe94e6f99..0f576c71841 100644 --- a/doc/api/members.md +++ b/doc/api/members.md @@ -329,7 +329,6 @@ GET /groups/:id/billable_members | `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) owned by the authenticated user | | `search` | string | no | A query string to search for group members by name, username, or public email. | | `sort` | string | no | A query string containing parameters that specify the sort attribute and order. See supported values below. | -| `include_awaiting_members` | boolean | no | Determines if awaiting members are included. | The supported values for the `sort` attribute are: diff --git a/doc/api/tags.md b/doc/api/tags.md index 903cb361587..aa3897d1472 100644 --- a/doc/api/tags.md +++ b/doc/api/tags.md @@ -20,7 +20,7 @@ Parameters: | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `id` | integer/string| yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user| -| `order_by` | string | no | Return tags ordered by `name` or `updated` fields. Default is `updated` | +| `order_by` | string | no | Return tags ordered by `name`, `updated`, or `version` (since 15.4) fields. Default is `updated` | | `sort` | string | no | Return tags sorted in `asc` or `desc` order. Default is `desc` | | `search` | string | no | Return list of tags matching the search criteria. You can use `^term` and `term$` to find tags that begin and end with `term` respectively. No other regular expressions are supported. | diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md index 2a796baa5b5..ca30bcec35b 100644 --- a/doc/ci/variables/predefined_variables.md +++ b/doc/ci/variables/predefined_variables.md @@ -67,7 +67,7 @@ as it can cause the pipeline to behave unexpectedly. | `CI_JOB_JWT` | 12.10 | all | A RS256 JSON web token to authenticate with third party systems that support JWT authentication, for example [HashiCorp's Vault](../secrets/index.md). | | `CI_JOB_JWT_V1` | 14.6 | all | The same value as `CI_JOB_JWT`. | | `CI_JOB_JWT_V2` | 14.6 | all | [**alpha:**](../../policy/alpha-beta-support.md#alpha-features) A newly formatted RS256 JSON web token to increase compatibility. Similar to `CI_JOB_JWT`, except the issuer (`iss`) claim is changed from `gitlab.com` to `https://gitlab.com`, `sub` has changed from `job_id` to a string that contains the project path, and an `aud` claim is added. Format is subject to change. Be aware, the `aud` field is a constant value. Trusting JWTs in multiple relying parties can lead to [one RP sending a JWT to another one and acting maliciously as a job](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72555#note_769112331). | -| `CI_JOB_MANUAL` | 8.12 | all | `true` if a job was started manually. | +| `CI_JOB_MANUAL` | 8.12 | all | Only available if the job was started manually. `true` when available. | | `CI_JOB_NAME` | 9.0 | 0.5 | The name of the job. | | `CI_JOB_STAGE` | 9.0 | 0.5 | The name of the job's stage. | | `CI_JOB_STATUS` | all | 13.5 | The status of the job as each runner stage is executed. Use with [`after_script`](../yaml/index.md#after_script). Can be `success`, `failed`, or `canceled`. | diff --git a/doc/cloud_seed/index.md b/doc/cloud_seed/index.md index 0a54c5b2d91..4c56b6c4aaa 100644 --- a/doc/cloud_seed/index.md +++ b/doc/cloud_seed/index.md @@ -66,7 +66,7 @@ The generated service account has the following roles: - `roles/cloudbuild.builds.builder` - `roles/run.admin` - `roles/storage.admin` -- `roles/cloudsql.admin` +- `roles/cloudsql.client` - `roles/browser` You can enhance security by storing CI variables in secret managers. Learn more about [secret management with GitLab](../ci/secrets/index.md). diff --git a/doc/development/backend/ruby_style_guide.md b/doc/development/backend/ruby_style_guide.md index aec4a255dbd..6ba5b8dd2c5 100644 --- a/doc/development/backend/ruby_style_guide.md +++ b/doc/development/backend/ruby_style_guide.md @@ -20,6 +20,17 @@ See also [guidelines for reusing abstractions](../reusing_abstractions.md). Everything listed here can be [reopened for discussion](https://about.gitlab.com/handbook/values/#disagree-commit-and-disagree). +## String literals quoting + +Due to the sheer amount of work to rectify, we do not care whether string +literals are single, or double quoted. + +Previous discussions include: + +- <https://gitlab.com/gitlab-org/gitlab-foss/-/issues/44234> +- <https://gitlab.com/gitlab-org/gitlab-foss/-/issues/36076> +- <https://gitlab.com/gitlab-org/gitlab/-/issues/198046> + ## Instance variable access using `attr_reader` > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52351) in GitLab 14.1. diff --git a/doc/user/project/merge_requests/drafts.md b/doc/user/project/merge_requests/drafts.md index 4bb6034c0bd..cdd8c6a80aa 100644 --- a/doc/user/project/merge_requests/drafts.md +++ b/doc/user/project/merge_requests/drafts.md @@ -20,6 +20,7 @@ the **Merge** button until you remove the **Draft** flag: > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32692) in GitLab 13.2, Work-In-Progress (WIP) merge requests were renamed to **Draft**. > - [Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/228685) all support for using **WIP** in GitLab 14.8. > - **Mark as draft** and **Mark as ready** buttons [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227421) in GitLab 13.5. +> `/draft` quick action as a toggle [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92654) in GitLab 15.3. There are several ways to flag a merge request as a draft: @@ -29,8 +30,7 @@ There are several ways to flag a merge request as a draft: below the **Title** field. - **Commenting in an existing merge request**: Add the `/draft` [quick action](../quick_actions.md#issues-merge-requests-and-epics) - in a comment. This quick action is a toggle, and can be repeated to change the status - back to Ready. + in a comment. GitLab 15.4 [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92654) the toggle behavior of `/draft`. To mark a merge request as ready, use `/ready`. - **Creating a commit**: Add `draft:`, `Draft:`, `fixup!`, or `Fixup!` to the beginning of a commit message targeting the merge request's source branch. This is not a toggle, and adding this text again in a later commit doesn't mark the diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md index af43d53679b..8b8eba62cce 100644 --- a/doc/user/project/quick_actions.md +++ b/doc/user/project/quick_actions.md @@ -67,7 +67,7 @@ threads. Some quick actions might not be available to all subscription tiers. | `/copy_metadata <#issue>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Copy labels and milestone from another issue in the project. | | `/create_merge_request <branch name>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Create a new merge request starting from the current issue. | | `/done` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Mark to do as done. | -| `/draft` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Toggle the [draft status](merge_requests/drafts.md). | +| `/draft` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Set the [draft status](merge_requests/drafts.md). Use for toggling the draft status ([deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92654) in GitLab 15.4.) | | `/due <date>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set due date. Examples of valid `<date>` include `in 2 days`, `this Friday` and `December 31st`. | | `/duplicate <#issue>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Close this issue and mark as a duplicate of another issue. Also, mark both as related. | | `/epic <epic>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add to epic `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic. | diff --git a/lib/api/tags.rb b/lib/api/tags.rb index 97a2aebf53b..c8ac68189f5 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -22,8 +22,8 @@ module API params do optional :sort, type: String, values: %w[asc desc], default: 'desc', desc: 'Return tags sorted in updated by `asc` or `desc` order.' - optional :order_by, type: String, values: %w[name updated], default: 'updated', - desc: 'Return tags ordered by `name` or `updated` fields.' + optional :order_by, type: String, values: %w[name updated version], default: 'updated', + desc: 'Return tags ordered by `name`, `updated`, `version` fields.' optional :search, type: String, desc: 'Return list of tags matching the search criteria' optional :page_token, type: String, desc: 'Name of tag to start the paginaition from' use :pagination diff --git a/lib/gitlab/quick_actions/merge_request_actions.rb b/lib/gitlab/quick_actions/merge_request_actions.rb index 3cb01db1491..d38b81bff0b 100644 --- a/lib/gitlab/quick_actions/merge_request_actions.rb +++ b/lib/gitlab/quick_actions/merge_request_actions.rb @@ -88,33 +88,21 @@ module Gitlab @execution_message[:rebase] = _('Scheduled a rebase of branch %{branch}.') % { branch: branch } end - desc { _('Toggle the Draft status') } + desc { _('Set the Draft status') } explanation do - noun = quick_action_target.to_ability_name.humanize(capitalize: false) - if quick_action_target.draft? - _("Marks this %{noun} as ready.") - else - _("Marks this %{noun} as a draft.") - end % { noun: noun } + draft_action_message(_("Marks")) end execution_message do - noun = quick_action_target.to_ability_name.humanize(capitalize: false) - if quick_action_target.draft? - _("Marked this %{noun} as ready.") - else - _("Marked this %{noun} as a draft.") - end % { noun: noun } + draft_action_message(_("Marked")) end types MergeRequest condition do quick_action_target.respond_to?(:draft?) && - # Allow it to mark as draft on MR creation page or through MR notes - # (quick_action_target.new_record? || current_user.can?(:"update_#{quick_action_target.to_ability_name}", quick_action_target)) end command :draft do - @updates[:wip_event] = quick_action_target.draft? ? 'ready' : 'draft' + @updates[:wip_event] = draft_action_command end desc { _('Set the Ready status') } @@ -317,6 +305,25 @@ module Gitlab end end + def draft_action_message(verb) + noun = quick_action_target.to_ability_name.humanize(capitalize: false) + if !quick_action_target.draft? + _("%{verb} this %{noun} as a draft.") + elsif Feature.disabled?(:draft_quick_action_non_toggle, quick_action_target.project) + _("%{verb} this %{noun} as ready.") + else + _("No change to this %{noun}'s draft status.") + end % { verb: verb, noun: noun } + end + + def draft_action_command + if Feature.disabled?(:draft_quick_action_non_toggle, quick_action_target.project) + quick_action_target.draft? ? 'ready' : 'draft' + else + 'draft' + end + end + def merge_orchestration_service @merge_orchestration_service ||= ::MergeRequests::MergeOrchestrationService.new(project, current_user) end diff --git a/lib/google_api/cloud_platform/client.rb b/lib/google_api/cloud_platform/client.rb index 39cf994ca3f..38a1a968aec 100644 --- a/lib/google_api/cloud_platform/client.rb +++ b/lib/google_api/cloud_platform/client.rb @@ -22,7 +22,7 @@ module GoogleApi "https://www.googleapis.com/auth/logging.write", "https://www.googleapis.com/auth/monitoring" ].freeze - ROLES_LIST = %w[roles/iam.serviceAccountUser roles/artifactregistry.admin roles/cloudbuild.builds.builder roles/run.admin roles/storage.admin roles/cloudsql.admin roles/browser].freeze + ROLES_LIST = %w[roles/iam.serviceAccountUser roles/artifactregistry.admin roles/cloudbuild.builds.builder roles/run.admin roles/storage.admin roles/cloudsql.client roles/browser].freeze REVOKE_URL = 'https://oauth2.googleapis.com/revoke' class << self diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 8446b04684d..914bc7fdf89 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -1180,6 +1180,12 @@ msgstr "" msgid "%{verb} %{time_spent_value} spent time." msgstr "" +msgid "%{verb} this %{noun} as a draft." +msgstr "" + +msgid "%{verb} this %{noun} as ready." +msgstr "" + msgid "%{webhooks_link_start}%{webhook_type}%{link_end} enable you to send notifications to web applications in response to events in a group or project." msgstr "" @@ -16781,6 +16787,9 @@ msgstr "" msgid "For example, the application using the token or the purpose of the token. Do not give sensitive information for the name of the token, as it will be visible to all %{resource_type} members." msgstr "" +msgid "For faster browsing, not all history is shown." +msgstr "" + msgid "For files larger than this limit, only index the file name. The file content is neither indexed nor searchable." msgstr "" @@ -24281,6 +24290,9 @@ msgstr "" msgid "MarkdownToolbar|Supports %{markdownDocsLinkStart}Markdown%{markdownDocsLinkEnd}" msgstr "" +msgid "Marked" +msgstr "" + msgid "Marked For Deletion At - %{deletion_time}" msgstr "" @@ -24290,9 +24302,6 @@ msgstr "" msgid "Marked as ready. Merging is now allowed." msgstr "" -msgid "Marked this %{noun} as a draft." -msgstr "" - msgid "Marked this %{noun} as ready." msgstr "" @@ -24305,7 +24314,7 @@ msgstr "" msgid "Marked to do as done." msgstr "" -msgid "Marks this %{noun} as a draft." +msgid "Marks" msgstr "" msgid "Marks this %{noun} as ready." @@ -36334,6 +36343,9 @@ msgstr "" msgid "Set target branch to %{branch_name}." msgstr "" +msgid "Set the Draft status" +msgstr "" + msgid "Set the Ready status" msgstr "" @@ -41420,9 +41432,6 @@ msgstr "" msgid "Toggle sidebar" msgstr "" -msgid "Toggle the Draft status" -msgstr "" - msgid "Toggle the Performance Bar" msgstr "" @@ -43485,6 +43494,9 @@ msgstr "" msgid "View eligible approvers" msgstr "" +msgid "View entire blame" +msgstr "" + msgid "View epics list" msgstr "" @@ -46047,6 +46059,9 @@ msgstr "" msgid "added %{emails}" msgstr "" +msgid "added a %{link_type} link" +msgstr "" + msgid "added a Zoom call to this issue" msgstr "" @@ -47701,6 +47716,9 @@ msgstr "" msgid "removed" msgstr "" +msgid "removed a %{link_type} link" +msgstr "" + msgid "removed a Zoom call from this issue" msgstr "" diff --git a/qa/qa/page/file/show.rb b/qa/qa/page/file/show.rb index 581aa835ada..31899d9a0c7 100644 --- a/qa/qa/page/file/show.rb +++ b/qa/qa/page/file/show.rb @@ -14,11 +14,6 @@ module QA element :lock_button end - view 'app/helpers/blob_helper.rb' do - element :edit_button, "_('Edit')" # rubocop:disable QA/ElementWithPattern - element :delete_button, '_("Delete")' # rubocop:disable QA/ElementWithPattern - end - view 'app/assets/javascripts/vue_shared/components/web_ide_link.vue' do element :edit_button end diff --git a/spec/controllers/projects/merge_requests/drafts_controller_spec.rb b/spec/controllers/projects/merge_requests/drafts_controller_spec.rb index 7a53b0f96df..182d654aaa8 100644 --- a/spec/controllers/projects/merge_requests/drafts_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/drafts_controller_spec.rb @@ -471,6 +471,21 @@ RSpec.describe Projects::MergeRequests::DraftsController do expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter) .to have_received(:track_submit_review_approve).with(user: user) end + + context 'when merge request is already approved by user' do + before do + create(:approval, merge_request: merge_request, user: user) + end + + it 'does return 200' do + post :publish, params: params.merge!(approve: true) + + expect(response).to have_gitlab_http_status(:ok) + + expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter) + .to have_received(:track_submit_review_approve).with(user: user) + end + end end end end diff --git a/spec/features/projects/blobs/blame_spec.rb b/spec/features/projects/blobs/blame_spec.rb index 3377fcdee48..f50f6911120 100644 --- a/spec/features/projects/blobs/blame_spec.rb +++ b/spec/features/projects/blobs/blame_spec.rb @@ -35,8 +35,10 @@ RSpec.describe 'File blame', :js do it 'displays the blame page without pagination' do visit_blob_blame(path) - expect(page).to have_css('.blame-commit') - expect(page).not_to have_css('.gl-pagination') + within '[data-testid="blob-content-holder"]' do + expect(page).to have_css('.blame-commit') + expect(page).not_to have_css('.gl-pagination') + end end context 'when blob length is over the blame range limit' do @@ -47,12 +49,15 @@ RSpec.describe 'File blame', :js do it 'displays two first lines of the file with pagination' do visit_blob_blame(path) - expect(page).to have_css('.blame-commit') - expect(page).to have_css('.gl-pagination') + within '[data-testid="blob-content-holder"]' do + expect(page).to have_css('.blame-commit') + expect(page).to have_css('.gl-pagination') + expect(page).to have_link _('View entire blame') - expect(page).to have_css('#L1') - expect(page).not_to have_css('#L3') - expect(find('.page-link.active')).to have_text('1') + expect(page).to have_css('#L1') + expect(page).not_to have_css('#L3') + expect(find('.page-link.active')).to have_text('1') + end end context 'when user clicks on the next button' do @@ -63,15 +68,35 @@ RSpec.describe 'File blame', :js do end it 'displays next two lines of the file with pagination' do - expect(page).not_to have_css('#L1') - expect(page).to have_css('#L3') - expect(find('.page-link.active')).to have_text('2') + within '[data-testid="blob-content-holder"]' do + expect(page).not_to have_css('#L1') + expect(page).to have_css('#L3') + expect(find('.page-link.active')).to have_text('2') + end end it 'correctly redirects to the prior blame page' do - find('.version-link').click + within '[data-testid="blob-content-holder"]' do + find('.version-link').click - expect(find('.page-link.active')).to have_text('2') + expect(find('.page-link.active')).to have_text('2') + end + end + end + + context 'when user clicks on View entire blame button' do + before do + visit_blob_blame(path) + end + + it 'displays the blame page without pagination' do + within '[data-testid="blob-content-holder"]' do + click_link _('View entire blame') + + expect(page).to have_css('#L1') + expect(page).to have_css('#L3') + expect(page).not_to have_css('.gl-pagination') + end end end @@ -83,8 +108,11 @@ RSpec.describe 'File blame', :js do it 'displays the blame page without pagination' do visit_blob_blame(path) - expect(page).to have_css('.blame-commit') - expect(page).not_to have_css('.gl-pagination') + within '[data-testid="blob-content-holder"]' do + expect(page).to have_css('.blame-commit') + expect(page).not_to have_css('.gl-pagination') + expect(page).not_to have_link _('View entire blame') + end end end end @@ -99,25 +127,29 @@ RSpec.describe 'File blame', :js do it 'displays two hundred lines of the file with pagination' do visit_blob_blame(path) - expect(page).to have_css('.blame-commit') - expect(page).to have_css('.gl-pagination') + within '[data-testid="blob-content-holder"]' do + expect(page).to have_css('.blame-commit') + expect(page).to have_css('.gl-pagination') - expect(page).to have_css('#L1') - expect(page).not_to have_css('#L201') - expect(find('.page-link.active')).to have_text('1') + expect(page).to have_css('#L1') + expect(page).not_to have_css('#L201') + expect(find('.page-link.active')).to have_text('1') + end end context 'when user clicks on the next button' do before do visit_blob_blame(path) - - find('.js-next-button').click end it 'displays next two hundred lines of the file with pagination' do - expect(page).not_to have_css('#L1') - expect(page).to have_css('#L201') - expect(find('.page-link.active')).to have_text('2') + within '[data-testid="blob-content-holder"]' do + find('.js-next-button').click + + expect(page).not_to have_css('#L1') + expect(page).to have_css('#L201') + expect(find('.page-link.active')).to have_text('2') + end end end end diff --git a/spec/frontend/__helpers__/keep_alive_component_helper_spec.js b/spec/frontend/__helpers__/keep_alive_component_helper_spec.js index dcccc14f396..54d397d0997 100644 --- a/spec/frontend/__helpers__/keep_alive_component_helper_spec.js +++ b/spec/frontend/__helpers__/keep_alive_component_helper_spec.js @@ -17,16 +17,16 @@ describe('keepAlive', () => { }); it('converts a component to a keep-alive component', async () => { - const { element } = wrapper.find(component); + const { element } = wrapper.findComponent(component); await wrapper.vm.deactivate(); - expect(wrapper.find(component).exists()).toBe(false); + expect(wrapper.findComponent(component).exists()).toBe(false); await wrapper.vm.activate(); // assert that when the component is destroyed and re-rendered, the // newly rendered component has the reference to the old component // (i.e. the old component was deactivated and activated) - expect(wrapper.find(component).element).toBe(element); + expect(wrapper.findComponent(component).element).toBe(element); }); }); diff --git a/spec/frontend/access_tokens/components/new_access_token_app_spec.js b/spec/frontend/access_tokens/components/new_access_token_app_spec.js index b25ac4c6aa8..9aaf2d310e6 100644 --- a/spec/frontend/access_tokens/components/new_access_token_app_spec.js +++ b/spec/frontend/access_tokens/components/new_access_token_app_spec.js @@ -23,12 +23,14 @@ describe('~/access_tokens/components/new_access_token_app', () => { }; const triggerSuccess = async (newToken = 'new token') => { - wrapper.find(DomElementListener).vm.$emit(EVENT_SUCCESS, { detail: [{ new_token: newToken }] }); + wrapper + .findComponent(DomElementListener) + .vm.$emit(EVENT_SUCCESS, { detail: [{ new_token: newToken }] }); await nextTick(); }; const triggerError = async (errors = ['1', '2']) => { - wrapper.find(DomElementListener).vm.$emit(EVENT_ERROR, { detail: [{ errors }] }); + wrapper.findComponent(DomElementListener).vm.$emit(EVENT_ERROR, { detail: [{ errors }] }); await nextTick(); }; diff --git a/spec/frontend/add_context_commits_modal/components/add_context_commits_modal_spec.js b/spec/frontend/add_context_commits_modal/components/add_context_commits_modal_spec.js index bffadbde087..a2dc3ac0a76 100644 --- a/spec/frontend/add_context_commits_modal/components/add_context_commits_modal_spec.js +++ b/spec/frontend/add_context_commits_modal/components/add_context_commits_modal_spec.js @@ -48,8 +48,8 @@ describe('AddContextCommitsModal', () => { return wrapper; }; - const findModal = () => wrapper.find(GlModal); - const findSearch = () => wrapper.find(GlSearchBoxByType); + const findModal = () => wrapper.findComponent(GlModal); + const findSearch = () => wrapper.findComponent(GlSearchBoxByType); beforeEach(() => { wrapper = createWrapper(); @@ -107,7 +107,7 @@ describe('AddContextCommitsModal', () => { it('a disabled ok button in first tab, when row is selected in second tab', () => { createWrapper({ selectedContextCommits: [commit] }); - expect(wrapper.find(GlModal).attributes('ok-disabled')).toBe('true'); + expect(wrapper.findComponent(GlModal).attributes('ok-disabled')).toBe('true'); }); }); diff --git a/spec/frontend/add_context_commits_modal/components/review_tab_container_spec.js b/spec/frontend/add_context_commits_modal/components/review_tab_container_spec.js index 85ecb4313c2..1a92b9ee536 100644 --- a/spec/frontend/add_context_commits_modal/components/review_tab_container_spec.js +++ b/spec/frontend/add_context_commits_modal/components/review_tab_container_spec.js @@ -32,7 +32,7 @@ describe('ReviewTabContainer', () => { it('shows loading icon when commits are being loaded', () => { createWrapper({ isLoading: true }); - expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); + expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true); }); it('shows loading error text when API call fails', () => { diff --git a/spec/frontend/admin/topics/components/topic_select_spec.js b/spec/frontend/admin/topics/components/topic_select_spec.js index fae5ca1ef5f..f61af6203f0 100644 --- a/spec/frontend/admin/topics/components/topic_select_spec.js +++ b/spec/frontend/admin/topics/components/topic_select_spec.js @@ -77,8 +77,8 @@ describe('TopicSelect', () => { const dropdownItems = findAllDropdownItems(); - expect(dropdownItems.at(0).find(GlAvatarLabeled).props('label')).toBe('Topic 1'); - expect(dropdownItems.at(1).find(GlAvatarLabeled).props('label')).toBe('GitLab'); + expect(dropdownItems.at(0).findComponent(GlAvatarLabeled).props('label')).toBe('Topic 1'); + expect(dropdownItems.at(1).findComponent(GlAvatarLabeled).props('label')).toBe('GitLab'); }); it('emits `click` event when topic selected', () => { diff --git a/spec/frontend/alert_management/components/alert_management_empty_state_spec.js b/spec/frontend/alert_management/components/alert_management_empty_state_spec.js index c2bf90e7635..0d6bc1b74fb 100644 --- a/spec/frontend/alert_management/components/alert_management_empty_state_spec.js +++ b/spec/frontend/alert_management/components/alert_management_empty_state_spec.js @@ -25,7 +25,7 @@ describe('AlertManagementEmptyState', () => { } }); - const EmptyState = () => wrapper.find(GlEmptyState); + const EmptyState = () => wrapper.findComponent(GlEmptyState); describe('Empty state', () => { it('shows empty state', () => { diff --git a/spec/frontend/alert_management/components/alert_management_list_wrapper_spec.js b/spec/frontend/alert_management/components/alert_management_list_wrapper_spec.js index bba5fcbbf08..3a5fb99fdf1 100644 --- a/spec/frontend/alert_management/components/alert_management_list_wrapper_spec.js +++ b/spec/frontend/alert_management/components/alert_management_list_wrapper_spec.js @@ -28,8 +28,8 @@ describe('AlertManagementList', () => { describe('Alert List Wrapper', () => { it('should show the empty state when alerts are not enabled', () => { - expect(wrapper.find(AlertManagementEmptyState).exists()).toBe(true); - expect(wrapper.find(AlertManagementTable).exists()).toBe(false); + expect(wrapper.findComponent(AlertManagementEmptyState).exists()).toBe(true); + expect(wrapper.findComponent(AlertManagementTable).exists()).toBe(false); }); it('should show the alerts table when alerts are enabled', () => { @@ -39,8 +39,8 @@ describe('AlertManagementList', () => { }, }); - expect(wrapper.find(AlertManagementEmptyState).exists()).toBe(false); - expect(wrapper.find(AlertManagementTable).exists()).toBe(true); + expect(wrapper.findComponent(AlertManagementEmptyState).exists()).toBe(false); + expect(wrapper.findComponent(AlertManagementTable).exists()).toBe(true); }); }); }); diff --git a/spec/frontend/alert_management/components/alert_management_table_spec.js b/spec/frontend/alert_management/components/alert_management_table_spec.js index 5b823694b99..3e1438c37d6 100644 --- a/spec/frontend/alert_management/components/alert_management_table_spec.js +++ b/spec/frontend/alert_management/components/alert_management_table_spec.js @@ -172,8 +172,8 @@ describe('AlertManagementTable', () => { await nextTick(); - expect(wrapper.find(GlTable).exists()).toBe(true); - expect(findAlertsTable().find(GlIcon).classes('icon-critical')).toBe(true); + expect(wrapper.findComponent(GlTable).exists()).toBe(true); + expect(findAlertsTable().findComponent(GlIcon).classes('icon-critical')).toBe(true); }); it('renders severity text', () => { @@ -200,7 +200,7 @@ describe('AlertManagementTable', () => { loading: false, }); - const avatar = findAssignees().at(1).find(GlAvatar); + const avatar = findAssignees().at(1).findComponent(GlAvatar); const { src, label } = avatar.attributes(); const { name, avatarUrl } = mockAlerts[1].assignees.nodes[0]; diff --git a/spec/frontend/alerts_settings/components/alert_mapping_builder_spec.js b/spec/frontend/alerts_settings/components/alert_mapping_builder_spec.js index dba9c8be669..1e125bdfd3a 100644 --- a/spec/frontend/alerts_settings/components/alert_mapping_builder_spec.js +++ b/spec/frontend/alerts_settings/components/alert_mapping_builder_spec.js @@ -47,7 +47,7 @@ describe('AlertMappingBuilder', () => { expect(findColumnInRow(0, 2).text()).toContain(i18n.columns.payloadKeyTitle); expect(findColumnInRow(0, 3).text()).toContain(i18n.columns.fallbackKeyTitle); - const fallbackColumnIcon = findColumnInRow(0, 3).find(GlIcon); + const fallbackColumnIcon = findColumnInRow(0, 3).findComponent(GlIcon); expect(fallbackColumnIcon.exists()).toBe(true); expect(fallbackColumnIcon.attributes('name')).toBe('question'); expect(fallbackColumnIcon.attributes('title')).toBe(i18n.fallbackTooltip); @@ -55,7 +55,7 @@ describe('AlertMappingBuilder', () => { it('renders disabled form input for each mapped field', () => { alertFields.forEach((field, index) => { - const input = findColumnInRow(index + 1, 0).find(GlFormInput); + const input = findColumnInRow(index + 1, 0).findComponent(GlFormInput); const types = field.types.map((t) => capitalizeFirstCharacter(t.toLowerCase())).join(' or '); expect(input.attributes('value')).toBe(`${field.label} (${types})`); expect(input.attributes('disabled')).toBe(''); @@ -71,7 +71,7 @@ describe('AlertMappingBuilder', () => { it('renders mapping dropdown for each field', () => { alertFields.forEach(({ types }, index) => { - const dropdown = findColumnInRow(index + 1, 2).find(GlDropdown); + const dropdown = findColumnInRow(index + 1, 2).findComponent(GlDropdown); const { searchBox, dropdownItems, mappingOptions } = getDropdownContent(dropdown, types); expect(dropdown.exists()).toBe(true); @@ -82,7 +82,7 @@ describe('AlertMappingBuilder', () => { it('renders fallback dropdown only for the fields that have fallback', () => { alertFields.forEach(({ types, numberOfFallbacks }, index) => { - const dropdown = findColumnInRow(index + 1, 3).find(GlDropdown); + const dropdown = findColumnInRow(index + 1, 3).findComponent(GlDropdown); expect(dropdown.exists()).toBe(Boolean(numberOfFallbacks)); if (numberOfFallbacks) { @@ -96,8 +96,8 @@ describe('AlertMappingBuilder', () => { it('emits event with selected mapping', () => { const mappingToSave = { fieldName: 'TITLE', mapping: 'PARSED_TITLE' }; jest.spyOn(transformationUtils, 'transformForSave').mockReturnValue(mappingToSave); - const dropdown = findColumnInRow(1, 2).find(GlDropdown); - const option = dropdown.find(GlDropdownItem); + const dropdown = findColumnInRow(1, 2).findComponent(GlDropdown); + const option = dropdown.findComponent(GlDropdownItem); option.vm.$emit('click'); expect(wrapper.emitted('onMappingUpdate')[0]).toEqual([mappingToSave]); }); diff --git a/spec/frontend/alerts_settings/components/alerts_form_spec.js b/spec/frontend/alerts_settings/components/alerts_form_spec.js index a045954dfb8..33098282bf8 100644 --- a/spec/frontend/alerts_settings/components/alerts_form_spec.js +++ b/spec/frontend/alerts_settings/components/alerts_form_spec.js @@ -5,7 +5,7 @@ describe('Alert integration settings form', () => { let wrapper; const service = { updateSettings: jest.fn().mockResolvedValue() }; - const findForm = () => wrapper.find({ ref: 'settingsForm' }); + const findForm = () => wrapper.findComponent({ ref: 'settingsForm' }); beforeEach(() => { wrapper = shallowMount(AlertsSettingsForm, { diff --git a/spec/frontend/alerts_settings/components/alerts_integrations_list_spec.js b/spec/frontend/alerts_settings/components/alerts_integrations_list_spec.js index 3ffbb7ab60a..6658db92524 100644 --- a/spec/frontend/alerts_settings/components/alerts_integrations_list_spec.js +++ b/spec/frontend/alerts_settings/components/alerts_integrations_list_spec.js @@ -53,8 +53,8 @@ describe('AlertIntegrationsList', () => { mountComponent(); }); - const findTableComponent = () => wrapper.find(GlTable); - const findTableComponentRows = () => wrapper.find(GlTable).findAll('table tbody tr'); + const findTableComponent = () => wrapper.findComponent(GlTable); + const findTableComponentRows = () => wrapper.findComponent(GlTable).findAll('table tbody tr'); const finsStatusCell = () => wrapper.findAll('[data-testid="integration-activated-status"]'); it('renders a table', () => { @@ -78,7 +78,7 @@ describe('AlertIntegrationsList', () => { describe('integration status', () => { it('enabled', () => { const cell = finsStatusCell().at(0); - const activatedIcon = cell.find(GlIcon); + const activatedIcon = cell.findComponent(GlIcon); expect(cell.text()).toBe(i18n.status.enabled.name); expect(activatedIcon.attributes('name')).toBe('check'); expect(activatedIcon.attributes('title')).toBe(i18n.status.enabled.tooltip); @@ -86,7 +86,7 @@ describe('AlertIntegrationsList', () => { it('disabled', () => { const cell = finsStatusCell().at(1); - const notActivatedIcon = cell.find(GlIcon); + const notActivatedIcon = cell.findComponent(GlIcon); expect(cell.text()).toBe(i18n.status.disabled.name); expect(notActivatedIcon.attributes('name')).toBe('warning-solid'); expect(notActivatedIcon.attributes('title')).toBe(i18n.status.disabled.tooltip); diff --git a/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js b/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js index 7d9d2875cf8..fb9e97e7505 100644 --- a/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js +++ b/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js @@ -325,9 +325,9 @@ describe('AlertsSettingsForm', () => { }); await nextTick(); - expect(findSamplePayloadSection().find(GlFormTextarea).attributes('disabled')).toBe( - disabled, - ); + expect( + findSamplePayloadSection().findComponent(GlFormTextarea).attributes('disabled'), + ).toBe(disabled); }); }); diff --git a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js index ed185c11732..aa9bee59e8c 100644 --- a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js +++ b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js @@ -63,14 +63,14 @@ describe('AlertsSettingsWrapper', () => { const findLoader = () => wrapper.findComponent(IntegrationsList).findComponent(GlLoadingIcon); const findIntegrationsList = () => wrapper.findComponent(IntegrationsList); - const findIntegrations = () => wrapper.find(IntegrationsList).findAll('table tbody tr'); + const findIntegrations = () => wrapper.findComponent(IntegrationsList).findAll('table tbody tr'); const findAddIntegrationBtn = () => wrapper.findByTestId('add-integration-btn'); const findAlertsSettingsForm = () => wrapper.findComponent(AlertsSettingsForm); const findAlert = () => wrapper.findComponent(GlAlert); function destroyHttpIntegration(localWrapper) { localWrapper - .find(IntegrationsList) + .findComponent(IntegrationsList) .vm.$emit('delete-integration', { id: integrationToDestroy.id }); } @@ -189,7 +189,7 @@ describe('AlertsSettingsWrapper', () => { data: { integrations: [] }, loading: true, }); - expect(wrapper.find(IntegrationsList).exists()).toBe(true); + expect(wrapper.findComponent(IntegrationsList).exists()).toBe(true); expect(findLoader().exists()).toBe(true); }); }); diff --git a/spec/frontend/analytics/components/activity_chart_spec.js b/spec/frontend/analytics/components/activity_chart_spec.js index a6b45ffe20f..c26407f5c1d 100644 --- a/spec/frontend/analytics/components/activity_chart_spec.js +++ b/spec/frontend/analytics/components/activity_chart_spec.js @@ -18,7 +18,7 @@ describe('Activity Chart Bundle', () => { wrapper = null; }); - const findChart = () => wrapper.find(GlColumnChart); + const findChart = () => wrapper.findComponent(GlColumnChart); const findNoData = () => wrapper.find('[data-testid="noActivityChartData"]'); describe('Activity Chart', () => { diff --git a/spec/frontend/analytics/shared/components/metric_popover_spec.js b/spec/frontend/analytics/shared/components/metric_popover_spec.js index ffec77c2708..6a58f8c6d29 100644 --- a/spec/frontend/analytics/shared/components/metric_popover_spec.js +++ b/spec/frontend/analytics/shared/components/metric_popover_spec.js @@ -30,7 +30,7 @@ describe('MetricPopover', () => { const findAllMetricLinks = () => wrapper.findAll('[data-testid="metric-link"]'); const findMetricDescription = () => wrapper.findByTestId('metric-description'); const findMetricDocsLink = () => wrapper.findByTestId('metric-docs-link'); - const findMetricDocsLinkIcon = () => findMetricDocsLink().find(GlIcon); + const findMetricDocsLinkIcon = () => findMetricDocsLink().findComponent(GlIcon); afterEach(() => { wrapper.destroy(); @@ -83,7 +83,9 @@ describe('MetricPopover', () => { const allLinkContainers = findAllMetricLinks(); expect(allLinkContainers.at(idx).text()).toContain(link.name); - expect(allLinkContainers.at(idx).find(GlLink).attributes('href')).toBe(link.url); + expect(allLinkContainers.at(idx).findComponent(GlLink).attributes('href')).toBe( + link.url, + ); }); }); diff --git a/spec/frontend/analytics/shared/components/projects_dropdown_filter_spec.js b/spec/frontend/analytics/shared/components/projects_dropdown_filter_spec.js index 69918c1db65..b3e0d77a40f 100644 --- a/spec/frontend/analytics/shared/components/projects_dropdown_filter_spec.js +++ b/spec/frontend/analytics/shared/components/projects_dropdown_filter_spec.js @@ -79,7 +79,7 @@ describe('ProjectsDropdownFilter component', () => { const findClearAllButton = () => wrapper.findByText('Clear all'); const findSelectedProjectsLabel = () => wrapper.findComponent(GlTruncate); - const findDropdown = () => wrapper.find(GlDropdown); + const findDropdown = () => wrapper.findComponent(GlDropdown); const findDropdownItems = () => findDropdown() diff --git a/spec/frontend/analytics/usage_trends/components/app_spec.js b/spec/frontend/analytics/usage_trends/components/app_spec.js index 156be26f895..4d8107adf33 100644 --- a/spec/frontend/analytics/usage_trends/components/app_spec.js +++ b/spec/frontend/analytics/usage_trends/components/app_spec.js @@ -21,7 +21,7 @@ describe('UsageTrendsApp', () => { }); it('displays the usage counts component', () => { - expect(wrapper.find(UsageCounts).exists()).toBe(true); + expect(wrapper.findComponent(UsageCounts).exists()).toBe(true); }); ['Total projects & groups', 'Pipelines', 'Issues & merge requests'].forEach((usage) => { @@ -35,6 +35,6 @@ describe('UsageTrendsApp', () => { }); it('displays the users chart component', () => { - expect(wrapper.find(UsersChart).exists()).toBe(true); + expect(wrapper.findComponent(UsersChart).exists()).toBe(true); }); }); diff --git a/spec/frontend/analytics/usage_trends/components/usage_trends_count_chart_spec.js b/spec/frontend/analytics/usage_trends/components/usage_trends_count_chart_spec.js index 02cf7f42a0b..65ccca7334d 100644 --- a/spec/frontend/analytics/usage_trends/components/usage_trends_count_chart_spec.js +++ b/spec/frontend/analytics/usage_trends/components/usage_trends_count_chart_spec.js @@ -50,9 +50,9 @@ describe('UsageTrendsCountChart', () => { wrapper = null; }); - const findLoader = () => wrapper.find(ChartSkeletonLoader); - const findChart = () => wrapper.find(GlLineChart); - const findAlert = () => wrapper.find(GlAlert); + const findLoader = () => wrapper.findComponent(ChartSkeletonLoader); + const findChart = () => wrapper.findComponent(GlLineChart); + const findAlert = () => wrapper.findComponent(GlAlert); describe('while loading', () => { beforeEach(() => { diff --git a/spec/frontend/analytics/usage_trends/components/users_chart_spec.js b/spec/frontend/analytics/usage_trends/components/users_chart_spec.js index 32a664a5026..6b9f09629e2 100644 --- a/spec/frontend/analytics/usage_trends/components/users_chart_spec.js +++ b/spec/frontend/analytics/usage_trends/components/users_chart_spec.js @@ -47,9 +47,9 @@ describe('UsersChart', () => { wrapper = null; }); - const findLoader = () => wrapper.find(ChartSkeletonLoader); - const findAlert = () => wrapper.find(GlAlert); - const findChart = () => wrapper.find(GlAreaChart); + const findLoader = () => wrapper.findComponent(ChartSkeletonLoader); + const findAlert = () => wrapper.findComponent(GlAlert); + const findChart = () => wrapper.findComponent(GlAreaChart); describe('while loading', () => { beforeEach(() => { diff --git a/spec/frontend/artifacts_settings/components/keep_latest_artifact_checkbox_spec.js b/spec/frontend/artifacts_settings/components/keep_latest_artifact_checkbox_spec.js index 2f3ff2b22f2..ca94acfa444 100644 --- a/spec/frontend/artifacts_settings/components/keep_latest_artifact_checkbox_spec.js +++ b/spec/frontend/artifacts_settings/components/keep_latest_artifact_checkbox_spec.js @@ -39,8 +39,8 @@ describe('Keep latest artifact checkbox', () => { const fullPath = 'gitlab-org/gitlab'; const helpPagePath = '/help/ci/pipelines/job_artifacts'; - const findCheckbox = () => wrapper.find(GlFormCheckbox); - const findHelpLink = () => wrapper.find(GlLink); + const findCheckbox = () => wrapper.findComponent(GlFormCheckbox); + const findHelpLink = () => wrapper.findComponent(GlLink); const createComponent = (handlers) => { requestHandlers = { diff --git a/spec/frontend/authentication/two_factor_auth/components/recovery_codes_spec.js b/spec/frontend/authentication/two_factor_auth/components/recovery_codes_spec.js index 2dcc537809f..beb9efd00ed 100644 --- a/spec/frontend/authentication/two_factor_auth/components/recovery_codes_spec.js +++ b/spec/frontend/authentication/two_factor_auth/components/recovery_codes_spec.js @@ -31,9 +31,9 @@ describe('RecoveryCodes', () => { }; const queryByText = (text, options) => within(wrapper.element).queryByText(text, options); - const findAlert = () => wrapper.find(GlAlert); + const findAlert = () => wrapper.findComponent(GlAlert); const findRecoveryCodes = () => wrapper.findByTestId('recovery-codes'); - const findCopyButton = () => wrapper.find(ClipboardButton); + const findCopyButton = () => wrapper.findComponent(ClipboardButton); const findButtonByText = (text) => wrapper.findAll(GlButton).wrappers.find((buttonWrapper) => buttonWrapper.text() === text); const findDownloadButton = () => findButtonByText('Download codes'); diff --git a/spec/frontend/authentication/two_factor_auth/index_spec.js b/spec/frontend/authentication/two_factor_auth/index_spec.js index f9a6b2df662..427159bacd9 100644 --- a/spec/frontend/authentication/two_factor_auth/index_spec.js +++ b/spec/frontend/authentication/two_factor_auth/index_spec.js @@ -10,7 +10,7 @@ describe('initRecoveryCodes', () => { let el; let wrapper; - const findRecoveryCodesComponent = () => wrapper.find(RecoveryCodes); + const findRecoveryCodesComponent = () => wrapper.findComponent(RecoveryCodes); beforeEach(() => { el = document.createElement('div'); diff --git a/spec/frontend/badges/components/badge_settings_spec.js b/spec/frontend/badges/components/badge_settings_spec.js index 79cf5f3e4ff..bddb6d3801c 100644 --- a/spec/frontend/badges/components/badge_settings_spec.js +++ b/spec/frontend/badges/components/badge_settings_spec.js @@ -42,7 +42,7 @@ describe('BadgeSettings component', () => { button.vm.$emit('click'); await nextTick(); - const modal = wrapper.find(GlModal); + const modal = wrapper.findComponent(GlModal); expect(modal.isVisible()).toBe(true); }); @@ -51,7 +51,7 @@ describe('BadgeSettings component', () => { }); it('displays badge list', () => { - expect(wrapper.find(BadgeList).isVisible()).toBe(true); + expect(wrapper.findComponent(BadgeList).isVisible()).toBe(true); }); describe('when editing', () => { @@ -64,7 +64,7 @@ describe('BadgeSettings component', () => { }); it('displays no badge list', () => { - expect(wrapper.find(BadgeList).isVisible()).toBe(false); + expect(wrapper.findComponent(BadgeList).isVisible()).toBe(false); }); }); }); diff --git a/spec/frontend/batch_comments/components/draft_note_spec.js b/spec/frontend/batch_comments/components/draft_note_spec.js index d7796f38680..03ecbc01a56 100644 --- a/spec/frontend/batch_comments/components/draft_note_spec.js +++ b/spec/frontend/batch_comments/components/draft_note_spec.js @@ -61,7 +61,7 @@ describe('Batch comments draft note component', () => { createComponent(); expect(wrapper.findComponent(GlBadge).exists()).toBe(true); - const note = wrapper.find(NoteableNote); + const note = wrapper.findComponent(NoteableNote); expect(note.exists()).toBe(true); expect(note.props().note).toEqual(draft); @@ -122,7 +122,7 @@ describe('Batch comments draft note component', () => { describe('update', () => { it('dispatches updateDraft', async () => { createComponent(); - const note = wrapper.find(NoteableNote); + const note = wrapper.findComponent(NoteableNote); note.vm.$emit('handleEdit'); @@ -147,7 +147,7 @@ describe('Batch comments draft note component', () => { createComponent(); jest.spyOn(window, 'confirm').mockImplementation(() => true); - const note = wrapper.find(NoteableNote); + const note = wrapper.findComponent(NoteableNote); note.vm.$emit('handleDeleteNote', draft); diff --git a/spec/frontend/batch_comments/components/publish_dropdown_spec.js b/spec/frontend/batch_comments/components/publish_dropdown_spec.js index a3168931f1f..7506dc49a18 100644 --- a/spec/frontend/batch_comments/components/publish_dropdown_spec.js +++ b/spec/frontend/batch_comments/components/publish_dropdown_spec.js @@ -34,6 +34,6 @@ describe('Batch comments publish dropdown component', () => { it('renders draft count in dropdown title', () => { createComponent(); - expect(wrapper.find(GlDropdown).props('headerText')).toEqual('2 pending comments'); + expect(wrapper.findComponent(GlDropdown).props('headerText')).toEqual('2 pending comments'); }); }); diff --git a/spec/frontend/captcha/captcha_modal_spec.js b/spec/frontend/captcha/captcha_modal_spec.js index b8448f9ff0a..20e69b5a834 100644 --- a/spec/frontend/captcha/captcha_modal_spec.js +++ b/spec/frontend/captcha/captcha_modal_spec.js @@ -40,7 +40,7 @@ describe('Captcha Modal', () => { }); const findGlModal = () => { - const glModal = wrapper.find(GlModal); + const glModal = wrapper.findComponent(GlModal); jest.spyOn(glModal.vm, 'show').mockImplementation(() => glModal.vm.$emit('shown')); jest diff --git a/spec/frontend/cascading_settings/components/lock_popovers_spec.js b/spec/frontend/cascading_settings/components/lock_popovers_spec.js index 182e3c1c8ff..ff6ce18ead8 100644 --- a/spec/frontend/cascading_settings/components/lock_popovers_spec.js +++ b/spec/frontend/cascading_settings/components/lock_popovers_spec.js @@ -39,7 +39,7 @@ describe('LockPopovers', () => { wrapper = mountExtended(LockPopovers); }; - const findPopover = () => extendedWrapper(wrapper.find(GlPopover)); + const findPopover = () => extendedWrapper(wrapper.findComponent(GlPopover)); const findByTextInPopover = (text, options) => findPopover().findByText((_, element) => element.textContent === text, options); diff --git a/spec/frontend/ci_lint/components/ci_lint_spec.js b/spec/frontend/ci_lint/components/ci_lint_spec.js index 0ad6ed56b0e..ea69a80274e 100644 --- a/spec/frontend/ci_lint/components/ci_lint_spec.js +++ b/spec/frontend/ci_lint/components/ci_lint_spec.js @@ -36,9 +36,9 @@ describe('CI Lint', () => { }); }; - const findEditor = () => wrapper.find(SourceEditor); - const findAlert = () => wrapper.find(GlAlert); - const findCiLintResults = () => wrapper.find(CiLintResults); + const findEditor = () => wrapper.findComponent(SourceEditor); + const findAlert = () => wrapper.findComponent(GlAlert); + const findCiLintResults = () => wrapper.findComponent(CiLintResults); const findValidateBtn = () => wrapper.find('[data-testid="ci-lint-validate"]'); const findClearBtn = () => wrapper.find('[data-testid="ci-lint-clear"]'); diff --git a/spec/frontend/ci_secure_files/components/secure_files_list_spec.js b/spec/frontend/ci_secure_files/components/secure_files_list_spec.js index 04d38a3281a..5273aafbb04 100644 --- a/spec/frontend/ci_secure_files/components/secure_files_list_spec.js +++ b/spec/frontend/ci_secure_files/components/secure_files_list_spec.js @@ -83,7 +83,9 @@ describe('SecureFilesList', () => { const [secureFile] = secureFiles; expect(findCell(0, 0).text()).toBe(secureFile.name); - expect(findCell(0, 1).find(TimeAgoTooltip).props('time')).toBe(secureFile.created_at); + expect(findCell(0, 1).findComponent(TimeAgoTooltip).props('time')).toBe( + secureFile.created_at, + ); }); describe('event tracking', () => { diff --git a/spec/frontend/ci_settings_pipeline_triggers/components/triggers_list_spec.js b/spec/frontend/ci_settings_pipeline_triggers/components/triggers_list_spec.js index 2c6155055bf..01eb08f4ece 100644 --- a/spec/frontend/ci_settings_pipeline_triggers/components/triggers_list_spec.js +++ b/spec/frontend/ci_settings_pipeline_triggers/components/triggers_list_spec.js @@ -16,13 +16,13 @@ describe('TriggersList', () => { }); }; - const findTable = () => wrapper.find(GlTable); + const findTable = () => wrapper.findComponent(GlTable); const findHeaderAt = (i) => wrapper.findAll('thead th').at(i); const findRows = () => wrapper.findAll('tbody tr'); const findRowAt = (i) => findRows().at(i); const findCell = (i, col) => findRowAt(i).findAll('td').at(col); - const findClipboardBtn = (i) => findCell(i, 0).find(ClipboardButton); - const findInvalidBadge = (i) => findCell(i, 0).find(GlBadge); + const findClipboardBtn = (i) => findCell(i, 0).findComponent(ClipboardButton); + const findInvalidBadge = (i) => findCell(i, 0).findComponent(GlBadge); const findEditBtn = (i) => findRowAt(i).find('[data-testid="edit-btn"]'); const findRevokeBtn = (i) => findRowAt(i).find('[data-testid="trigger_revoke_button"]'); @@ -65,7 +65,7 @@ describe('TriggersList', () => { it('displays a time ago label when last used', () => { expect(findCell(0, 3).text()).toBe('Never'); - expect(findCell(1, 3).find(TimeAgoTooltip).props('time')).toBe(triggers[1].lastUsed); + expect(findCell(1, 3).findComponent(TimeAgoTooltip).props('time')).toBe(triggers[1].lastUsed); }); it('displays actions in a rows', () => { diff --git a/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js b/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js index 94a1c5e16e7..a8fec9b23dd 100644 --- a/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js +++ b/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js @@ -58,9 +58,9 @@ describe('Ci variable modal', () => { }); }; - const findCiEnvironmentsDropdown = () => wrapper.find(CiEnvironmentsDropdown); + const findCiEnvironmentsDropdown = () => wrapper.findComponent(CiEnvironmentsDropdown); const findReferenceWarning = () => wrapper.findByTestId('contains-variable-reference'); - const findModal = () => wrapper.find(ModalStub); + const findModal = () => wrapper.findComponent(ModalStub); const findAWSTip = () => wrapper.findByTestId('aws-guidance-tip'); const findAddorUpdateButton = () => wrapper.findByTestId('ciUpdateOrAddVariableBtn'); const deleteVariableButton = () => @@ -72,7 +72,8 @@ describe('Ci variable modal', () => { const findMaskedVariableCheckbox = () => wrapper.findByTestId('ci-variable-masked-checkbox'); const findValueField = () => wrapper.find('#ci-variable-value'); const findEnvScopeLink = () => wrapper.findByTestId('environment-scope-link'); - const findEnvScopeInput = () => wrapper.findByTestId('environment-scope').find(GlFormInput); + const findEnvScopeInput = () => + wrapper.findByTestId('environment-scope').findComponent(GlFormInput); const findVariableTypeDropdown = () => wrapper.find('#ci-variable-type'); afterEach(() => { diff --git a/spec/frontend/ci_variable_list/components/ci_variable_popover_spec.js b/spec/frontend/ci_variable_list/components/ci_variable_popover_spec.js index b43153d3d7c..4d0c378d10e 100644 --- a/spec/frontend/ci_variable_list/components/ci_variable_popover_spec.js +++ b/spec/frontend/ci_variable_list/components/ci_variable_popover_spec.js @@ -18,7 +18,7 @@ describe('Ci Variable Popover', () => { }); }; - const findButton = () => wrapper.find(GlButton); + const findButton = () => wrapper.findComponent(GlButton); beforeEach(() => { createComponent(); diff --git a/spec/frontend/ci_variable_list/components/legacy_ci_variable_modal_spec.js b/spec/frontend/ci_variable_list/components/legacy_ci_variable_modal_spec.js index 6681ab91a4a..d8d2cbc764f 100644 --- a/spec/frontend/ci_variable_list/components/legacy_ci_variable_modal_spec.js +++ b/spec/frontend/ci_variable_list/components/legacy_ci_variable_modal_spec.js @@ -40,8 +40,8 @@ describe('Ci variable modal', () => { }); }; - const findCiEnvironmentsDropdown = () => wrapper.find(CiEnvironmentsDropdown); - const findModal = () => wrapper.find(ModalStub); + const findCiEnvironmentsDropdown = () => wrapper.findComponent(CiEnvironmentsDropdown); + const findModal = () => wrapper.findComponent(ModalStub); const findAddorUpdateButton = () => findModal().find('[data-testid="ciUpdateOrAddVariableBtn"]'); const deleteVariableButton = () => findModal() @@ -213,7 +213,7 @@ describe('Ci variable modal', () => { const environmentScopeInput = wrapper .find('[data-testid="environment-scope"]') - .find(GlFormInput); + .findComponent(GlFormInput); expect(findCiEnvironmentsDropdown().exists()).toBe(false); expect(environmentScopeInput.attributes('readonly')).toBe('readonly'); }); diff --git a/spec/frontend/clusters_list/components/agent_table_spec.js b/spec/frontend/clusters_list/components/agent_table_spec.js index b78f0a3686c..9cbb83eedd2 100644 --- a/spec/frontend/clusters_list/components/agent_table_spec.js +++ b/spec/frontend/clusters_list/components/agent_table_spec.js @@ -36,7 +36,7 @@ describe('AgentTable', () => { const findAgentLink = (at) => wrapper.findAllByTestId('cluster-agent-name-link').at(at); const findStatusText = (at) => wrapper.findAllByTestId('cluster-agent-connection-status').at(at); - const findStatusIcon = (at) => findStatusText(at).find(GlIcon); + const findStatusIcon = (at) => findStatusText(at).findComponent(GlIcon); const findLastContactText = (at) => wrapper.findAllByTestId('cluster-agent-last-contact').at(at); const findVersionText = (at) => wrapper.findAllByTestId('cluster-agent-version').at(at); const findConfiguration = (at) => @@ -113,7 +113,7 @@ describe('AgentTable', () => { texts, lineNumber, }) => { - const findIcon = () => findVersionText(lineNumber).find(GlIcon); + const findIcon = () => findVersionText(lineNumber).findComponent(GlIcon); const findPopover = () => wrapper.findByTestId(`popover-${agent}`); const versionWarning = versionMismatch || versionOutdated; @@ -151,7 +151,7 @@ describe('AgentTable', () => { `( 'displays config file path as "$agentPath" at line $lineNumber', ({ agentConfig, link, lineNumber }) => { - const findLink = findConfiguration(lineNumber).find(GlLink); + const findLink = findConfiguration(lineNumber).findComponent(GlLink); expect(findLink.attributes('href')).toBe(link); expect(findConfiguration(lineNumber).text()).toBe(agentConfig); diff --git a/spec/frontend/clusters_list/components/agents_spec.js b/spec/frontend/clusters_list/components/agents_spec.js index 92cfff7d490..bff1e573dbd 100644 --- a/spec/frontend/clusters_list/components/agents_spec.js +++ b/spec/frontend/clusters_list/components/agents_spec.js @@ -334,7 +334,7 @@ describe('Agents', () => { }); it('displays a loading icon', () => { - expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); + expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true); }); }); }); diff --git a/spec/frontend/clusters_list/components/ancestor_notice_spec.js b/spec/frontend/clusters_list/components/ancestor_notice_spec.js index a9f11e6fdf8..758f6586e1a 100644 --- a/spec/frontend/clusters_list/components/ancestor_notice_spec.js +++ b/spec/frontend/clusters_list/components/ancestor_notice_spec.js @@ -46,7 +46,7 @@ describe('ClustersAncestorNotice', () => { }); it('displays link', () => { - expect(wrapper.find(GlLink).exists()).toBe(true); + expect(wrapper.findComponent(GlLink).exists()).toBe(true); }); }); }); diff --git a/spec/frontend/clusters_list/components/clusters_spec.js b/spec/frontend/clusters_list/components/clusters_spec.js index 5c7635c1617..a3f42c1f161 100644 --- a/spec/frontend/clusters_list/components/clusters_spec.js +++ b/spec/frontend/clusters_list/components/clusters_spec.js @@ -142,7 +142,7 @@ describe('Clusters', () => { ({ lineNumber, result }) => { const statuses = findStatuses(); const status = statuses.at(lineNumber); - expect(status.find(GlLoadingIcon).exists()).toBe(result); + expect(status.findComponent(GlLoadingIcon).exists()).toBe(result); }, ); }); diff --git a/spec/frontend/clusters_list/components/node_error_help_text_spec.js b/spec/frontend/clusters_list/components/node_error_help_text_spec.js index 8187ab75c58..3211ba44eff 100644 --- a/spec/frontend/clusters_list/components/node_error_help_text_spec.js +++ b/spec/frontend/clusters_list/components/node_error_help_text_spec.js @@ -11,7 +11,7 @@ describe('NodeErrorHelpText', () => { await nextTick(); }; - const findPopover = () => wrapper.find(GlPopover); + const findPopover = () => wrapper.findComponent(GlPopover); afterEach(() => { wrapper.destroy(); diff --git a/spec/frontend/code_navigation/components/app_spec.js b/spec/frontend/code_navigation/components/app_spec.js index b85047dc816..b9be262efd0 100644 --- a/spec/frontend/code_navigation/components/app_spec.js +++ b/spec/frontend/code_navigation/components/app_spec.js @@ -63,7 +63,7 @@ describe('Code navigation app component', () => { it('hides popover when no definition set', () => { factory(); - expect(wrapper.find(Popover).exists()).toBe(false); + expect(wrapper.findComponent(Popover).exists()).toBe(false); }); it('renders popover when definition set', () => { @@ -73,7 +73,7 @@ describe('Code navigation app component', () => { currentBlobPath: 'index.js', }); - expect(wrapper.find(Popover).exists()).toBe(true); + expect(wrapper.findComponent(Popover).exists()).toBe(true); }); it('calls showDefinition when clicking blob viewer', () => { diff --git a/spec/frontend/code_navigation/components/popover_spec.js b/spec/frontend/code_navigation/components/popover_spec.js index c038c04a0f8..874263e046a 100644 --- a/spec/frontend/code_navigation/components/popover_spec.js +++ b/spec/frontend/code_navigation/components/popover_spec.js @@ -115,8 +115,8 @@ describe('Code navigation popover component', () => { definitionPathPrefix: DEFINITION_PATH_PREFIX, }); - expect(wrapper.find({ ref: 'code-output' }).exists()).toBe(true); - expect(wrapper.find({ ref: 'doc-output' }).exists()).toBe(false); + expect(wrapper.findComponent({ ref: 'code-output' }).exists()).toBe(true); + expect(wrapper.findComponent({ ref: 'doc-output' }).exists()).toBe(false); }); }); @@ -128,8 +128,8 @@ describe('Code navigation popover component', () => { definitionPathPrefix: DEFINITION_PATH_PREFIX, }); - expect(wrapper.find({ ref: 'code-output' }).exists()).toBe(false); - expect(wrapper.find({ ref: 'doc-output' }).exists()).toBe(true); + expect(wrapper.findComponent({ ref: 'code-output' }).exists()).toBe(false); + expect(wrapper.findComponent({ ref: 'doc-output' }).exists()).toBe(true); }); }); }); diff --git a/spec/frontend/commit/commit_pipeline_status_component_spec.js b/spec/frontend/commit/commit_pipeline_status_component_spec.js index 43db6db00c1..73720c1cc88 100644 --- a/spec/frontend/commit/commit_pipeline_status_component_spec.js +++ b/spec/frontend/commit/commit_pipeline_status_component_spec.js @@ -37,9 +37,9 @@ describe('Commit pipeline status component', () => { }); }; - const findLoader = () => wrapper.find(GlLoadingIcon); + const findLoader = () => wrapper.findComponent(GlLoadingIcon); const findLink = () => wrapper.find('a'); - const findCiIcon = () => findLink().find(CiIcon); + const findCiIcon = () => findLink().findComponent(CiIcon); afterEach(() => { wrapper.destroy(); diff --git a/spec/frontend/confidential_merge_request/components/dropdown_spec.js b/spec/frontend/confidential_merge_request/components/dropdown_spec.js index 14a0b98a0d5..bbb6d4f9a26 100644 --- a/spec/frontend/confidential_merge_request/components/dropdown_spec.js +++ b/spec/frontend/confidential_merge_request/components/dropdown_spec.js @@ -36,12 +36,12 @@ describe('Confidential merge request project dropdown component', () => { it('shows lock icon', () => { factory(); - expect(vm.find(GlDropdown).props('icon')).toBe('lock'); + expect(vm.findComponent(GlDropdown).props('icon')).toBe('lock'); }); it('has dropdown text', () => { factory(); - expect(vm.find(GlDropdown).props('text')).toBe('Select private project'); + expect(vm.findComponent(GlDropdown).props('text')).toBe('Select private project'); }); }); diff --git a/spec/frontend/content_editor/components/bubble_menus/media_bubble_menu_spec.js b/spec/frontend/content_editor/components/bubble_menus/media_bubble_menu_spec.js index c34862b6f3f..13c6495ac41 100644 --- a/spec/frontend/content_editor/components/bubble_menus/media_bubble_menu_spec.js +++ b/spec/frontend/content_editor/components/bubble_menus/media_bubble_menu_spec.js @@ -62,7 +62,7 @@ describe.each` }; const selectFile = async (file) => { - const input = wrapper.find({ ref: 'fileSelector' }); + const input = wrapper.findComponent({ ref: 'fileSelector' }); // override the property definition because `input.files` isn't directly modifyable Object.defineProperty(input.element, 'files', { value: [file], writable: true }); diff --git a/spec/frontend/content_editor/components/toolbar_image_button_spec.js b/spec/frontend/content_editor/components/toolbar_image_button_spec.js index 5a9d16e7a87..5473d43f5a1 100644 --- a/spec/frontend/content_editor/components/toolbar_image_button_spec.js +++ b/spec/frontend/content_editor/components/toolbar_image_button_spec.js @@ -27,7 +27,7 @@ describe('content_editor/components/toolbar_image_button', () => { const findDropdown = () => wrapper.findComponent(GlDropdown); const selectFile = async (file) => { - const input = wrapper.find({ ref: 'fileSelector' }); + const input = wrapper.findComponent({ ref: 'fileSelector' }); // override the property definition because `input.files` isn't directly modifyable Object.defineProperty(input.element, 'files', { value: [file], writable: true }); diff --git a/spec/frontend/content_editor/components/toolbar_link_button_spec.js b/spec/frontend/content_editor/components/toolbar_link_button_spec.js index 295b41528fe..40e859e96af 100644 --- a/spec/frontend/content_editor/components/toolbar_link_button_spec.js +++ b/spec/frontend/content_editor/components/toolbar_link_button_spec.js @@ -30,7 +30,7 @@ describe('content_editor/components/toolbar_link_button', () => { const findRemoveLinkButton = () => wrapper.findByText('Remove link'); const selectFile = async (file) => { - const input = wrapper.find({ ref: 'fileSelector' }); + const input = wrapper.findComponent({ ref: 'fileSelector' }); // override the property definition because `input.files` isn't directly modifyable Object.defineProperty(input.element, 'files', { value: [file], writable: true }); diff --git a/spec/frontend/content_editor/components/wrappers/code_block_spec.js b/spec/frontend/content_editor/components/wrappers/code_block_spec.js index 17a365e12bb..a5ef19fb8e8 100644 --- a/spec/frontend/content_editor/components/wrappers/code_block_spec.js +++ b/spec/frontend/content_editor/components/wrappers/code_block_spec.js @@ -104,7 +104,7 @@ describe('content/components/wrappers/code_block', () => { it('does not render a preview if showPreview: false', async () => { createWrapper({ language: 'plantuml', isDiagram: true, showPreview: false }); - expect(wrapper.find({ ref: 'diagramContainer' }).exists()).toBe(false); + expect(wrapper.findComponent({ ref: 'diagramContainer' }).exists()).toBe(false); }); it('does not update preview when diagram is not active', async () => { @@ -134,7 +134,7 @@ describe('content/components/wrappers/code_block', () => { await nextTick(); expect(wrapper.find('img').attributes('src')).toBe('url/to/some/diagram'); - expect(wrapper.find(SandboxedMermaid).exists()).toBe(false); + expect(wrapper.findComponent(SandboxedMermaid).exists()).toBe(false); }); it('renders an iframe with preview for a mermaid diagram', async () => { @@ -143,7 +143,7 @@ describe('content/components/wrappers/code_block', () => { await emitEditorEvent({ event: 'transaction', tiptapEditor }); await nextTick(); - expect(wrapper.find(SandboxedMermaid).props('source')).toBe(''); + expect(wrapper.findComponent(SandboxedMermaid).props('source')).toBe(''); expect(wrapper.find('img').exists()).toBe(false); }); }); diff --git a/spec/frontend/crm/form_spec.js b/spec/frontend/crm/form_spec.js index f0e9150cada..57e28b396cf 100644 --- a/spec/frontend/crm/form_spec.js +++ b/spec/frontend/crm/form_spec.js @@ -298,7 +298,7 @@ describe('Reusable form component', () => { `( 'should render the correct component for #$id with the value "$value"', ({ index, id, component, value }) => { - const findFormElement = () => findFormGroup(index).find(component); + const findFormElement = () => findFormGroup(index).findComponent(component); expect(findFormElement().attributes('id')).toBe(id); expect(findFormElement().attributes('value')).toBe(value); @@ -307,7 +307,8 @@ describe('Reusable form component', () => { it('should render a checked GlFormCheckbox for #active', () => { const activeCheckboxIndex = 6; - const findFormElement = () => findFormGroup(activeCheckboxIndex).find(GlFormCheckbox); + const findFormElement = () => + findFormGroup(activeCheckboxIndex).findComponent(GlFormCheckbox); expect(findFormElement().attributes('id')).toBe('active'); expect(findFormElement().attributes('checked')).toBe('true'); diff --git a/spec/frontend/cycle_analytics/base_spec.js b/spec/frontend/cycle_analytics/base_spec.js index ea3da86c7b2..013bea671a8 100644 --- a/spec/frontend/cycle_analytics/base_spec.js +++ b/spec/frontend/cycle_analytics/base_spec.js @@ -201,7 +201,7 @@ describe('Value stream analytics component', () => { it('renders the stage table with a loading icon', () => { const tableWrapper = findStageTable(); expect(tableWrapper.exists()).toBe(true); - expect(tableWrapper.find(GlLoadingIcon).exists()).toBe(true); + expect(tableWrapper.findComponent(GlLoadingIcon).exists()).toBe(true); }); it('renders the path navigation loading state', () => { diff --git a/spec/frontend/cycle_analytics/path_navigation_spec.js b/spec/frontend/cycle_analytics/path_navigation_spec.js index fa9eadbd071..fec1526359c 100644 --- a/spec/frontend/cycle_analytics/path_navigation_spec.js +++ b/spec/frontend/cycle_analytics/path_navigation_spec.js @@ -56,7 +56,9 @@ describe('Project PathNavigation', () => { describe('displays correctly', () => { it('has the correct props', () => { - expect(wrapper.find(GlPath).props('items')).toMatchObject(transformedProjectStagePathData); + expect(wrapper.findComponent(GlPath).props('items')).toMatchObject( + transformedProjectStagePathData, + ); }); it('contains all the expected stages', () => { @@ -69,11 +71,11 @@ describe('Project PathNavigation', () => { describe('loading', () => { describe('is false', () => { it('displays the gl-path component', () => { - expect(wrapper.find(GlPath).exists()).toBe(true); + expect(wrapper.findComponent(GlPath).exists()).toBe(true); }); it('hides the gl-skeleton-loading component', () => { - expect(wrapper.find(GlSkeletonLoader).exists()).toBe(false); + expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(false); }); it('renders each stage', () => { @@ -112,11 +114,11 @@ describe('Project PathNavigation', () => { }); it('hides the gl-path component', () => { - expect(wrapper.find(GlPath).exists()).toBe(false); + expect(wrapper.findComponent(GlPath).exists()).toBe(false); }); it('displays the gl-skeleton-loading component', () => { - expect(wrapper.find(GlSkeletonLoader).exists()).toBe(true); + expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(true); }); }); }); diff --git a/spec/frontend/deploy_freeze/components/deploy_freeze_modal_spec.js b/spec/frontend/deploy_freeze/components/deploy_freeze_modal_spec.js index 7c46c280d46..bbafdc000db 100644 --- a/spec/frontend/deploy_freeze/components/deploy_freeze_modal_spec.js +++ b/spec/frontend/deploy_freeze/components/deploy_freeze_modal_spec.js @@ -42,7 +42,7 @@ describe('Deploy freeze modal', () => { wrapper.find('#deploy-freeze-start').trigger('input'); wrapper.find('#deploy-freeze-end').trigger('input'); - wrapper.find(TimezoneDropdown).trigger('input'); + wrapper.findComponent(TimezoneDropdown).trigger('input'); }; afterEach(() => { diff --git a/spec/frontend/deploy_freeze/components/deploy_freeze_settings_spec.js b/spec/frontend/deploy_freeze/components/deploy_freeze_settings_spec.js index cc044800e5e..637efe30022 100644 --- a/spec/frontend/deploy_freeze/components/deploy_freeze_settings_spec.js +++ b/spec/frontend/deploy_freeze/components/deploy_freeze_settings_spec.js @@ -31,11 +31,11 @@ describe('Deploy freeze settings', () => { describe('Deploy freeze table contains components', () => { it('contains deploy freeze table', () => { - expect(wrapper.find(DeployFreezeTable).exists()).toBe(true); + expect(wrapper.findComponent(DeployFreezeTable).exists()).toBe(true); }); it('contains deploy freeze modal', () => { - expect(wrapper.find(DeployFreezeModal).exists()).toBe(true); + expect(wrapper.findComponent(DeployFreezeModal).exists()).toBe(true); }); }); }); diff --git a/spec/frontend/deploy_freeze/components/timezone_dropdown_spec.js b/spec/frontend/deploy_freeze/components/timezone_dropdown_spec.js index aea81daecef..304c8250151 100644 --- a/spec/frontend/deploy_freeze/components/timezone_dropdown_spec.js +++ b/spec/frontend/deploy_freeze/components/timezone_dropdown_spec.js @@ -96,7 +96,7 @@ describe('Deploy freeze timezone dropdown', () => { }); it('renders selected time zone as dropdown label', () => { - expect(wrapper.find(GlDropdown).vm.text).toBe('Alaska'); + expect(wrapper.findComponent(GlDropdown).vm.text).toBe('Alaska'); }); }); }); diff --git a/spec/frontend/diffs/components/app_spec.js b/spec/frontend/diffs/components/app_spec.js index 96f2ac1692c..65831b71fd4 100644 --- a/spec/frontend/diffs/components/app_spec.js +++ b/spec/frontend/diffs/components/app_spec.js @@ -30,7 +30,7 @@ const UPDATED_COMMIT_URL = `${TEST_HOST}/COMMIT/NEW`; Vue.use(Vuex); function getCollapsedFilesWarning(wrapper) { - return wrapper.find(CollapsedFilesWarning); + return wrapper.findComponent(CollapsedFilesWarning); } describe('diffs/components/app', () => { @@ -167,7 +167,7 @@ describe('diffs/components/app', () => { state.diffs.isLoading = true; }); - expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); + expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true); }); it('displays loading icon on batch loading', () => { @@ -175,13 +175,13 @@ describe('diffs/components/app', () => { state.diffs.batchLoadingState = 'loading'; }); - expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); + expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true); }); it('displays diffs container when not loading', () => { createComponent(); - expect(wrapper.find(GlLoadingIcon).exists()).toBe(false); + expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false); expect(wrapper.find('#diffs').exists()).toBe(true); }); @@ -263,7 +263,7 @@ describe('diffs/components/app', () => { it('renders empty state when no diff files exist', () => { createComponent(); - expect(wrapper.find(NoChanges).exists()).toBe(true); + expect(wrapper.findComponent(NoChanges).exists()).toBe(true); }); it('does not render empty state when diff files exist', () => { @@ -273,7 +273,7 @@ describe('diffs/components/app', () => { }); }); - expect(wrapper.find(NoChanges).exists()).toBe(false); + expect(wrapper.findComponent(NoChanges).exists()).toBe(false); expect(wrapper.findAll(DiffFile).length).toBe(1); }); }); @@ -487,8 +487,8 @@ describe('diffs/components/app', () => { state.diffs.mergeRequestDiff = mergeRequestDiff; }); - expect(wrapper.find(CompareVersions).exists()).toBe(true); - expect(wrapper.find(CompareVersions).props()).toEqual( + expect(wrapper.findComponent(CompareVersions).exists()).toBe(true); + expect(wrapper.findComponent(CompareVersions).props()).toEqual( expect.objectContaining({ diffFilesCountText: null, }), @@ -506,8 +506,8 @@ describe('diffs/components/app', () => { state.diffs.size = 1; }); - expect(wrapper.find(HiddenFilesWarning).exists()).toBe(true); - expect(wrapper.find(HiddenFilesWarning).props()).toEqual( + expect(wrapper.findComponent(HiddenFilesWarning).exists()).toBe(true); + expect(wrapper.findComponent(HiddenFilesWarning).props()).toEqual( expect.objectContaining({ total: '5', plainDiffPath: 'plain diff path', @@ -547,7 +547,7 @@ describe('diffs/components/app', () => { }; }); - expect(wrapper.find(CommitWidget).exists()).toBe(true); + expect(wrapper.findComponent(CommitWidget).exists()).toBe(true); }); it('should display diff file if there are diff files', () => { @@ -555,13 +555,13 @@ describe('diffs/components/app', () => { state.diffs.diffFiles.push({ sha: '123' }); }); - expect(wrapper.find(DiffFile).exists()).toBe(true); + expect(wrapper.findComponent(DiffFile).exists()).toBe(true); }); it("doesn't render tree list when no changes exist", () => { createComponent(); - expect(wrapper.find(TreeList).exists()).toBe(false); + expect(wrapper.findComponent(TreeList).exists()).toBe(false); }); it('should render tree list', () => { @@ -569,7 +569,7 @@ describe('diffs/components/app', () => { state.diffs.diffFiles = [{ file_hash: '111', file_path: '111.js' }]; }); - expect(wrapper.find(TreeList).exists()).toBe(true); + expect(wrapper.findComponent(TreeList).exists()).toBe(true); }); }); @@ -641,7 +641,7 @@ describe('diffs/components/app', () => { describe('pagination', () => { const fileByFileNav = () => wrapper.find('[data-testid="file-by-file-navigation"]'); - const paginator = () => fileByFileNav().find(GlPagination); + const paginator = () => fileByFileNav().findComponent(GlPagination); it('sets previous button as disabled', async () => { createComponent({ fileByFileUserPreference: true }, ({ state }) => { diff --git a/spec/frontend/diffs/components/collapsed_files_warning_spec.js b/spec/frontend/diffs/components/collapsed_files_warning_spec.js index cc4f13ab0cf..eca5b536a35 100644 --- a/spec/frontend/diffs/components/collapsed_files_warning_spec.js +++ b/spec/frontend/diffs/components/collapsed_files_warning_spec.js @@ -28,8 +28,8 @@ describe('CollapsedFilesWarning', () => { Vue.use(Vuex); const getAlertActionButton = () => - wrapper.find(CollapsedFilesWarning).find('button.gl-alert-action:first-child'); - const getAlertCloseButton = () => wrapper.find(CollapsedFilesWarning).find('button'); + wrapper.findComponent(CollapsedFilesWarning).find('button.gl-alert-action:first-child'); + const getAlertCloseButton = () => wrapper.findComponent(CollapsedFilesWarning).find('button'); const createComponent = (props = {}, { full } = { full: false }) => { const mounter = full ? mount : shallowMount; diff --git a/spec/frontend/diffs/components/commit_item_spec.js b/spec/frontend/diffs/components/commit_item_spec.js index 286e94ae7fd..440f169be86 100644 --- a/spec/frontend/diffs/components/commit_item_spec.js +++ b/spec/frontend/diffs/components/commit_item_spec.js @@ -27,7 +27,7 @@ describe('diffs/components/commit_item', () => { const getAvatarElement = () => wrapper.find('.user-avatar-link'); const getCommitterElement = () => wrapper.find('.committer'); const getCommitActionsElement = () => wrapper.find('.commit-actions'); - const getCommitPipelineStatus = () => wrapper.find(CommitPipelineStatus); + const getCommitPipelineStatus = () => wrapper.findComponent(CommitPipelineStatus); const mountComponent = (propsData) => { wrapper = mount(Component, { diff --git a/spec/frontend/diffs/components/commit_widget_spec.js b/spec/frontend/diffs/components/commit_widget_spec.js index fbff473e4df..f650ead6f83 100644 --- a/spec/frontend/diffs/components/commit_widget_spec.js +++ b/spec/frontend/diffs/components/commit_widget_spec.js @@ -12,7 +12,7 @@ describe('diffs/components/commit_widget', () => { }); it('renders commit item', () => { - const commitElement = wrapper.find(CommitItem); + const commitElement = wrapper.findComponent(CommitItem); expect(commitElement.exists()).toBe(true); }); diff --git a/spec/frontend/diffs/components/diff_comment_cell_spec.js b/spec/frontend/diffs/components/diff_comment_cell_spec.js index b636a178593..2acfc2c6d7e 100644 --- a/spec/frontend/diffs/components/diff_comment_cell_spec.js +++ b/spec/frontend/diffs/components/diff_comment_cell_spec.js @@ -20,24 +20,24 @@ describe('DiffCommentCell', () => { it('renders discussions if line has discussions', () => { const wrapper = createWrapper({ renderDiscussion: true }); - expect(wrapper.find(DiffDiscussions).exists()).toBe(true); + expect(wrapper.findComponent(DiffDiscussions).exists()).toBe(true); }); it('does not render discussions if line has no discussions', () => { const wrapper = createWrapper(); - expect(wrapper.find(DiffDiscussions).exists()).toBe(false); + expect(wrapper.findComponent(DiffDiscussions).exists()).toBe(false); }); it('renders discussion reply if line has no draft', () => { const wrapper = createWrapper(); - expect(wrapper.find(DiffDiscussionReply).exists()).toBe(true); + expect(wrapper.findComponent(DiffDiscussionReply).exists()).toBe(true); }); it('does not render discussion reply if line has draft', () => { const wrapper = createWrapper({ hasDraft: true }); - expect(wrapper.find(DiffDiscussionReply).exists()).toBe(false); + expect(wrapper.findComponent(DiffDiscussionReply).exists()).toBe(false); }); }); diff --git a/spec/frontend/diffs/components/diff_content_spec.js b/spec/frontend/diffs/components/diff_content_spec.js index 6844e6e497a..9f593ee0d49 100644 --- a/spec/frontend/diffs/components/diff_content_spec.js +++ b/spec/frontend/diffs/components/diff_content_spec.js @@ -110,13 +110,13 @@ describe('DiffContent', () => { props: { diffFile: textDiffFile }, }); - expect(wrapper.find(DiffView).exists()).toBe(true); + expect(wrapper.findComponent(DiffView).exists()).toBe(true); }); it('renders rendering more lines loading icon', () => { createComponent({ props: { diffFile: { ...textDiffFile, renderingLines: true } } }); - expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); + expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true); }); }); @@ -133,7 +133,7 @@ describe('DiffContent', () => { props: { diffFile: { ...emptyDiffFile, viewer: { name: diffViewerModes.no_preview } } }, }); - expect(wrapper.find(NoPreviewViewer).exists()).toBe(true); + expect(wrapper.findComponent(NoPreviewViewer).exists()).toBe(true); }); it('should render not diffable view if viewer set to non_diffable', () => { @@ -141,7 +141,7 @@ describe('DiffContent', () => { props: { diffFile: { ...emptyDiffFile, viewer: { name: diffViewerModes.not_diffable } } }, }); - expect(wrapper.find(NotDiffableViewer).exists()).toBe(true); + expect(wrapper.findComponent(NotDiffableViewer).exists()).toBe(true); }); }); @@ -156,7 +156,7 @@ describe('DiffContent', () => { }, }); - expect(wrapper.find(DiffDiscussions).exists()).toBe(true); + expect(wrapper.findComponent(DiffDiscussions).exists()).toBe(true); }); it('emits saveDiffDiscussion when note-form emits `handleFormUpdate`', () => { @@ -169,7 +169,7 @@ describe('DiffContent', () => { }, }); - wrapper.find(NoteForm).vm.$emit('handleFormUpdate', noteStub); + wrapper.findComponent(NoteForm).vm.$emit('handleFormUpdate', noteStub); expect(saveDiffDiscussionMock).toHaveBeenCalledWith(expect.any(Object), { note: noteStub, formData: { diff --git a/spec/frontend/diffs/components/diff_discussion_reply_spec.js b/spec/frontend/diffs/components/diff_discussion_reply_spec.js index f03c0357a0e..5ccd2002462 100644 --- a/spec/frontend/diffs/components/diff_discussion_reply_spec.js +++ b/spec/frontend/diffs/components/diff_discussion_reply_spec.js @@ -64,7 +64,7 @@ describe('DiffDiscussionReply', () => { hasForm: false, }); - expect(wrapper.find(ReplyPlaceholder).exists()).toBe(true); + expect(wrapper.findComponent(ReplyPlaceholder).exists()).toBe(true); }); }); @@ -83,6 +83,6 @@ describe('DiffDiscussionReply', () => { hasForm: false, }); - expect(wrapper.find(NoteSignedOutWidget).exists()).toBe(true); + expect(wrapper.findComponent(NoteSignedOutWidget).exists()).toBe(true); }); }); diff --git a/spec/frontend/diffs/components/diff_discussions_spec.js b/spec/frontend/diffs/components/diff_discussions_spec.js index 2da68adddf6..b3945257fbd 100644 --- a/spec/frontend/diffs/components/diff_discussions_spec.js +++ b/spec/frontend/diffs/components/diff_discussions_spec.js @@ -32,9 +32,9 @@ describe('DiffDiscussions', () => { it('should have notes list', () => { createComponent(); - expect(wrapper.find(NoteableDiscussion).exists()).toBe(true); - expect(wrapper.find(DiscussionNotes).exists()).toBe(true); - expect(wrapper.find(DiscussionNotes).findAll(TimelineEntryItem).length).toBe( + 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, ); }); @@ -48,7 +48,7 @@ describe('DiffDiscussions', () => { const diffNotesToggle = findDiffNotesToggle(); expect(diffNotesToggle.exists()).toBe(true); - expect(diffNotesToggle.find(GlIcon).exists()).toBe(true); + expect(diffNotesToggle.findComponent(GlIcon).exists()).toBe(true); expect(diffNotesToggle.classes('diff-notes-collapse')).toBe(true); }); @@ -80,12 +80,12 @@ describe('DiffDiscussions', () => { discussions[0].expanded = false; createComponent({ discussions, shouldCollapseDiscussions: true }); - expect(wrapper.find(NoteableDiscussion).isVisible()).toBe(false); + expect(wrapper.findComponent(NoteableDiscussion).isVisible()).toBe(false); }); it('renders badge on avatar', () => { createComponent({ renderAvatarBadge: true }); - const noteableDiscussion = wrapper.find(NoteableDiscussion); + const noteableDiscussion = wrapper.findComponent(NoteableDiscussion); expect(noteableDiscussion.find('.design-note-pin').exists()).toBe(true); expect(noteableDiscussion.find('.design-note-pin').text().trim()).toBe('1'); diff --git a/spec/frontend/diffs/components/diff_file_header_spec.js b/spec/frontend/diffs/components/diff_file_header_spec.js index 92b8b2d4aa3..c23eb2f3d24 100644 --- a/spec/frontend/diffs/components/diff_file_header_spec.js +++ b/spec/frontend/diffs/components/diff_file_header_spec.js @@ -76,18 +76,19 @@ describe('DiffFileHeader component', () => { wrapper.destroy(); }); - const findHeader = () => wrapper.find({ ref: 'header' }); - const findTitleLink = () => wrapper.find({ ref: 'titleWrapper' }); - const findExpandButton = () => wrapper.find({ ref: 'expandDiffToFullFileButton' }); + const findHeader = () => wrapper.findComponent({ ref: 'header' }); + const findTitleLink = () => wrapper.findComponent({ ref: 'titleWrapper' }); + const findExpandButton = () => wrapper.findComponent({ ref: 'expandDiffToFullFileButton' }); const findFileActions = () => wrapper.find('.file-actions'); - const findModeChangedLine = () => wrapper.find({ ref: 'fileMode' }); + const findModeChangedLine = () => wrapper.findComponent({ ref: 'fileMode' }); const findLfsLabel = () => wrapper.find('[data-testid="label-lfs"]'); - const findToggleDiscussionsButton = () => wrapper.find({ ref: 'toggleDiscussionsButton' }); - const findExternalLink = () => wrapper.find({ ref: 'externalLink' }); - const findReplacedFileButton = () => wrapper.find({ ref: 'replacedFileButton' }); - const findViewFileButton = () => wrapper.find({ ref: 'viewButton' }); - const findCollapseIcon = () => wrapper.find({ ref: 'collapseIcon' }); - const findEditButton = () => wrapper.find({ ref: 'editButton' }); + const findToggleDiscussionsButton = () => + wrapper.findComponent({ ref: 'toggleDiscussionsButton' }); + const findExternalLink = () => wrapper.findComponent({ ref: 'externalLink' }); + const findReplacedFileButton = () => wrapper.findComponent({ ref: 'replacedFileButton' }); + const findViewFileButton = () => wrapper.findComponent({ ref: 'viewButton' }); + const findCollapseIcon = () => wrapper.findComponent({ ref: 'collapseIcon' }); + const findEditButton = () => wrapper.findComponent({ ref: 'editButton' }); const findReviewFileCheckbox = () => wrapper.find("[data-testid='fileReviewCheckbox']"); const createComponent = ({ props, options = {} } = {}) => { @@ -153,7 +154,7 @@ describe('DiffFileHeader component', () => { }); it('displays a copy to clipboard button', () => { - expect(wrapper.find(ClipboardButton).exists()).toBe(true); + expect(wrapper.findComponent(ClipboardButton).exists()).toBe(true); }); it('triggers the copy to clipboard tracking event', () => { diff --git a/spec/frontend/diffs/components/diff_file_row_spec.js b/spec/frontend/diffs/components/diff_file_row_spec.js index 1d1c5fec293..c5b76551fcc 100644 --- a/spec/frontend/diffs/components/diff_file_row_spec.js +++ b/spec/frontend/diffs/components/diff_file_row_spec.js @@ -32,7 +32,7 @@ describe('Diff File Row component', () => { ...diffFileRowProps, }); - expect(wrapper.find(FileRow).props()).toEqual( + expect(wrapper.findComponent(FileRow).props()).toEqual( expect.objectContaining({ ...sharedProps, }), @@ -47,7 +47,7 @@ describe('Diff File Row component', () => { showTooltip: true, }); - expect(wrapper.find(ChangedFileIcon).props()).toEqual( + expect(wrapper.findComponent(ChangedFileIcon).props()).toEqual( expect.objectContaining({ file: {}, size: 16, @@ -74,7 +74,7 @@ describe('Diff File Row component', () => { hideFileStats: false, viewedFiles: isViewed ? { '#123456789': true } : {}, }); - expect(wrapper.find(FileRow).props('fileClasses')).toBe(expected); + expect(wrapper.findComponent(FileRow).props('fileClasses')).toBe(expected); }, ); @@ -92,7 +92,7 @@ describe('Diff File Row component', () => { }, hideFileStats, }); - expect(wrapper.find(FileRowStats).exists()).toEqual(value); + expect(wrapper.findComponent(FileRowStats).exists()).toEqual(value); }); }); diff --git a/spec/frontend/diffs/components/diff_file_spec.js b/spec/frontend/diffs/components/diff_file_spec.js index 9e8d9e1ca29..944cec77efb 100644 --- a/spec/frontend/diffs/components/diff_file_spec.js +++ b/spec/frontend/diffs/components/diff_file_spec.js @@ -100,7 +100,7 @@ function createComponent({ file, first = false, last = false, options = {}, prop }; } -const findDiffHeader = (wrapper) => wrapper.find(DiffFileHeaderComponent); +const findDiffHeader = (wrapper) => wrapper.findComponent(DiffFileHeaderComponent); const findDiffContentArea = (wrapper) => wrapper.find('[data-testid="content-area"]'); const findLoader = (wrapper) => wrapper.find('[data-testid="loader-icon"]'); const findToggleButton = (wrapper) => wrapper.find('[data-testid="expand-button"]'); @@ -209,14 +209,14 @@ describe('DiffFile', () => { expect(el.querySelectorAll('.diff-content.hidden').length).toEqual(0); expect(el.querySelector('.js-file-title')).toBeDefined(); - expect(wrapper.find(DiffFileHeaderComponent).exists()).toBe(true); + expect(wrapper.findComponent(DiffFileHeaderComponent).exists()).toBe(true); expect(el.querySelector('.js-syntax-highlight')).toBeDefined(); markFileToBeRendered(store); await nextTick(); - expect(wrapper.find(DiffContentComponent).exists()).toBe(true); + expect(wrapper.findComponent(DiffContentComponent).exists()).toBe(true); }); }); @@ -320,7 +320,7 @@ describe('DiffFile', () => { }); it('should have the file content', async () => { - expect(wrapper.find(DiffContentComponent).exists()).toBe(true); + expect(wrapper.findComponent(DiffContentComponent).exists()).toBe(true); }); it('should style the component so that it `.has-body` for layout purposes', () => { @@ -473,8 +473,8 @@ describe('DiffFile', () => { await nextTick(); expect(wrapper.classes('has-body')).toBe(true); - expect(wrapper.find(DiffContentComponent).exists()).toBe(true); - expect(wrapper.find(DiffContentComponent).isVisible()).toBe(true); + expect(wrapper.findComponent(DiffContentComponent).exists()).toBe(true); + expect(wrapper.findComponent(DiffContentComponent).isVisible()).toBe(true); }, ); }); diff --git a/spec/frontend/diffs/components/diff_line_note_form_spec.js b/spec/frontend/diffs/components/diff_line_note_form_spec.js index 542d61c4680..9493dc8855e 100644 --- a/spec/frontend/diffs/components/diff_line_note_form_spec.js +++ b/spec/frontend/diffs/components/diff_line_note_form_spec.js @@ -82,7 +82,7 @@ describe('DiffLineNoteForm', () => { }); it('shows note form', () => { - expect(wrapper.find(NoteForm).exists()).toBe(true); + expect(wrapper.findComponent(NoteForm).exists()).toBe(true); }); it('passes the provided range of lines to comment form', () => { diff --git a/spec/frontend/diffs/components/diff_view_spec.js b/spec/frontend/diffs/components/diff_view_spec.js index c681d0cf7f9..33b6acd5fd5 100644 --- a/spec/frontend/diffs/components/diff_view_spec.js +++ b/spec/frontend/diffs/components/diff_view_spec.js @@ -89,7 +89,7 @@ describe('DiffView', () => { inline: type === 'inline', }); expect(wrapper.findAll(DiffCommentCell).length).toBe(total); - expect(wrapper.find(container).find(DiffCommentCell).exists()).toBe(true); + expect(wrapper.find(container).findComponent(DiffCommentCell).exists()).toBe(true); }, ); @@ -97,7 +97,7 @@ describe('DiffView', () => { const wrapper = createWrapper({ diffLines: [{ renderCommentRow: true, left: { lineDraft: { isDraft: true } } }], }); - expect(wrapper.find(DraftNote).exists()).toBe(true); + expect(wrapper.findComponent(DraftNote).exists()).toBe(true); }); describe('drag operations', () => { diff --git a/spec/frontend/diffs/components/image_diff_overlay_spec.js b/spec/frontend/diffs/components/image_diff_overlay_spec.js index 70191620eb6..ccf942bdcef 100644 --- a/spec/frontend/diffs/components/image_diff_overlay_spec.js +++ b/spec/frontend/diffs/components/image_diff_overlay_spec.js @@ -57,7 +57,7 @@ describe('Diffs image diff overlay component', () => { it('renders icon when showCommentIcon is true', () => { createComponent({ showCommentIcon: true }); - expect(wrapper.find(GlIcon).exists()).toBe(true); + expect(wrapper.findComponent(GlIcon).exists()).toBe(true); }); it('sets badge comment positions', () => { diff --git a/spec/frontend/diffs/components/no_changes_spec.js b/spec/frontend/diffs/components/no_changes_spec.js index 6903b844e5e..dbfe9770e07 100644 --- a/spec/frontend/diffs/components/no_changes_spec.js +++ b/spec/frontend/diffs/components/no_changes_spec.js @@ -56,7 +56,7 @@ describe('Diff no changes empty state', () => { it('Show create commit button', () => { createComponent(); - expect(wrapper.find(GlButton).exists()).toBe(true); + expect(wrapper.findComponent(GlButton).exists()).toBe(true); }); it.each` diff --git a/spec/frontend/diffs/components/tree_list_spec.js b/spec/frontend/diffs/components/tree_list_spec.js index 931a9562d36..2e37425519d 100644 --- a/spec/frontend/diffs/components/tree_list_spec.js +++ b/spec/frontend/diffs/components/tree_list_spec.js @@ -175,7 +175,7 @@ describe('Diffs tree list component', () => { await nextTick(); // Have to use $attrs['viewed-files'] because we are passing down an object // and attributes('') stringifies values (e.g. [object])... - expect(wrapper.find(FileTree).vm.$attrs['viewed-files']).toBe(viewedDiffFileIds); + expect(wrapper.findComponent(FileTree).vm.$attrs['viewed-files']).toBe(viewedDiffFileIds); }); }); }); diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb index aeca7b09a88..0f117f495d1 100644 --- a/spec/lib/google_api/cloud_platform/client_spec.rb +++ b/spec/lib/google_api/cloud_platform/client_spec.rb @@ -306,7 +306,7 @@ RSpec.describe GoogleApi::CloudPlatform::Client do .with({ 'role': 'roles/storage.admin', 'members': ["serviceAccount:#{mock_email}"] }) expect(Google::Apis::CloudresourcemanagerV1::Binding).to receive(:new) - .with({ 'role': 'roles/cloudsql.admin', 'members': ["serviceAccount:#{mock_email}"] }) + .with({ 'role': 'roles/cloudsql.client', 'members': ["serviceAccount:#{mock_email}"] }) expect(Google::Apis::CloudresourcemanagerV1::Binding).to receive(:new) .with({ 'role': 'roles/browser', 'members': ["serviceAccount:#{mock_email}"] }) diff --git a/spec/models/concerns/counter_attribute_spec.rb b/spec/models/concerns/counter_attribute_spec.rb index 8d32ef14f47..2dd70188740 100644 --- a/spec/models/concerns/counter_attribute_spec.rb +++ b/spec/models/concerns/counter_attribute_spec.rb @@ -79,4 +79,14 @@ RSpec.describe CounterAttribute, :counter_attribute, :clean_gitlab_redis_shared_ end end end + + describe '.counter_attribute_enabled?' do + it 'is true when counter attribute is defined' do + expect(CounterAttributeModel.counter_attribute_enabled?(:build_artifacts_size)).to be_truthy + end + + it 'is false when counter attribute is not defined' do + expect(CounterAttributeModel.counter_attribute_enabled?(:nope)).to be_falsey + end + end end diff --git a/spec/models/project_statistics_spec.rb b/spec/models/project_statistics_spec.rb index f4edc68457b..b2158baa670 100644 --- a/spec/models/project_statistics_spec.rb +++ b/spec/models/project_statistics_spec.rb @@ -407,6 +407,25 @@ RSpec.describe ProjectStatistics do end end + describe '#refresh_storage_size!' do + it 'recalculates storage size from its components and save it' do + statistics.update_columns( + repository_size: 2, + wiki_size: 4, + lfs_objects_size: 3, + snippets_size: 2, + pipeline_artifacts_size: 3, + build_artifacts_size: 3, + packages_size: 6, + uploads_size: 5, + + storage_size: 0 + ) + + expect { statistics.refresh_storage_size! }.to change { statistics.storage_size }.from(0).to(28) + end + end + describe '.increment_statistic' do shared_examples 'a statistic that increases storage_size' do it 'increases the statistic by that amount' do @@ -432,16 +451,15 @@ RSpec.describe ProjectStatistics do end end - it 'schedules a worker to update the statistic and storage_size async' do + it 'schedules a worker to update the statistic and storage_size async', :sidekiq_inline do expect(FlushCounterIncrementsWorker) .to receive(:perform_in) .with(CounterAttribute::WORKER_DELAY, described_class.name, statistics.id, stat) + .and_call_original - expect(FlushCounterIncrementsWorker) - .to receive(:perform_in) - .with(CounterAttribute::WORKER_DELAY, described_class.name, statistics.id, :storage_size) - - described_class.increment_statistic(project, stat, 20) + expect { described_class.increment_statistic(project, stat, 20) } + .to change { statistics.reload.send(stat) }.by(20) + .and change { statistics.reload.send(:storage_size) }.by(20) end end diff --git a/spec/policies/packages/policies/project_policy_spec.rb b/spec/policies/packages/policies/project_policy_spec.rb index 15c5942ea4d..5d54ee54572 100644 --- a/spec/policies/packages/policies/project_policy_spec.rb +++ b/spec/policies/packages/policies/project_policy_spec.rb @@ -33,55 +33,132 @@ RSpec.describe Packages::Policies::ProjectPolicy do end end - describe 'read_package' do - context 'with admin' do - let(:current_user) { admin } - - it { is_expected.to be_allowed(:read_package) } - - it_behaves_like 'package access with repository disabled' + describe 'read_package', :enable_admin_mode do + using RSpec::Parameterized::TableSyntax + + where(:project, :package_registry_access_level, :current_user, :expect_to_be_allowed) do + ref(:private_project) | ProjectFeature::DISABLED | ref(:anonymous) | false + ref(:private_project) | ProjectFeature::DISABLED | ref(:non_member) | false + ref(:private_project) | ProjectFeature::DISABLED | ref(:guest) | false + ref(:private_project) | ProjectFeature::DISABLED | ref(:reporter) | false + ref(:private_project) | ProjectFeature::DISABLED | ref(:developer) | false + ref(:private_project) | ProjectFeature::DISABLED | ref(:maintainer) | false + ref(:private_project) | ProjectFeature::DISABLED | ref(:owner) | false + ref(:private_project) | ProjectFeature::DISABLED | ref(:admin) | false + + ref(:private_project) | ProjectFeature::PRIVATE | ref(:anonymous) | false + ref(:private_project) | ProjectFeature::PRIVATE | ref(:non_member) | false + ref(:private_project) | ProjectFeature::PRIVATE | ref(:guest) | false + ref(:private_project) | ProjectFeature::PRIVATE | ref(:reporter) | true + ref(:private_project) | ProjectFeature::PRIVATE | ref(:developer) | true + ref(:private_project) | ProjectFeature::PRIVATE | ref(:maintainer) | true + ref(:private_project) | ProjectFeature::PRIVATE | ref(:owner) | true + ref(:private_project) | ProjectFeature::PRIVATE | ref(:admin) | true + + ref(:private_project) | ProjectFeature::PUBLIC | ref(:anonymous) | true + ref(:private_project) | ProjectFeature::PUBLIC | ref(:non_member) | true + ref(:private_project) | ProjectFeature::PUBLIC | ref(:guest) | true + ref(:private_project) | ProjectFeature::PUBLIC | ref(:reporter) | true + ref(:private_project) | ProjectFeature::PUBLIC | ref(:developer) | true + ref(:private_project) | ProjectFeature::PUBLIC | ref(:maintainer) | true + ref(:private_project) | ProjectFeature::PUBLIC | ref(:owner) | true + ref(:private_project) | ProjectFeature::PUBLIC | ref(:admin) | true + + ref(:internal_project) | ProjectFeature::DISABLED | ref(:anonymous) | false + ref(:internal_project) | ProjectFeature::DISABLED | ref(:non_member) | false + ref(:internal_project) | ProjectFeature::DISABLED | ref(:guest) | false + ref(:internal_project) | ProjectFeature::DISABLED | ref(:reporter) | false + ref(:internal_project) | ProjectFeature::DISABLED | ref(:developer) | false + ref(:internal_project) | ProjectFeature::DISABLED | ref(:maintainer) | false + ref(:internal_project) | ProjectFeature::DISABLED | ref(:owner) | false + ref(:internal_project) | ProjectFeature::DISABLED | ref(:admin) | false + + ref(:internal_project) | ProjectFeature::ENABLED | ref(:anonymous) | false + ref(:internal_project) | ProjectFeature::ENABLED | ref(:non_member) | true + ref(:internal_project) | ProjectFeature::ENABLED | ref(:guest) | true + ref(:internal_project) | ProjectFeature::ENABLED | ref(:reporter) | true + ref(:internal_project) | ProjectFeature::ENABLED | ref(:developer) | true + ref(:internal_project) | ProjectFeature::ENABLED | ref(:maintainer) | true + ref(:internal_project) | ProjectFeature::ENABLED | ref(:owner) | true + ref(:internal_project) | ProjectFeature::ENABLED | ref(:admin) | true + + ref(:internal_project) | ProjectFeature::PUBLIC | ref(:anonymous) | true + ref(:internal_project) | ProjectFeature::PUBLIC | ref(:non_member) | true + ref(:internal_project) | ProjectFeature::PUBLIC | ref(:guest) | true + ref(:internal_project) | ProjectFeature::PUBLIC | ref(:reporter) | true + ref(:internal_project) | ProjectFeature::PUBLIC | ref(:developer) | true + ref(:internal_project) | ProjectFeature::PUBLIC | ref(:maintainer) | true + ref(:internal_project) | ProjectFeature::PUBLIC | ref(:owner) | true + ref(:internal_project) | ProjectFeature::PUBLIC | ref(:admin) | true + + ref(:public_project) | ProjectFeature::DISABLED | ref(:anonymous) | false + ref(:public_project) | ProjectFeature::DISABLED | ref(:non_member) | false + ref(:public_project) | ProjectFeature::DISABLED | ref(:guest) | false + ref(:public_project) | ProjectFeature::DISABLED | ref(:reporter) | false + ref(:public_project) | ProjectFeature::DISABLED | ref(:developer) | false + ref(:public_project) | ProjectFeature::DISABLED | ref(:maintainer) | false + ref(:public_project) | ProjectFeature::DISABLED | ref(:owner) | false + ref(:public_project) | ProjectFeature::DISABLED | ref(:admin) | false + + ref(:public_project) | ProjectFeature::PUBLIC | ref(:anonymous) | true + ref(:public_project) | ProjectFeature::PUBLIC | ref(:non_member) | true + ref(:public_project) | ProjectFeature::PUBLIC | ref(:guest) | true + ref(:public_project) | ProjectFeature::PUBLIC | ref(:reporter) | true + ref(:public_project) | ProjectFeature::PUBLIC | ref(:developer) | true + ref(:public_project) | ProjectFeature::PUBLIC | ref(:maintainer) | true + ref(:public_project) | ProjectFeature::PUBLIC | ref(:owner) | true + ref(:public_project) | ProjectFeature::PUBLIC | ref(:admin) | true end - context 'with owner' do - let(:current_user) { owner } + with_them do + it do + project.project_feature.update!(package_registry_access_level: package_registry_access_level) - it { is_expected.to be_allowed(:read_package) } + if expect_to_be_allowed + is_expected.to be_allowed(:read_package) + else + is_expected.to be_disallowed(:read_package) + end + end end - context 'with maintainer' do - let(:current_user) { maintainer } - - it { is_expected.to be_allowed(:read_package) } + context 'with feature flag disabled' do + before do + stub_feature_flags(package_registry_access_level: false) + end + + where(:project, :current_user, :expect_to_be_allowed) do + ref(:private_project) | ref(:anonymous) | false + ref(:private_project) | ref(:non_member) | false + ref(:private_project) | ref(:guest) | false + ref(:internal_project) | ref(:anonymous) | false + ref(:public_project) | ref(:admin) | true + ref(:public_project) | ref(:owner) | true + ref(:public_project) | ref(:maintainer) | true + ref(:public_project) | ref(:developer) | true + ref(:public_project) | ref(:reporter) | true + ref(:public_project) | ref(:guest) | true + ref(:public_project) | ref(:non_member) | true + ref(:public_project) | ref(:anonymous) | true + end + + with_them do + it do + project.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC) + + if expect_to_be_allowed + is_expected.to be_allowed(:read_package) + else + is_expected.to be_disallowed(:read_package) + end + end + end end - context 'with developer' do - let(:current_user) { developer } - - it { is_expected.to be_allowed(:read_package) } - end - - context 'with reporter' do - let(:current_user) { reporter } - - it { is_expected.to be_allowed(:read_package) } - end - - context 'with guest' do - let(:current_user) { guest } - - it { is_expected.to be_allowed(:read_package) } - end - - context 'with non member' do - let(:current_user) { non_member } - - it { is_expected.to be_allowed(:read_package) } - end - - context 'with anonymous' do - let(:current_user) { anonymous } + context 'with admin' do + let(:current_user) { admin } - it { is_expected.to be_allowed(:read_package) } + it_behaves_like 'package access with repository disabled' end end end diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index afe5a7d4a21..401db766589 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -47,7 +47,7 @@ RSpec.describe API::ProjectImport, :aggregate_failures do it 'executes a limited number of queries' do control_count = ActiveRecord::QueryRecorder.new { subject }.count - expect(control_count).to be <= 109 + expect(control_count).to be <= 110 end it 'schedules an import using a namespace' do diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index fbedb69a640..b62fbaead6f 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -55,6 +55,18 @@ RSpec.describe API::Tags do expect(json_response.map { |tag| tag['name'] }).to eq(ordered_by_name) end + + it 'sorts by version in ascending order when requested' do + repository = project.repository + repository.add_tag(user, 'v1.2.0', repository.commit.id) + repository.add_tag(user, 'v1.10.0', repository.commit.id) + + get api("#{route}?order_by=version&sort=asc", current_user) + + ordered_by_version = VersionSorter.sort(project.repository.tags.map { |tag| tag.name }) + + expect(json_response.map { |tag| tag['name'] }).to eq(ordered_by_version) + end end context 'searching' do diff --git a/spec/requests/projects/merge_requests/context_commit_diffs_spec.rb b/spec/requests/projects/merge_requests/context_commit_diffs_spec.rb index bb087e8778a..c859e91e21a 100644 --- a/spec/requests/projects/merge_requests/context_commit_diffs_spec.rb +++ b/spec/requests/projects/merge_requests/context_commit_diffs_spec.rb @@ -63,21 +63,13 @@ RSpec.describe 'Merge Requests Context Commit Diffs' do let(:collection) { Gitlab::Diff::FileCollection::Compare } let(:expected_options) { collection_arguments } - before do - stub_feature_flags(remove_caching_diff_batches: true) - end - it_behaves_like 'serializes diffs with expected arguments' end context 'with caching', :use_clean_rails_memory_store_caching do - subject { go(page: 0, per_page: 5) } - - before do - stub_feature_flags(remove_caching_diff_batches: false) - end - context 'when the request has not been cached' do + subject { go(headers: { 'If-None-Match' => '' }, page: 0, per_page: 5) } + it_behaves_like 'serializes diffs with expected arguments' do let(:collection) { Gitlab::Diff::FileCollection::Compare } let(:expected_options) { collection_arguments } @@ -85,26 +77,18 @@ RSpec.describe 'Merge Requests Context Commit Diffs' do end context 'when the request has already been cached' do + subject { go(headers: { 'If-None-Match' => response.etag }, page: 0, per_page: 5) } + before do go(page: 0, per_page: 5) end it 'does not serialize diffs' do - expect_next_instance_of(PaginatedDiffSerializer) do |instance| - expect(instance).not_to receive(:represent) - end - - subject - end - - context 'when using ETags' do - it 'does not serialize diffs' do - expect(PaginatedDiffSerializer).not_to receive(:new) + expect(PaginatedDiffSerializer).not_to receive(:new) - go(headers: { 'If-None-Match' => response.etag }, page: 0, per_page: 5) + go(headers: { 'If-None-Match' => response.etag }, page: 0, per_page: 5) - expect(response).to have_gitlab_http_status(:not_modified) - end + expect(response).to have_gitlab_http_status(:not_modified) end context 'with the different user' do diff --git a/spec/requests/projects/merge_requests/diffs_spec.rb b/spec/requests/projects/merge_requests/diffs_spec.rb index c79173e7441..9f0b9a9cb1b 100644 --- a/spec/requests/projects/merge_requests/diffs_spec.rb +++ b/spec/requests/projects/merge_requests/diffs_spec.rb @@ -60,222 +60,147 @@ RSpec.describe 'Merge Requests Diffs' do let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch } let(:expected_options) { collection_arguments(total_pages: 20) } - before do - stub_feature_flags(remove_caching_diff_batches: true) - end - it_behaves_like 'serializes diffs with expected arguments' end context 'with caching', :use_clean_rails_memory_store_caching do subject { go(headers: headers, page: 0, per_page: 5) } - let(:headers) { {} } + let(:headers) { { 'If-None-Match' => response.etag } } before do - stub_feature_flags(remove_caching_diff_batches: false) + go(page: 0, per_page: 5) end - context 'when the request has not been cached' do - let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch } - let(:expected_options) { collection_arguments(total_pages: 20) } + it 'does not serialize diffs' do + expect(PaginatedDiffSerializer).not_to receive(:new) - it_behaves_like 'serializes diffs with expected arguments' - end + go(headers: headers, page: 0, per_page: 5) - context 'when the request has already been cached' do - before do - go(page: 0, per_page: 5) - end + expect(response).to have_gitlab_http_status(:not_modified) + end - it 'does not serialize diffs' do - expect_next_instance_of(PaginatedDiffSerializer) do |instance| - expect(instance).not_to receive(:represent) - end + context 'with the different user' do + let(:another_user) { create(:user) } + let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch } + let(:expected_options) { collection_arguments(total_pages: 20) } - subject + before do + project.add_maintainer(another_user) + sign_in(another_user) end - context 'when using ETags' do - let(:headers) { { 'If-None-Match' => response.etag } } - - it 'does not serialize diffs' do - expect(PaginatedDiffSerializer).not_to receive(:new) + it_behaves_like 'serializes diffs with expected arguments' + end - go(headers: headers, page: 0, per_page: 5) + context 'with a new unfoldable diff position' do + let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch } + let(:expected_options) { collection_arguments(total_pages: 20) } - expect(response).to have_gitlab_http_status(:not_modified) - end + let(:unfoldable_position) do + create(:diff_position) end - context 'with the different user' do - let(:another_user) { create(:user) } - let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch } - let(:expected_options) { collection_arguments(total_pages: 20) } - - before do - project.add_maintainer(another_user) - sign_in(another_user) - end - - it_behaves_like 'serializes diffs with expected arguments' - - context 'when using ETag caching' do - it_behaves_like 'serializes diffs with expected arguments' do - let(:headers) { { 'If-None-Match' => response.etag } } - end + before do + expect_next_instance_of(Gitlab::Diff::PositionCollection) do |instance| + expect(instance) + .to receive(:unfoldable) + .and_return([unfoldable_position]) end end - context 'with a new unfoldable diff position' do - let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch } - let(:expected_options) { collection_arguments(total_pages: 20) } - - let(:unfoldable_position) do - create(:diff_position) - end - - before do - expect_next_instance_of(Gitlab::Diff::PositionCollection) do |instance| - expect(instance) - .to receive(:unfoldable) - .and_return([unfoldable_position]) - end - end + it_behaves_like 'serializes diffs with expected arguments' + end - it_behaves_like 'serializes diffs with expected arguments' + context 'with disabled display_merge_conflicts_in_diff feature' do + let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch } + let(:expected_options) { collection_arguments(total_pages: 20).merge(allow_tree_conflicts: false) } - context 'when using ETag caching' do - it_behaves_like 'serializes diffs with expected arguments' do - let(:headers) { { 'If-None-Match' => response.etag } } - end - end + before do + stub_feature_flags(display_merge_conflicts_in_diff: false) end - context 'with disabled display_merge_conflicts_in_diff feature' do - let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch } - let(:expected_options) { collection_arguments(total_pages: 20).merge(allow_tree_conflicts: false) } + it_behaves_like 'serializes diffs with expected arguments' + end - before do - stub_feature_flags(display_merge_conflicts_in_diff: false) - end + context 'with diff_head option' do + subject { go(page: 0, per_page: 5, diff_head: true) } - it_behaves_like 'serializes diffs with expected arguments' + let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch } + let(:expected_options) { collection_arguments(total_pages: 20).merge(merge_ref_head_diff: true) } - context 'when using ETag caching' do - it_behaves_like 'serializes diffs with expected arguments' do - let(:headers) { { 'If-None-Match' => response.etag } } - end - end + before do + merge_request.create_merge_head_diff! end - context 'with diff_head option' do - subject { go(page: 0, per_page: 5, diff_head: true) } - - let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch } - let(:expected_options) { collection_arguments(total_pages: 20).merge(merge_ref_head_diff: true) } - - before do - merge_request.create_merge_head_diff! - end + it_behaves_like 'serializes diffs with expected arguments' + end - it_behaves_like 'serializes diffs with expected arguments' + context 'with the different pagination option' do + subject { go(page: 5, per_page: 5) } - context 'when using ETag caching' do - it_behaves_like 'serializes diffs with expected arguments' do - let(:headers) { { 'If-None-Match' => response.etag } } - end - end - end + let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch } + let(:expected_options) { collection_arguments(total_pages: 20) } - context 'with the different pagination option' do - subject { go(page: 5, per_page: 5) } + it_behaves_like 'serializes diffs with expected arguments' + end - let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch } - let(:expected_options) { collection_arguments(total_pages: 20) } + context 'with the different diff_view' do + subject { go(page: 0, per_page: 5, view: :parallel) } - it_behaves_like 'serializes diffs with expected arguments' + let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch } + let(:expected_options) { collection_arguments(total_pages: 20).merge(diff_view: :parallel) } - context 'when using ETag caching' do - it_behaves_like 'serializes diffs with expected arguments' do - let(:headers) { { 'If-None-Match' => response.etag } } - end - end - end + it_behaves_like 'serializes diffs with expected arguments' + end - context 'with the different diff_view' do - subject { go(page: 0, per_page: 5, view: :parallel) } + context 'with the different expanded option' do + subject { go(page: 0, per_page: 5, expanded: true ) } - let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch } - let(:expected_options) { collection_arguments(total_pages: 20).merge(diff_view: :parallel) } + let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch } + let(:expected_options) { collection_arguments(total_pages: 20) } - it_behaves_like 'serializes diffs with expected arguments' + it_behaves_like 'serializes diffs with expected arguments' + end - context 'when using ETag caching' do - it_behaves_like 'serializes diffs with expected arguments' do - let(:headers) { { 'If-None-Match' => response.etag } } - end - end - end + context 'with the different ignore_whitespace_change option' do + subject { go(page: 0, per_page: 5, w: 1) } - context 'with the different expanded option' do - subject { go(page: 0, per_page: 5, expanded: true ) } + let(:collection) { Gitlab::Diff::FileCollection::Compare } + let(:expected_options) { collection_arguments(total_pages: 20) } - let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch } - let(:expected_options) { collection_arguments(total_pages: 20) } + it_behaves_like 'serializes diffs with expected arguments' + end + end - it_behaves_like 'serializes diffs with expected arguments' + context 'when the paths is given' do + subject { go(headers: headers, page: 0, per_page: 5, paths: %w[README CHANGELOG]) } - context 'when using ETag caching' do - it_behaves_like 'serializes diffs with expected arguments' do - let(:headers) { { 'If-None-Match' => response.etag } } - end - end - end + before do + go(page: 0, per_page: 5, paths: %w[README CHANGELOG]) + end - context 'with the different ignore_whitespace_change option' do - subject { go(page: 0, per_page: 5, w: 1) } + context 'when using ETag caching' do + let(:headers) { { 'If-None-Match' => response.etag } } - let(:collection) { Gitlab::Diff::FileCollection::Compare } - let(:expected_options) { collection_arguments(total_pages: 20) } + it 'does not serialize diffs' do + expect(PaginatedDiffSerializer).not_to receive(:new) - it_behaves_like 'serializes diffs with expected arguments' + subject - context 'when using ETag caching' do - it_behaves_like 'serializes diffs with expected arguments' do - let(:headers) { { 'If-None-Match' => response.etag } } - end - end + expect(response).to have_gitlab_http_status(:not_modified) end end - context 'when the paths is given' do - subject { go(headers: headers, page: 0, per_page: 5, paths: %w[README CHANGELOG]) } + context 'when not using ETag caching' do + let(:headers) { {} } - before do - go(page: 0, per_page: 5, paths: %w[README CHANGELOG]) - end - - context 'when using ETag caching' do - let(:headers) { { 'If-None-Match' => response.etag } } - - it 'does not serialize diffs' do - expect(PaginatedDiffSerializer).not_to receive(:new) - - subject - - expect(response).to have_gitlab_http_status(:not_modified) - end - end - - context 'when not using ETag caching' do - it 'does not use cache' do - expect(Rails.cache).not_to receive(:fetch).with(/cache:gitlab:PaginatedDiffSerializer/).and_call_original + it 'does not use cache' do + expect(Rails.cache).not_to receive(:fetch).with(/cache:gitlab:PaginatedDiffSerializer/).and_call_original - subject + subject - expect(response).to have_gitlab_http_status(:success) - end + expect(response).to have_gitlab_http_status(:success) end end end diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb index 74684bc05ce..4922e72b7a4 100644 --- a/spec/services/notes/create_service_spec.rb +++ b/spec/services/notes/create_service_spec.rb @@ -406,9 +406,9 @@ RSpec.describe Notes::CreateService do expect(issuable.draft?).to eq(can_use_quick_action) } ), - # Remove draft status + # Remove draft (set ready) status QuickAction.new( - action_text: "/draft", + action_text: "/ready", before_action: -> { issuable.reload.update!(title: "Draft: title") }, diff --git a/spec/services/projects/blame_service_spec.rb b/spec/services/projects/blame_service_spec.rb index 54c4315d242..52b0ed3412d 100644 --- a/spec/services/projects/blame_service_spec.rb +++ b/spec/services/projects/blame_service_spec.rb @@ -54,6 +54,12 @@ RSpec.describe Projects::BlameService, :aggregate_failures do it { is_expected.to eq(1..2) } end + context 'when user disabled the pagination' do + let(:params) { super().merge(no_pagination: 1) } + + it { is_expected.to be_nil } + end + context 'when feature flag disabled' do before do stub_feature_flags(blame_page_pagination: false) @@ -75,6 +81,12 @@ RSpec.describe Projects::BlameService, :aggregate_failures do expect(subject.total_count).to eq(4) end + context 'when user disabled the pagination' do + let(:params) { super().merge(no_pagination: 1) } + + it { is_expected.to be_nil } + end + context 'when feature flag disabled' do before do stub_feature_flags(blame_page_pagination: false) diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index edde2e0a247..9c8aeb5cf7b 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -125,6 +125,26 @@ RSpec.describe Projects::CreateService, '#execute' do expect(project.namespace).to eq(user.namespace) expect(project.project_namespace).to be_in_sync_with_project(project) end + + context 'project_authorizations record creation' do + context 'when the project_authrizations records are not created via the callback' do + it 'still creates project_authrizations record for the user' do + # stub out the callback that creates project_authorizations records on the `ProjectMember` model. + expect_next_instance_of(ProjectMember) do |member| + expect(member).to receive(:refresh_member_authorized_projects).and_return(nil) + end + + project = create_project(user, opts) + + expected_record = project.project_authorizations.where( + user: user, + access_level: ProjectMember::OWNER + ) + + expect(expected_record).to exist + end + end + end end describe 'after create actions' do diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb index 2d38d968ce4..a43f3bc55bf 100644 --- a/spec/services/quick_actions/interpret_service_spec.rb +++ b/spec/services/quick_actions/interpret_service_spec.rb @@ -1393,14 +1393,41 @@ RSpec.describe QuickActions::InterpretService do let(:issuable) { issue } end + # /draft is a toggle (ff disabled) it_behaves_like 'draft command' do let(:content) { '/draft' } let(:issuable) { merge_request } + + before do + stub_feature_flags(draft_quick_action_non_toggle: false) + end end + # /draft is a toggle (ff disabled) it_behaves_like 'ready command' do let(:content) { '/draft' } let(:issuable) { merge_request } + + before do + stub_feature_flags(draft_quick_action_non_toggle: false) + issuable.update!(title: issuable.draft_title) + end + end + + # /draft is one way (ff enabled) + it_behaves_like 'draft command' do + let(:content) { '/draft' } + let(:issuable) { merge_request } + end + + # /draft is one way (ff enabled) + it_behaves_like 'draft/ready command no action' do + let(:content) { '/draft' } + let(:issuable) { merge_request } + + before do + issuable.update!(title: issuable.draft_title) + end end it_behaves_like 'draft/ready command no action' do @@ -2646,7 +2673,28 @@ RSpec.describe QuickActions::InterpretService do end end - describe 'draft command' do + describe 'draft command toggle (deprecated)' do + let(:content) { '/draft' } + + before do + stub_feature_flags(draft_quick_action_non_toggle: false) + end + + it 'includes the new status' do + _, explanations = service.explain(content, merge_request) + + expect(explanations).to match_array(['Marks this merge request as a draft.']) + end + + it 'sets the ready status on a draft' do + merge_request.update!(title: merge_request.draft_title) + _, explanations = service.explain(content, merge_request) + + expect(explanations).to match_array(["Marks this merge request as ready."]) + end + end + + describe 'draft command set' do let(:content) { '/draft' } it 'includes the new status' do @@ -2654,6 +2702,13 @@ RSpec.describe QuickActions::InterpretService do expect(explanations).to match_array(['Marks this merge request as a draft.']) end + + it 'includes the no change message when status unchanged' do + merge_request.update!(title: merge_request.draft_title) + _, explanations = service.explain(content, merge_request) + + expect(explanations).to match_array(["No change to this merge request's draft status."]) + end end describe 'ready command' do diff --git a/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb b/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb index ad0bbc0aeff..b81bd514d0a 100644 --- a/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb +++ b/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb @@ -26,9 +26,6 @@ RSpec.shared_examples 'UpdateProjectStatistics' do |with_counter_attribute| expect(FlushCounterIncrementsWorker) .to receive(:perform_in) .with(CounterAttribute::WORKER_DELAY, project.statistics.class.name, project.statistics.id, project_statistics_name) - expect(FlushCounterIncrementsWorker) - .to receive(:perform_in) - .with(CounterAttribute::WORKER_DELAY, project.statistics.class.name, project.statistics.id, :storage_size) yield |